Compare commits

...

175 Commits

Author SHA1 Message Date
李玉
6b2e9b51d5
update README.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-12-08 06:11:03 +00:00
李玉
3502b6e878
update README.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-12-01 08:25:48 +00:00
李玉
cf761aea98
update README.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-11-24 03:36:12 +00:00
wang
59aeae7f5a
!17 修复AdminApiRouteGenerator类路径变量未正确解析的问题
Merge pull request !17 from Codyyuan/fix-route-generator-bug
2025-11-22 08:12:29 +00:00
yuanhao
3c2609221b 修复AdminApiRouteGenerator类路径变量未正确解析的问题 2025-11-20 15:12:26 +08:00
李玉
cdd869f49c
update README.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-11-18 02:17:48 +00:00
全栈小学生
cf293a1dec up 2025-11-14 11:39:40 +08:00
李玉
81bb4682d5
add upgrade.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-11-13 07:52:44 +00:00
李玉
2168a82f8b
update README.md.
Signed-off-by: 李玉 <16193307+li-yu125521@user.noreply.gitee.com>
2025-11-13 06:56:37 +00:00
CQ
b4563d004b 同步niucloud 2025-11-13 09:54:45 +08:00
CQ
ef43d9e616 同步admin 2025-11-13 09:44:49 +08:00
CQ
16a8825962 同步uni-app 2025-11-13 09:42:44 +08:00
CQ
57d110237b fix 2025-10-31 11:38:10 +08:00
CQ
567eea9d1c feat 2025-10-31 10:40:23 +08:00
CQ
a5390d475b feat 2025-10-31 10:40:06 +08:00
CQ
f1a9688f47 fix 2025-10-31 09:28:10 +08:00
CQ
9e5c053d76 fix 2025-10-31 09:22:32 +08:00
CQ
8cb683f8fe fix admin 2025-10-31 09:22:14 +08:00
CQ
08d14372e1 fix ttf 2025-10-24 17:28:23 +08:00
CQ
17b26f3f52 fix ttl 2025-10-24 17:26:57 +08:00
CQ
d4001f8109 fix 2025-10-16 09:31:28 +08:00
CQ
ca75099899 fix niucloud 2025-10-15 18:13:44 +08:00
CQ
e879cf0b7a fix uni-app 2025-10-15 18:07:46 +08:00
CQ
231f476a6f fix admin 2025-10-15 18:07:03 +08:00
LY999888
4282130387 Update README.md 2025-09-25 11:47:19 +08:00
全栈小学生
b6677ff3f3 up 2025-09-23 16:04:29 +08:00
全栈小学生
585ceba4be up 2025-09-13 16:21:10 +08:00
全栈小学生
e89397c648 Update pages.json 2025-09-13 16:20:50 +08:00
全栈小学生
5f9ff5bfbe up 2025-09-13 16:20:30 +08:00
全栈小学生
934d7c6dba up 2025-09-13 10:33:06 +08:00
全栈小学生
2ed6fbae02 up 2025-09-13 10:32:31 +08:00
全栈小学生
29213bc600 up 2025-09-13 10:30:14 +08:00
全栈小学生
d497fdcc30 up 2025-09-13 10:26:51 +08:00
全栈小学生
7900632322 up 2025-09-13 10:26:31 +08:00
全栈小学生
8bf3968007 up 2025-09-13 10:26:07 +08:00
全栈小学生
5b0dfeee91 up 2025-09-13 10:24:40 +08:00
niucloud
804537dc10
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-09-13 01:57:51 +00:00
niucloud
9cf7ea02a9
update docker-compose/README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-09-10 07:14:24 +00:00
niucloud
31bdf530c3
update .gitignore.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-09-10 07:13:03 +00:00
niucloud
46758e52b6
update LICENSE.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-09-10 07:12:36 +00:00
niucloud
65769b4957
删除文件 README.en.md 2025-09-10 07:12:11 +00:00
niucloud
58dd67bc25
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-09-10 07:08:59 +00:00
CQ
4a7880532a fix update README.md 2025-08-25 09:19:47 +08:00
CQ
50c2be1485 up 2025-08-23 17:13:15 +08:00
CQ
195cea7bd4 up uni-app 2025-08-23 17:12:25 +08:00
CQ
0022acba80 up niucloud 2025-08-23 17:10:48 +08:00
CQ
fd7f31e40a up 2025-08-23 17:06:12 +08:00
官方售后
1edbfb2a1f
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-08-12 03:26:54 +00:00
官方售后
b9e55fa6ba
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-08-08 03:57:31 +00:00
官方售后
11d24f9110
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-07-21 09:43:50 +00:00
CQ
ca914dde7f fix public web 2025-07-21 09:50:24 +08:00
CQ
4f117c3f5e fix public static wap 2025-07-21 09:49:32 +08:00
CQ
6a34c905e5 fix public admin 2025-07-21 09:46:27 +08:00
CQ
efb93e0e80 fix 2025-07-21 09:34:04 +08:00
CQ
12369907f6 fix web 2025-07-21 09:31:42 +08:00
CQ
c763d20b6d fix uni-app 2025-07-21 09:31:14 +08:00
CQ
34e68e2e70 fix 2025-07-21 09:29:10 +08:00
官方售后
4e2c6f80b5
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-07-14 07:49:30 +00:00
niucloud
4b03b1ea8f
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-07-07 08:10:09 +00:00
门海潮
188d5512f2 Update README.md 2025-07-01 16:04:16 +08:00
门海潮
0312f7aeef Updates README.md
Auto commit by GitBook Editor
2025-06-23 15:38:28 +08:00
门海潮
3b10e8ab14 Updates README.md
Auto commit by GitBook Editor
2025-06-23 14:35:41 +08:00
门海潮
613c7876f6 Updates README.md
Auto commit by GitBook Editor
2025-06-23 14:31:56 +08:00
门海潮
bc1eecbbd7 Updates README.md
Auto commit by GitBook Editor
2025-06-23 12:29:43 +08:00
门海潮
e46a615372 Updates README.md
Auto commit by GitBook Editor
2025-06-23 12:10:39 +08:00
门海潮
6b51fe2253 Updates README.md
Auto commit by GitBook Editor
2025-06-23 11:46:52 +08:00
门海潮
5010cb5d28 Updates README.md
Auto commit by GitBook Editor
2025-06-23 11:02:30 +08:00
全栈小学生
e87cf1b9d7 up 2025-06-20 17:30:14 +08:00
全栈小学生
1bf33cb02b up 2025-06-20 09:26:49 +08:00
全栈小学生
40184ead35 up 2025-06-20 09:26:11 +08:00
全栈小学生
550fd44b23 up 2025-06-20 09:25:43 +08:00
全栈小学生
0bc687481f up 2025-06-20 09:24:40 +08:00
全栈小学生
9b76843a1e up 2025-06-20 09:23:06 +08:00
全栈小学生
8dda9f6989 up 2025-06-20 09:22:36 +08:00
全栈小学生
d8f1246d44 Update index.vue 2025-06-20 09:19:22 +08:00
全栈小学生
e927a7cf8c up 2025-06-20 09:18:51 +08:00
官方售后
0e6dac960d
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-06-09 03:32:07 +00:00
niucloud
60e285d923
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:10:36 +00:00
niucloud
7245ea6150
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:08:53 +00:00
niucloud
dd552d2cfa
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:08:27 +00:00
niucloud
be9676343d
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:06:09 +00:00
niucloud
37c8edce09
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:05:51 +00:00
niucloud
d038c65773
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:04:29 +00:00
niucloud
20d70f63ce
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 11:01:25 +00:00
niucloud
3b9623b3b6
update README.md.
Signed-off-by: niucloud <niucloud@outlook.com>
2025-06-05 10:59:44 +00:00
全栈小学生
f3a0db8a56 up 2025-05-30 17:14:54 +08:00
全栈小学生
a4fdcce61a Update UpgradeService.php 2025-05-30 11:11:10 +08:00
全栈小学生
bccb4ff5c4 up 2025-05-30 10:09:11 +08:00
全栈小学生
d4a5a0538a Update system.ts 2025-05-30 10:06:24 +08:00
全栈小学生
ce11b02971 up 2025-05-30 09:50:46 +08:00
全栈小学生
680441cecc up 2025-05-30 09:50:14 +08:00
全栈小学生
568269b6a7 Update publish.cjs 2025-05-30 09:48:00 +08:00
全栈小学生
1ae60052fe Update README.md 2025-05-30 09:47:47 +08:00
全栈小学生
1440a81fea up 2025-05-30 09:46:17 +08:00
全栈小学生
faa8ab97bf up 2025-05-30 09:46:04 +08:00
全栈小学生
074aac0423 up 2025-05-30 09:45:43 +08:00
全栈小学生
f32273e61e up 2025-05-30 09:44:51 +08:00
全栈小学生
bd1dc03968 Update ueditor.css 2025-05-30 09:44:16 +08:00
全栈小学生
9a4d22fccc Update components.d.ts 2025-05-30 09:43:27 +08:00
官方售后
12dbe3fad7
update README.md.
Signed-off-by: 官方售后 <1412681764@qq.com>
2025-05-28 01:44:37 +00:00
全栈小学生
3e2649339d Update version.php 2025-05-19 11:16:22 +08:00
全栈小学生
7e010a3074 Create tools_update_cache.png 2025-04-21 14:36:50 +08:00
全栈小学生
77fd469ab4 Delete tools_Update_cache.png 2025-04-21 14:36:28 +08:00
全栈小学生
c79501bd6a up 2025-04-19 14:44:57 +08:00
全栈小学生
88ba3a410f Update index.vue 2025-04-19 14:42:37 +08:00
全栈小学生
fcd3ec6ae1 Update CoreMenuService.php 2025-04-19 10:55:19 +08:00
全栈小学生
8e4464ccea up 2025-04-19 10:46:34 +08:00
全栈小学生
a943c22556 up 2025-04-19 10:45:50 +08:00
全栈小学生
99437ed472 up code 2025-04-19 10:44:40 +08:00
全栈小学生
0964fcfe43 up 2025-04-19 10:43:09 +08:00
全栈小学生
c19349e588 Update README.md 2025-04-19 10:42:08 +08:00
全栈小学生
c5bd55ed63 Update index.vue 2025-04-19 10:41:53 +08:00
全栈小学生
3fe60374bc up 2025-04-19 10:41:41 +08:00
全栈小学生
a602c4645b Update publish.cjs 2025-04-19 10:41:06 +08:00
全栈小学生
6f941fab4d up 2025-04-19 10:40:44 +08:00
全栈小学生
8fa97dd4a7 up 2025-04-19 10:38:30 +08:00
全栈小学生
a9de59b00a up 2025-03-14 09:37:46 +08:00
全栈小学生
6ee13de48e Update Upgrade.php 2025-03-10 15:38:46 +08:00
全栈小学生
837057ace6 up 2025-03-10 14:16:12 +08:00
全栈小学生
8b945bbf21 up 2025-03-10 14:15:35 +08:00
全栈小学生
2480a21fee up 2025-03-10 14:15:28 +08:00
全栈小学生
afc88d510d up 2025-03-10 14:15:21 +08:00
全栈小学生
1b06e9a04c up 2025-03-10 14:14:51 +08:00
全栈小学生
582c7a832d up 2025-03-10 14:09:09 +08:00
全栈小学生
b021bc6660 up 2025-03-10 14:07:44 +08:00
全栈小学生
6648951a28 up 2025-02-17 18:36:32 +08:00
全栈小学生
44e7ce9a5d
update uni-app/src/app/pages/index/close.vue.
Signed-off-by: 全栈小学生 <1518079521@qq.com>
2025-01-21 01:38:48 +00:00
全栈小学生
1f9dcda153
update uni-app/src/utils/request.ts.
Signed-off-by: 全栈小学生 <1518079521@qq.com>
2025-01-21 01:38:11 +00:00
全栈小学生
bd1279bb97
update uni-app/src/pages.json.
Signed-off-by: 全栈小学生 <1518079521@qq.com>
2025-01-21 01:37:45 +00:00
全栈小学生
f65e2d1ff8 up 2025-01-17 19:46:41 +08:00
全栈小学生
982213b49a up 2025-01-17 19:46:19 +08:00
全栈小学生
962288cc81 up 2025-01-17 19:45:58 +08:00
全栈小学生
9ab7b8da67 up 2025-01-17 19:45:39 +08:00
全栈小学生
9c829c2d1b up 2025-01-17 19:45:12 +08:00
全栈小学生
c9d2b78828 up 2025-01-17 19:44:38 +08:00
全栈小学生
2e643290e4 up 2025-01-17 19:44:10 +08:00
全栈小学生
9f058e2702 up 2025-01-17 19:43:35 +08:00
全栈小学生
eb61e38612 up 2025-01-17 19:42:15 +08:00
全栈小学生
e3df26a532 up 2024-12-21 18:43:00 +08:00
全栈小学生
e978947715 up 2024-12-21 18:42:42 +08:00
全栈小学生
719e0fb513 Update database.sql 2024-12-21 18:41:41 +08:00
全栈小学生
a1ca5d64cc up 2024-12-21 18:41:00 +08:00
全栈小学生
a3a9995e59 up 2024-12-21 18:40:35 +08:00
全栈小学生
d792963b14 up 2024-12-21 18:40:00 +08:00
全栈小学生
0b5b0de1db up 2024-12-21 18:39:12 +08:00
全栈小学生
4b78c8b6dd up 2024-12-04 14:37:20 +08:00
全栈小学生
70eae71e17 up 2024-12-04 14:35:53 +08:00
全栈小学生
b0e443d299 Update Config.php 2024-12-04 14:35:24 +08:00
全栈小学生
b53870492c Update Config.php 2024-12-04 14:18:37 +08:00
全栈小学生
25339f3ba5 up 2024-12-04 14:10:00 +08:00
全栈小学生
cc23fe0a31 up 2024-12-04 14:09:34 +08:00
全栈小学生
ec90e6f9b2 up 2024-12-04 14:07:49 +08:00
全栈小学生
971f078abc Update .htaccess 2024-11-18 09:11:02 +08:00
全栈小学生
c012b18982 up 2024-11-16 14:36:26 +08:00
全栈小学生
2dce1fe5a7 up 2024-11-16 14:36:04 +08:00
全栈小学生
7088f5add1 Update package-lock.json 2024-11-16 14:35:44 +08:00
全栈小学生
f695425554 Update .htaccess 2024-11-16 14:35:24 +08:00
全栈小学生
686d92cc4c wap 2024-11-16 14:35:09 +08:00
全栈小学生
390a5a2fc8 admin 2024-11-16 14:34:54 +08:00
全栈小学生
1541a473ae up 2024-11-16 14:34:28 +08:00
全栈小学生
493a6a6286 up 2024-11-16 14:33:50 +08:00
全栈小学生
5086c3b49e Update package-lock.json 2024-11-16 14:33:00 +08:00
全栈小学生
80e16b16f8 up 2024-11-16 14:32:37 +08:00
全栈小学生
087069d17e Delete admin.zip 2024-10-31 15:39:10 +08:00
全栈小学生
ffac07e5d0 up 2024-10-31 15:36:07 +08:00
全栈小学生
79d5170ecc up 2024-10-31 15:35:28 +08:00
全栈小学生
8c0e4ad87a up 2024-10-31 15:35:03 +08:00
全栈小学生
bae138de57 update 2024-10-31 15:29:27 +08:00
全栈小学生
ede5f226e0 update uni-app 2024-10-31 15:28:26 +08:00
全栈小学生
d01a343c56 Update package-lock.json 2024-10-31 15:27:12 +08:00
全栈小学生
8cc61e4d6e update 2024-10-31 15:26:50 +08:00
全栈小学生
e492f37467 Update package.json 2024-10-31 15:25:06 +08:00
wangchen147
8daa5277ae Merge branch 'master' of https://gitee.com/niucloud-team/niucloud 2024-09-18 11:00:40 +08:00
wangchen147
00b8dbd9b6 添加addon目录 2024-09-18 11:00:38 +08:00
wangchen147
b66ef73d10 1 2024-09-18 10:57:57 +08:00
3565 changed files with 114540 additions and 38251 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ bin-release/
# should NOT be excluded as they contain compiler settings and other important
# information for Eclipse / Flash Builder.
/.idea
/upgrade
niucloud/public/.htaccess

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2023 niucloud-admin
Copyright (c) 2025 niucloud-admin
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View File

@ -1,36 +0,0 @@
# niucloud-admin
#### Description
{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

View File

@ -2,9 +2,9 @@
### NIUCLOUD-ADMIN 是什么?
niucloud-admin-saas是一款快速开发通用管理后台框架整体功能架构全部精心设计代码干净整洁低耦合高质量前后端API接口完全分离 :raised_hands: !!!前端采用最新技术 **Vite+TypeScript+Vue3+ElementPlus** 后台采用PHP8、MYSQL8、THINKPHP8 全部最新技术栈内置Workman高性能消息队列计划任务处理完全兼容容器路由运行技术。 采用多租户多站点多应用多插件任意组合使用的SAAS架构设计内置代码生成器插件生成器一键云编译、一键云部署集成用户权限、表单设计、云存储、短信发送、素材中心、微信及公众号、Api模块一系列开箱即用功能是一款快速搭建开发企业级应用的软件系统。源码100%开源无加密框架采用MIT协议终身免费商用免费
目前已经有 NIUCLOUD **SHOP商城** + **分销** + **VIPCard** + **上门服务** 插件。 更多应用插件正在陆续上线中... :clap: :clap: :clap:
目前NIUCLOUD生态开发者入驻近千人各行业应用插件正在陆续上线中... :clap: :clap: :clap:
请到官方网站了解更多 http://www.niucloud.com
请到官方网站了解更多 https://www.niucloud.com
### NIUCLOUD-ADMIN 技术特点
@ -18,36 +18,37 @@ niucloud-admin-saas是一款快速开发通用管理后台框架整体功能
- niucloud-admin已经搭建好常规系统的开发底层具体的底层功能包括管理员管理权限管理网站设置计划任务管理素材管理会员管理会员账户管理微信公众号以及小程序管理支付管理第三方登录管理消息管理短信管理文章管理前端装修等全面的基础功能这样开发者不需要开发基础的结构而专心开发业务。
- niucloud-admin系统内置支持微信/支付宝支付,微信公众号/小程序/短信消息管理,阿里云/腾讯云短信,七牛云/阿里云存储等基础的功能扩展,后续会根据实际业务不断扩展基础组件。
- niucloud-admin结合系统结构特点专门开发了代码生成器这样开发者根据数据表就可以一键生成基础的业务代码包括后台php业务代码以及对应的前端vue代码。
- 手机端设计开发了自定义装修,同时提供了基础的开发组件,方便开发者设计开发手机自定义页面装修的开发需求
- niucloud-admin手机端设计开发了自定义装修同时提供了基础的开发组件方便开发者设计开发手机自定义页面装修的开发需求
- niucloud-admin框架支持APP在线一键云打包升级功能2025/9/10已上线
```
### NIUCLOUD 框架截图
![输入图片说明](https://media.niucloud.com/1704066345d7742c4c0a1a941e836e8d633f209396_aliyun.jpg)
![输入图片说明](https://media.niucloud.com/17040664219c6ce47b234eac495c3c4aa6e83920b6_aliyun.png)
![输入图片说明](https://media.niucloud.com/17040665085ed007bd6daf220cc1ecd4e301c6008f_aliyun.png)
![输入图片说明](https://media.niucloud.com/1704066609bad8bba9c5bfe5a243f5ebcec54866af_aliyun.png)
![输入图片说明](https://media.niucloud.com/170406669739221755c392e165470c486d0025ad17_aliyun.png)
![输入图片说明](https://media.niucloud.com/1704067302721c5360c622b4934fae659ee1eb0987_aliyun.png)
![](https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas/saas_kj1.png)
![](https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas/saas_kj2.png)
![](https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas/31f58ff2ca4c1b8421b4ad622921dfed.png)
![](https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas/saas_kj4.png)
### 商城插件 :point_right: SHOP
![输入图片说明](https://media.niucloud.com/1704067355ecae8e7b09c482e8c3a5ebc1a4cd0fcc_aliyun.png)
![输入图片说明](https://media.niucloud.com/1711597084402f1f74c47080b20c7feb4166b2ab2a_aliyun.png)
![](https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas/saas_shop1.png)
<table>
<tbody>
<tr>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop3.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop7.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop9.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop11.jpg"></td>
</tr>
<tr>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop6.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop10.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop12.jpg"></td>
<td><img src="https://niucloud-bucket.oss-cn-beijing.aliyuncs.com/gitee_saas/saas_shop13.jpg"></td>
</tr>
</tbody></table>
### 分销插件 :point_right: FENXIAO
![输入图片说明](https://media.niucloud.com/1705744442d046875f3a6356e20b4201683c2f08fe_aliyun.png)
![输入图片说明](https://media.niucloud.com/17057444230b1a32898072457faa8888b77d9621ff_aliyun.png)
![输入图片说明](https://media.niucloud.com/17057444220f55b9430e07e635968a843bcc3e2845_aliyun.png)
### 上门服务插件 :point_right: O2O
![输入图片说明](https://media.niucloud.com/1705739434bf20bb824783fcee9eea6361f3cfb7aa_aliyun.png)
![输入图片说明](https://media.niucloud.com/1705739434483707b8617e0e1489d7d2f51cda111c_aliyun.png)
![输入图片说明](https://media.niucloud.com/17057394343bdd84db9c1fa966c7cb0c03c5618ad9_aliyun.png)
### 会员卡插件 :point_right: VIPCard
![输入图片说明](https://media.niucloud.com/1704067355ecae8e7b09c482e8c3a5ebc1a4cd0fcc_aliyun.png)
### 旅游管理插件 :point_right: TOURS
![输入图片说明](https://media.niucloud.com/17057440675004b59c88adaebcb492a31d3a909944_aliyun.png)
![输入图片说明](https://media.niucloud.com/1705744067bad60e348b4dd479d223aa5de963a1bf_aliyun.png)
### 操作指南
@ -57,7 +58,7 @@ niucloud-admin-saas是一款快速开发通用管理后台框架整体功能
| [二开手册](https://www.niucloud.com/doc)
| [开发视频](https://www.niucloud.com/doc)
| [API接口手册](https://api.niucloud.com/apidoc.html?target_id=001)
| [论坛地址](https://bbs.niucloud.com)
| [论坛地址](https://niucloud.com/bbs)
### 二次开发视频教程
@ -73,14 +74,21 @@ niucloud-admin-saas是一款快速开发通用管理后台框架整体功能
### 演示地址
- 站点后台演示网址:[<a href='http://demo-saas.site.niucloud.com/site/' target="_blank"> 查看 </a>]
<a href='http://demo-saas.site.niucloud.com/site/' target="_blank">http://demo-saas.site.niucloud.com/site/
旅游系统账号tourtest 密码123456<br/>
商城系统账号shoptest 密码123456<br/>
会员卡系统账号cardtest 密码123456<br/>
- 平台后台演示网址:[<a href='http://demo-saas.site.niucloud.com/admin/' target="_blank"> 查看 </a>]
<a href='http://demo-saas.site.niucloud.com/admin/' target="_blank">http://demo-saas.site.niucloud.com/admin/ 账号admin 密码123456
- 商城后台演示网址:[<a href='https://demo-saas.site.niucloud.com/site/shop/index' target="_blank"> 查看 </a>]
<a href='https://demo-saas.site.niucloud.com/site/shop/index' target="_blank">http://demo-saas.site.niucloud.com/site/ 商城系统账号admin 密码123456<br/>
- 旅游后台演示网址:[<a href='https://demo-saas.site.niucloud.com/site/tourism/index' target="_blank"> 查看 </a>]
<a href='https://demo-saas.site.niucloud.com/site/tourism/index' target="_blank">https://demo-saas.site.niucloud.com/site/tourism/index 旅游系统账号admin 密码123456<br/>
- 会员卡后台演示网址:[<a href='https://demo-saas.site.niucloud.com/site/vipcard/index' target="_blank"> 查看 </a>]
<a href='https://demo-saas.site.niucloud.com/site/vipcard/index' target="_blank">https://demo-saas.site.niucloud.com/site/vipcard/index 会员卡系统账号admin 密码123456<br/>
### 加入NIUCLOUD开发者生态一起助力成就程序员创业梦想
加入企业微信群技术交流,请扫描下面二维码 :point_down:
@ -108,7 +116,7 @@ niucloud-admin-saas是一款快速开发通用管理后台框架整体功能
6.一切事物有个人喜好的标准,本开源代码意在分享,不喜勿喷。
![输入图片说明](https://www.niucloud.com/img/readme/%E9%A1%B6%E9%83%A8%E5%B9%BF%E5%91%8A1.jpg)
### 版权信息
@ -118,5 +126,4 @@ All rights reserved。
杭州数字云动科技有限公司
杭州牛之云科技有限公司
提供技术支持

143
admin/DEVELOPMENT_GUIDE.md Normal file
View File

@ -0,0 +1,143 @@
# NiuCloud Admin 开发规范
## 技术栈区分
本项目采用前后端分离架构,包含两个主要前端部分:
1. **PC端后台管理系统**
- 框架: Vue 3 + TypeScript + Vite
- UI组件库: Element Plus
- 状态管理: Pinia
- 样式处理: SCSS + Tailwind CSS
2. **移动端应用**
- 框架: uni-app + Vue 3 + TypeScript
- UI组件库: uview-plus
- 状态管理: Pinia
- 样式处理: SCSS + Windi CSS
## 关键组件使用规范
### 消息提示组件
**重要注意事项:请根据开发平台选择正确的消息提示组件!**
#### PC端 (admin目录)
- **必须使用Element Plus的消息提示组件**而不是uni-app的方法
- 主要组件包括:`ElMessage``ElMessageBox``ElNotification`
- 导入方式:`import { ElMessage, ElMessageBox } from 'element-plus'`
- 使用示例:
```typescript
import { ElMessage } from 'element-plus'
// 成功消息
ElMessage.success('操作成功')
// 错误消息
ElMessage.error('操作失败')
// 确认对话框
ElMessageBox.confirm('确定要执行此操作吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 用户点击确认后的逻辑
}).catch(() => {
// 用户点击取消后的逻辑
})
```
#### 移动端 (uni-app目录)
- **使用uni-app提供的API**进行消息提示
- 主要方法包括:`uni.showToast``uni.showModal``uni.showLoading`
- 使用示例:
```typescript
// 成功提示
uni.showToast({
title: '操作成功',
icon: 'success',
duration: 2000
})
// 模态对话框
uni.showModal({
title: '提示',
content: '确定要执行此操作吗?',
success: (res) => {
if (res.confirm) {
// 用户点击确认后的逻辑
}
}
})
```
## API请求规范
### PC端API请求
- 使用`@/utils/request.ts`封装的请求工具
- 支持`showSuccessMessage``showErrorMessage`选项控制消息显示
- 示例:
```typescript
import request from '@/utils/request'
// GET请求
export function getOrderList(params: Record<string, any>) {
return request.get('order/list', params)
}
// POST请求带成功消息
export function createOrder(params: Record<string, any>) {
return request.post('order/create', params, { showSuccessMessage: true })
}
```
### 移动端API请求
- 使用uni-app的`uni.request`或封装的请求工具
- 示例:
```typescript
// 发送请求
uni.request({
url: 'https://example.com/api/order/list',
method: 'GET',
data: {
page: 1,
limit: 10
},
success: (res) => {
// 处理成功响应
},
fail: (err) => {
// 处理请求失败
}
})
```
## 代码风格规范
1. **文件命名**
- 组件文件PascalCase`OrderList.vue`
- 普通文件kebab-case 或 camelCase`api-service.ts``commonUtils.ts`
2. **TypeScript规范**
- 为函数参数、返回值和重要变量添加明确的类型注解
- 使用接口 (interface) 定义复杂数据结构
- 避免 `any` 类型的滥用
3. **Vue组件规范**
- 使用 Vue 3 Composition API 和 `<script setup lang="ts">` 语法
- 组件样式建议使用 scoped 属性或 CSS Modules
## 国际化规范
- PC端使用Vue I18n进行国际化语言文件位于`src/lang`目录
- 移动端同样使用Vue I18n语言文件位于`src/app/locale`目录
- 使用`t('key')`函数获取翻译文本
## 其他重要规范
- 严格遵循RESTful API设计规范
- 统一处理API响应数据和错误情况
- 代码提交前确保通过TypeScript类型检查
- 组件开发遵循高内聚低耦合原则
- 优先复用现有组件和工具函数

View File

@ -1,5 +1,7 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
const ElMessage: typeof import('element-plus/es')['ElMessage']
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
const ElNotification: typeof import('element-plus/es')['ElNotification']
}

10
admin/components.d.ts vendored
View File

@ -9,6 +9,7 @@ declare module '@vue/runtime-core' {
export interface GlobalComponents {
Attachment: typeof import('./src/components/upload-attachment/attachment.vue')['default']
DiyLink: typeof import('./src/components/diy-link/index.vue')['default']
DiyPage: typeof import('./src/components/diy-page/index.vue')['default']
Editor: typeof import('./src/components/editor/index.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAside: typeof import('element-plus/es')['ElAside']
@ -17,6 +18,8 @@ declare module '@vue/runtime-core' {
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
@ -51,6 +54,7 @@ declare module '@vue/runtime-core' {
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
@ -58,6 +62,8 @@ declare module '@vue/runtime-core' {
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElStatistic: typeof import('element-plus/es')['ElStatistic']
ElStep: typeof import('element-plus/es')['ElStep']
@ -71,6 +77,7 @@ declare module '@vue/runtime-core' {
ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
@ -78,13 +85,16 @@ declare module '@vue/runtime-core' {
ExportSure: typeof import('./src/components/export-sure/index.vue')['default']
HeatMap: typeof import('./src/components/heat-map/index.vue')['default']
Icon: typeof import('./src/components/icon/index.vue')['default']
Markdown: typeof import('./src/components/markdown/index.vue')['default']
PopoverInput: typeof import('./src/components/popover-input/index.vue')['default']
RangeInput: typeof import('./src/components/range-input/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SelectArea: typeof import('./src/components/select-area/index.vue')['default']
SelectIcon: typeof import('./src/components/select-icon/index.vue')['default']
SpreadPopup: typeof import('./src/components/spread-popup/index.vue')['default']
UploadAttachment: typeof import('./src/components/upload-attachment/index.vue')['default']
UploadAudio: typeof import('./src/components/upload-audio/index.vue')['default']
UploadFile: typeof import('./src/components/upload-file/index.vue')['default']
UploadImage: typeof import('./src/components/upload-image/index.vue')['default']
UploadVideo: typeof import('./src/components/upload-video/index.vue')['default']

136
admin/package-lock.json generated
View File

@ -9,12 +9,18 @@
"version": "1.0.0",
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/vue3": "^6.1.19",
"@heroicons/vue": "^2.2.0",
"@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0",
"axios": "1.4.0",
"crypto-js": "4.1.1",
"css-color-function": "1.3.3",
"date-fns": "^4.1.0",
"day": "^0.0.2",
"echarts": "5.4.1",
"element-plus": "^2.7.4",
@ -25,6 +31,7 @@
"qrcode": "1.5.1",
"sass": "1.58.0",
"sortablejs": "1.15.0",
"vditor": "^3.10.9",
"vue": "3.2.45",
"vue-i18n": "9.2.2",
"vue-jsonp": "2.0.0",
@ -962,6 +969,47 @@
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
"node_modules/@fullcalendar/core": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/core/-/core-6.1.19.tgz",
"integrity": "sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==",
"dependencies": {
"preact": "~10.12.1"
}
},
"node_modules/@fullcalendar/daygrid": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/daygrid/-/daygrid-6.1.19.tgz",
"integrity": "sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/interaction": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/interaction/-/interaction-6.1.19.tgz",
"integrity": "sha512-GOciy79xe8JMVp+1evAU3ytdwN/7tv35t5i1vFkifiuWcQMLC/JnLg/RA2s4sYmQwoYhTw/p4GLcP0gO5B3X5w==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19"
}
},
"node_modules/@fullcalendar/vue3": {
"version": "6.1.19",
"resolved": "https://registry.npmmirror.com/@fullcalendar/vue3/-/vue3-6.1.19.tgz",
"integrity": "sha512-j5eUSxx0xIy3ADljo0f5B9PhjqXnCQ+7nUMPfsslc2eGVjp4F74YvY3dyd6OBbg13IvpsjowkjncGipYMQWmTA==",
"peerDependencies": {
"@fullcalendar/core": "~6.1.19",
"vue": "^3.0.11"
}
},
"node_modules/@heroicons/vue": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/@heroicons/vue/-/vue-2.2.0.tgz",
"integrity": "sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==",
"peerDependencies": {
"vue": ">= 3"
}
},
"node_modules/@highlightjs/vue-plugin": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.0.tgz",
@ -2330,15 +2378,15 @@
}
},
"node_modules/browserslist": {
"version": "4.22.2",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.2.tgz",
"integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==",
"version": "4.24.2",
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.2.tgz",
"integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
"dev": true,
"dependencies": {
"caniuse-lite": "^1.0.30001565",
"electron-to-chromium": "^1.4.601",
"node-releases": "^2.0.14",
"update-browserslist-db": "^1.0.13"
"caniuse-lite": "^1.0.30001669",
"electron-to-chromium": "^1.5.41",
"node-releases": "^2.0.18",
"update-browserslist-db": "^1.1.1"
},
"bin": {
"browserslist": "cli.js"
@ -2413,9 +2461,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001571",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001571.tgz",
"integrity": "sha512-tYq/6MoXhdezDLFZuCO/TKboTzuQ/xR5cFdgXPfDtM7/kchBO3b4VWghE/OAi/DV7tTdhmLjZiZBZi1fA/GheQ==",
"version": "1.0.30001680",
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz",
"integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==",
"dev": true
},
"node_modules/chalk": {
@ -2623,6 +2671,15 @@
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
},
"node_modules/day": {
"version": "0.0.2",
"resolved": "https://registry.npmmirror.com/day/-/day-0.0.2.tgz",
@ -2755,6 +2812,11 @@
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
"dev": true
},
"node_modules/diff-match-patch": {
"version": "1.0.5",
"resolved": "https://registry.npmmirror.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
"integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
},
"node_modules/dijkstrajs": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
@ -2809,9 +2871,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.616",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.616.tgz",
"integrity": "sha512-1n7zWYh8eS0L9Uy+GskE0lkBUNK83cXTVJI0pU3mGprFsbfSdAc15VTFbo+A+Bq4pwstmL30AVcEU3Fo463lNg==",
"version": "1.5.56",
"resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.56.tgz",
"integrity": "sha512-7lXb9dAvimCFdvUMTyucD4mnIndt/xhRKFAlky0CyFogdnNmdPQNoHI23msF/2V4mpTxMzgMdjK4+YRlFlRQZw==",
"dev": true
},
"node_modules/element-plus": {
@ -2982,9 +3044,9 @@
}
},
"node_modules/escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"engines": {
"node": ">=6"
@ -4585,9 +4647,9 @@
"dev": true
},
"node_modules/node-releases": {
"version": "2.0.14",
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
"version": "2.0.18",
"resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz",
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
"dev": true
},
"node_modules/normalize-path": {
@ -4809,9 +4871,9 @@
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
},
"node_modules/picomatch": {
"version": "2.3.1",
@ -4997,6 +5059,15 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/preact": {
"version": "10.12.1",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.12.1.tgz",
"integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -5925,13 +5996,13 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.13",
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"dev": true,
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
"escalade": "^3.2.0",
"picocolors": "^1.1.0"
},
"bin": {
"update-browserslist-db": "cli.js"
@ -5955,6 +6026,17 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
},
"node_modules/vditor": {
"version": "3.10.9",
"resolved": "https://registry.npmmirror.com/vditor/-/vditor-3.10.9.tgz",
"integrity": "sha512-cJE/pMv/kg3dW9TIoAe2VBM4CI1JuTEjsD8YhveqYZm8h4pjcXSmYtu/0QVqH6/KA2BeUXvZilryOXlwho2QZg==",
"dependencies": {
"diff-match-patch": "^1.0.5"
},
"funding": {
"url": "https://ld246.com/sponsor"
}
},
"node_modules/vite": {
"version": "4.1.0",
"resolved": "https://registry.npmmirror.com/vite/-/vite-4.1.0.tgz",

View File

@ -10,12 +10,18 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.0.10",
"@fullcalendar/core": "^6.1.19",
"@fullcalendar/daygrid": "^6.1.19",
"@fullcalendar/interaction": "^6.1.19",
"@fullcalendar/vue3": "^6.1.19",
"@heroicons/vue": "^2.2.0",
"@highlightjs/vue-plugin": "2.1.0",
"@types/lodash-es": "4.17.6",
"@vueuse/core": "9.12.0",
"axios": "1.4.0",
"crypto-js": "4.1.1",
"css-color-function": "1.3.3",
"date-fns": "^4.1.0",
"day": "^0.0.2",
"echarts": "5.4.1",
"element-plus": "^2.7.4",
@ -26,6 +32,7 @@
"qrcode": "1.5.1",
"sass": "1.58.0",
"sortablejs": "1.15.0",
"vditor": "^3.10.9",
"vue": "3.2.45",
"vue-i18n": "9.2.2",
"vue-jsonp": "2.0.0",

View File

@ -157,7 +157,7 @@ UE.I18N['zh-cn'] = {
'elementPathTip': "元素路径",
'wordCountTip': "字数统计",
'wordCountMsg': '{#count} / {#leave}',
'wordOverFlowMsg': '<span style="color:red;">字数超出最大允许值,服务器可能拒绝保存</span>',
'wordOverFlowMsg': '<span style="color:red;">字数超出最大允许值</span>',
'ok': "确认",
'cancel': "取消",
'closeDialog': "关闭对话框",

View File

@ -145,9 +145,12 @@ div.edui-box {
background-color: white;
position: relative;
overflow: visible;
z-index: 1;
z-index: 1 !important;
}
/* 全屏状态 */
.edui-default .edui-editor.edui-fullscreen {
z-index: 999 !important;
}
.edui-editor div {
width: auto;
height: auto;

View File

@ -13,6 +13,7 @@ import useAppStore from '@/stores/modules/app'
import { useDark, useToggle } from '@vueuse/core'
import { setThemeColor } from '@/utils/common'
import { useRoute } from 'vue-router'
import { getSiteAllowChange} from '@/app/api/site'
const route = useRoute()
@ -24,7 +25,14 @@ systemStore.getWebsiteInfo()
systemStore.getWebsiteLayout()
const toggleDark = useToggle(useDark())
const getSiteAllowChangeFn = ()=>{
getSiteAllowChange().then(({data})=>{
let isAllowChange = data.is_allow ? true : false
localStorage.setItem('isAllowChange',isAllowChange.toString())
})
}
getSiteAllowChangeFn()
watch(route, () => {
useAppStore().$patch(state => {
state.route = route
@ -35,12 +43,13 @@ onMounted(() => {
//
toggleDark(systemStore.dark)
setThemeColor(systemStore.theme, systemStore.dark ? 'dark' : 'light')
// importIconFontCss();
// getIcon()
})
</script>
<style lang="scss" scoped></style>
<style>
.el-page-header__header .el-page-header__left .el-page-header__content{
font-size: 14px !important;
font-weight: 500 !important;
}
</style>

View File

@ -12,7 +12,7 @@ export function getAddonLocal(params: Record<string, any>) {
*
* @returns
*/
export function getAddonDetial(id: number) {
export function getAddonDetail(id: number) {
return request.get(`addon/${ id }`)
}
@ -90,3 +90,19 @@ export function cancelInstall(addon: string) {
export function getInstalledAddonList() {
return request.get('addon/list/install')
}
export function getGroupAppList() {
return request.get('home/site/group/app_list')
}
export function getAddonInit() {
return request.get('addon/init')
}
export function getAppIndex() {
return request.get('app/index')
}
export function getAdvList() {
return request.get('index/adv_list')
}

68
admin/src/app/api/app.ts Normal file
View File

@ -0,0 +1,68 @@
import request from '@/utils/request'
/**
* app配置
* @returns
*/
export function getAppConfig() {
return request.get('channel/app/config')
}
/**
* app配置
* @param params
* @returns
*/
export function setAppConfig(params: Record<string, any>) {
return request.put('channel/app/config', params, { showSuccessMessage: true })
}
export function getVersionList(params: Record<string, any>) {
return request.get('channel/app/version', { params })
}
export function getVersionInfo(id: number) {
return request.get(`channel/app/version/${id}`)
}
export function getAppPlatform() {
return request.get(`channel/app/platfrom`)
}
/**
*
* @param params
* @returns
*/
export function addVersion(params: Record<string, any>) {
return request.post('channel/app/version', params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editVersion(params: Record<string, any>) {
return request.put(`channel/app/version/${ params.id }`, params, { showSuccessMessage: true })
}
/**
*
* @param siteId
*/
export function deleteVersion(params: Record<string, any>) {
return request.delete(`channel/app/version/${ params.id }`)
}
export function getBuildLog(key: string) {
return request.get(`channel/app/build/log/${ key }`)
}
export function releaseVersion(id: number) {
return request.put(`channel/app/version/${ id }/release`, {}, { showSuccessMessage: true })
}
export function generateSingCert(params: Record<string, any>) {
return request.post(`channel/app/generate_sing_cert`, params, { showSuccessMessage: true });
}

View File

@ -190,3 +190,69 @@ export function changeTemplate(params: Record<string, any>) {
export function getApps(params: Record<string, any>) {
return request.get(`diy/apps`)
}
/**
*
* @param params
*/
export function copyDiy(params: Record<string, any>) {
return request.post(`diy/copy`, params, { showSuccessMessage: true })
}
/**
*
*/
export function getPageLink(params: Record<string, any>) {
return request.get(`diy/page_link`, { params })
}
/***************************************************** 主题风格 ****************************************************/
/**
*
* @param params
*/
export function getDefaultTheme(params: Record<string, any>) {
return request.get(`diy/theme/color`, { params })
}
/**
*
* @param params
*/
export function getDiyTheme(params: Record<string, any>) {
return request.get(`diy/theme`, { params })
}
/**
*
* @param params
*/
export function addTheme(params: Record<string, any>) {
return request.post(`diy/theme/add`, params)
}
/**
*
* @param params
*/
export function editTheme(params: Record<string, any>) {
return request.put(`diy/theme/edit/${ params.id }`, params, { showSuccessMessage: true })
}
/**
*
* @param id
*/
export function deleteTheme(id: number) {
return request.delete(`diy/theme/delete/${ id }`, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function setDiyTheme(params: Record<string, any>) {
return request.post(`diy/theme`, params, { showSuccessMessage: true })
}

View File

@ -0,0 +1,237 @@
import request from '@/utils/request'
/***************************************************** 万能表单 ****************************************************/
/**
*
* @param params
* @returns
*/
export function getDiyFormPageList(params: Record<string, any>) {
return request.get(`diy/form`, { params })
}
/**
*
* @param params
* @returns
*/
export function getDiyFormList(params: Record<string, any>) {
return request.get(`diy/form/list`, { params })
}
/**
*
* @param params
* @returns
*/
export function getDiyFormSelectPageList(params: Record<string, any>) {
return request.get(`diy/form/select`, { params })
}
/**
*
* @param form_id id
* @returns
*/
export function getDiyFormInfo(form_id: number) {
return request.get(`diy/form/${ form_id }`);
}
/**
*
* @param params
* @returns
*/
export function addDiyForm(params: Record<string, any>) {
return request.post('diy/form', params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editDiyForm(params: Record<string, any>) {
return request.put(`diy/form/${ params.form_id }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function editDiyFormShare(params: Record<string, any>) {
return request.put(`diy/form/share`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function deleteDiyForm(params: Record<string, any>) {
return request.put(`diy/form/delete`, params, { showSuccessMessage: true })
}
/**
*
*/
export function initPage(params: Record<string, any>) {
return request.get(`diy/form/init`, { params })
}
/**
*
* @param params
* @returns
*/
export function getDiyFormQrcode(params: Record<string, any>) {
return request.get(`diy/form/qrcode`, { params })
}
/**
*
* @param params
* @returns
*/
export function getDiyFormFieldsList(params: Record<string, any>) {
return request.get(`diy/form/fields/list`, { params })
}
/**
*
* @param params
* @returns
*/
export function getDiyFormFieldStat(params: Record<string, any>) {
return request.get(`diy/form/records/field/stat`, { params })
}
/**
*
*/
export function getDiyTemplate(params: Record<string, any>) {
return request.get(`diy/template`, { params })
}
/**
*
*/
export function getDiyTemplatePages(params: Record<string, any>) {
return request.get(`diy/form/template`, { params })
}
/**
*
* @param params
* @returns
*/
export function editFormStatus(params: Record<string, any>) {
return request.put(`diy/form/status`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @param params
* @returns
*/
export function getApps(params: Record<string, any>) {
return request.get(`diy/apps`)
}
/**
*
* @param params
*/
export function copyDiy(params: Record<string, any>) {
return request.post(`diy/form/copy`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getFormType(params: Record<string, any>) {
return request.get(`diy/form/type`)
}
/**
*
* @param form_id
* @returns
*/
export function getFormWriteConfig(form_id: any) {
return request.get(`diy/form/write/${ form_id }`)
}
/**
*
* @param params
*/
export function editDiyFormWriteConfig(params: Record<string, any>) {
return request.put(`diy/form/write`, params, { showSuccessMessage: true })
}
/**
*
* @param form_id
* @returns
*/
export function getFormSubmitConfig(form_id: any) {
return request.get(`diy/form/submit/${ form_id }`)
}
/**
*
* @param params
*/
export function editDiyFormSubmitConfig(params: Record<string, any>) {
return request.put(`diy/form/submit`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getFormRecords(params: Record<string, any>) {
return request.get(`diy/form/records`, { params })
}
/**
*
* @param id
* @returns
*/
export function getFormRecordsInfo(id: number) {
return request.get(`diy/form/records/${ id }`);
}
/**
*
* @param params
* @returns
*/
export function deleteFormRecords(params: Record<string, any>) {
return request.put(`diy/form/records/delete`, params, { showSuccessMessage: true })
}
/**
*
* @param params
* @returns
*/
export function getFormRecordsMember(params: Record<string, any>) {
return request.get(`diy/form/records/member/stat`, { params })
}
/**
*
* @param params
*/
export function copyForm(params: Record<string, any>) {
return request.post(`diy/form/copy`, params, { showSuccessMessage: true })
}

View File

@ -125,6 +125,14 @@ export function editMemberDetail(params: Record<string, any>) {
return request.put(`member/member/modify/${ params.member_id }/${ params.field }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function memberBatchModify(params: Record<string, any>) {
return request.post(`member/member/batch_modify`, params, { showSuccessMessage: true })
}
/***************************************************** 会员零钱 ****************************************************/
@ -360,6 +368,18 @@ export function memberAudit(params: Record<string, any>) {
return request.put(`member/cash_out/audit/${ params.id }/${ params.action }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function memberCancel(params: Record<string, any>) {
return request.put(`member/cash_out/cancel/${ params.id }`, params, {
showSuccessMessage: true,
showErrorMessage: true
})
}
/**
*
* @param params
@ -368,6 +388,22 @@ export function memberTransfer(params: Record<string, any>) {
return request.put(`member/cash_out/transfer/${ params.id }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function memberRemark(params: Record<string, any>) {
return request.put(`member/cash_out/remark/${ params.id }`, params, { showSuccessMessage: true })
}
/**
*
* @param id
*/
export function memberCheck(id: number) {
return request.put(`member/cash_out/check/${ id }`, {}, { showSuccessMessage: true })
}
/**
*
* @param params
@ -422,6 +458,7 @@ export function getGrowthRuleDict() {
export function getPointRuleDict() {
return request.get(`member/dict/point_rule`)
}
/***************************************************** 会员等级 ****************************************************/
/**
@ -481,14 +518,14 @@ export function getMemberLevelAll() {
*
*/
export function getMemberBenefitsContent() {
return request.get(`member/benefits/content`);
return request.post(`member/benefits/content`);
}
/**
*
*/
export function getMemberGiftsContent(params: Record<string, any>) {
return request.get(`member/gifts/content`, { params });
return request.post(`member/gifts/content`, params);
}
/**
@ -513,3 +550,26 @@ export function setSignConfig(params: Record<string, any>) {
export function getMemberSignList(params: Record<string, any>) {
return request.get(`member/sign`, { params });
}
/***************************************************** 地址管理 ****************************************************/
/**
*
*/
export function getMemberAddress(params: Record<string, any>) {
return request.get(`member/address`, { params });
}
/**
*
*/
export function addMemberAddress(params: Record<string, any>) {
return request.post(`member/address`, params);
}
/**
*
*/
export function editMemberAddress(params: Record<string, any>) {
return request.put(`member/address`, params);
}

View File

@ -61,3 +61,11 @@ export function getFrameworkNewVersion() {
export function getFrameworkVersionList() {
return request.get(`niucloud/framework/version/list`)
}
/**
* /
* @param params
*/
export function getAppVersionList(params: Record<string, any>) {
return request.get(`niucloud/app_version/list`, { params })
}

View File

@ -78,3 +78,245 @@ export function editSms(params: Record<string, any>) {
export function getSmsLog(params: Record<string, any>) {
return request.get(`notice/sms/log`, { params })
}
/**
*
*/
export function getAccountIsLogin() {
return request.get(`notice/niusms/config`)
}
/**
*
* @param params
*/
export function loginAccount(params: Record<string, any>) {
return request.post(`notice/niusms/account/login`,params,{ showSuccessMessage: true })
}
/**
*
* @param params
*/
export function registerAccount(params: Record<string, any>) {
return request.post(`notice/niusms/account/register`,params,{ showSuccessMessage: true })
}
/**
*
* @param username
*/
export function getAccountInfo(username: string) {
return request.get(`notice/niusms/account/info/${username}`)
}
/**
*
* @param params
*/
export function getTemplateList(params: Record<string, any>) {
return request.get(`notice/niusms/template/list/${params.sms_type}/${params.username}`,{})
}
/**
*
* @param username
* @param params
*/
export function getSignList(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/sign/list/${username}`,{params})
}
/**
*
* @param username
* @param params
*/
export function addSign(username: string, params: Record<string, any>) {
return request.post(`notice/niusms/sign/report/${username}`, params, { showSuccessMessage: true });
}
/**
*
* @param username
* @param params
*/
export function deleteSign(username: string, params: Record<string, any>) {
return request.post(`notice/niusms/sign/delete/${username}`, params, { showSuccessMessage: true });
}
/**
*
* @param username
* @param params
*/
export function editAccount(username: string,params: Record<string, any>) {
return request.post(`notice/niusms/account/edit/${username}`, params, { showSuccessMessage: true });
}
/**
*
* @param username
* @param params
*/
export function getSmsSendList(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/account/send_list/${username}`,{params})
}
/**
*
* @param username
* @param params
*/
export function getSmsOrdersList(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/order/list/${username}`,{params})
}
/**
*
*/
export function getSmsPackagesList() {
return request.get(`notice/niusms/packages`)
}
/**
*
*/
export function getSmsCaptcha() {
return request.get(`notice/niusms/captcha`)
}
/**
*
*/
export function getsiteCaptcha() {
return request.get(`site/captcha/create`)
}
/**
*
* @param params
*/
export function getSmsSend(params: Record<string, any>) {
return request.post(`notice/niusms/send`,params,{ showSuccessMessage: true })
}
/**
*
*/
export function getSmsSignConfig() {
return request.get(`notice/niusms/sign/report/config`)
}
/**
*
*/
export function getTemplateReportConfig() {
return request.get(`notice/niusms/template/report/config`)
}
/**
*
* @param sms_type
* @param username
* @param sms_type
* @param username
* @param params
*/
export function reportTemplate(sms_type: string, username: string, params: Record<string, any>) {
return request.post(`notice/niusms/template/report/${sms_type}/${username}`,params,{ showSuccessMessage: true })
}
/**
*
* @param sms_type
* @param username
* @param sms_type
* @param username
* @param params
*/
export function getreportTemplateInfo(sms_type: string, username: string,params: Record<string, any>) {
return request.get(`notice/niusms/template/info/${sms_type}/${username}`,{params})
}
/**
*
* @param username
* @param params
*/
export function smsOrderCreate(username: string, params: Record<string, any>) {
return request.post(`notice/niusms/order/create/${username}`, params)
}
/**
*
* @param username
* @param params
*/
export function getOrderPayInfo(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/order/pay/${username}`, {params})
}
/**
*
* @param username
* @param params
*/
export function getOrderInfo(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/order/info/${username}`, {params})
}
/**
*
* @param username
* @param params
*/
export function getOrderPayStatus(username: string, params: Record<string, any>) {
return request.get(`notice/niusms/order/status/${username}`, {params})
}
/**
*
* @param username
* @param params
*/
export function calculateOrderPay(username: string, params: Record<string, any>) {
return request.post(`notice/niusms/order/calculate/${username}`, params)
}
/**
*
* @param params
*/
export function enableNiusms(params: Record<string, any>) {
return request.put(`notice/niusms/enable`,params,{ showSuccessMessage: true })
}
/**
*
* @param username
* @param sms_type
* @param username
*/
export function templateSync(sms_type: string, username: string) {
return request.get(`notice/niusms/template/sync/${sms_type}/${username}`)
}
/**
*
* @param username
* @param params
*/
export function resetPassword(username: string,params: Record<string, any>) {
return request.post(`notice/niusms/account/reset/password/${username}`,params,{ showSuccessMessage: true})
}
/**
*
* @param template_id
* @param username
* @param template_id
*/
export function clearTemplate(username: string,template_id: string) {
return request.delete(`notice/niusms/template/${username}/${template_id}`)
}

View File

@ -54,6 +54,13 @@ export function getPayRefundInfo(refund_no: string) {
return request.get(`pay/refund/${refund_no}`)
}
/**
* 退
*/
export function getRefundStatus() {
return request.get(`pay/refund/status`)
}
/**
* 退
*/
@ -75,3 +82,49 @@ export function getRefundTransfer(params: Record<string, any>) {
export function getAllPayType() {
return request.get(`pay/type/all`)
}
/**
*
*/
export function getPayList() {
return request.get(`pay/type/list`)
}
/**
*
*/
export function pay(params: Record<string, any>) {
return request.post(`pay`, params)
}
/**
*
* @param tradeType
* @param tradeId
* @param channel
*/
export function getFriendsPay(tradeType : string, tradeId : number, channel: string) {
return request.get(`pay/friendspay/info/${tradeType}/${tradeId}/${channel}`, { showErrorMessage: false })
}
/**
*
*/
export function getTransferScene() {
return request.get(`pay/transfer_scene`)
}
/**
* id
*/
export function setSceneId(params: Record<string, any>) {
return request.post(`pay/transfer_scene/set_scene_id/${params.scene}`, params, { showSuccessMessage: true })
}
/**
*
*/
export function setTradeScene(params: Record<string, any>) {
return request.post(`pay/transfer_scene/set_trade_scene/${params.type}`, params)
}

View File

@ -104,3 +104,16 @@ export function initPoster(params: Record<string, any>) {
export function getPreviewPoster(params: Record<string, any>) {
return request.get(`sys/poster/preview`, { params })
}
/**
*
* @param params
* @returns
*/
export function getPosterGenerate(params: Record<string, any>) {
return request.get(`sys/poster/generate`, { params, showErrorMessage: false })
}
// 判断是否安装imagemagick扩展
export function checkImagick() {
return request.get(`sys/check_imagick`, { showErrorMessage: false })
}

View File

@ -42,8 +42,8 @@ export function editSite(params: Record<string, any>) {
*
* @param siteId
*/
export function deleteSite(siteId: number) {
return request.delete(`site/site/${siteId}`)
export function deleteSite(params: Record<string, any>) {
return request.delete(`site/site/${ params.site_id }?captcha_code=${ params.captcha_code }&captcha_key=${ params.captcha_key }`)
}
/**
@ -54,6 +54,15 @@ export function closeSite(params: Record<string, any>) {
return request.put(`site/closesite/${ params.site_id }`, params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function initSite(params: Record<string, any>) {
return request.post(`site/init`, params, { showSuccessMessage: true })
}
/**
*
* @param params
@ -69,6 +78,20 @@ export function getStatusList() {
return request.get(`site/statuslist`)
}
/**
*
*/
export function getSiteAllowChange() {
return request.get(`site/allow_change`)
}
/**
*
*/
export function putSiteAllowChange(params: Record<string, any>) {
return request.put(`site/allow_change`,params, { showSuccessMessage: true })
}
/***************************************************** 站点分组管理 ****************************************************/
/**
@ -212,6 +235,14 @@ export function getLogInfo(id: number) {
return request.get(`site/log/${ id }`)
}
/**
*
* @returns
*/
export function logDestroy() {
return request.delete(`site/log/destroy`)
}
/***************************************************** 账单列表 **************************************************/
/**
@ -254,3 +285,19 @@ export function getAccountType() {
export function getSiteAddons() {
return request.get('site/addons')
}
/**
*
* @returns
*/
export function getShowApp() {
return request.get('site/showCustomer')
}
/**
*
* @returns
*/
export function getShowSpecialMenu() {
return request.get('site/special_menu')
}

View File

@ -61,6 +61,14 @@ export function deleteRole(roleId: number) {
return request.delete(`sys/role/${ roleId }`, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function modifyRoleStatus(params: Record<string, any>) {
return request.put(`sys/role/status`, params, { showSuccessMessage: true })
}
/**
*
* @returns
@ -277,24 +285,6 @@ export function getAuthMenu() {
return request.get(`auth/site/showmenu`)
}
/**
*
* @param params
* @returns
*/
export function getIconCategoryList(params: Record<string, any>) {
return request.get(`sys/attachment/icon_category`, { params })
}
/**
*
* @param params
* @returns
*/
export function getIconList(params: Record<string, any>) {
return request.get(`sys/attachment/icon`, { params })
}
/**
* evn
* @returns
@ -410,7 +400,7 @@ export function getTransferInfo(channel: string) {
* @returns
*/
export function setTransferInfo(params: Record<string, any>) {
return request.post(`pay/channel/set/transfer`, params)
return request.post(`pay/channel/set/transfer`, params, { showSuccessMessage: true })
}
/***************************************************** 定时任务 ****************************************************/
@ -479,6 +469,50 @@ export function deleteCron(id: string) {
return request.delete(`sys/schedule/${ id }`, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function doCron(params: Record<string, any>) {
return request.put(`sys/schedule/do/${ params.id }`, params, {
showErrorMessage: true,
showSuccessMessage: true
})
}
/**
*
* @returns
*/
export function getCronLogList(params: any) {
return request.get(`sys/schedule/log/list`, { params })
}
/**
*
* @returns
*/
export function deleteCronLog(params: Record<string, any>) {
return request.put(`sys/schedule/log/delete`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function clearCronLog(params: Record<string, any>) {
return request.put(`sys/schedule/log/clear`, params, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function resetCron() {
return request.post(`sys/schedule/reset`, { showSuccessMessage: true })
}
/***************************************************** 协议管理 ****************************************************/
/**
@ -568,6 +602,13 @@ export function clearSchemaCache(params: Record<string, any>) {
return request.post(`sys/schema/clear`, {}, { showSuccessMessage: true })
}
/**
*
*/
export function clearCache(params: Record<string, any>) {
return request.post(`sys/cache/clear`, {}, { showSuccessMessage: true })
}
/***************************************************** 获取应用 ****************************************************/
/**
*
@ -745,3 +786,11 @@ export function deleteExport(id: number) {
export function getWxoplatform() {
return request.get('sys/wxoplatform/config')
}
/**
*
* @returns
*/
export function getQrcode(params: Record<string, any>) {
return request.get(`sys/qrcode`, { params, showErrorMessage: false })
}

View File

@ -85,6 +85,7 @@ export function addonDevelopBuild(key: any) {
export function addonDevelopDownload(key: any) {
return request.post(`addon_develop/download/${ key }`, {})
}
/***************************************************** 代码生成 ****************************************************/
/**

View File

@ -19,16 +19,17 @@ export function getUpgradeTask() {
/**
*
* @param addon
* @param params
*/
export function upgradeAddon(addon: string = '') {
return request.post(addon ? `upgrade/${addon}` : 'upgrade')
export function upgradeAddon(addon: string = '', params: Record<string, any> = {}) {
return request.post(addon ? `upgrade/${ addon }` : 'upgrade', params)
}
/**
*
*/
export function executeUpgrade() {
return request.post('upgrade/execute', {})
return request.post('upgrade/execute', {}, { showErrorMessage: false })
}
/**
@ -44,3 +45,118 @@ export function preUpgradeCheck(addon: string = '') {
export function clearUpgradeTask() {
return request.post('upgrade/clear')
}
/**
*
* @param operate
*/
export function upgradeUserOperate(operate: string) {
return request.post(`upgrade/operate/${ operate }`)
}
/**
*
* @param params
* @returns
*/
export function getUpgradeRecords(params: Record<string, any>) {
return request.get(`upgrade/records`, { params })
}
/**
*
* @param params
*/
export function delUpgradeRecords(params: Record<string, any>) {
return request.delete(`upgrade/records`, { params })
}
/**
*
* @param params
* @returns
*/
export function getBackupRecords(params: Record<string, any>) {
return request.get(`backup/records`, { params })
}
/**
*
* @param params
*/
export function modifyBackupRemark(params: Record<string, any>) {
return request.put(`backup/remark`, params, { showSuccessMessage: true })
}
/**
*
*/
export function checkDirExist(params: Record<string, any>) {
return request.post('backup/check_dir', params)
}
/**
*
*/
export function checkPermission(params: Record<string, any>) {
return request.post('backup/check_permission', params)
}
/**
*
*/
export function restoreUpgradeBackup(params: Record<string, any>) {
return request.post('backup/restore', params)
}
/**
*
*/
export function deleteRecords(params: Record<string, any>) {
return request.post('backup/delete', params, { showSuccessMessage: true })
}
/**
*
*/
export function manualBackup(params: Record<string, any>) {
return request.post("backup/manual", params)
}
/**
*
* @param params
*/
export function performRecoveryTasks(params: Record<string, any>) {
return request.get("backup/restore_task", params)
}
/**
*
* @param params
*/
export function performBackupTasks(params: Record<string, any>) {
return request.get("backup/task", params)
}
/**
*
* @param params
*/
export function connectTest(params: Record<string, any>) {
return request.post("niucloud/build/connect_test", params)
}
/**
*
* @param params
*/
export function setLocalUrl(params: Record<string, any>) {
return request.post("niucloud/build/set_local_url", params)
}
/**
*
* @param params
*/
export function getLocalUrl(params: Record<string, any>) {
return request.get("niucloud/build/get_local_url", params)
}

View File

@ -41,7 +41,7 @@ export function deleteUser(uid: number) {
/**
*
* @param uid
* @param params
*/
export function editUser(params: Record<string, any>) {
return request.put(`user/user/${ params.uid }`, params, { showSuccessMessage: true })

View File

@ -21,6 +21,23 @@ export function getVerifyDetail(verifyCode: string) {
return request.get(`verify/verify/${ verifyCode }`)
}
/**
*
* @param verifyCode
* @returns
*/
export function getVerifyDetailInfo(verifyCode: string) {
return request.get(`verify/detail/${ verifyCode }`)
}
/**
*
* @param verifyCode
* @returns
*/
export function verify(verifyCode: string, params: Record<string, any>) {
return request.post(`verify/verify/${ verifyCode }`,params,{ showSuccessMessage: true})
}
/***************************************************** 核销员 ****************************************************/
/**
@ -66,3 +83,19 @@ export function addVerifier(params: Record<string, any>) {
export function deleteVerifier(id: number) {
return request.delete(`verify/verifier/${ id }`, { showSuccessMessage: true })
}
/**
*
* @returns
*/
export function getVerifyInfo(id: number) {
return request.get(`verify/verifier/${ id }`)
}
/**
*
* @returns
*/
export function editVerifier(params: Record<string, any>) {
return request.post(`verify/verifier/${ params.id }`, params,{ showSuccessMessage: true })
}

View File

@ -33,6 +33,7 @@ export function getTemplateList() {
export function getBatchAcquisition(params: Record<string, any>) {
return request.put('weapp/template/sync', params, { showSuccessMessage: true })
}
/**
*
* @param params
@ -131,3 +132,26 @@ export function deleteVersion(id: string) {
export function getIsTradeManaged() {
return request.get('weapp/delivery/getIsTradeManaged')
}
/**
*
* @param params
*/
export function setWeappDomain(params: Record<string, any>) {
return request.put('weapp/domain', params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function setWeappPrivacySetting(params: Record<string, any>) {
return request.put('weapp/privacysetting', params, { showSuccessMessage: true })
}
/**
*
*/
export function getWeappPrivacySetting() {
return request.get('weapp/privacysetting')
}

View File

@ -42,8 +42,8 @@ export function getAuthorizationResult(params: Record<string, any>) {
*
* @returns
*/
export function weappCommit() {
return request.post('wxoplatform/weapp/version/commit', {}, { showSuccessMessage: true })
export function weappCommit(params: Record<string, any>) {
return request.post('wxoplatform/weapp/version/commit', params, { showSuccessMessage: true })
}
/**
@ -67,3 +67,38 @@ export function getWeappLastCommitRecord() {
export function siteWeappCommit() {
return request.post('wxoplatform/site/weapp/commit', {}, { showSuccessMessage: true })
}
/**
*
*/
export function getSiteGroupCommitRecord(params: Record<string, any>) {
return request.get('wxoplatform/sitegroup/commit', { params })
}
/**
*
* @param params
*/
export function undoAudit(params: Record<string, any>) {
return request.put('wxoplatform/undo/weappaudit', params, { showSuccessMessage: true })
}
export function syncSiteWeapp(params: Record<string, any>) {
return request.post('wxoplatform/async/siteweapp', params, { showSuccessMessage: true })
}
/**
*
* @param params
*/
export function getAuthRecord(params: Record<string, any>) {
return request.get('wxoplatform/authorization/record', { params })
}
/**
*
*/
export function cancelAuthorization(params: Record<string, any>) {
return request.post('wxoplatform/authorization/cancel', params, { showSuccessMessage: true })
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 B

View File

@ -1,31 +1,41 @@
<template>
<el-dialog v-model="showDialog" :title="t('cloudbuild.title')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
<div v-if="active == 'build'" class="h-[60vh]" v-loading="loading">
<div class="h-[60vh] flex flex-col" v-if="cloudBuildCheck && !cloudBuildTask">
<el-scrollbar>
<div v-show="active == 'build'" class="h-[50vh]" v-loading="loading">
<div class="h-[50vh] flex flex-col" v-if="cloudBuildCheck && !cloudBuildTask">
<!-- <el-scrollbar> -->
<div class="bg-[#fff] my-3" v-if="cloudBuildCheck.dir">
<p class="pt-[20px] pl-[20px] ">{{ t('cloudbuild.dirPermission') }}</p>
<div>
<p class="pl-[20px] ">{{ t('cloudbuild.dirPermission') }}</p>
<div class="mt-[10px] mx-[20px] text-[14px] cursor-pointer text-primary flex items-center justify-between bg-[#EFF6FF] rounded-[4px] p-[10px]" @click="cloudBuildCheckDirFn">
<div class="flex items-center">
<el-icon :size="17"><QuestionFilled /></el-icon>
<span class="ml-[5px] leading-[20px]">编译权限错误查看解决方案</span></div>
<div class="border-[1px] border-primary rounded-[3px] w-[72px] h-[26px] leading-[25px] text-center">立即查看</div>
</div>
</div>
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<el-col :span="18">
<span>{{ t('cloudbuild.path') }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3">
<span>{{ t('cloudbuild.demand') }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3">
<span>{{ t('status') }}</span>
</el-col>
</el-row>
<el-scrollbar style="height: calc(300px); overflow: auto">
<el-row class="pb-[10px] items pl-[15px]" v-for="item in cloudBuildCheck.dir.is_readable">
<el-col :span="12">
<el-col :span="18">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3">
<span>{{ t('cloudbuild.readable') }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
@ -35,13 +45,13 @@
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in cloudBuildCheck.dir.is_write">
<el-col :span="12">
<el-col :span="18">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3">
<span>{{ t('cloudbuild.write') }}</span>
</el-col>
<el-col :span="6">
<el-col :span="3" >
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<span v-else>
<el-icon color="red">
@ -50,19 +60,49 @@
</span>
</el-col>
</el-row>
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[60vh]" v-show="cloudBuildTask">
<terminal ref="terminalRef" context="" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
<!-- </el-scrollbar> -->
</div>
<div class="h-[45vh]" v-show="cloudBuildTask">
<terminal ref="terminalRef" :name="`cloud-build-${terminalId}`" context="" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
<div class="flex justify-end mt-[20px]" v-show="cloudBuildTask">
<el-button @click="dialogCancel()" class="!w-[90px]">取消</el-button>
<el-button type="primary" :loading="timeloading" class="!w-[140px]">已用时 {{ formattedDuration }}</el-button>
</div>
</div>
<div v-if="active == 'complete'">
<div class="h-[60vh] flex flex-col">
<div class="flex-1 h-0">
<el-result icon="success" :title="t('cloudbuild.cloudbuildSuccess')"></el-result>
<div v-show="active == 'error'">
<div class="h-[50vh] flex flex-col">
<div class="flex-1 h-0 flex justify-center items-center flex-col">
<el-result icon="error" :title="t('编译失败')">
<template #icon>
<img src="@/app/assets/images/error_icon.png" alt="">
</template>
<template #extra>
<el-scrollbar class="max-h-[150px] !overflow-auto text-[15px] text-[#4F516D] mb-[15px] mt-[-15px]">
{{errorInfo}}
</el-scrollbar>
<el-button @click="handleReturn" class="!w-[90px]">错误信息</el-button>
<el-button @click="showDialog=false" type="primary" class="!w-[90px]">完成</el-button>
</template>
</el-result>
</div>
</div>
</div>
<div v-show="active == 'complete'">
<div class="h-[50vh] flex flex-col">
<div class="flex-1 h-0 flex justify-center items-center flex-col">
<el-result icon="success" :title="t('cloudbuild.cloudbuildSuccess')" :sub-title="`编译耗时${formattedDuration},成功编译完成。`">
<template #icon>
<img src="@/app/assets/images/success_icon.png" alt="">
</template>
<template #extra>
<el-button @click="handleReturn" class="!w-[90px]">返回</el-button>
<el-button @click="showDialog=false" type="primary" class="!w-[90px]">完成</el-button>
</template>
</el-result>
</div>
</div>
</div>
@ -70,23 +110,35 @@
</template>
<script lang="ts" setup>
import { ref, h, watch } from 'vue'
import { ref, h, watch, computed } from 'vue'
import { t } from '@/lang'
import { getCloudBuildLog, getCloudBuildTask, cloudBuild, clearCloudBuildTask, preBuildCheck } from '@/app/api/cloud'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { AnyObject } from "@/types/global"
import { ElNotification, ElMessageBox } from "element-plus"
import { AnyObject } from '@/types/global'
import { ElNotification, ElMessageBox } from 'element-plus'
const showDialog = ref<boolean>(false)
const terminalId = ref(Date.now());
const cloudBuildTask = ref<null | AnyObject>(null)
const active = ref('build')
const cloudBuildCheck = ref<null | AnyObject>(null)
const loading = ref(false)
const terminalRef = ref(null)
const emits = defineEmits(['complete'])
let cloudBuildLog = []
//
const buildStartTime = ref<number | null>(null)
const buildDuration = ref<number>(0)
let buildTimer: number | null = null
const formattedDuration = computed(() => {
const seconds = buildDuration.value
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return mins > 0 ? `${mins}${secs}` : `${secs}`
})
/**
* 查询升级任务
*/
@ -97,42 +149,82 @@ const getCloudBuildTaskFn = () => {
cloudBuildTask.value = data
if (!showDialog.value) {
showElNotification()
// showElNotification()
localStorage.setItem('cloud_build_task', 'true')
}
}).catch()
}
getCloudBuildTaskFn()
const errorInfo = ref('')
const timeloading = ref(false)
const getCloudBuildLogFn = () => {
timeloading.value = true
getCloudBuildLog().then(res => {
if (!res.data) {
if (showDialog.value && cloudBuildLog.length) {
active.value = 'complete'
timeloading.value = false
terminalRef.value.execute('clear')
clearCloudBuildTask()
buildTimer && clearInterval(buildTimer) //
localStorage.removeItem('cloud_build_start_time')
localStorage.removeItem('cloud_build_task')
}
notificationEl && notificationEl.close()
cloudBuildTask.value = null
// cloudBuildTask.value = null
return
}
const data = res.data.data ?? [];
const data = res.data.data ?? []
let error = ''
if (data[0] && data[0].length && showDialog.value) {
if (cloudBuildLog.length == 0) {
const storedTime = localStorage.getItem('cloud_build_start_time')
if (storedTime) {
buildStartTime.value = Number(storedTime)
} else {
const now = Date.now()
buildStartTime.value = now
localStorage.setItem('cloud_build_start_time', String(now))
}
buildDuration.value = Math.floor((Date.now() - buildStartTime.value) / 1000)
buildTimer && clearInterval(buildTimer)
buildTimer = setInterval(() => {
if (buildStartTime.value) {
buildDuration.value = Math.floor((Date.now() - buildStartTime.value) / 1000)
}
}, 1000)
terminalRef.value.execute('clear')
terminalRef.value.execute('开始编译')
}
data[0].forEach(item => {
if (!cloudBuildLog.includes(item.action)) {
terminalRef.value.pushMessage({ content: `正在执行:${item.action}` })
terminalRef.value.pushMessage({ content: `${item.action}` })
cloudBuildLog.push(item.action)
if (item.code == 0) {
error = item.msg
terminalRef.value.pushMessage({ content: item.msg, class: 'error' })
timeloading.value = false
active.value = 'error'
terminalRef.value.execute('clear')
clearCloudBuildTask()
errorInfo.value = item.msg
//
if (buildTimer) {
clearInterval(buildTimer)
buildTimer = null
}
// duration
if (buildStartTime.value) {
buildDuration.value = Math.floor((Date.now() - buildStartTime.value) / 1000)
}
localStorage.removeItem('cloud_build_start_time')
localStorage.removeItem('cloud_build_task')
}
}
})
@ -146,22 +238,28 @@ const getCloudBuildLogFn = () => {
}).catch()
}
let notificationEl : any = null
const closeType = ref('normal')
const handleReturn = () => {
active.value = 'build'
closeType.value = 'success'
}
const notificationEl : any = null
/**
* 升级中任务提示
*/
const showElNotification = () => {
notificationEl = ElNotification.success({
title: t('warning'),
dangerouslyUseHTMLString: true,
message: h('div', {}, [
t('cloudbuild.executingTips'),
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('cloudbuild.clickView')])
]),
duration: 0,
showClose: false
})
}
// const showElNotification = () => {
// notificationEl = ElNotification.success({
// title: t('warning'),
// dangerouslyUseHTMLString: true,
// message: h('div', {}, [
// t('cloudbuild.executingTips'),
// h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('cloudbuild.clickView')])
// ]),
// duration: 0,
// showClose: false
// })
// }
const elNotificationClick = () => {
showDialog.value = true
@ -172,7 +270,7 @@ const elNotificationClick = () => {
const open = async () => {
loading.value = true
active.value = 'build'
closeType.value = 'normal'
if (cloudBuildTask.value) {
showDialog.value = true
loading.value = false
@ -194,6 +292,7 @@ const open = async () => {
} else {
loading.value = false
cloudBuildCheck.value = data
showDialog.value = true
}
}).catch(() => {
showDialog.value = false
@ -216,7 +315,7 @@ const onExecCmd = (key, command, success, failed, name) => {
}
const makeIterator = (array: string[]) => {
var nextIndex = 0
let nextIndex = 0
return {
next () {
if ((nextIndex + 1) == array.length) {
@ -228,7 +327,7 @@ const makeIterator = (array: string[]) => {
}
const dialogClose = (done: () => {}) => {
if (active.value == 'build' && cloudBuildTask.value) {
if (active.value == 'build' && cloudBuildTask.value && closeType.value == 'normal') {
ElMessageBox.confirm(
t('cloudbuild.showDialogCloseTips'),
t('warning'),
@ -239,19 +338,57 @@ const dialogClose = (done: () => {}) => {
}
).then(() => {
terminalRef.value.execute('clear')
localStorage.removeItem('cloud_build_start_time')
localStorage.removeItem('cloud_build_task')
done()
buildTimer && clearInterval(buildTimer)
buildTimer = null
buildStartTime.value = null
buildDuration.value = 0
}).catch(() => { })
} else {
done()
}
}
const dialogCancel = () => {
if (active.value == 'build' && cloudBuildTask.value && closeType.value == 'normal') {
ElMessageBox.confirm(
t('cloudbuild.showDialogCloseTips'),
t('warning'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
terminalRef.value.execute('clear')
localStorage.removeItem('cloud_build_start_time')
localStorage.removeItem('cloud_build_task')
buildTimer && clearInterval(buildTimer)
buildTimer = null
buildStartTime.value = null
buildDuration.value = 0
showDialog.value = false
}).catch(() => { })
} else {
showDialog.value = false
}
}
const cloudBuildCheckDirFn = () => {
window.open('https://doc.niucloud.com/v6.html?keywords=/chang-jian-wen-ti-chu-li/er-shi-wu-3001-sheng-7ea7-yun-bian-yi-mu-lu-du-xie-quan-xian-zhuang-tai-bu-tong-guo-ru-he-chu-li')
}
watch(() => showDialog.value, () => {
if (!showDialog.value) {
cloudBuildTask.value = null
active.value = 'build'
cloudBuildLog = []
flashInterval && clearInterval(flashInterval)
buildTimer && clearInterval(buildTimer)
buildStartTime.value = null
buildDuration.value = 0
clearCloudBuildTask()
}
})
@ -259,7 +396,9 @@ watch(() => showDialog.value, () => {
defineExpose({
open,
cloudBuildTask,
loading
loading,
elNotificationClick,
getCloudBuildTaskFn
})
</script>
@ -270,4 +409,30 @@ defineExpose({
:deep(.terminal .t-log-box span) {
white-space: pre-wrap;
}
:deep(.el-result__icon) {
color: unset !important; //
}
:deep(.el-dialog__title){
font-size: 20px;
font-weight: bold;
}
:deep(.el-result__title p){
font-size: 25px;
color: #1D1F3A;
font-weight: 500;
}
:deep(.el-result__subtitle p){
font-size: 15px;
color: #4F516D;
font-weight: 500;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
}
:deep(.el-result){
margin-top: -80px !important;
}
</style>

View File

@ -0,0 +1,245 @@
<template>
<el-dialog v-model="dialogVisible" :title="t('gxx')" width="850" :destroy-on-close="true">
<el-card class="box-card !border-none" shadow="never" >
<div v-loading="loading">
<div class="text-page-title mb-[20px]">历史版本</div>
<div class="time-dialog h-[500px]" style="overflow: auto">
<el-scrollbar>
<el-timeline style="width: 100%" v-if="!loading">
<el-timeline-item v-for="(item, index) in frameworkVersionList" :key="index" placement="left" :color="color">
<div class="relative">
<span class="text-[#333333] text-[14px] absolute">{{ timeSplit(item.release_time)[0] }}</span>
<br />
<span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute text-right">{{ timeSplit(item.release_time)[1] }}</span>
</div>
<el-collapse v-model="activeName" accordion>
<el-collapse-item :name="index">
<template #title>
<span class="text-[#333] text-[14px]"> v{{ item.version_no }} </span>
</template>
<div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">
<div v-html="item['upgrade_log']"></div>
</div>
</el-collapse-item>
</el-collapse>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</div>
</div>
</el-card>
</el-dialog>
</template>
<script lang="ts" setup>
import { computed, ref, defineProps, nextTick } from "vue"
import { t } from "@/lang"
import { getAppVersionList, getFrameworkVersionList } from "@/app/api/module"
const props = defineProps({
upgradeKey: {
type: String,
required: true
}
})
const frameworkVersionList = ref([])
const newVersion: any = computed(() => {
return frameworkVersionList.value.length ? frameworkVersionList.value[0] : null
})
const getAppVersionListFn = () => {
getAppVersionList({ app_key: props.upgradeKey }).then(({ data }) => {
loading.value = false
data.forEach((item: any, index) => {
if (index == 0) {
item.important = 1
} else {
item.important = 0
}
})
frameworkVersionList.value = data
if(frameworkVersionList.value.length == 0){
ElMessage.warning('暂无版本更新信息')
return
}else{
dialogVisible.value = true
}
})
}
const getFrameworkVersionListFn = () => {
getFrameworkVersionList().then(({ data }) => {
loading.value = false
data.forEach((item: any, index) => {
if (index == 0) {
item.important = 1
} else {
item.important = 0
}
})
frameworkVersionList.value = data
dialogVisible.value = true
})
}
const activeName = ref(0)
//
const loading = ref(true)
const dialogVisible = ref(false)
const open = async () => {
nextTick(() => {
activeName.value = 0 //
if (props.upgradeKey) {
getAppVersionListFn()
} else {
getFrameworkVersionListFn()
}
})
}
const timeSplit = (str: string) => {
const [date, time] = str.split(" ")
const [hours, minutes] = time.split(":")
return [date, `${ hours }:${ minutes }`]
}
defineExpose({
open
})
</script>
<style lang="scss" scoped></style>
<style scoped>
.el-timeline-item {
min-height: 75px;
}
.el-timeline-item >>> .el-timeline-item__node--normal {
left: 117px;
width: 18px;
height: 18px;
background: rgba(25, 103, 249, 0.12) !important;
border-radius: 50%;
/* 创建圆形 */
position: relative;
/* 用于定位伪元素 */
}
.el-timeline-item >>> .el-timeline-item__node--normal::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 8px;
/* 中心圆直径 */
height: 8px;
background-color: var(--el-color-primary);
/* 中心圆颜色 */
border-radius: 50%;
/* 中心圆为圆形 */
transform: translate(-50%, -50%);
/* 居中显示 */
}
.el-timeline-item >>> .el-timeline-item__tail {
left: 125px;
border-left-color: #dddddd;
border-left-style: dashed;
margin: 24px 0 12px;
height: calc(100% - 24px - 12px);
}
.time-dialog >>> .el-dialog__header {
padding: 10px 20px;
height: 25px;
line-height: 25px;
text-align: left;
background: #fff;
border-bottom: solid 1px #e4e7ed;
}
.time-dialog >>> .el-card__body {
padding: 8px;
}
.time-dialog >>> .el-card.is-always-shadow {
box-shadow: none;
}
.time-dialog >>> .el-dialog__headerbtn .el-dialog__close {
color: #666;
}
.time-dialog >>> .el-dialog__headerbtn:hover .el-dialog__close {
color: #666;
}
.time-dialog >>> .el-dialog__headerbtn {
top: 14px;
}
.time-dialog >>> .el-collapse {
margin-left: 119px;
border: none;
margin-top: -22px;
}
.time-dialog >>> .el-collapse-item__header {
border: none;
line-height: 25px;
height: 25px;
position: relative;
z-index: 999;
}
.time-dialog >>> .el-collapse-item__wrap {
border: none;
}
.time-dialog >>> .el-collapse-item__content {
margin-top: 15px;
padding-bottom: 0 !important;
}
.time-dialog >>> .el-timeline-item__node--01 {
width: 18px !important;
height: 18px !important;
left: 117px !important;
}
</style>
<style>
.time-dialog .el-dialog {
margin: 0 auto !important;
max-height: 90%;
overflow: hidden;
top: 5%;
}
.time-dialog .el-dialog {
margin: 0 auto !important;
height: 65%;
overflow: hidden;
top: 10%;
}
.time-dialog .el-dialog__body {
position: absolute;
left: 0;
top: 46px;
bottom: 0;
right: 0;
z-index: 1;
overflow: hidden;
overflow-y: auto;
padding: 10px 20px 0 0;
}
.time-dialog .el-timeline-item__wrapper {
top: -20px !important;
}
.el-scrollbar__bar{
z-index:999;
}
</style>

View File

@ -1,58 +1,102 @@
<template>
<el-dialog v-model="showDialog" :title="t('upgrade.title')" width="850px" :close-on-click-modal="false" :close-on-press-escape="false" :before-close="dialogClose">
<div v-show="active == 'content'">
<div class="h-[60vh] flex flex-col" v-if="upgradeContent">
<template v-if="upgradeContent">
<!-- 检测服务是否到期 -->
<template v-if="step == 1">
<template v-for="(item, index) in upgradeContent.content">
<div class="text-lg">
本次升级将从<span class="font-bold">{{ upgradeContent.version }}</span>升级到<span class="font-bold">{{ upgradeContent.upgrade_version }}</span>版本
<template v-if="item.upgrade_version">
<span>{{ item.app.app_name }}本次升级将从</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
<span>升级到</span>
<span class="font-bold px-[2px]">{{ item.upgrade_version }}</span>
<span>版本</span>
</template>
<template v-else>
<template v-if="upgradeContent.content.length > 1">
<span>{{ item.app.app_name }}当前版本</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
</template>
<template v-else>
<span>当前版本</span>
<span class="font-bold px-[2px]">{{ item.version }}</span>
</template>
</template>
</div>
<div class="mt-[10px]" v-if="upgradeContent.upgrade_version != upgradeContent.last_version">
<el-alert type="info" show-icon>
<div class="mt-[10px]" :class="{ 'mb-[10px]' : (index + 1) < upgradeContent.content.length }" v-if="item.upgrade_version != item.last_version">
<el-alert type="info" show-icon :closable="false">
<template #title>
当前最新版本为{{ upgradeContent.last_version }}您的服务已于{{ upgradeContent.expire_time }}到期如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级
<span>当前最新版本为{{ item.last_version }}您的服务{{ item.expire_time ? `已于${item.expire_time}到期` : '长期有效' }}</span>
<span>如需升级到最新版可在<a class="text-primary" href="https://www.niucloud.com" target="_blank">niucloud-admin官网</a>购买相关服务后再进行升级</span>
</template>
</el-alert>
</div>
<el-scrollbar class="flex-1 h-0 mt-[20px]">
<div class="mt-[20px]" v-for="(item, index) in upgradeContent.version_list" :key="index">
<div class="font-bold text-lg">{{ item.version_no }}</div>
<div class="mt-[5px]" v-if="item.release_time">{{ item.release_time }}</div>
<div class="mt-[10px] p-[10px] rounded bg-[#f4f4f5] whitespace-pre-wrap !break-all" v-if="item.upgrade_log" v-html="item.upgrade_log"></div>
</div>
</el-scrollbar>
</div>
<div class="flex justify-end" v-if="upgradeContent.version_list.length">
<el-button type="primary" @click="handleUpgrade" :loading="uploading">{{ t('upgrade.upgradeButton') }}</el-button>
</div>
</div>
</template>
</template>
<div v-if="step == 2">
<el-steps :active="numberOfSteps" align-center class="number-of-steps" process-status="process" v-if="!errorDialog && active != 'complete'">
<el-step :title="t('testDirectoryPermissions')" />
<el-step :title="t('upgrade.option')" />
<el-step :title="t('startUpgrade')" />
<el-step :title="t('upgradeEnd')" />
</el-steps>
<div class="h-[400px]" style="overflow: auto">
<!-- <div class="time-dialog-wrap mt-[30px]" v-show="active == 'content'">-->
<!-- <el-timeline style="width: 100%">-->
<!-- <el-timeline-item v-for="(item, index) in upgradeContent.version_list" :key="index" placement="left">-->
<!-- <div class="relative">-->
<!-- <span class="text-[#333333] text-[16px] absolute">{{ timeSplit(item.release_time)[0] }}</span>-->
<!-- <br />-->
<!-- <span class="text-[#999999] text-[14px] w-[78px] block mt-[10px] absolute" style="text-align: right"> {{ timeSplit(item.release_time)[1] }}</span>-->
<!-- </div>-->
<!-- <el-collapse v-model="activeName" accordion>-->
<!-- <el-collapse-item :name="index">-->
<!-- <template #title>-->
<!-- <span class="text-[#333] text-[16px]"> v{{ item.version_no }} </span>-->
<!-- </template>-->
<!-- <div class="px-[20px] py-[20px] bg-overlay timeline-log-wrap whitespace-pre-wrap rounded-[4px]" style="background: rgba(25, 103, 249, 0.03);" v-if="item['upgrade_log']">-->
<!-- <div v-html="item['upgrade_log']"></div>-->
<!-- </div>-->
<!-- </el-collapse-item>-->
<!-- </el-collapse>-->
<!-- </el-timeline-item>-->
<!-- </el-timeline>-->
<!-- </div>-->
<!-- 判断文件权限 -->
<div v-show="active == 'upgrade'">
<div class="h-[60vh] flex flex-col" v-if="upgradeCheck && !upgradeTask">
<div class="flex flex-col" v-if="upgradeCheck && !upgradeTask">
<el-scrollbar>
<div class="bg-[#fff] my-3" v-if="upgradeCheck.dir">
<p class="pt-[20px] pl-[20px] ">{{ t('upgrade.dirPermission') }}</p>
<div class="px-[20px] pt-[10px] text-[14px] el-table">
<el-row class="py-[10px] items table-head-bg pl-[15px] mb-[10px]">
<el-col :span="12">
<span>{{ t('upgrade.path') }}</span>
<el-col :span="18">
<span>{{ t("upgrade.path") }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.demand') }}</span>
<el-col :span="3">
<span>{{ t("upgrade.demand") }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('status') }}</span>
<el-col :span="3">
<span>{{ t("status") }}</span>
</el-col>
</el-row>
<div style="height: calc(300px); overflow: auto">
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_readable">
<el-col :span="12">
<el-col :span="18">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.readable') }}</span>
<el-col :span="3">
<span>{{ t("upgrade.readable") }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<el-col :span="3" >
<span v-if="item.status">
<el-icon color="green">
<Select />
</el-icon>
</span>
<span v-else>
<el-icon color="red">
<CloseBold />
@ -61,14 +105,18 @@
</el-col>
</el-row>
<el-row class="pb-[10px] items pl-[15px]" v-for="item in upgradeCheck.dir.is_write">
<el-col :span="12">
<el-col :span="18">
<span>{{ item.dir }}</span>
</el-col>
<el-col :span="6">
<span>{{ t('upgrade.write') }}</span>
<el-col :span="3">
<span>{{ t("upgrade.write") }}</span>
</el-col>
<el-col :span="6">
<span v-if="item.status"><el-icon color="green"><Select /></el-icon></span>
<el-col :span="3">
<span v-if="item.status">
<el-icon color="green">
<Select />
</el-icon>
</span>
<span v-else>
<el-icon color="red">
<CloseBold />
@ -78,109 +126,306 @@
</el-row>
</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="h-[60vh]" v-show="upgradeTask">
<terminal ref="terminalRef" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd"/>
</div>
<div class="h-[370px] mt-[30px]" v-show="showTerminal && upgradeTask && !errorDialog">
<terminal ref="terminalRef" :name="`upgrade-${terminalId}`" :context="upgradeTask ? upgradeTask.upgrade.app_key : ''" :init-log="null" :show-header="false" :show-log-time="true" @exec-cmd="onExecCmd" />
</div>
<div v-show="active == 'complete'">
<div class="h-[60vh] flex flex-col">
<div class="flex-1 h-0">
<el-result icon="success" :title="t('upgrade.upgradeSuccess')"></el-result>
<el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" />
</div>
<div class="flex justify-end">
<el-button type="default" @click="showDialog = false">{{ t('upgrade.localBuild') }}</el-button>
<el-button type="primary" @click="handleCloudBuild">{{ t('upgrade.cloudBuild') }}</el-button>
<!-- 是否备份选择 -->
<div class="flex flex-col" v-show="active == 'backup'">
<el-scrollbar>
<div class="bg-[#fff] my-3">
<div class="p-[20px] mt-[50px] mx-[10px] border-[1px] border-[#E6E6E6] rounded-[10px]">
<div class="flex justify-between items-center mt-[-9px]">
<el-checkbox v-model="upgradeOption.is_need_cloudbuild" :label="t('upgrade.isNeedCloudbuild')" :true-value="true" :false-value="false" size="large" ></el-checkbox>
</div>
<div class="text-[14px] text-[#374151] mb-[10px]">{{ t('upgrade.cloudbuildTips') }}</div>
</div>
<div class="p-[20px] mt-[20px] mx-[10px] border-[1px] border-[#E6E6E6] rounded-[10px]" v-if="upgradeContent.last_backup">
<div class="flex justify-between items-center mt-[-9px]">
<el-checkbox v-model="upgradeOption.is_need_backup" :label="t('upgrade.isNeedBackup')" :true-value="true" :false-value="false" size="large" ></el-checkbox>
<el-button link type="primary" class="!text-[#9699B6]" @click="toBackupRecord">{{ t('upgrade.isNeedBackupBtn') }}</el-button>
</div>
<div class="text-[14px] text-[#374151] mb-[10px]">{{ t('upgrade.isNeedBackupTips') }}</div>
<div class="text-[14px] text-[#9699B6]">{{ t('上次备份时间:') }}{{ upgradeContent.last_backup.complete_time }}</div>
</div>
</div>
</el-scrollbar>
</div>
<div class="mt-[20px] h-[370px]" v-show="errorDialog">
<el-result icon="error" :title="t('升级失败')">
<template #icon>
<img src="@/app/assets/images/error_icon.png" alt="" />
</template>
<template #extra>
<el-scrollbar class="max-h-[120px] !overflow-auto text-[15px] text-[#4F516D] mb-[15px] mt-[-15px]">
{{errorMsg}}
</el-scrollbar>
<el-button @click="handleBack()" class="!w-[90px]">错误信息</el-button>
<el-button @click="showDialog=false" type="primary" class="!w-[90px]">完成</el-button>
</template>
</el-result>
</div>
<div class="mt-[20px]" v-show="active == 'complete'">
<el-result icon="success" :title="t('upgrade.upgradeSuccess')">
<template #icon>
<img src="@/app/assets/images/success_icon.png" alt="">
</template>
<template #extra>
<div class="text-[16px] text-[#4F516D] mt-[-5px]" v-show="upgradeTask && upgradeTask.executed && !upgradeTask.executed.includes('cloudBuild')">{{ t('upgrade.upgradeCompleteTips') }}</div>
<div class="text-[16px] text-[#9699B6] mt-[10px]">本次升级用时{{ formatUpgradeDuration }}</div>
<div class="mt-[20px]">
<el-button @click="handleBack()" class="!w-[90px]">返回</el-button>
<el-button @click="showDialog=false" type="primary" class="!w-[90px]">完成</el-button>
</div>
</template>
</el-result>
<!-- <el-alert :title="t('upgrade.upgradeCompleteTips')" type="error" :closable="false" v-show="upgradeTask && upgradeTask.executed && !upgradeTask.executed.includes('cloudBuild')"/> -->
</div>
</div>
</div>
</template>
<template #footer>
<div class="dialog-footer">
<!-- 查看升级内容 -->
<el-button v-if="step == 1 && upgradeContent.content.length && isAllowUpgrade" @click="step = 2" type="primary">{{ t("upgrade.upgradeButton") }}</el-button>
<el-button type="primary" v-show="step == 2 && showTerminal && upgradeTask && !errorDialog" :loading="timeloading" class="!w-[140px]">已用时 {{ formatUpgradeDuration }}</el-button>
<template v-if="step == 2 && active != 'complete'">
<!-- <el-button v-if="active == 'content'" @click="showDialog = false">{{ t("return") }}</el-button>-->
<el-button type="primary" :disabled="!is_pass" v-if="active == 'upgrade' && !upgradeTask" @click="() => { active = 'backup'; numberOfSteps = 1 }">{{ t("nextStep") }}</el-button>
<el-button v-if="active == 'backup'" @click="() => { active = 'upgrade'; numberOfSteps = 1 } ">{{ t("prev") }}</el-button>
<el-button type="primary" v-if="active == 'backup'" :loading="loading" @click="() => { upgradeAddonFn() }">{{ t("nextStep") }}</el-button>
<el-button v-if="active == 'complete'" @click="showDialog = false">{{ t("complete") }}</el-button>
</template>
</div>
</template>
</el-dialog>
<el-dialog v-model="upgradeTipsShowDialog" :title="t('warning')" width="500px" draggable>
<span v-html="t('upgrade.upgradeTips')"></span>
<template #footer>
<div class="flex justify-end">
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t('upgrade.knownToKnow') }}</el-button>
<el-button @click="upgradeTipsConfirm()" type="primary" plain>{{ t('upgrade.upgradeButton') }}</el-button>
<el-button @click="upgradeTipsShowDialog = false">{{ t('cancel') }}</el-button>
<el-button @click="upgradeTipsConfirm(true)" type="primary">{{ t("upgrade.knownToKnow") }}</el-button>
<el-button @click="handleUpgrade()" type="primary" plain :loading="readyLoading">{{ t("upgrade.upgradeButton") }}</el-button>
<el-button @click="upgradeTipsShowDialog = false">{{ t("cancel") }}</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="cloudBuildErrorTipsShowDialog" :title="t('warning')" width="500px" draggable :show-close="false">
<span v-html="t('upgrade.cloudBuildErrorTips')"></span>
<template #footer>
<div class="flex justify-end">
<el-button @click="cloudBuildError('local')" type="primary">{{ t("upgrade.localBuild") }}</el-button>
<el-button @click="cloudBuildError('retry')" type="primary">{{ t("upgrade.cloudBuild") }}{{ retrySecond }}S</el-button>
<el-button @click="cloudBuildError('rollback')" type="primary">{{ t("upgrade.rollback") }}</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref, h, watch } from 'vue'
import { ref, h, watch, computed } from 'vue'
import { t } from '@/lang'
import { getVersions } from '@/app/api/auth'
import { getFrameworkNewVersion } from '@/app/api/module'
import { getUpgradeContent, getUpgradeTask, upgradeAddon, executeUpgrade, preUpgradeCheck, clearUpgradeTask } from '@/app/api/upgrade'
import {
getUpgradeContent,
getUpgradeTask,
upgradeAddon,
executeUpgrade,
preUpgradeCheck,
clearUpgradeTask, upgradeUserOperate
} from '@/app/api/upgrade'
import { Terminal, TerminalFlash } from 'vue-web-terminal'
import 'vue-web-terminal/lib/theme/dark.css'
import { AnyObject } from '@/types/global'
import { ElNotification, ElMessage, ElMessageBox } from 'element-plus'
import Storage from '@/utils/storage'
import { useRouter } from 'vue-router'
const router = useRouter()
const terminalId = ref(Date.now());
const showDialog = ref<boolean>(false)
const upgradeContent = ref<null | AnyObject>(null)
const isAllowUpgrade = ref(true) //
const upgradeTask = ref<null | AnyObject>(null)
const active = ref('content')
const active = ref('upgrade')
const step = ref(1)
const upgradeCheck = ref<null | AnyObject>(null)
const uploading = ref(false)
const loading = ref(false)
const terminalRef: any = ref(null)
const emits = defineEmits(['complete', 'cloudbuild'])
const upgradeTipsShowDialog = ref<boolean>(false)
let upgradeLog: any = []
let errorLog: any = []
const cloudBuildErrorTipsShowDialog = ref<boolean>(false)
const retrySecond = ref(30)
let retrySecondInterval: any = null
const upgradeOption = ref({
is_need_backup: true,
is_need_cloudbuild: true
})
// backupCode backupSql
const excludeSteps: any = ref([
{
name: '备份源码',
code: 'backupCode'
},
{
name: '备份数据库',
code: 'backupSql'
}
])
/**
* 查询升级任务
*/
const timeloading = ref(false)
const showTerminal = ref(false)
const upgradeStartTime = ref<number | null>(null)
const upgradeDuration = ref(0) //
let upgradeTimer: ReturnType<typeof setInterval> | null = null
const errorDialog = ref(false)
const errorMsg = ref('')
const getUpgradeTaskFn = () => {
getUpgradeTask().then(({ data }) => {
if (!data) return
if (!upgradeContent.value) {
upgradeContent.value = data.upgrade_content
if (!data.upgrade_content || !Array.isArray(data.upgrade_content.content)) {
return
}
let upgradeCount = 0
let failUpgradeCount = 0
for (let i = 0; i < upgradeContent.value.content.length; i++) {
if (upgradeContent.value.content[i].version_list.length) {
upgradeCount++
} else {
failUpgradeCount++
}
}
if (upgradeContent.value.content.length == upgradeCount) {
isAllowUpgrade.value = true
} else if (upgradeContent.value.content.length == failUpgradeCount) {
isAllowUpgrade.value = false
}
}
//
if (!showDialog.value) {
showElNotification()
return
}
if (!upgradeTask.value) {
showTerminal.value = true
terminalRef.value.execute('clear')
terminalRef.value.execute('开始升级')
timeloading.value = true
upgradeStartTime.value = Date.now()
upgradeDuration.value = 0
if (upgradeTimer) clearInterval(upgradeTimer)
upgradeTimer = setInterval(() => {
upgradeDuration.value++
}, 1000)
}
data.log.forEach(item => {
upgradeTask.value = data
data.log.forEach((item) => {
if (!upgradeLog.includes(item)) {
terminalRef.value.pushMessage({content: `正在执行:${item}`})
terminalRef.value.pushMessage({ content: `${item}` })
upgradeLog.push(item)
}
})
//
if (data.error) {
upgradeTask.value = data
ElMessage({ message: '升级失败', type: 'error' })
terminalRef.value.pushMessage({ content: data.error, class: 'error' })
data.error.forEach((item) => {
if (!errorLog.includes(item)) {
terminalRef.value.pushMessage({ content: item, class: 'error' })
errorLog.push(item)
errorMsg.value = item
}
})
errorDialog.value = true
showTerminal.value = false
if (upgradeTimer) {
clearInterval(upgradeTimer)
upgradeTimer = null
}
timeloading.value = false
}
//
if (data.step == 'restoreComplete') {
flashInterval && clearInterval(flashInterval)
return
}
//
//
if (data.step == 'upgradeComplete') {
active.value = 'complete'
showTerminal.value = false
numberOfSteps.value = 4
notificationEl && notificationEl.close()
emits('complete')
if (upgradeTimer) {
clearInterval(upgradeTimer)
upgradeTimer = null
}
timeloading.value = false
clearUpgradeTask()
return
}
upgradeTask.value = data
numberOfSteps.value = 2
active.value = 'upgrade'
executeUpgradeFn()
}).catch()
})
}
getUpgradeTaskFn()
const isBack = ref(false)
const handleBack = () => {
active.value = 'upgrade'
isBack.value = true
showTerminal.value = true
errorDialog.value = false //
}
const formatUpgradeDuration = computed(() => {
const s = upgradeDuration.value
const h = Math.floor(s / 3600)
const m = Math.floor((s % 3600) / 60)
const sec = s % 60
return [
h > 0 ? `${h}小时` : '',
m > 0 ? `${m}分钟` : '',
`${sec}`
].filter(Boolean).join('')
})
const executeUpgradeFn = () => {
executeUpgrade().then(() => {
getUpgradeTaskFn()
}).catch()
}).catch((err) => {
if (err.message.indexOf('队列') != -1) {
retrySecond.value = 30
retrySecondInterval = setInterval(() => {
retrySecond.value--
if (retrySecond.value == 0) {
cloudBuildError('retry')
}
}, 1000)
cloudBuildErrorTipsShowDialog.value = true
} else {
ElMessage({ message: err.message, type: 'error' })
}
})
}
let notificationEl: any = null
@ -191,10 +436,10 @@ const showElNotification = () => {
notificationEl = ElNotification.success({
title: t('warning'),
dangerouslyUseHTMLString: true,
message: h('div', {}, [
t('upgrade.upgradingTips'),
h('span', { class: 'text-primary cursor-pointer', onClick: elNotificationClick }, [t('upgrade.clickView')])
]),
message: h('div', {}, [t('upgrade.upgradingTips'), h('span', {
class: 'text-primary cursor-pointer',
onClick: elNotificationClick
}, [t('upgrade.clickView')])]),
duration: 0,
showClose: false
})
@ -202,16 +447,18 @@ const showElNotification = () => {
const elNotificationClick = () => {
showDialog.value = true
active.value = 'upgrade'
getUpgradeTaskFn()
step.value = 2
numberOfSteps.value = 3
active.value = 'upgrade'
notificationEl && notificationEl.close()
}
const frameworkVersion = ref('')
getVersions().then(res => {
getVersions().then((res) => {
frameworkVersion.value = res.data.version.version
})
const newFrameworkVersion = ref("")
const newFrameworkVersion = ref('')
getFrameworkNewVersion().then(({ data }) => {
newFrameworkVersion.value = data.last_version
})
@ -219,44 +466,90 @@ getFrameworkNewVersion().then(({ data }) => {
/**
* 执行升级
*/
const handleUpgrade = async () => {
if (uploading.value) return
uploading.value = true
const is_pass = ref(false)
const repeat = ref(false)
const readyLoading = ref(false)
const appKey = upgradeContent.value?.app.app_key != 'niucloud-admin' ? upgradeContent.value?.app.app_key : ''
const handleUpgrade = async () => {
if (repeat.value) return
repeat.value = true
readyLoading.value = true
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
await preUpgradeCheck(appKey).then(async ({ data }) => {
if (data.is_pass) {
await upgradeAddon(appKey).then(() => {
upgradeCheck.value = data
is_pass.value = data.is_pass
active.value = 'upgrade'
!upgradeTask.value ? (numberOfSteps.value = 0) : numberOfSteps.value
upgradeTipsShowDialog.value = false
showDialog.value = true
repeat.value = false
readyLoading.value = false
}).catch(() => {
repeat.value = false
readyLoading.value = false
})
}
const upgradeAddonFn = () => {
if (!is_pass.value) return
if (loading.value) return
loading.value = true
const appKey = upgradeContent.value?.upgrade_apps.join(',') != 'niucloud-admin' ? upgradeContent.value?.upgrade_apps.join(',') : ''
upgradeAddon(appKey, upgradeOption.value).then(() => {
getUpgradeTaskFn()
}).catch(() => {
uploading.value = false
loading.value = false
})
} else {
upgradeCheck.value = data
}
}).catch()
if (uploading.value) active.value = 'upgrade'
}
const open = (addonKey: string = '') => {
const open = (addonKey: string = '', callback = null) => {
errorDialog.value = false //
if (upgradeTask.value) {
ElMessage({ message: '已有正在执行中的升级任务', type: 'error' })
showDialog.value = true
step.value = 2
numberOfSteps.value = 3
active.value = 'upgrade'
if (callback) callback()
} else {
if (addonKey && frameworkVersion.value != newFrameworkVersion.value) {
if (addonKey && addonKey.indexOf('niucloud-admin') == -1 && frameworkVersion.value != newFrameworkVersion.value) {
ElMessage({ message: '存在新版本框架,请先升级框架', type: 'error' })
if (callback) callback()
return
}
if (loading.value) return
loading.value = true
getUpgradeContent(addonKey).then(({ data }) => {
loading.value = false
upgradeContent.value = data
let upgradeCount = 0
let failUpgradeCount = 0
for (let i = 0; i < upgradeContent.value.content.length; i++) {
if (upgradeContent.value.content[i].version_list.length) {
upgradeCount++
} else {
failUpgradeCount++
}
}
if (upgradeContent.value.content.length == upgradeCount) {
isAllowUpgrade.value = true
} else if (upgradeContent.value.content.length == failUpgradeCount) {
isAllowUpgrade.value = false
}
if (Storage.get('upgradeTipsLock')) {
showDialog.value = true
handleUpgrade()
} else {
upgradeTipsShowDialog.value = true
}
}).catch()
if (callback) callback()
}).catch(() => {
loading.value = false
if (callback) callback()
})
}
}
@ -276,10 +569,10 @@ const onExecCmd = (key, command, success, failed, name) => {
}
const makeIterator = (array: string[]) => {
var nextIndex = 0
let nextIndex = 0
return {
next () {
if ((nextIndex + 1) == array.length) {
if (nextIndex + 1 == array.length) {
nextIndex = 0
}
return { value: array[nextIndex++] }
@ -288,44 +581,78 @@ const makeIterator = (array: string[]) => {
}
const dialogClose = (done: () => {}) => {
if (active.value == 'upgrade' && upgradeTask.value && !upgradeTask.value.error) {
ElMessageBox.confirm(
t('upgrade.showDialogCloseTips'),
t('warning'),
{
if (active.value == 'upgrade' && upgradeTask.value && ['upgradeComplete', 'restoreComplete'].includes(upgradeTask.value.step) === false && !isBack.value) {
ElMessageBox.confirm(t('upgrade.showDialogCloseTips'), t('warning'), {
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
type: 'warning'
}
).then(() => {
}).then(() => {
done()
}).catch(() => { })
})
} else {
done()
}
}
watch(() => showDialog.value, () => {
watch(
() => showDialog.value,
() => {
if (!showDialog.value) {
clearUpgradeTaskFn()
}
})
}
)
const clearUpgradeTaskFn = () => {
active.value = 'content'
uploading.value = false
active.value = 'upgrade'
loading.value = false
upgradeTask.value = null
isBack.value = false
errorDialog.value = false
errorMsg.value = ''
showTerminal.value = false
upgradeLog = []
errorLog = []
numberOfSteps.value = 0
flashInterval && clearInterval(flashInterval)
clearUpgradeTask().then(() => {}).catch()
retrySecondInterval && clearInterval(retrySecondInterval)
upgradeOption.value = {
is_need_backup: true,
is_need_cloudbuild: true
}
step.value = 1
clearUpgradeTask().then(() => {
})
}
/**
* 云编译
* 云编译队列不足操作
* @param event
*/
const handleCloudBuild = () => {
showDialog.value = false
emits('cloudbuild')
const cloudBuildError = (event: string) => {
cloudBuildErrorTipsShowDialog.value = false
switch (event) {
case 'local':
upgradeUserOperate(event).then(() => {
getUpgradeTaskFn()
})
break
case 'retry':
executeUpgradeFn()
retrySecondInterval && clearInterval(retrySecondInterval)
break
case 'rollback':
upgradeUserOperate(event).then(() => {
getUpgradeTaskFn()
})
break
}
}
const timeSplit = (str: string) => {
const [date, time] = str.split(' ')
const [hours, minutes] = time.split(':')
return [date, `${hours}:${minutes}`]
}
const upgradeTipsConfirm = (isLock: boolean = false) => {
@ -333,17 +660,253 @@ const upgradeTipsConfirm = (isLock: boolean = false) => {
upgradeTipsShowDialog.value = false
!isLock && (showDialog.value = true)
}
const activeName = ref(0)
const numberOfSteps = ref(0)
const toBackupRecord = () => {
const routeUrl = router.resolve({
path: '/tools/backup_records'
})
window.open(routeUrl.href, '_blank')
}
defineExpose({
open
open,
loading
})
</script>
<style lang="scss" scoped>
:deep(.el-button){
border-radius: 4px !important;
}
.table-head-bg {
background-color: var(--el-table-header-bg-color);
}
:deep(.el-checkbox__label){
color: var(--el-color-primary);
}
:deep(.terminal .t-log-box span) {
white-space: pre-wrap;
}
:deep(.number-of-steps) {
.el-step__line {
margin: 0 25px;
background: #dddddd;
}
.el-step__head {
margin-top: 10px;
}
.is-success {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
.el-step__icon {
background: var(--el-color-primary);
color: #fff;
// box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
i {
color: #fff;
}
}
.el-step__line {
margin: 0 25px;
background: var(--el-color-primary);
}
}
.is-finish {
color: var(--el-color-primary);
border-color: var(--el-color-primary);
.el-step__icon {
background: var(--el-color-primary)!important;
color: #fff !important;
// box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
i {
color: #fff;
}
}
.el-step__line {
margin: 0 25px;
background: var(--el-color-primary);
}
}
.is-process {
color: var(--el-color-primary);
font-weight: inherit;
// font-size: 18px;
.el-step__icon {
padding: 10px;
border: 1px solid var(--el-color-primary);
background: var(--el-color-primary)!important;
color: #fff !important;
// box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
}
}
.is-wait {
color: #333;
}
}
:deep(.el-dialog__title){
font-size: 20px;
font-weight: bold;
}
:deep(.el-result__title p){
font-size: 25px;
color: #1D1F3A;
font-weight: 500;
}
:deep(.el-result__subtitle p){
font-size: 15px;
color: #4F516D;
font-weight: 500;
word-break: break-all;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
</style>
<style scoped>
.el-timeline-item {
min-height: 100px;
}
.el-timeline-item >>> .el-timeline-item__node--normal {
left: 117px;
width: 18px;
height: 18px;
background: rgba(25, 103, 249, 0.12) !important;
border-radius: 50%;
/* 创建圆形 */
position: relative;
/* 用于定位伪元素 */
}
.el-timeline-item >>> .el-timeline-item__node--normal::before {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 8px;
/* 中心圆直径 */
height: 8px;
background-color: var(--el-color-primary);
/* 中心圆颜色 */
border-radius: 50%;
/* 中心圆为圆形 */
transform: translate(-50%, -50%);
/* 居中显示 */
}
.el-timeline-item >>> .el-timeline-item__tail {
left: 125px;
border-left-color: #dddddd;
border-left-style: dashed;
margin: 24px 0 12px;
height: calc(100% - 24px - 12px);
}
.time-dialog-wrap >>> .el-dialog__header {
padding: 10px 20px;
height: 25px;
line-height: 25px;
text-align: left;
background: #fff;
border-bottom: solid 1px #e4e7ed;
}
.time-dialog-wrap >>> .el-card__body {
padding: 8px;
}
.time-dialog-wrap >>> .el-card.is-always-shadow {
box-shadow: none;
}
.time-dialog-wrap >>> .el-dialog__headerbtn .el-dialog__close {
color: #666;
}
.time-dialog-wrap >>> .el-dialog__headerbtn:hover .el-dialog__close {
color: #666;
}
.time-dialog-wrap >>> .el-dialog__headerbtn {
top: 14px;
}
.time-dialog-wrap >>> .el-collapse {
margin-left: 119px;
border: none;
margin-top: -22px;
}
.time-dialog-wrap >>> .el-collapse-item__header {
border: none;
line-height: 25px;
height: 25px;
position: relative;
z-index: 999;
}
.time-dialog-wrap >>> .el-collapse-item__wrap {
border: none;
}
.time-dialog-wrap >>> .el-collapse-item__content {
margin-top: 15px;
padding-bottom: 0 !important;
}
.time-dialog-wrap >>> .el-timeline-item__node--01 {
width: 18px !important;
height: 18px !important;
left: 117px !important;
}
</style>
<style>
.time-dialog-wrap .el-dialog {
margin: 0 auto !important;
max-height: 90%;
overflow: hidden;
top: 5%;
}
.time-dialog-wrap .el-dialog {
margin: 0 auto !important;
height: 65%;
overflow: hidden;
top: 10%;
}
.time-dialog-wrap .el-dialog__body {
position: absolute;
left: 0;
top: 46px;
bottom: 0;
right: 0;
z-index: 1;
overflow: hidden;
overflow-y: auto;
padding: 10px 20px 0 0;
}
.time-dialog-wrap .el-timeline-item__wrapper {
top: -20px !important;
}
</style>

View File

@ -2,13 +2,13 @@
<el-dialog v-model="dialogVisible" :title="t('accountSettings')" width="500">
<el-form :model="saveInfo" label-width="90px" ref="formRef" class="page-form">
<el-form-item :label="t('headImg')">
<upload-image v-model="saveInfo.head_img" :limit="1" :type="'avatar'" imageFit="cover" />
<upload-image v-model="saveInfo.head_img" :limit="1" imageFit="cover" />
</el-form-item>
<el-form-item :label="t('userName')">
<span>{{saveInfo.username}}</span>
</el-form-item>
<el-form-item :label="t('realName')">
<el-input v-model="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
<el-input v-model.trim="saveInfo.real_name" :placeholder="t('realNamePlaceholder')" clearable class="input-width" />
</el-form-item>
</el-form>
<template #footer>
@ -25,11 +25,9 @@ import { t } from '@/lang'
import type { FormInstance } from 'element-plus'
import { deepClone } from '@/utils/common'
import { getUserInfo, setUserInfo } from '@/app/api/personal'
import { useRouter } from 'vue-router'
import useUserStore from '@/stores/modules/user'
const userStore = useUserStore()
const router = useRouter()
//
const saveInfo: any = reactive({})
const formRef = ref<FormInstance>()

View File

@ -0,0 +1,18 @@
{
"companyName": "授权主体",
"siteAddress": "授权域名",
"contactName": "授权联系人",
"authCode": "授权码",
"authSecret": "授权秘钥",
"createTime": "授权时间",
"expireTime": "到期时间",
"authApp": "授权应用",
"authAppKey": "应用标识",
"siteAddressTips": "授权域名不匹配",
"authCodePlaceholder": "请输入授权码",
"authSecretPlaceholder": "请输入授权秘钥",
"updateCode": "重新绑定",
"notHaveAuth": "还没有授权?去购买",
"authInfoTips": "授权码和授权秘钥可在Niucloud官网我的授权 授权详情中查看",
"versionTips": "已经升级到最新版本"
}

View File

@ -1,6 +1,7 @@
{
"ip":"登录IP",
"username":"管理员姓名",
"operationLog": "操作日志",
"url":"链接",
"detail": "详情",
"params":"参数",
@ -14,6 +15,5 @@
"typePlaceholder":"请输入请求方式",
"createTimePlaceholder":"请输入操作时间",
"addSysUserLog":"添加管理员操作记录表",
"updateSysUserLog":"编辑管理员操作记录表",
"sys_user_logDeleteTips":"确定要删除该管理员操作记录表吗?"
"updateSysUserLog":"编辑管理员操作记录表"
}

View File

@ -29,5 +29,14 @@
"managerPlaceholder": "请选择用户",
"managerTips": "选择或者新增用户作为管理员",
"newAddManager": "新增用户",
"userDeleteTips": "是否要删除该管理员?"
"userDeleteTips": "是否要删除该管理员?",
"addRole": "新增角色",
"updateRole": "编辑角色",
"roleName": "角色名称",
"roleDeleteTips": "确定要删除该角色吗?",
"roleNamePlaceholder": "请输入角色名称",
"rulesPlaceholder": "请选择权限",
"checkStrictly": "父子级不关联",
"permission": "权限",
"foldText":"展开/折叠"
}

View File

@ -0,0 +1,13 @@
{
"accessFlow": "接入流程",
"versionManage": "版本管理",
"title": "APP端管理",
"appInlet": "App接入流程",
"uniappApp": "uni-app应用开通",
"appAttestation1": "点击进入Dcloud官网开发者后台,创建或选择应用并维护好应用平台信息",
"clickAccess": "点击接入",
"appSetting": "App端配置",
"settingInfo": "点击配置",
"releaseVersion": "发布版本",
"toCreate": "去创建"
}

View File

@ -0,0 +1,17 @@
{
"wechatAppInfo": "微信应用信息",
"wechatAppid": "微信移动应用AppID",
"wechatAppidTips": "用于app端 微信登录 微信支付 微信分享",
"wechatAppsecret": "微信移动应用AppSecret",
"appidPlaceholder": "请输入AppID",
"appSecretPlaceholder": "请输入AppSecret",
"appInfo": "应用信息",
"uniAppId": "uniapp应用id",
"uniAppIdTips": "uniapp应用id需在Dcloud开发者中心创建",
"toCreate": "前往Dcloud官网",
"appName": "应用名称",
"applicationId": "安卓应用包名",
"applicationIdTips": "安卓应用的包名是Android系统中用于唯一标识应用的字符串采用反向域名格式如com.example.myapp。每个应用在系统中拥有唯一的包名用于区分不同应用",
"androidAppKey": "安卓离线打包Key",
"androidAppKeyTips": "安卓离线打包Key在Dcloud开发者中心 - 应用管理 - 点击应用 - 各平台信息 创建以及查看离线AppKey"
}

View File

@ -0,0 +1,55 @@
{
"accessFlow": "接入流程",
"versionManage": "版本管理",
"versionCode":"版本号",
"versionCodePlaceholder":"请输入版本号",
"versionCodeTips": "应用版本号必须是整数取值范围1~2147483647;必须高于上一版本设置的值",
"versionName":"版本名称",
"versionNamePlaceholder":"请输入版本名称",
"versionDesc":"",
"versionDescPlaceholder":"请输入",
"icon":"应用图标",
"iconPlaceholder":"请输入应用图标",
"push":"消息推送图标",
"pushPlaceholder":"请输入消息推送图标",
"splash":"应用启动页图标",
"splashPlaceholder":"请输入应用启动页的图标",
"platform":"客户端",
"packagePath":"安装包路径",
"packagePathPlaceholder":"请输入安装包路径",
"status":"状态",
"statusPlaceholder":"请输入状态",
"addAppVersion":"添加版本",
"updateAppVersion":"编辑版本",
"startDate":"请选择开始时间",
"endDate":"请选择结束时间",
"isForcedUpgrade": "强制升级",
"versionDesc": "更新内容",
"isForcedUpgradeTitle": "是否强制升级",
"releaseTime": "发布时间",
"release": "发布",
"resourceFile": "上传资源文件",
"androidResourceFileTips": "只能上传apk文件",
"iosResourceFileTips": "只能上传wgt文件",
"index": "序号",
"next": "下一步",
"prev": "上一步",
"certType": "证书类型",
"certFile": "证书文件",
"certAlias": "证书别名",
"certKeyPassword": "证书密码",
"certStorePassword": "证书库密码",
"publicCertTips": "niucloud提供的公共测试证书证书的描述信息都是测试数据任何人都可以使用仅适合应用开发期间体验测试使用",
"privateCertTips": "Android平台打包发布apk应用需要使用数字证书.keystore文件进行签名用于表明开发者身份。",
"download": "下载",
"failReason": "失败原因",
"appVersionReleaseTips": "发布后无法再对该版本进行修改,确定要发布该版本吗?",
"appVersionDeleteTips": "确定要删除该版本吗?",
"upgradeType": "升级方式",
"seeBuildLog": "查看打包日志",
"buildLog": "打包日志",
"authTips": "上传代码需先绑定授权码如果已有授权请先进行绑定没有授权可到niucloud官网购买云服务之后再进行操作",
"toBind": "绑定授权",
"toNiucloud": "去niucloud官网",
"siteAuthTips": "上传代码需先绑定授权码,请联系平台管理员进行绑定"
}

View File

@ -42,5 +42,8 @@
"uploadSuccessTips": "小程序上传成功后还需到<a href='https://mp.weixin.qq.com/' target='_blank' class='text-primary'>微信公众平台</a>提交审核,审核通过后发布才算正式上线。",
"knownToKnow": "我已知晓,不需要再次提示",
"siteAuthTips": "上传代码需先绑定授权码,请联系平台管理员进行绑定",
"againUpload": "重新上传"
"againUpload": "重新上传",
"uploadWeapp": "上传小程序",
"undoAudit" : "撤回审核",
"undoAuditTips" : "撤回代码审核,单个账号每天审核撤回次数最多不超过 5 次每天的额度从0点开始生效一个月不超过 10 次。是否要继续撤回?"
}

View File

@ -34,5 +34,26 @@
"weappUpload": "小程序代码上传",
"uploadKey": "上传密钥",
"uploadKeyTips": "配置之后可实现在线上传小程序版本",
"uploadIpTips": "如果小程序代码上传开启了ip白名单设置在ip白名单中添加ip"
"uploadIpTips": "如果小程序代码上传开启了ip白名单设置在ip白名单中添加ip",
"update": "修改",
"udpUrl": "udp合法域名",
"tcpUrl": "tcp合法域名",
"requestdomainPlaceholder": "以 https:// 开头。域名间请用 ; 分割",
"wsrequestdomainPlaceholder": "以 wss:// 开头。域名间请用 ; 分割",
"uploaddomainPlaceholder": "以 https:// 开头。域名间请用 ; 分割",
"downloaddomainPlaceholder": "以 https:// 开头。域名间请用 ; 分割",
"udpdomainPlaceholder": "以 udp:// 开头。域名间请用 ; 分割",
"tcpdomainPlaceholder": "以 tcp:// 开头。域名间请用 ; 分割",
"domainError": "该域名协议头非法",
"serviceContentStatement": "服务内容声明",
"privacyAgreement": "用户隐私保护指引",
"privacyAgreementTips": "基于微信提供的 标准化用户隐私保护指引,根据小程序实际情况更新并展示给用户。",
"setting": "设置",
"privacyAgreementTitle": "用户隐私保护指引设置",
"settingPlaceholder": "请填写用途",
"addSettingType": "增加信息类型",
"settingTypeTitle": "使用用户信息类型",
"addContact": "增加联系方式",
"addSdkInfo": "增加第三方SDK信息",
"addSdkSettingList": "增加SDK提供方的隐私信息"
}

View File

@ -29,5 +29,8 @@
"encodingAesKeyPlaceholder": "请输入EncodingAESKey",
"cleartextModeTips": "明文模式下,不使用消息体加解密功能,安全系数较低",
"compatibleModeTips": "兼容模式下,明文、密文将共存,方便开发者调试和维护",
"safeModeTips": "安全模式下,消息包为纯密文,需要开发者加密和解密,安全系数高"
"safeModeTips": "安全模式下,消息包为纯密文,需要开发者加密和解密,安全系数高",
"wechatBaseUri": "借权域名",
"wechatBaseUriPlaceholder": "",
"wechatBaseUriTips": "默认留空填写后将替换https://open.weixin.gg.com/获取授权!"
}

View File

@ -14,6 +14,14 @@
"statusBarStyle": "导航栏样式",
"statusBarSwitchTips": "此处控制当前页面导航栏是否显示",
"bottomNavContent": "底部导航内容",
"popWindowAds": "弹窗广告",
"popAdsLink": "广告链接",
"popAdsImage": "广告图",
"popAdsType": "显示类型",
"popAdsIsEnabled": "是否显示",
"firstPop": "首次弹出",
"popWindowCountTips": "建议上传图片大小290px * 410px",
"everyTimePops": "每次弹出",
"diyPageTitle": "页面名称",
"diyPageTitlePlaceholder": "请输入页面名称",
"pageTitleTips": "页面名称用于后台显示",
@ -62,6 +70,8 @@
"imageUpload": "图片上传",
"imageSet": "图片设置",
"imageAdsTips": "建议上传尺寸相同的图片推荐尺寸750*350",
"imageAdsSameScreenTips": "开启沉浸式样式,请确保该图片广告组件在页面中位于最顶端;为保证体验,请不要开导航栏;沉浸式样式仅在微信小程序中生效。",
"sameScreen": "沉浸式",
"addImageAd": "添加图片",
"imageUrlTip": "请上传图片",
"imageHeight": "图片高度",
@ -86,6 +96,7 @@
"graphicNavShowStyle": "展示风格",
"graphicNavStyleFixed": "固定显示",
"graphicNavStyleSingleSlide": "单行滑动",
"graphicNavStyleMultiLine": "多行滑动",
"graphicNavStylePageSlide": "分页滑动",
"graphicNavRowCount": "每行数量",
"graphicNavPageCount": "显示方式",
@ -104,6 +115,9 @@
"styleSet": "风格设置",
"titleStyle": "标题样式",
"selectStyle": "风格选择",
"activeCubeBlockBtnText": "按钮文字",
"btnTextItalics": "斜体",
"btnTextNormal": "常规",
"styleLabel": "风格",
"styleShowTips": "风格 1 2 3仅在小程序中展示",
"titleContent": "标题内容",
@ -198,8 +212,17 @@
"carouselSearchShowWayStatic": "正常显示",
"carouselSearchShowWayFixed": "滚动至顶部固定",
"carouselSearchFixedBgColor": "置顶背景",
"carouselSearchStyleSelect": "风格选择",
"carouselSearchSet": "搜索设置",
"carouselSearchSubTitle": "副标题",
"carouselSearchSubTitleStyle": "副标题样式",
"carouselSearchPositionStyle": "定位样式",
"carouselSearchSubTitlePlaceholder": "请输入副标题内容",
"carouselSearchText": "搜索内容",
"carouselSearchTextColor": "文字颜色",
"carouselSearchBgColor": "背景颜色",
"carouselSearchBtnColor": "按钮颜色",
"carouselSearchBtnBgColor": "按钮背景色",
"carouselSearchHotWordSet": "搜索热词",
"carouselSearchHotWordInterval": "显示时间 / 秒",
"carouselSearchHotWordText": "内容",
@ -222,6 +245,7 @@
"carouselSearchSwiperInterval": "切换间隔 / 秒",
"carouselSearchSwiperTips": "建议上传尺寸相同的图片推荐尺寸750*350鼠标拖拽可调整图片顺序",
"carouselSearchTabStyle": "选项卡样式",
"carouselSearchStyle": "搜索框样式",
"noColor": "常规颜色",
"selectColor": "选中颜色",
"fixedNoColor": "下滑常规颜色",
@ -231,6 +255,7 @@
"carouselSearchSwiperStyle": "轮播样式",
"carouselSearchSwiperIndicatorStyle1": "样式1",
"carouselSearchSwiperIndicatorStyle2": "样式2",
"carouselSearchSwiperIndicatorStyle3": "样式3",
"carouselSearchSwiperIndicatorAlign": "显示位置",
"alignLeft": "居左",
"alignCenter": "居中",
@ -240,8 +265,9 @@
"horzLineStyleDashed": "虚线",
"horzLineBorderColor": "线条颜色",
"horzLineBorderWidth": "线条宽度",
"floatBtnBtton": "按钮位置",
"floatBtnButton": "按钮位置",
"floatBtnOffset": "上下偏移",
"lateralBtnOffset": "左右偏移",
"floatBtnImageSet": "图片设置",
"floatBtnImageSize": "图片大小",
"floatBtnAroundRadius": "图片圆角",
@ -256,5 +282,13 @@
"rollTopStatusBarTextColor": "滚动后标题颜色",
"topStatusBarSearchName": "搜索内容",
"topStatusBarSearchNamePlaceholder": "请输入搜索关键词",
"settingTips": "点击查看如何配置"
"settingTips": "点击查看如何配置",
"pictureShowBlockOne": "模块一",
"pictureShowBlockTwo": "模块二",
"subTitleTextColor": "标题颜色",
"pictureShowBgColor": "背景颜色",
"pictureShowBtnText": "按钮文字",
"pictureShowBtnColor": "文字颜色",
"pictureShowBtnBgColor": "背景颜色",
"pictureShowBlockStyle": "模块样式"
}

View File

@ -20,6 +20,7 @@
"titlePlaceholder": "请输入页面名称",
"addDiyPage": "添加页面",
"diyPageDeleteTips": "确定要删除该自定义页面吗?",
"diyPageCopyTips": "确定要复制该自定义页面吗?",
"preview": "预览",
"share": "分享",
"shareSet": "分享设置",

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