mirror of
https://github.com/certd/certd.git
synced 2026-04-04 15:00:54 +08:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98445afd3e | ||
|
|
3eb9671640 | ||
|
|
95fb4e3e8b | ||
|
|
818015d24a | ||
|
|
e11bd3e813 | ||
|
|
2a4d64af95 | ||
|
|
d73cb4be2b | ||
|
|
82752040f8 | ||
|
|
1bde6d8b9c | ||
|
|
be55d36f67 | ||
|
|
f07724372d | ||
|
|
5b8d5dd975 | ||
|
|
e36aa065e9 | ||
|
|
45486ef399 | ||
|
|
4fedf6f452 | ||
|
|
702b80ef75 | ||
|
|
a5c7789dbc | ||
|
|
bddef199ff | ||
|
|
fa6fc07009 | ||
|
|
8d9b44528b | ||
|
|
1d108d775c | ||
|
|
7af111ac57 | ||
|
|
6451f00639 | ||
|
|
31718d1a4a | ||
|
|
832b74b66b | ||
|
|
082802e119 | ||
|
|
efa9c748c5 | ||
|
|
fc8bef5aae | ||
|
|
441b15ed2f | ||
|
|
11d1f6e141 | ||
|
|
0f5b9564c6 | ||
|
|
0c8a84656a | ||
|
|
330f91e15b | ||
|
|
490141a920 | ||
|
|
1e288b14d0 | ||
|
|
81df96bf45 | ||
|
|
d1ab5f74a6 | ||
|
|
e43dd03132 | ||
|
|
2c6d64976f | ||
|
|
065713cdb6 | ||
|
|
746d3c97c3 | ||
|
|
9557fc799e | ||
|
|
8fcabc5e9f | ||
|
|
2b4b15f558 | ||
|
|
057b0b4565 | ||
|
|
70a2402521 | ||
|
|
de34db3394 | ||
|
|
0c2ae792ec | ||
|
|
a4c0b92777 | ||
|
|
b9d78135e0 | ||
|
|
5641c19502 | ||
|
|
0110dfdb70 | ||
|
|
140606744b | ||
|
|
335d175d57 | ||
|
|
779db9da70 | ||
|
|
5d29a08ab7 | ||
|
|
1fe3365e10 | ||
|
|
f0584c88e8 | ||
|
|
c7620dfc48 | ||
|
|
de26ee9383 | ||
|
|
d442462952 | ||
|
|
558fc9f306 | ||
|
|
2eebb3388a | ||
|
|
fe4367c580 | ||
|
|
e70732c9ac | ||
|
|
42ad04cabd | ||
|
|
7f5e89d489 | ||
|
|
c504f33b1f | ||
|
|
ed6a18dae7 | ||
|
|
844c4bf983 | ||
|
|
43961c1c18 | ||
|
|
7b42d7252e | ||
|
|
4aa136189a | ||
|
|
c66802af2d | ||
|
|
49e65c611f | ||
|
|
abf29bc164 | ||
|
|
08854e0ab9 | ||
|
|
575416a16d | ||
|
|
3dd0783510 | ||
|
|
fadb1d35b3 | ||
|
|
27a9fc32a6 | ||
|
|
7008a408ca | ||
|
|
b928bb46c7 | ||
|
|
18c9c4a166 | ||
|
|
49fa01f209 | ||
|
|
fe9d443100 | ||
|
|
3dec43d8d4 | ||
|
|
5ab2943c3a | ||
|
|
6e8b0eeca9 | ||
|
|
d0c4dfca97 | ||
|
|
f4a11ed328 | ||
|
|
b2971cf5fb | ||
|
|
f97827ec76 | ||
|
|
f230a2a94d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,7 +21,7 @@ gen
|
||||
/packages/ui/*/.idea
|
||||
/packages/ui/*/node_modules
|
||||
/packages/*/node_modules
|
||||
/pnpm-lock.yaml
|
||||
#/pnpm-lock.yaml
|
||||
|
||||
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
31
CHANGELOG.md
31
CHANGELOG.md
@@ -3,6 +3,37 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复cname记录查找bug ([95fb4e3](https://github.com/certd/certd/commit/95fb4e3e8be6ca13cc43b451f6141d62190ba453))
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 一些手机端适配优化 ([5b8d5dd](https://github.com/certd/certd/commit/5b8d5dd97536456a9d5d1384216eac1093b2dc3d))
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复CDN插件我爱云因更换接口导致部署失败的问题 ([5641c19](https://github.com/certd/certd/commit/5641c19502970f67af19709bddf8c781b1a25bdc))
|
||||
* 修复CDN插件我爱云因更换接口导致部署失败的问题 ([0110dfd](https://github.com/certd/certd/commit/0110dfdb70b12dfb0a7a067717f3773ed75aae7c))
|
||||
* 修复webhook headers value中带等号是解析错误的bug ([1fe3365](https://github.com/certd/certd/commit/1fe3365e10c464c4c60c82f424cf74fe35b883e0))
|
||||
* ProxmoxUploadCert 增加强制部署证书 ([441b15e](https://github.com/certd/certd/commit/441b15ed2fe5a143a5bd5508613b3816ddbff596))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 历史记录查看详情,可以切换到对应的历史记录日志上去 ([082802e](https://github.com/certd/certd/commit/082802e1197156837800f814728ee0f6b300b18c))
|
||||
* 流水线同一个阶段任务优化为并行执行 ([efa9c74](https://github.com/certd/certd/commit/efa9c748c5c07fc950af3db742ef9310f1ac9a4b))
|
||||
* 升级midwayjs版本 ([057b0b4](https://github.com/certd/certd/commit/057b0b4565e19bb93195633f767b2942e8e40e59))
|
||||
* 是否允许爬虫爬取增加ui设置选项 ([779db9d](https://github.com/certd/certd/commit/779db9da705d2dfef36fec21f52bd38af9fc5f2e))
|
||||
* 通知支持钉钉群聊机器人 ([fc8bef5](https://github.com/certd/certd/commit/fc8bef5aae522d75d408d8c3aa74543269da5398))
|
||||
* 易支付支持固定支付方式,适合没有收银台版本使用 ([81df96b](https://github.com/certd/certd/commit/81df96bf4542ce8d8ef4a428a4460dd554e4719a))
|
||||
* 支持易盾RCDN部署 ([065713c](https://github.com/certd/certd/commit/065713cdb6953d16df08585c316c1a7a8eaec437))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -9,7 +9,7 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书(支持所有注册商注册的域名)
|
||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持40+部署插件)
|
||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件)
|
||||
* 支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
|
||||
* 邮件通知、webhook通知
|
||||
|
||||
@@ -1 +1 @@
|
||||
15:10
|
||||
23:33
|
||||
|
||||
@@ -7,10 +7,10 @@ services:
|
||||
restart: unless-stopped # 自动重启
|
||||
volumes:
|
||||
# ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】
|
||||
# 只要修改冒号前面的,冒号后面的别动
|
||||
# 只要修改冒号前面的,冒号后面的/app/data不要动
|
||||
- /data/certd:/app/data
|
||||
ports: # 端口映射
|
||||
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号
|
||||
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号,第二个7001不要动
|
||||
- "7001:7001"
|
||||
# ↓↓↓↓ ---------------------------------------------------------- https端口,可以根据实际情况,是否暴露该端口
|
||||
- "7002:7002"
|
||||
@@ -27,6 +27,8 @@ services:
|
||||
# extra_hosts:
|
||||
# # ↓↓↓↓ -------------------------------------------------------- 这里可以配置自定义hosts,外网域名可以指向本地局域网ip地址
|
||||
# - "localdomain.com:192.168.1.3"
|
||||
# # ↓↓↓↓ ------------------------------------------------ 直接使用主机的网络,如果网络问题实在找不到原因,可以尝试打开此参数
|
||||
# network_mode: host
|
||||
labels:
|
||||
com.centurylinklabs.watchtower.enable: "true"
|
||||
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把下面networks的注释放开
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 一些手机端适配优化 ([5b8d5dd](https://github.com/certd/certd/commit/5b8d5dd97536456a9d5d1384216eac1093b2dc3d))
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复CDN插件我爱云因更换接口导致部署失败的问题 ([5641c19](https://github.com/certd/certd/commit/5641c19502970f67af19709bddf8c781b1a25bdc))
|
||||
* 修复CDN插件我爱云因更换接口导致部署失败的问题 ([0110dfd](https://github.com/certd/certd/commit/0110dfdb70b12dfb0a7a067717f3773ed75aae7c))
|
||||
* 修复webhook headers value中带等号是解析错误的bug ([1fe3365](https://github.com/certd/certd/commit/1fe3365e10c464c4c60c82f424cf74fe35b883e0))
|
||||
* ProxmoxUploadCert 增加强制部署证书 ([441b15e](https://github.com/certd/certd/commit/441b15ed2fe5a143a5bd5508613b3816ddbff596))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 历史记录查看详情,可以切换到对应的历史记录日志上去 ([082802e](https://github.com/certd/certd/commit/082802e1197156837800f814728ee0f6b300b18c))
|
||||
* 流水线同一个阶段任务优化为并行执行 ([efa9c74](https://github.com/certd/certd/commit/efa9c748c5c07fc950af3db742ef9310f1ac9a4b))
|
||||
* 升级midwayjs版本 ([057b0b4](https://github.com/certd/certd/commit/057b0b4565e19bb93195633f767b2942e8e40e59))
|
||||
* 是否允许爬虫爬取增加ui设置选项 ([779db9d](https://github.com/certd/certd/commit/779db9da705d2dfef36fec21f52bd38af9fc5f2e))
|
||||
* 通知支持钉钉群聊机器人 ([fc8bef5](https://github.com/certd/certd/commit/fc8bef5aae522d75d408d8c3aa74543269da5398))
|
||||
* 易支付支持固定支付方式,适合没有收银台版本使用 ([81df96b](https://github.com/certd/certd/commit/81df96bf4542ce8d8ef4a428a4460dd554e4719a))
|
||||
* 支持易盾RCDN部署 ([065713c](https://github.com/certd/certd/commit/065713cdb6953d16df08585c316c1a7a8eaec437))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 禁止爬虫爬取本网站 ([5164116](https://github.com/certd/certd/commit/5164116bde60dabac774cdf94f5317ff386e95ca))
|
||||
* 上传到阿里云证书名称后缀增加毫秒时间戳 ([9f0ee21](https://github.com/certd/certd/commit/9f0ee219d02907ffe128a5cf10173397d934ccd7))
|
||||
* 支持部署到阿里云FC3.0 ([bcaf54d](https://github.com/certd/certd/commit/bcaf54d4cb7bc469486aae6cdb127ae017eb3abb))
|
||||
* 支持新版本LeCDN ([44d43f4](https://github.com/certd/certd/commit/44d43f45cb9094619df7494c2a64a51ba77ad116))
|
||||
|
||||
## [1.30.5](https://github.com/certd/certd/compare/v1.30.4...v1.30.5) (2025-02-14)
|
||||
|
||||
**Note:** Version bump only for package root
|
||||
|
||||
@@ -28,7 +28,7 @@ packages:
|
||||
### 安装依赖和初始化:
|
||||
```shell
|
||||
# 安装pnpm,如果提示npm命令不存在,就需要先安装nodejs
|
||||
npm install -g pnpm@8.15.7 --registry=https://registry.npmmirror.com
|
||||
npm install -g pnpm--registry=https://registry.npmmirror.com
|
||||
|
||||
# 使用国内镜像源,如果有代理,就不需要
|
||||
pnpm config set registry https://registry.npmmirror.com
|
||||
@@ -36,19 +36,19 @@ pnpm config set registry https://registry.npmmirror.com
|
||||
pnpm install
|
||||
|
||||
# 初始化构建
|
||||
npm run init
|
||||
pnpm init
|
||||
```
|
||||
|
||||
### 启动 server:
|
||||
```shell
|
||||
cd packages/ui/certd-server
|
||||
npm run dev
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 启动 client:
|
||||
```shell
|
||||
cd packages/ui/certd-client
|
||||
npm run dev
|
||||
pnpm dev
|
||||
|
||||
# 会自动打开浏览器,确认正常运行
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书(支持所有注册商注册的域名)
|
||||
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等,目前已支持30+部署插件)
|
||||
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等,目前已支持60+部署插件)
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||
* 邮件通知
|
||||
* 私有化部署,保障数据安全
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 源码部署
|
||||
不推荐
|
||||
如果没有`git`和`nodejs`基础,则不推荐
|
||||
## 一、源码安装
|
||||
|
||||
### 环境要求
|
||||
@@ -7,7 +7,7 @@
|
||||
### 源码启动
|
||||
```shell
|
||||
# 克隆代码
|
||||
git clone https://github.com/certd/certd
|
||||
git clone https://github.com/certd/certd --depth=1
|
||||
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时,可以尝试此命令,1.x.x换成最新版本号
|
||||
cd certd
|
||||
# 启动服务
|
||||
|
||||
@@ -24,7 +24,7 @@ features:
|
||||
- title: 全自动申请证书
|
||||
details: 支持所有注册商注册的域名
|
||||
- title: 全自动部署证书
|
||||
details: 支持部署到主机、阿里云、腾讯云等,目前已支持30+部署插件
|
||||
details: 支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件
|
||||
- title: 多域名、泛域名打到一个证书上
|
||||
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||
- title: 多证书格式支持
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow: /
|
||||
Allow: /
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.30.6"
|
||||
"version": "1.31.2"
|
||||
}
|
||||
|
||||
@@ -41,5 +41,8 @@
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/**"
|
||||
]
|
||||
],
|
||||
"pnpm": {
|
||||
"neverBuiltDependencies": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/publishlab/node-acme-client/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.31.1](https://github.com/publishlab/node-acme-client/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
# [1.31.0](https://github.com/publishlab/node-acme-client/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.30.6](https://github.com/publishlab/node-acme-client/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"module": "scr/index.js",
|
||||
"main": "src/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.7.2",
|
||||
@@ -65,5 +65,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线同一个阶段任务优化为并行执行 ([efa9c74](https://github.com/certd/certd/commit/efa9c748c5c07fc950af3db742ef9310f1ac9a4b))
|
||||
* 支持易盾RCDN部署 ([065713c](https://github.com/certd/certd/commit/065713cdb6953d16df08585c316c1a7a8eaec437))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1 +1 @@
|
||||
01:03
|
||||
10:23
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -44,5 +44,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ import { nanoid } from 'nanoid';
|
||||
import * as id from './util.id.js';
|
||||
import { locker } from './util.lock.js';
|
||||
import { mitter } from './util.mitter.js';
|
||||
|
||||
import * as request from './util.request.js';
|
||||
export const utils = {
|
||||
sleep,
|
||||
http,
|
||||
@@ -52,4 +54,5 @@ export const utils = {
|
||||
locker,
|
||||
mitter,
|
||||
amount: amountUtils,
|
||||
request,
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import crypto from 'crypto';
|
||||
import crypto, { BinaryToTextEncoding } from 'crypto';
|
||||
|
||||
function md5(data: string) {
|
||||
return crypto.createHash('md5').update(data).digest('hex');
|
||||
function md5(data: string, digest: BinaryToTextEncoding = 'hex') {
|
||||
return crypto.createHash('md5').update(data).digest(digest);
|
||||
}
|
||||
function sha256(data: string) {
|
||||
return crypto.createHash('sha256').update(data).digest('hex');
|
||||
function sha256(data: string, digest: BinaryToTextEncoding = 'hex') {
|
||||
return crypto.createHash('sha256').update(data).digest(digest);
|
||||
}
|
||||
|
||||
function base64(data: string) {
|
||||
|
||||
@@ -310,3 +310,14 @@ export async function download(req: { http: HttpClient; config: HttpRequestConfi
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getCookie(response: any, name: string) {
|
||||
const cookies = response.headers['set-cookie'];
|
||||
//根据name 返回对应的cookie
|
||||
const found = cookies.find((cookie: any) => cookie.includes(name));
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
const cookie = found.split(';')[0];
|
||||
return cookie.substring(cookie.indexOf('=') + 1);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线同一个阶段任务优化为并行执行 ([efa9c74](https://github.com/certd/certd/commit/efa9c748c5c07fc950af3db742ef9310f1ac9a4b))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,8 +16,8 @@
|
||||
"test": "mocha --loader=ts-node/esm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/plus-core": "^1.30.6",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@certd/plus-core": "^1.31.2",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -43,5 +43,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import type { ResultError } from "../dt/index.js";
|
||||
|
||||
export class CancelError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export class RunnableError extends Error {
|
||||
errors: ResultError[];
|
||||
constructor(message: string, errors: ResultError[]) {
|
||||
super(message);
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../dt/index.js";
|
||||
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task, ResultError } from "../dt/index.js";
|
||||
import { RunHistory, RunnableCollection } from "./run-history.js";
|
||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js";
|
||||
import { ContextFactory, IContext } from "./context.js";
|
||||
@@ -12,6 +12,7 @@ import { FileStore } from "./file-store.js";
|
||||
import { cloneDeep, forEach, merge } from "lodash-es";
|
||||
import { INotificationService } from "../notification/index.js";
|
||||
import { taskEmitterCreate } from "../service/emit.js";
|
||||
import { RunnableError } from "./exceptions.js";
|
||||
|
||||
export type SysInfo = {
|
||||
//系统标题
|
||||
@@ -192,6 +193,8 @@ export class Executor {
|
||||
}
|
||||
|
||||
let resList: ResultType[] = [];
|
||||
const errorList: ResultError[] = [];
|
||||
let errorMessage = "";
|
||||
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
||||
//并行
|
||||
const pList = [];
|
||||
@@ -200,10 +203,25 @@ export class Executor {
|
||||
}
|
||||
resList = await Promise.all(pList);
|
||||
} else {
|
||||
//串行
|
||||
//串行且报错继续
|
||||
for (let i = 0; i < runnerList.length; i++) {
|
||||
const runner = runnerList[i];
|
||||
resList[i] = await runner();
|
||||
try {
|
||||
resList[i] = await runner();
|
||||
} catch (e: any) {
|
||||
const t = stage.tasks[i];
|
||||
this.logger.error(`任务 ${t.title} 执行异常:`, e.message);
|
||||
resList[i] = ResultType.error;
|
||||
errorList.push({
|
||||
e,
|
||||
returnType: ResultType.error,
|
||||
runnable: t,
|
||||
});
|
||||
errorMessage += `任务${t.title}执行失败,错误详情:${e.message};`;
|
||||
}
|
||||
}
|
||||
if (errorList.length > 0) {
|
||||
throw new RunnableError(errorMessage, errorList);
|
||||
}
|
||||
}
|
||||
return this.compositionResultType(resList);
|
||||
@@ -402,7 +420,16 @@ export class Executor {
|
||||
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}`;
|
||||
} else if (when === "error") {
|
||||
subject = `执行失败,${this.pipeline.title}【${this.pipeline.id}】`;
|
||||
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}\n\n${this.currentStatusMap?.currentStep?.title} 执行失败\n\n错误详情:${error.message}`;
|
||||
|
||||
if (error instanceof RunnableError) {
|
||||
const runnableError = error as RunnableError;
|
||||
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}\n\n`;
|
||||
for (const re of runnableError.errors) {
|
||||
content += ` - ${re.runnable.title} 执行失败\n 错误详情:${re.e.message}\n\n`;
|
||||
}
|
||||
} else {
|
||||
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}\n\n${this.currentStatusMap?.currentStep?.title} 执行失败\n\n错误详情:${error.message}`;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,12 @@ export type Log = {
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type ResultError = {
|
||||
e: any;
|
||||
returnType: ResultType;
|
||||
runnable: Runnable;
|
||||
};
|
||||
|
||||
export enum ResultType {
|
||||
start = "start",
|
||||
success = "success",
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -21,5 +21,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -30,5 +30,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,7 +16,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -31,5 +31,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 升级midwayjs版本 ([057b0b4](https://github.com/certd/certd/commit/057b0b4565e19bb93195633f767b2942e8e40e59))
|
||||
* 是否允许爬虫爬取增加ui设置选项 ([779db9d](https://github.com/certd/certd/commit/779db9da705d2dfef36fec21f52bd38af9fc5f2e))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -27,18 +27,18 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.30.6",
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/pipeline": "^1.30.6",
|
||||
"@certd/plus-core": "^1.30.6",
|
||||
"@certd/acme-client": "^1.31.2",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@certd/pipeline": "^1.31.2",
|
||||
"@certd/plus-core": "^1.31.2",
|
||||
"@midwayjs/cache": "~3.14.0",
|
||||
"@midwayjs/core": "~3.17.1",
|
||||
"@midwayjs/i18n": "~3.17.3",
|
||||
"@midwayjs/info": "~3.17.3",
|
||||
"@midwayjs/koa": "~3.17.1",
|
||||
"@midwayjs/core": "~3.20.3",
|
||||
"@midwayjs/i18n": "~3.20.3",
|
||||
"@midwayjs/info": "~3.20.3",
|
||||
"@midwayjs/koa": "~3.20.3",
|
||||
"@midwayjs/logger": "~3.4.2",
|
||||
"@midwayjs/typeorm": "~3.17.1",
|
||||
"@midwayjs/upload": "^3.17.3",
|
||||
"@midwayjs/typeorm": "~3.20.3",
|
||||
"@midwayjs/upload": "^3.20.3",
|
||||
"better-sqlite3": "^11.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.7",
|
||||
@@ -61,5 +61,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export class SysPublicSettings extends BaseSettings {
|
||||
limitUserPipelineCount = 0;
|
||||
managerOtherUserPipeline = false;
|
||||
icpNo?: string;
|
||||
robots?: boolean = true;
|
||||
}
|
||||
|
||||
export class SysPrivateSettings extends BaseSettings {
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 升级midwayjs版本 ([057b0b4](https://github.com/certd/certd/commit/057b0b4565e19bb93195633f767b2942e8e40e59))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -25,9 +25,9 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@midwayjs/core": "~3.17.1",
|
||||
"@midwayjs/core": "~3.20.3",
|
||||
"@midwayjs/logger": "~3.4.2",
|
||||
"@midwayjs/typeorm": "~3.17.1",
|
||||
"@midwayjs/typeorm": "~3.20.3",
|
||||
"better-sqlite3": "^11.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -46,5 +46,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复cname记录查找bug ([95fb4e3](https://github.com/certd/certd/commit/95fb4e3e8be6ca13cc43b451f6141d62190ba453))
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -15,10 +15,10 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.30.6",
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/pipeline": "^1.30.6",
|
||||
"@certd/plugin-lib": "^1.30.6",
|
||||
"@certd/acme-client": "^1.31.2",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@certd/pipeline": "^1.31.2",
|
||||
"@certd/plugin-lib": "^1.31.2",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -41,5 +41,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
import { dnsProviderRegistry } from "./registry.js";
|
||||
import { Decorator } from "@certd/pipeline";
|
||||
|
||||
@@ -144,7 +144,7 @@ export class AcmeService {
|
||||
accountKey: conf.key,
|
||||
accountUrl: conf.accountUrl,
|
||||
externalAccountBinding: this.eab,
|
||||
backoffAttempts: 15,
|
||||
backoffAttempts: 20,
|
||||
backoffMin: 5000,
|
||||
backoffMax: 10000,
|
||||
urlMapping,
|
||||
|
||||
@@ -58,7 +58,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
||||
},
|
||||
required: false,
|
||||
order: 100,
|
||||
helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456",
|
||||
helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456\npfx不传则为空密码",
|
||||
})
|
||||
pfxPassword!: string;
|
||||
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -16,8 +16,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/basic": "^1.30.6",
|
||||
"@certd/pipeline": "^1.30.6",
|
||||
"@certd/basic": "^1.31.2",
|
||||
"@certd/pipeline": "^1.31.2",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.21.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
@@ -48,5 +48,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
||||
"gitHead": "2a4d64af9502881e2e553ea86a4479158cfa8918"
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@ VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
||||
VITE_APP_LOGO=/static/images/logo/logo.svg
|
||||
VITE_APP_LOGIN_LOGO=/static/images/logo/rect-black.svg
|
||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||
|
||||
VITE_APP_NAMESPACE=fs
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
|
||||
"trailingComma": "none",
|
||||
"printWidth": 160
|
||||
"printWidth": 220
|
||||
}
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.31.2](https://github.com/certd/certd/compare/v1.31.1...v1.31.2) (2025-03-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.31.1](https://github.com/certd/certd/compare/v1.31.0...v1.31.1) (2025-03-11)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 一些手机端适配优化 ([5b8d5dd](https://github.com/certd/certd/commit/5b8d5dd97536456a9d5d1384216eac1093b2dc3d))
|
||||
|
||||
# [1.31.0](https://github.com/certd/certd/compare/v1.30.6...v1.31.0) (2025-03-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 历史记录查看详情,可以切换到对应的历史记录日志上去 ([082802e](https://github.com/certd/certd/commit/082802e1197156837800f814728ee0f6b300b18c))
|
||||
* 是否允许爬虫爬取增加ui设置选项 ([779db9d](https://github.com/certd/certd/commit/779db9da705d2dfef36fec21f52bd38af9fc5f2e))
|
||||
* 易支付支持固定支付方式,适合没有收银台版本使用 ([81df96b](https://github.com/certd/certd/commit/81df96bf4542ce8d8ef4a428a4460dd554e4719a))
|
||||
* 支持易盾RCDN部署 ([065713c](https://github.com/certd/certd/commit/065713cdb6953d16df08585c316c1a7a8eaec437))
|
||||
|
||||
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
@@ -13,6 +13,11 @@ https://github.com/fast-crud/fs-server-js
|
||||
* [fs-admin-naive](https://github.com/fast-crud/fs-admin-naive-ui) naive版示例
|
||||
* [fs-in-vben-starter](https://github.com/fast-crud/fs-in-vben-starter) vben示例
|
||||
|
||||
# build
|
||||
|
||||
```sh
|
||||
set NODE_OPTIONS=--max-old-space-size=32768 && npm run build
|
||||
```
|
||||
# 感谢
|
||||
|
||||
### 依赖
|
||||
|
||||
254
packages/ui/certd-client/build/tailwind-config/index.mjs
Normal file
254
packages/ui/certd-client/build/tailwind-config/index.mjs
Normal file
@@ -0,0 +1,254 @@
|
||||
import path from "node:path";
|
||||
|
||||
import { addDynamicIconSelectors } from "@iconify/tailwind";
|
||||
import { getPackagesSync } from "@manypkg/get-packages";
|
||||
import typographyPlugin from "@tailwindcss/typography";
|
||||
import animate from "tailwindcss-animate";
|
||||
|
||||
import { enterAnimationPlugin } from "./plugins/entry.mjs";
|
||||
|
||||
// import defaultTheme from 'tailwindcss/defaultTheme';
|
||||
|
||||
const { packages } = getPackagesSync(process.cwd());
|
||||
|
||||
const tailwindPackages = [];
|
||||
|
||||
packages.forEach((pkg) => {
|
||||
// apps目录下和 @vben-core/tailwind-ui 包需要使用到 tailwindcss ui
|
||||
// if (fs.existsSync(path.join(pkg.dir, 'tailwind.config.mjs'))) {
|
||||
tailwindPackages.push(pkg.dir);
|
||||
// }
|
||||
});
|
||||
|
||||
const shadcnUiColors = {
|
||||
accent: {
|
||||
DEFAULT: "hsl(var(--accent))",
|
||||
foreground: "hsl(var(--accent-foreground))",
|
||||
hover: "hsl(var(--accent-hover))",
|
||||
lighter: "has(val(--accent-lighter))"
|
||||
},
|
||||
background: {
|
||||
deep: "hsl(var(--background-deep))",
|
||||
DEFAULT: "hsl(var(--background))"
|
||||
},
|
||||
border: {
|
||||
DEFAULT: "hsl(var(--border))"
|
||||
},
|
||||
card: {
|
||||
DEFAULT: "hsl(var(--card))",
|
||||
foreground: "hsl(var(--card-foreground))"
|
||||
},
|
||||
destructive: {
|
||||
...createColorsPalette("destructive"),
|
||||
DEFAULT: "hsl(var(--destructive))"
|
||||
},
|
||||
|
||||
foreground: {
|
||||
DEFAULT: "hsl(var(--foreground))"
|
||||
},
|
||||
|
||||
input: {
|
||||
background: "hsl(var(--input-background))",
|
||||
DEFAULT: "hsl(var(--input))"
|
||||
},
|
||||
muted: {
|
||||
DEFAULT: "hsl(var(--muted))",
|
||||
foreground: "hsl(var(--muted-foreground))"
|
||||
},
|
||||
popover: {
|
||||
DEFAULT: "hsl(var(--popover))",
|
||||
foreground: "hsl(var(--popover-foreground))"
|
||||
},
|
||||
primary: {
|
||||
...createColorsPalette("primary"),
|
||||
DEFAULT: "hsl(var(--primary))"
|
||||
},
|
||||
|
||||
ring: "hsl(var(--ring))",
|
||||
secondary: {
|
||||
DEFAULT: "hsl(var(--secondary))",
|
||||
desc: "hsl(var(--secondary-desc))",
|
||||
foreground: "hsl(var(--secondary-foreground))"
|
||||
}
|
||||
};
|
||||
|
||||
const customColors = {
|
||||
green: {
|
||||
...createColorsPalette("green"),
|
||||
foreground: "hsl(var(--success-foreground))"
|
||||
},
|
||||
header: {
|
||||
DEFAULT: "hsl(var(--header))"
|
||||
},
|
||||
heavy: {
|
||||
DEFAULT: "hsl(var(--heavy))",
|
||||
foreground: "hsl(var(--heavy-foreground))"
|
||||
},
|
||||
main: {
|
||||
DEFAULT: "hsl(var(--main))"
|
||||
},
|
||||
overlay: {
|
||||
content: "hsl(var(--overlay-content))",
|
||||
DEFAULT: "hsl(var(--overlay))"
|
||||
},
|
||||
red: {
|
||||
...createColorsPalette("red"),
|
||||
foreground: "hsl(var(--destructive-foreground))"
|
||||
},
|
||||
sidebar: {
|
||||
deep: "hsl(var(--sidebar-deep))",
|
||||
DEFAULT: "hsl(var(--sidebar))"
|
||||
},
|
||||
success: {
|
||||
...createColorsPalette("success"),
|
||||
DEFAULT: "hsl(var(--success))"
|
||||
},
|
||||
warning: {
|
||||
...createColorsPalette("warning"),
|
||||
DEFAULT: "hsl(var(--warning))"
|
||||
},
|
||||
yellow: {
|
||||
...createColorsPalette("yellow"),
|
||||
foreground: "hsl(var(--warning-foreground))"
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
content: ["./index.html", ...tailwindPackages.map((item) => path.join(item, "src/**/*.{vue,js,ts,jsx,tsx,svelte,astro,html}"))],
|
||||
darkMode: "selector",
|
||||
plugins: [animate, typographyPlugin, addDynamicIconSelectors(), enterAnimationPlugin],
|
||||
prefix: "",
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px"
|
||||
}
|
||||
},
|
||||
extend: {
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"collapsible-down": "collapsible-down 0.2s ease-in-out",
|
||||
"collapsible-up": "collapsible-up 0.2s ease-in-out",
|
||||
float: "float 5s linear 0ms infinite"
|
||||
},
|
||||
|
||||
animationDuration: {
|
||||
2000: "2000ms",
|
||||
3000: "3000ms"
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
xl: "calc(var(--radius) + 4px)"
|
||||
},
|
||||
boxShadow: {
|
||||
float: `0 6px 16px 0 rgb(0 0 0 / 8%),
|
||||
0 3px 6px -4px rgb(0 0 0 / 12%),
|
||||
0 9px 28px 8px rgb(0 0 0 / 5%)`
|
||||
},
|
||||
colors: {
|
||||
...customColors,
|
||||
...shadcnUiColors
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [
|
||||
"var(--font-family)"
|
||||
// ...defaultTheme.fontFamily.sans
|
||||
]
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" }
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" }
|
||||
},
|
||||
"collapsible-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-collapsible-content-height)" }
|
||||
},
|
||||
"collapsible-up": {
|
||||
from: { height: "var(--radix-collapsible-content-height)" },
|
||||
to: { height: "0" }
|
||||
},
|
||||
float: {
|
||||
"0%": { transform: "translateY(0)" },
|
||||
"50%": { transform: "translateY(-20px)" },
|
||||
"100%": { transform: "translateY(0)" }
|
||||
}
|
||||
},
|
||||
zIndex: {
|
||||
100: "100",
|
||||
1000: "1000"
|
||||
}
|
||||
}
|
||||
},
|
||||
safelist: ["dark"]
|
||||
};
|
||||
|
||||
function createColorsPalette(name) {
|
||||
// backgroundLightest: '#EFF6FF', // Tailwind CSS 默认的 `blue-50`
|
||||
// backgroundLighter: '#DBEAFE', // Tailwind CSS 默认的 `blue-100`
|
||||
// backgroundLight: '#BFDBFE', // Tailwind CSS 默认的 `blue-200`
|
||||
// borderLight: '#93C5FD', // Tailwind CSS 默认的 `blue-300`
|
||||
// border: '#60A5FA', // Tailwind CSS 默认的 `blue-400`
|
||||
// main: '#3B82F6', // Tailwind CSS 默认的 `blue-500`
|
||||
// hover: '#2563EB', // Tailwind CSS 默认的 `blue-600`
|
||||
// active: '#1D4ED8', // Tailwind CSS 默认的 `blue-700`
|
||||
// backgroundDark: '#1E40AF', // Tailwind CSS 默认的 `blue-800`
|
||||
// backgroundDarker: '#1E3A8A', // Tailwind CSS 默认的 `blue-900`
|
||||
// backgroundDarkest: '#172554', // Tailwind CSS 默认的 `blue-950`
|
||||
|
||||
// • backgroundLightest (#EFF6FF): 适用于最浅的背景色,可能用于非常轻微的阴影或卡片的背景。
|
||||
// • backgroundLighter (#DBEAFE): 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
||||
// • backgroundLight (#BFDBFE): 适用于浅色背景,可能用于输入框或表单区域的背景。
|
||||
// • borderLight (#93C5FD): 适用于浅色边框,可能用于输入框或卡片的边框。
|
||||
// • border (#60A5FA): 适用于普通边框,可能用于按钮或卡片的边框。
|
||||
// • main (#3B82F6): 适用于主要的主题色,通常用于按钮、链接或主要的强调色。
|
||||
// • hover (#2563EB): 适用于鼠标悬停状态下的颜色,例如按钮悬停时的背景色或边框色。
|
||||
// • active (#1D4ED8): 适用于激活状态下的颜色,例如按钮按下时的背景色或边框色。
|
||||
// • backgroundDark (#1E40AF): 适用于深色背景,可能用于主要按钮或深色卡片背景。
|
||||
// • backgroundDarker (#1E3A8A): 适用于更深的背景,通常用于头部导航栏或页脚。
|
||||
// • backgroundDarkest (#172554): 适用于最深的背景,可能用于非常深色的区域或极端对比色。
|
||||
|
||||
return {
|
||||
50: `hsl(var(--${name}-50))`,
|
||||
100: `hsl(var(--${name}-100))`,
|
||||
200: `hsl(var(--${name}-200))`,
|
||||
300: `hsl(var(--${name}-300))`,
|
||||
400: `hsl(var(--${name}-400))`,
|
||||
500: `hsl(var(--${name}-500))`,
|
||||
600: `hsl(var(--${name}-600))`,
|
||||
700: `hsl(var(--${name}-700))`,
|
||||
// 800: `hsl(var(--${name}-800))`,
|
||||
// 900: `hsl(var(--${name}-900))`,
|
||||
// 950: `hsl(var(--${name}-950))`,
|
||||
// 激活状态下的颜色,适用于按钮按下时的背景色或边框色。
|
||||
active: `hsl(var(--${name}-700))`,
|
||||
// 浅色背景,适用于输入框或表单区域的背景。
|
||||
"background-light": `hsl(var(--${name}-200))`,
|
||||
// 适用于略浅的背景色,通常用于次要背景或略浅的区域。
|
||||
"background-lighter": `hsl(var(--${name}-100))`,
|
||||
// 最浅的背景色,适用于非常轻微的阴影或卡片的背景。
|
||||
"background-lightest": `hsl(var(--${name}-50))`,
|
||||
// 适用于普通边框,可能用于按钮或卡片的边框。
|
||||
border: `hsl(var(--${name}-400))`,
|
||||
// 浅色边框,适用于输入框或卡片的边框。
|
||||
"border-light": `hsl(var(--${name}-300))`,
|
||||
foreground: `hsl(var(--${name}-foreground))`,
|
||||
// 鼠标悬停状态下的颜色,适用于按钮悬停时的背景色或边框色。
|
||||
hover: `hsl(var(--${name}-600))`,
|
||||
// 主色文本
|
||||
text: `hsl(var(--${name}-500))`,
|
||||
// 主色文本激活态
|
||||
"text-active": `hsl(var(--${name}-700))`,
|
||||
// 主色文本悬浮态
|
||||
"text-hover": `hsl(var(--${name}-600))`
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import plugin from "tailwindcss/plugin.js";
|
||||
|
||||
const enterAnimationPlugin = plugin(({ addUtilities }) => {
|
||||
const maxChild = 5;
|
||||
const utilities = {};
|
||||
for (let i = 1; i <= maxChild; i++) {
|
||||
const baseDelay = 0.1;
|
||||
const delay = `${baseDelay * i}s`;
|
||||
|
||||
utilities[`.enter-x:nth-child(${i})`] = {
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: "0",
|
||||
transform: `translateX(50px)`
|
||||
};
|
||||
|
||||
utilities[`.enter-y:nth-child(${i})`] = {
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: "0",
|
||||
transform: `translateY(50px)`
|
||||
};
|
||||
|
||||
utilities[`.-enter-x:nth-child(${i})`] = {
|
||||
animation: `enter-x-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: "0",
|
||||
transform: `translateX(-50px)`
|
||||
};
|
||||
|
||||
utilities[`.-enter-y:nth-child(${i})`] = {
|
||||
animation: `enter-y-animation 0.3s ease-in-out ${delay} forwards`,
|
||||
opacity: "0",
|
||||
transform: `translateY(-50px)`
|
||||
};
|
||||
}
|
||||
|
||||
// 添加动画关键帧
|
||||
addUtilities(utilities);
|
||||
addUtilities({
|
||||
"@keyframes enter-x-animation": {
|
||||
to: {
|
||||
opacity: "1",
|
||||
transform: "translateX(0)"
|
||||
}
|
||||
},
|
||||
"@keyframes enter-y-animation": {
|
||||
to: {
|
||||
opacity: "1",
|
||||
transform: "translateY(0)"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export { enterAnimationPlugin };
|
||||
@@ -0,0 +1,15 @@
|
||||
import config from ".";
|
||||
|
||||
export default {
|
||||
plugins: {
|
||||
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
|
||||
// Specifying the config is not necessary in most cases, but it is included
|
||||
autoprefixer: {},
|
||||
// 修复 element-plus 和 ant-design-vue 的样式和tailwindcss冲突问题
|
||||
"postcss-antd-fixes": { prefixes: ["ant", "el"] },
|
||||
"postcss-import": {},
|
||||
"postcss-preset-env": {},
|
||||
tailwindcss: { config },
|
||||
"tailwindcss/nesting": {}
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.30.6",
|
||||
"version": "1.31.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -9,7 +9,7 @@
|
||||
"debug": "vite --mode debug --open",
|
||||
"debug:pm": "vite --mode debugpm",
|
||||
"debug:force": "vite --force --mode debug",
|
||||
"build": " vite build ",
|
||||
"build": "vite build ",
|
||||
"dev-build": "echo 1",
|
||||
"test:unit": "vitest",
|
||||
"serve": "vite preview",
|
||||
@@ -21,53 +21,82 @@
|
||||
"circle:check": "pnpm dependency-cruise --validate --output-type err-html -f dependency-report.html src",
|
||||
"afterPubPush": "git add . && git commit -m \"build: publish success\" && git push"
|
||||
},
|
||||
"author": "Greper",
|
||||
"author": "greper",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@fast-crud/fast-crud": "^1.25.0",
|
||||
"@fast-crud/fast-extends": "^1.25.0",
|
||||
"@fast-crud/ui-antdv4": "^1.25.0",
|
||||
"@fast-crud/ui-interface": "^1.25.0",
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@aws-sdk/client-s3": "^3.535.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.535.0",
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@fast-crud/fast-crud": "^1.25.4",
|
||||
"@fast-crud/fast-extends": "^1.25.4",
|
||||
"@fast-crud/ui-antdv4": "^1.25.4",
|
||||
"@fast-crud/ui-interface": "^1.25.4",
|
||||
"@iconify/tailwind": "^1.2.0",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@manypkg/get-packages": "^2.2.2",
|
||||
"@soerenmartius/vue3-clipboard": "^0.1.2",
|
||||
"@tailwindcss/nesting": "0.0.0-insiders.565cd3e",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/vue-store": "^0.7.0",
|
||||
"@vee-validate/zod": "^4.15.0",
|
||||
"@vue-js-cron/light": "^4.0.5",
|
||||
"ant-design-vue": "^4.1.2",
|
||||
"@vue/shared": "^3.5.13",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
"async-validator": "^4.2.5",
|
||||
"axios": "^1.7.2",
|
||||
"axios-mock-adapter": "^1.22.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"better-scroll": "^2.5.1",
|
||||
"china-division": "^2.7.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"core-js": "^3.36.0",
|
||||
"cos-js-sdk-v5": "^1.7.0",
|
||||
"cron-parser": "^4.9.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"cssnano": "^7.0.6",
|
||||
"dayjs": "^1.11.7",
|
||||
"defu": "^6.1.4",
|
||||
"echarts": "^5.5.1",
|
||||
"highlight.js": "^11.9.0",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-vue-next": "^0.477.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nanoid": "^4.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"pinia": "2.1.7",
|
||||
"pinia-plugin-persistedstate": "^4.2.0",
|
||||
"postcss-antd-fixes": "^0.2.0",
|
||||
"postcss-import": "^16.1.0",
|
||||
"postcss-preset-env": "^10.1.5",
|
||||
"psl": "^1.9.0",
|
||||
"qiniu-js": "^3.4.2",
|
||||
"qrcode": "^1.5.4",
|
||||
"sortablejs": "^1.15.2",
|
||||
"radix-vue": "^1.9.16",
|
||||
"sortablejs": "^1.15.3",
|
||||
"tailwind-merge": "^3.0.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"theme-colors": "^0.1.0",
|
||||
"vee-validate": "^4.15.0",
|
||||
"vitest": "^0.34.6",
|
||||
"vue": "^3.4.21",
|
||||
"vue-cropperjs": "^5.0.0",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-i18n": "^9.10.2",
|
||||
"vue-router": "^4.3.0",
|
||||
"vuedraggable": "^4.1.0"
|
||||
"vuedraggable": "^4.1.0",
|
||||
"watermark-js-plus": "^1.5.8",
|
||||
"zod": "^3.24.2",
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.30.6",
|
||||
"@certd/pipeline": "^1.30.6",
|
||||
"@certd/lib-iframe": "^1.31.2",
|
||||
"@certd/pipeline": "^1.31.2",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
@@ -83,7 +112,7 @@
|
||||
"@vue/compiler-sfc": "^3.4.21",
|
||||
"@vue/eslint-config-typescript": "^13.0.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"autoprefixer": "^10.4.18",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"caller-path": "^4.0.0",
|
||||
"chai": "^5.1.0",
|
||||
"dependency-cruiser": "^16.2.3",
|
||||
@@ -105,7 +134,7 @@
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"terser": "^5.29.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslint": "^6.1.3",
|
||||
@@ -114,6 +143,7 @@
|
||||
"vite": "^5.3.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-theme": "^0.8.6",
|
||||
"vite-plugin-windicss": "^1.9.3",
|
||||
"vitest": "^2.1.2",
|
||||
"vue-eslint-parser": "^9.4.2",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
// tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
};
|
||||
15
packages/ui/certd-client/postcss.config.mjs
Normal file
15
packages/ui/certd-client/postcss.config.mjs
Normal file
@@ -0,0 +1,15 @@
|
||||
import config from "./build/tailwind-config/index.mjs";
|
||||
|
||||
export default {
|
||||
plugins: {
|
||||
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}),
|
||||
// Specifying the config is not necessary in most cases, but it is included
|
||||
autoprefixer: {},
|
||||
// 修复 element-plus 和 ant-design-vue 的样式和tailwindcss冲突问题
|
||||
"postcss-antd-fixes": { prefixes: ["ant", "el"] },
|
||||
"postcss-import": {},
|
||||
"postcss-preset-env": {},
|
||||
tailwindcss: { config },
|
||||
"tailwindcss/nesting": {}
|
||||
}
|
||||
};
|
||||
@@ -1,21 +1,23 @@
|
||||
<template>
|
||||
<a-config-provider :locale="locale" :theme="settingStore.themeToken">
|
||||
<AConfigProvider :locale="locale" :theme="tokenTheme">
|
||||
<contextHolder />
|
||||
<fs-form-provider>
|
||||
<router-view v-if="routerEnabled" />
|
||||
<router-view />
|
||||
</fs-form-provider>
|
||||
</a-config-provider>
|
||||
</AConfigProvider>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
||||
import enUS from "ant-design-vue/es/locale/en_US";
|
||||
import { provide, ref } from "vue";
|
||||
import { usePageStore } from "/src/store/modules/page";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { computed, provide, ref } from "vue";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import "dayjs/locale/en";
|
||||
import dayjs from "dayjs";
|
||||
import { usePreferences, preferences } from "/@/vben/preferences";
|
||||
import { useAntdDesignTokens } from "/@/vben/hooks";
|
||||
import { theme } from "ant-design-vue";
|
||||
import AConfigProvider from "ant-design-vue/es/config-provider";
|
||||
import { Modal } from "ant-design-vue";
|
||||
|
||||
defineOptions({
|
||||
@@ -24,13 +26,12 @@ defineOptions({
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
provide("modal", modal);
|
||||
//刷新页面方法
|
||||
const routerEnabled = ref(true);
|
||||
const locale = ref(zhCN);
|
||||
async function reload() {
|
||||
// routerEnabled.value = false;
|
||||
// await nextTick();
|
||||
// routerEnabled.value = true;
|
||||
}
|
||||
async function reload() {}
|
||||
localeChanged("zh-cn");
|
||||
provide("fn:router.reload", reload);
|
||||
provide("fn:locale.changed", localeChanged);
|
||||
//刷新页面方法
|
||||
function localeChanged(value: any) {
|
||||
console.log("locale changed:", value);
|
||||
if (value === "zh-cn") {
|
||||
@@ -45,10 +46,27 @@ localeChanged("zh-cn");
|
||||
provide("fn:router.reload", reload);
|
||||
provide("fn:locale.changed", localeChanged);
|
||||
|
||||
const { isDark } = usePreferences();
|
||||
const { tokens } = useAntdDesignTokens();
|
||||
|
||||
const tokenTheme = computed(() => {
|
||||
const algorithm = isDark.value ? [theme.darkAlgorithm] : [theme.defaultAlgorithm];
|
||||
|
||||
// antd 紧凑模式算法
|
||||
if (preferences.app.compact) {
|
||||
algorithm.push(theme.compactAlgorithm);
|
||||
}
|
||||
|
||||
return {
|
||||
algorithm,
|
||||
token: tokens
|
||||
};
|
||||
});
|
||||
//其他初始化
|
||||
// const resourceStore = useResourceStore();
|
||||
// resourceStore.init();
|
||||
const pageStore = usePageStore();
|
||||
pageStore.init();
|
||||
const settingStore = useSettingStore();
|
||||
// const pageStore = usePageStore();
|
||||
// pageStore.init();
|
||||
// const settingStore = useSettingStore();
|
||||
// settingStore.init();
|
||||
</script>
|
||||
|
||||
@@ -34,6 +34,7 @@ export type SysPublicSetting = {
|
||||
limitUserPipelineCount?: number;
|
||||
managerOtherUserPipeline?: boolean;
|
||||
icpNo?: string;
|
||||
robots?: boolean;
|
||||
};
|
||||
export type SuiteSetting = {
|
||||
enabled?: boolean;
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface UserInfoRes {
|
||||
id: string | number;
|
||||
username: string;
|
||||
nickName: string;
|
||||
avatar: string;
|
||||
avatar?: string;
|
||||
roleIds: number[];
|
||||
isWeak?: boolean;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ function createService() {
|
||||
return dataAxios.data;
|
||||
default:
|
||||
// 不是正确的 code
|
||||
const errorMessage = dataAxios.msg;
|
||||
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
|
||||
// @ts-ignore
|
||||
if (response?.config?.onError) {
|
||||
// @ts-ignore
|
||||
|
||||
@@ -62,5 +62,6 @@ export default defineComponent({
|
||||
.fs-highlight {
|
||||
margin: 0px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<td style="width: 100px; text-align: center">记录类型</td>
|
||||
<td style="width: 250px">请设置CNAME记录(验证成功以后不要删除)</td>
|
||||
<td style="width: 120px" class="center">状态</td>
|
||||
<td style="width: 80px" class="center">操作</td>
|
||||
<td style="width: 90px" class="center">操作</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<template v-for="key in domains" :key="key">
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import TutorialSteps from "/@/components/tutorial/tutorial-steps.vue";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
|
||||
const props = defineProps<{
|
||||
showIcon?: boolean;
|
||||
}>();
|
||||
|
||||
const openedRef = ref(false);
|
||||
function open() {
|
||||
openedRef.value = true;
|
||||
@@ -12,8 +16,8 @@ const slots = defineSlots();
|
||||
<template>
|
||||
<div class="tutorial-button pointer" @click="open">
|
||||
<template v-if="!slots.default">
|
||||
<fs-icon icon="ant-design:question-circle-outlined"></fs-icon>
|
||||
<div class="ml-5">使用教程</div>
|
||||
<fs-icon v-if="showIcon === false" icon="ant-design:question-circle-outlined" class="mr-0.5"></fs-icon>
|
||||
<div class="hidden md:block">使用教程</div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<contextHolder />
|
||||
<fs-icon icon="mingcute:vip-1-line" :title="text.title" />
|
||||
|
||||
<div v-if="mode !== 'icon'" class="text">
|
||||
<div v-if="mode !== 'icon'" class="text hidden md:block ml-0.5">
|
||||
<a-tooltip>
|
||||
<template #title> {{ text.title }}</template>
|
||||
<span>{{ text.name }}</span>
|
||||
<span class="">{{ text.name }}</span>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
@@ -238,7 +238,7 @@ function openUpgrade() {
|
||||
title: "专业版",
|
||||
desc: "开源需要您的赞助支持",
|
||||
type: "plus",
|
||||
privilege: ["可加VIP群,您的需求将优先实现", "站点证书监控无限制", "更多通知方式", "更多强大的部署插件,宝塔、群晖、1Panel等"],
|
||||
privilege: ["可加VIP群,您的需求将优先实现", "站点证书监控无限制", "更多通知方式", "插件全开放,更多强大的部署插件,宝塔、群晖、1Panel等"],
|
||||
trial: {
|
||||
title: "点击获取7天试用",
|
||||
click: () => {
|
||||
@@ -382,7 +382,6 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
&.isPlus {
|
||||
@@ -390,7 +389,6 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
import { createI18n } from "vue-i18n";
|
||||
import en from "./locale/en";
|
||||
import zh from "./locale/zh_CN";
|
||||
const messages = {
|
||||
en: {
|
||||
import { SupportedLanguagesType } from "/@/vben/locales";
|
||||
export const messages = {
|
||||
"en-US": {
|
||||
label: "English",
|
||||
...en
|
||||
},
|
||||
"zh-cn": {
|
||||
"zh-CN": {
|
||||
label: "简体中文",
|
||||
...zh
|
||||
}
|
||||
};
|
||||
|
||||
export default createI18n({
|
||||
legacy: false,
|
||||
locale: "zh-cn",
|
||||
fallbackLocale: "zh-cn",
|
||||
messages
|
||||
});
|
||||
// export default createI18n({
|
||||
// legacy: false,
|
||||
// locale: "zh-cn",
|
||||
// fallbackLocale: "zh-cn",
|
||||
// messages
|
||||
// });
|
||||
|
||||
export async function loadMessages(lang: SupportedLanguagesType) {
|
||||
return messages[lang];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="flex flex-between w-full text-sm p-5 bg-neutral-100 dark:bg-neutral-900">
|
||||
<div class="flex items-center">
|
||||
<span v-if="!settingStore.isComm">
|
||||
<span>Powered by</span>
|
||||
<a> handsfree.work </a>
|
||||
<a-divider type="vertical" />
|
||||
</span>
|
||||
|
||||
<template v-if="siteInfo.licenseTo">
|
||||
<a :href="siteInfo.licenseToUrl || ''">{{ siteInfo.licenseTo }}</a>
|
||||
<a-divider type="vertical" />
|
||||
</template>
|
||||
|
||||
<template v-if="sysPublic.icpNo">
|
||||
<span>
|
||||
<a href="https://beian.miit.gov.cn/" target="_blank">{{ sysPublic.icpNo }}</a>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="ml-5">v{{ version }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
|
||||
defineOptions({
|
||||
name: "PageFooter"
|
||||
});
|
||||
const version = ref(import.meta.env.VITE_APP_VERSION);
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const sysPublic = computed(() => {
|
||||
return settingStore.sysPublic;
|
||||
});
|
||||
const siteInfo = computed(() => {
|
||||
return settingStore.siteInfo;
|
||||
});
|
||||
</script>
|
||||
@@ -25,14 +25,14 @@
|
||||
<script lang="ts">
|
||||
import i18n from "../../../i18n";
|
||||
import { computed, inject } from "vue";
|
||||
import * as _ from "lodash-es";
|
||||
import { forEach } from "lodash-es";
|
||||
export default {
|
||||
name: "FsLocale",
|
||||
setup() {
|
||||
const languages = computed(() => {
|
||||
const map: any = i18n.global.messages?.value || {};
|
||||
const list: any = [];
|
||||
_.forEach(map, (item, key) => {
|
||||
forEach(map, (item, key) => {
|
||||
list.push({
|
||||
key,
|
||||
label: item.label
|
||||
|
||||
84
packages/ui/certd-client/src/layout/layout-basic.vue
Normal file
84
packages/ui/certd-client/src/layout/layout-basic.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<script lang="ts" setup>
|
||||
import { BasicLayout, LockScreen, UserDropdown } from "/@/vben/layouts";
|
||||
|
||||
import { computed, onErrorCaptured, onMounted } from "vue";
|
||||
import { preferences } from "/@/vben/preferences";
|
||||
import { useAccessStore } from "/@/vben/stores";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import VipButton from "/@/components/vip-button/index.vue";
|
||||
import TutorialButton from "/@/components/tutorial/index.vue";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import PageFooter from "./components/footer/index.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
const userStore = useUserStore();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const router = useRouter();
|
||||
const menus = computed(() => [
|
||||
{
|
||||
handler: () => {
|
||||
router.push("/certd/mine/user-profile");
|
||||
},
|
||||
icon: "fa-solid:book",
|
||||
text: "账号信息"
|
||||
}
|
||||
]);
|
||||
|
||||
const avatar = computed(() => {
|
||||
const avt = userStore.getUserInfo?.avatar;
|
||||
return avt ? `/api/basic/file/download?key=${avt}` : "";
|
||||
});
|
||||
|
||||
async function handleLogout() {
|
||||
userStore.logout(true);
|
||||
}
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const sysPublic = computed(() => {
|
||||
return settingStore.sysPublic;
|
||||
});
|
||||
const siteInfo = computed(() => {
|
||||
return settingStore.siteInfo;
|
||||
});
|
||||
|
||||
onErrorCaptured((e) => {
|
||||
console.error("ErrorCaptured:", e);
|
||||
// notification.error({ message: e.message });
|
||||
//阻止错误向上传递
|
||||
return false;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await settingStore.checkUrlBound();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
||||
<template #user-dropdown>
|
||||
<UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" />
|
||||
</template>
|
||||
<template #lock-screen>
|
||||
<LockScreen :avatar @to-login="handleLogout" />
|
||||
</template>
|
||||
<template #header-right-0>
|
||||
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full hidden md:block">
|
||||
<tutorial-button v-if="!settingStore.isComm" class="flex-center header-btn" />
|
||||
</div>
|
||||
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
|
||||
<vip-button class="flex-center header-btn" mode="nav" />
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<PageFooter></PageFooter>
|
||||
</template>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.header-btn {
|
||||
font-size: 14px;
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -43,14 +43,7 @@
|
||||
<!-- </button>-->
|
||||
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="settingStore.getHeaderMenus" />
|
||||
|
||||
<fs-menu
|
||||
v-if="!settingStore?.isAgent && !settingStore.isComm"
|
||||
class="header-menu"
|
||||
mode="horizontal"
|
||||
:expand-selected="false"
|
||||
:selectable="false"
|
||||
:menus="headerMenus"
|
||||
/>
|
||||
<fs-menu v-if="!settingStore?.isAgent && !settingStore.isComm" class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="headerMenus" />
|
||||
<div class="header-btn">
|
||||
<fs-locale />
|
||||
</div>
|
||||
|
||||
@@ -73,6 +73,8 @@ const sysPublic: Ref<SysPublicSetting> = computed(() => {
|
||||
background-size: 100%;
|
||||
//padding: 50px 0 84px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.user-layout-content {
|
||||
height: 100%;
|
||||
|
||||
@@ -1,22 +1,44 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import Antd from "ant-design-vue";
|
||||
// import Antd from "ant-design-vue";
|
||||
import Antd from "./plugin/antdv-async/index";
|
||||
import "./style/common.less";
|
||||
|
||||
import i18n from "./i18n";
|
||||
import store from "./store";
|
||||
import { loadMessages } from "./i18n";
|
||||
import { i18n } from "/@/vben/locales";
|
||||
import components from "./components";
|
||||
import router from "./router";
|
||||
import plugin from "./plugin/";
|
||||
// 正式项目请删除mock,避免影响性能
|
||||
//import "./mock";
|
||||
|
||||
import { setupVben } from "./vben";
|
||||
import { util } from "/@/utils";
|
||||
import { initPreferences } from "/@/vben/preferences";
|
||||
// @ts-ignore
|
||||
const app = createApp(App);
|
||||
app.use(Antd);
|
||||
app.use(router);
|
||||
app.use(i18n);
|
||||
app.use(store);
|
||||
app.use(components);
|
||||
app.use(plugin, { i18n });
|
||||
app.mount("#app");
|
||||
async function bootstrap() {
|
||||
const app = createApp(App);
|
||||
// app.use(Antd);
|
||||
app.use(Antd);
|
||||
await setupVben(app, { loadMessages, router });
|
||||
app.use(router);
|
||||
// app.use(i18n);
|
||||
// app.use(store);
|
||||
app.use(components);
|
||||
app.use(plugin, { i18n });
|
||||
|
||||
const envMode = util.env.MODE;
|
||||
const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${envMode}`;
|
||||
|
||||
// app偏好设置初始化
|
||||
await initPreferences({
|
||||
namespace,
|
||||
overrides: {
|
||||
app: {
|
||||
name: import.meta.env.VITE_APP_TITLE
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.mount("#app");
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as _ from "lodash-es";
|
||||
import { cloneDeep, mergeWith, isArray } from "lodash-es";
|
||||
function copyList(originList: any, newList: any, options: any, parentId?: any) {
|
||||
for (const item of originList) {
|
||||
const newItem: any = _.cloneDeep(item);
|
||||
const newItem: any = cloneDeep(item);
|
||||
if (parentId != null && newItem.parentId == null) {
|
||||
newItem.parentId = parentId;
|
||||
}
|
||||
@@ -218,7 +218,7 @@ const mockUtil: any = {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: _.cloneDeep(req.body)
|
||||
data: cloneDeep(req.body)
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -228,12 +228,12 @@ const mockUtil: any = {
|
||||
handle(req: any): any {
|
||||
const item = findById(req.body.id, list);
|
||||
if (item) {
|
||||
_.mergeWith(item, req.body, (objValue: any, srcValue: any) => {
|
||||
mergeWith(item, req.body, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||
if (_.isArray(objValue)) {
|
||||
if (isArray(objValue)) {
|
||||
return srcValue;
|
||||
}
|
||||
});
|
||||
@@ -305,12 +305,12 @@ const mockUtil: any = {
|
||||
console.log("req", req);
|
||||
let item = findById(req.body.id, list);
|
||||
if (item) {
|
||||
_.mergeWith(item, { [req.body.key]: req.body.value }, (objValue: any, srcValue: any) => {
|
||||
mergeWith(item, { [req.body.key]: req.body.value }, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||
if (_.isArray(objValue)) {
|
||||
if (isArray(objValue)) {
|
||||
return srcValue;
|
||||
}
|
||||
});
|
||||
@@ -336,12 +336,12 @@ const mockUtil: any = {
|
||||
for (const item of req.body) {
|
||||
const item2 = findById(item.id, list);
|
||||
if (item2) {
|
||||
_.mergeWith(item2, item, (objValue: any, srcValue: any) => {
|
||||
mergeWith(item2, item, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||
if (_.isArray(objValue)) {
|
||||
if (isArray(objValue)) {
|
||||
return srcValue;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import cascaderData from "./cascader-data";
|
||||
import pcaDataLittle from "./pca-data-little";
|
||||
// @ts-ignore
|
||||
import { TreeNodesLazyLoader, getPcaData } from "./pcas-data";
|
||||
import * as _ from "lodash-es";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
const openStatus = [
|
||||
{ value: "1", label: "打开", color: "success", icon: "ion:radio-button-on" },
|
||||
{ value: "2", label: "停止", color: "cyan" },
|
||||
@@ -29,7 +29,7 @@ let manyStatus = [
|
||||
];
|
||||
let tempManyStatus: any[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
tempManyStatus = tempManyStatus.concat(_.cloneDeep(manyStatus));
|
||||
tempManyStatus = tempManyStatus.concat(cloneDeep(manyStatus));
|
||||
}
|
||||
manyStatus = tempManyStatus;
|
||||
let idIndex = 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { mock } from "../api/service";
|
||||
import * as tools from "../api/tools";
|
||||
import * as _ from "lodash-es";
|
||||
import { forEach } from "lodash-es";
|
||||
import { utils } from "@fast-crud/fast-crud";
|
||||
// @ts-ignore
|
||||
const commonMocks: any = import.meta.glob("./common/mock.*.[j|t]s", { eager: true });
|
||||
@@ -10,13 +10,13 @@ const apiMocks: any = import.meta.glob("../api/modules/*.mock.ts", { eager: true
|
||||
const viewMocks: any = import.meta.glob("../views/**/mock.[j|t]s", { eager: true });
|
||||
|
||||
const list: any = [];
|
||||
_.forEach(commonMocks, (value: any) => {
|
||||
forEach(commonMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
_.forEach(apiMocks, (value: any) => {
|
||||
forEach(apiMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
_.forEach(viewMocks, (value: any) => {
|
||||
forEach(viewMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
|
||||
|
||||
226
packages/ui/certd-client/src/plugin/antdv-async/index-bak.ts
Normal file
226
packages/ui/certd-client/src/plugin/antdv-async/index-bak.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import Input from "ant-design-vue/es/input/Input";
|
||||
import Button from "ant-design-vue/es/button/button";
|
||||
import Divider from "ant-design-vue/es/divider";
|
||||
import Badge from "ant-design-vue/es/badge";
|
||||
import Empty from "ant-design-vue/es/empty";
|
||||
import Avatar from "ant-design-vue/es/avatar";
|
||||
import Steps from "ant-design-vue/es/steps";
|
||||
import Select from "ant-design-vue/es/select";
|
||||
import PageHeader from "ant-design-vue/es/page-header";
|
||||
import Card from "ant-design-vue/es/card";
|
||||
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.use(Input);
|
||||
app.use(Button);
|
||||
app.use(Divider);
|
||||
app.use(Badge);
|
||||
app.use(Empty);
|
||||
app.use(Avatar);
|
||||
app.use(PageHeader);
|
||||
app.use(Steps);
|
||||
app.use(Select);
|
||||
app.use(Card);
|
||||
|
||||
app.component(
|
||||
"AAutoComplete",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/auto-complete/index"))
|
||||
);
|
||||
app.component(
|
||||
"ARadio",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/radio/Radio"))
|
||||
);
|
||||
app.component(
|
||||
"ARadioGroup",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/radio/Group"))
|
||||
);
|
||||
app.component(
|
||||
"ATable",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/table/Table"))
|
||||
);
|
||||
app.component(
|
||||
"AModal",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/modal/Modal"))
|
||||
);
|
||||
app.component(
|
||||
"AForm",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/form/Form"))
|
||||
);
|
||||
app.component(
|
||||
"AFormItem",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/form/FormItem"))
|
||||
);
|
||||
app.component(
|
||||
"AFormItemRest",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/form/FormItemContext"))
|
||||
);
|
||||
|
||||
app.component(
|
||||
"ATabs",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tabs/src/Tabs"))
|
||||
);
|
||||
|
||||
app.component(
|
||||
"ATabPane",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tabs/src/TabPanelList/TabPane"))
|
||||
);
|
||||
app.component(
|
||||
"ATextarea",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/input/TextArea"))
|
||||
);
|
||||
app.component(
|
||||
"AInputNumber",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/input-number/index"))
|
||||
);
|
||||
app.component(
|
||||
"ADrawer",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/drawer/index"))
|
||||
);
|
||||
app.component(
|
||||
"ASwitch",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/switch/index"))
|
||||
);
|
||||
app.component(
|
||||
"AUpload",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/upload/index"))
|
||||
);
|
||||
app.component(
|
||||
"ADatePicker",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/date-picker/index"))
|
||||
);
|
||||
app.component(
|
||||
"ARangePicker",
|
||||
defineAsyncComponent(async () => {
|
||||
const { RangePicker } = await import("ant-design-vue/es/date-picker/index");
|
||||
return RangePicker;
|
||||
})
|
||||
);
|
||||
app.component(
|
||||
"ATimePicker",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/time-picker/index"))
|
||||
);
|
||||
app.component(
|
||||
"ATag",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tag/index"))
|
||||
);
|
||||
app.component(
|
||||
"AAlert",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/alert/index"))
|
||||
);
|
||||
app.component(
|
||||
"AInputAutoComplete",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/auto-complete/index"))
|
||||
);
|
||||
|
||||
app.component(
|
||||
"ACascader",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/cascader/index"))
|
||||
);
|
||||
app.component(
|
||||
"ACheckbox",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/checkbox"))
|
||||
);
|
||||
app.component(
|
||||
"ACheckboxGroup",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/checkbox/Group"))
|
||||
);
|
||||
app.component(
|
||||
"ACol",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/col"))
|
||||
);
|
||||
app.component(
|
||||
"ARow",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/row"))
|
||||
);
|
||||
app.component(
|
||||
"ADropdown",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/dropdown"))
|
||||
);
|
||||
app.component(
|
||||
"AGrid",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/grid"))
|
||||
);
|
||||
app.component(
|
||||
"AImage",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/image"))
|
||||
);
|
||||
app.component(
|
||||
"APagination",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/pagination"))
|
||||
);
|
||||
app.component(
|
||||
"ATooltip",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tooltip"))
|
||||
);
|
||||
app.component(
|
||||
"ATree",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tree"))
|
||||
);
|
||||
app.component(
|
||||
"ATreeSelect",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tree-select"))
|
||||
);
|
||||
app.component(
|
||||
"ATour",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/tour"))
|
||||
);
|
||||
|
||||
app.component(
|
||||
"AMenu",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/menu/index"))
|
||||
);
|
||||
app.component(
|
||||
"ASubMenu",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/menu/src/SubMenu"))
|
||||
);
|
||||
app.component(
|
||||
"AMenuItem",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/menu/src/MenuItem"))
|
||||
);
|
||||
|
||||
app.component(
|
||||
"AProgress",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/progress"))
|
||||
);
|
||||
app.component(
|
||||
"ATimelineItem",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/timeline/TimelineItem"))
|
||||
);
|
||||
app.component(
|
||||
"ATimeline",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/timeline/Timeline"))
|
||||
);
|
||||
app.component(
|
||||
"APageHeader",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/page-header/index"))
|
||||
);
|
||||
app.component(
|
||||
"APopover",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/popover"))
|
||||
);
|
||||
app.component(
|
||||
"APopconfirm",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/popconfirm"))
|
||||
);
|
||||
app.component(
|
||||
"ACollapse",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/collapse"))
|
||||
);
|
||||
app.component(
|
||||
"ADescriptions",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/descriptions"))
|
||||
);
|
||||
app.component(
|
||||
"ADescriptionsItem",
|
||||
defineAsyncComponent(async () => {
|
||||
const m = await import("ant-design-vue/es/descriptions/");
|
||||
return m.DescriptionsItem;
|
||||
})
|
||||
);
|
||||
app.component(
|
||||
"AResult",
|
||||
defineAsyncComponent(() => import("ant-design-vue/es/result"))
|
||||
);
|
||||
}
|
||||
};
|
||||
6
packages/ui/certd-client/src/plugin/antdv-async/index.ts
Normal file
6
packages/ui/certd-client/src/plugin/antdv-async/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import antdv from "ant-design-vue";
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.use(antdv);
|
||||
}
|
||||
};
|
||||
@@ -1,38 +1,16 @@
|
||||
import { request, requestForMock } from "/src/api/service";
|
||||
// import "/src/mock";
|
||||
import {
|
||||
ColumnCompositionProps,
|
||||
CrudOptions,
|
||||
FastCrud,
|
||||
PageQuery,
|
||||
PageRes,
|
||||
setLogger,
|
||||
TransformResProps,
|
||||
useColumns,
|
||||
UseCrudProps,
|
||||
UserPageQuery,
|
||||
useTypes,
|
||||
utils
|
||||
} from "@fast-crud/fast-crud";
|
||||
import { ColumnCompositionProps, CrudOptions, FastCrud, PageQuery, PageRes, setLogger, TransformResProps, useColumns, UseCrudProps, UserPageQuery, useTypes, utils } from "@fast-crud/fast-crud";
|
||||
import "@fast-crud/fast-crud/dist/style.css";
|
||||
import {
|
||||
FsExtendsCopyable,
|
||||
FsExtendsEditor,
|
||||
FsExtendsJson,
|
||||
FsExtendsTime,
|
||||
FsExtendsUploader,
|
||||
FsExtendsInput,
|
||||
FsUploaderS3SignedUrlType,
|
||||
FsUploaderGetAuthContext,
|
||||
FsUploaderAliossSTS
|
||||
} from "@fast-crud/fast-extends";
|
||||
import { FsExtendsCopyable, FsExtendsEditor, FsExtendsJson, FsExtendsTime, FsExtendsUploader, FsExtendsInput, FsUploaderS3SignedUrlType, FsUploaderGetAuthContext, FsUploaderAliossSTS } from "@fast-crud/fast-extends";
|
||||
import "@fast-crud/fast-extends/dist/style.css";
|
||||
import UiAntdv from "@fast-crud/ui-antdv4";
|
||||
import "@fast-crud/ui-antdv4/dist/style.css";
|
||||
import * as _ from "lodash-es";
|
||||
import { merge } from "lodash-es";
|
||||
import { useCrudPermission } from "../permission";
|
||||
import { App } from "vue";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { usePreferences } from "/@/vben/preferences";
|
||||
|
||||
function install(app: App, options: any = {}) {
|
||||
app.use(UiAntdv);
|
||||
@@ -54,7 +32,18 @@ function install(app: App, options: any = {}) {
|
||||
commonOptions(props: UseCrudProps): CrudOptions {
|
||||
utils.logger.debug("commonOptions:", props);
|
||||
const crudBinding = props.crudExpose?.crudBinding;
|
||||
const { isMobile } = usePreferences();
|
||||
const opts: CrudOptions = {
|
||||
settings: {
|
||||
plugins: {
|
||||
mobile: {
|
||||
enabled: true,
|
||||
props: {
|
||||
isMobile: isMobile
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
table: {
|
||||
scroll: {
|
||||
x: 960
|
||||
@@ -90,6 +79,7 @@ function install(app: App, options: any = {}) {
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
buttons: {
|
||||
view: { type: "link", text: null, icon: "ion:eye-outline" },
|
||||
copy: { show: true, type: "link", text: null, icon: "ion:copy-outline" },
|
||||
@@ -163,6 +153,7 @@ function install(app: App, options: any = {}) {
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
//最后一列空白,用于自动伸缩列宽
|
||||
__blank__: {
|
||||
title: "",
|
||||
type: "text",
|
||||
@@ -170,9 +161,10 @@ function install(app: App, options: any = {}) {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
show: true,
|
||||
order: 999999,
|
||||
width: -1
|
||||
order: 99999,
|
||||
width: -1,
|
||||
columnSetShow: false,
|
||||
resizable: false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +190,11 @@ function install(app: App, options: any = {}) {
|
||||
action: "/basic/file/upload",
|
||||
name: "file",
|
||||
withCredentials: false,
|
||||
uploadRequest: async ({ action, file, onProgress }: any) => {
|
||||
test: 22,
|
||||
custom: { aaa: 22 },
|
||||
uploadRequest: async (opts: any) => {
|
||||
console.log("uploadRequest:", opts);
|
||||
const { action, file, onProgress } = opts;
|
||||
// @ts-ignore
|
||||
const data = new FormData();
|
||||
data.append("file", file);
|
||||
@@ -268,7 +264,7 @@ function install(app: App, options: any = {}) {
|
||||
// 比如你可以定义一个readonly的公共属性,处理该字段只读,不能编辑
|
||||
if (columnProps.readonly) {
|
||||
// 合并column配置
|
||||
_.merge(columnProps, {
|
||||
merge(columnProps, {
|
||||
form: { show: false },
|
||||
viewForm: { show: true }
|
||||
});
|
||||
@@ -277,6 +273,25 @@ function install(app: App, options: any = {}) {
|
||||
}
|
||||
});
|
||||
|
||||
//默认宽度,支持自动拖动调整列宽
|
||||
registerMergeColumnPlugin({
|
||||
name: "resize-column-plugin",
|
||||
order: 2,
|
||||
handle: (columnProps: ColumnCompositionProps) => {
|
||||
if (!columnProps.column) {
|
||||
columnProps.column = {};
|
||||
}
|
||||
if (columnProps.column.resizable == null) {
|
||||
columnProps.column.resizable = true;
|
||||
if (!columnProps.column.width) {
|
||||
columnProps.column.width = 200;
|
||||
}
|
||||
}
|
||||
|
||||
return columnProps;
|
||||
}
|
||||
});
|
||||
|
||||
registerMergeColumnPlugin({
|
||||
name: "resize-column-plugin",
|
||||
order: 2,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { message } from "ant-design-vue";
|
||||
import NProgress from "nprogress";
|
||||
export function registerRouterHook() {
|
||||
// 注册路由beforeEach钩子,在第一次加载路由页面时,加载权限
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
router.beforeEach(async (to, from) => {
|
||||
const permissionStore = usePermissionStore();
|
||||
if (permissionStore.isInited) {
|
||||
if (to.meta.permission) {
|
||||
@@ -20,26 +20,23 @@ export function registerRouterHook() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
next();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const token = userStore.getToken;
|
||||
if (!token || token === "undefined") {
|
||||
next();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化权限列表
|
||||
try {
|
||||
console.log("permission is enabled");
|
||||
await permissionStore.loadFromRemote();
|
||||
console.log("PM load success");
|
||||
next({ ...to, replace: true });
|
||||
return { ...to, replace: true };
|
||||
} catch (e) {
|
||||
console.error("加载动态路由失败", e);
|
||||
next();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { useResourceStore } from "/src/store/modules/resource";
|
||||
// import { useResourceStore } from "/src/store/modules/resource";
|
||||
import { getPermissions } from "./api";
|
||||
import { mitter } from "/@/utils/util.mitt";
|
||||
import { env } from "/@/utils/util.env";
|
||||
import { useAccessStore } from "/@/vben/stores";
|
||||
import { eachTree } from "/@/utils/util.tree";
|
||||
import util from "/@/plugin/permission/util.permission";
|
||||
|
||||
//监听注销事件
|
||||
mitter.on("app.logout", () => {
|
||||
const permissionStore = usePermissionStore();
|
||||
permissionStore.clear();
|
||||
const accessStore = useAccessStore();
|
||||
accessStore.setIsAccessChecked(false);
|
||||
});
|
||||
|
||||
mitter.on("app.login", () => {
|
||||
const permissionStore = useResourceStore();
|
||||
const accessStore = useAccessStore();
|
||||
accessStore.setIsAccessChecked(false);
|
||||
const permissionStore = usePermissionStore();
|
||||
permissionStore.clear();
|
||||
permissionStore.init();
|
||||
// const accessStore = useAccessStore();
|
||||
// accessStore.setAccessCode([]);
|
||||
// permissionStore.init();
|
||||
});
|
||||
|
||||
interface PermissionState {
|
||||
@@ -27,7 +36,7 @@ interface PermissionState {
|
||||
* @param permissionList
|
||||
* @returns {*}
|
||||
*/
|
||||
function formatPermissions(menuTree: Array<any>, permissionList: any[] = []) {
|
||||
export function formatPermissions(menuTree: Array<any>, permissionList: any[] = []) {
|
||||
if (menuTree == null) {
|
||||
menuTree = [];
|
||||
}
|
||||
@@ -75,8 +84,8 @@ export const usePermissionStore = defineStore({
|
||||
this.init({ permissions });
|
||||
|
||||
//过滤没有权限的菜单
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.filterByPermission(permissions);
|
||||
const accessStore = useAccessStore();
|
||||
accessStore.setAccessCodes(permissions);
|
||||
},
|
||||
async loadFromRemote() {
|
||||
let permissionTree = [];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { usePermission } from "/@/plugin/permission";
|
||||
import * as _ from "lodash-es";
|
||||
import { merge as LodashMerge } from "lodash-es";
|
||||
|
||||
export type UseCrudPermissionExtraProps = {
|
||||
hasActionPermission: (action: string) => boolean;
|
||||
@@ -30,7 +30,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
||||
return hasPermissions(prefix + ":" + action);
|
||||
}
|
||||
|
||||
function buildCrudPermission() {
|
||||
function buildCrudPermission(): any {
|
||||
if (permission == null) {
|
||||
return {};
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
||||
}
|
||||
}
|
||||
|
||||
return _.merge(
|
||||
return LodashMerge(
|
||||
{
|
||||
actionbar: {
|
||||
buttons: {
|
||||
@@ -64,7 +64,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
||||
|
||||
function merge(userOptions: any) {
|
||||
const permissionOptions = buildCrudPermission();
|
||||
_.merge(permissionOptions, userOptions);
|
||||
LodashMerge(permissionOptions, userOptions);
|
||||
return permissionOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ const util = {
|
||||
const permissionStore = usePermissionStore();
|
||||
const userPermissionList = permissionStore.getPermissions;
|
||||
return userPermissionList.some((permission: any) => {
|
||||
if (permission === "*") {
|
||||
return true;
|
||||
}
|
||||
return need.includes(permission);
|
||||
});
|
||||
},
|
||||
|
||||
35
packages/ui/certd-client/src/router/access.ts
Normal file
35
packages/ui/certd-client/src/router/access.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { ComponentRecordType, GenerateMenuAndRoutesOptions } from "/@/vben/types";
|
||||
|
||||
import { generateAccessible } from "/@/vben/access";
|
||||
import { preferences } from "/@/vben/preferences";
|
||||
|
||||
import { BasicLayout, IFrameView } from "/@/vben/layouts";
|
||||
|
||||
const forbiddenComponent = () => import("#/views/_core/fallback/forbidden.vue");
|
||||
|
||||
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
const pageMap: ComponentRecordType = import.meta.glob("../views/**/*.vue");
|
||||
|
||||
const layoutMap: ComponentRecordType = {
|
||||
BasicLayout,
|
||||
IFrameView
|
||||
} as any;
|
||||
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
...options,
|
||||
// fetchMenuListAsync: async () => {
|
||||
// message.loading({
|
||||
// content: `${$t("common.loadingMenu")}...`,
|
||||
// duration: 1.5
|
||||
// });
|
||||
// return await getAllMenusApi();
|
||||
// },
|
||||
// 可以指定没有权限跳转403页面
|
||||
forbiddenComponent,
|
||||
// 如果 route.meta.menuVisibleWithForbidden = true
|
||||
layoutMap,
|
||||
pageMap
|
||||
});
|
||||
}
|
||||
|
||||
export { generateAccess };
|
||||
130
packages/ui/certd-client/src/router/guard.ts
Normal file
130
packages/ui/certd-client/src/router/guard.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { Router } from "vue-router";
|
||||
|
||||
import { DEFAULT_HOME_PATH, LOGIN_PATH } from "/@/vben/constants";
|
||||
import { preferences } from "/@/vben/preferences";
|
||||
import { useAccessStore } from "/@/vben/stores";
|
||||
import { generateMenus, startProgress, stopProgress } from "/@/vben/utils";
|
||||
import { frameworkRoutes } from "/@/router/resolve";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { usePermissionStore } from "/@/plugin/permission/store.permission";
|
||||
import util from "/@/plugin/permission/util.permission";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
|
||||
function buildAccessedMenus(menus: any) {
|
||||
if (menus == null) {
|
||||
return;
|
||||
}
|
||||
const list: any = [];
|
||||
for (const sub of menus) {
|
||||
if (sub.meta?.permission != null) {
|
||||
if (!util.hasPermissions(sub.meta.permission)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const item: any = {
|
||||
...sub
|
||||
};
|
||||
|
||||
list.push(item);
|
||||
if (sub.children && sub.children.length > 0) {
|
||||
item.children = buildAccessedMenus(sub.children);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
/**
|
||||
* 通用守卫配置
|
||||
* @param router
|
||||
*/
|
||||
export function setupCommonGuard(router: Router) {
|
||||
// 记录已经加载的页面
|
||||
const loadedPaths = new Set<string>();
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
const settingStore = useSettingStore();
|
||||
await settingStore.initOnce();
|
||||
|
||||
to.meta.loaded = loadedPaths.has(to.path);
|
||||
|
||||
// 页面加载进度条
|
||||
if (!to.meta.loaded && preferences.transition.progress) {
|
||||
startProgress();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
||||
|
||||
loadedPaths.add(to.path);
|
||||
|
||||
// 关闭页面加载进度条
|
||||
if (preferences.transition.progress) {
|
||||
stopProgress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限访问守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function setupAccessGuard(router: Router) {
|
||||
router.beforeEach(async (to, from) => {
|
||||
const accessStore = useAccessStore();
|
||||
// 是否已经生成过动态路由
|
||||
if (!accessStore.isAccessChecked) {
|
||||
if (accessStore.accessToken) {
|
||||
//如果已登录
|
||||
const permissionStore = usePermissionStore();
|
||||
await permissionStore.loadFromRemote();
|
||||
const userStore = useUserStore();
|
||||
await userStore.getUserInfoAction();
|
||||
}
|
||||
|
||||
const settingsStore = useSettingStore();
|
||||
const headerMenus: any[] = settingsStore.getHeaderMenus;
|
||||
let allMenus = await generateMenus(frameworkRoutes[0].children, router);
|
||||
allMenus = allMenus.concat(headerMenus);
|
||||
const accessibleMenus = buildAccessedMenus(allMenus);
|
||||
accessStore.setAccessRoutes(frameworkRoutes);
|
||||
accessStore.setAccessMenus(accessibleMenus);
|
||||
accessStore.setIsAccessChecked(true);
|
||||
}
|
||||
|
||||
// 基本路由,这些路由不需要进入权限拦截
|
||||
const needAuth = to.matched.some((r) => {
|
||||
return r.meta?.auth || r.meta?.permission;
|
||||
});
|
||||
|
||||
if (!needAuth) {
|
||||
return true;
|
||||
}
|
||||
if (!accessStore.accessToken) {
|
||||
// 没有访问权限,跳转登录页面
|
||||
if (to.fullPath !== LOGIN_PATH) {
|
||||
return {
|
||||
path: LOGIN_PATH,
|
||||
// 如不需要,直接删除 query
|
||||
query: to.fullPath === DEFAULT_HOME_PATH ? {} : { redirect: encodeURIComponent(to.fullPath) },
|
||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||
replace: true
|
||||
};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function createRouterGuard(router: Router) {
|
||||
/** 通用 */
|
||||
setupCommonGuard(router);
|
||||
/** 权限访问 */
|
||||
setupAccessGuard(router);
|
||||
}
|
||||
|
||||
export { createRouterGuard };
|
||||
@@ -1,82 +1,73 @@
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
// 进度条
|
||||
import NProgress from "nprogress";
|
||||
import "nprogress/nprogress.css";
|
||||
import { usePageStore } from "../store/modules/page";
|
||||
import { site } from "../utils/util.site";
|
||||
import { routes } from "./resolve";
|
||||
import { useResourceStore } from "../store/modules/resource";
|
||||
import { useUserStore } from "../store/modules/user";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { createRouterGuard } from "/@/router/guard";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
});
|
||||
|
||||
/**
|
||||
* 路由拦截
|
||||
|
||||
*/
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 进度条
|
||||
NProgress.start();
|
||||
const settingStore = useSettingStore();
|
||||
await settingStore.initOnce();
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.init();
|
||||
// 修复三级以上路由页面无法缓存的问题
|
||||
if (to.matched && to.matched.length > 2) {
|
||||
to.matched.splice(1, to.matched.length - 2);
|
||||
}
|
||||
// 验证当前路由所有的匹配中是否需要有登录验证的
|
||||
if (
|
||||
to.matched.some((r) => {
|
||||
return r.meta?.auth || r.meta?.permission;
|
||||
})
|
||||
) {
|
||||
const userStore = useUserStore();
|
||||
// 这里暂时将cookie里是否存有token作为验证是否登录的条件
|
||||
// 请根据自身业务需要修改
|
||||
const token = userStore.getToken;
|
||||
if (token) {
|
||||
next();
|
||||
} else {
|
||||
// 没有登录的时候跳转到登录界面
|
||||
// 携带上登陆成功之后需要跳转的页面完整路径
|
||||
resourceStore.clear();
|
||||
next({
|
||||
name: "login",
|
||||
query: {
|
||||
redirect: to.fullPath
|
||||
}
|
||||
});
|
||||
// https://github.com/d2-projects/d2-admin/issues/138
|
||||
NProgress.done();
|
||||
}
|
||||
} else {
|
||||
// 不需要身份校验 直接通过
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach((to: any) => {
|
||||
// 进度条
|
||||
NProgress.done();
|
||||
// 多页控制 打开新的页面
|
||||
const pageStore = usePageStore();
|
||||
// for (const item of to.matched) {
|
||||
// pageStore.keepAlivePush(item.name);
|
||||
// }
|
||||
pageStore.open(to);
|
||||
// 更改标题
|
||||
const settingStore = useSettingStore();
|
||||
site.title(to.meta.title, settingStore.siteInfo.title);
|
||||
|
||||
//修改左侧边栏
|
||||
const matched = to.matched;
|
||||
if (matched.length > 0) {
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.setCurrentTopMenuByCurrentRoute(matched);
|
||||
}
|
||||
});
|
||||
createRouterGuard(router);
|
||||
export default router;
|
||||
//
|
||||
// /**
|
||||
// * 路由拦截
|
||||
// */
|
||||
// router.beforeEach(async (to, from, next) => {
|
||||
// // 进度条
|
||||
// NProgress.start();
|
||||
// const settingStore = useSettingStore();
|
||||
// await settingStore.initOnce();
|
||||
// const resourceStore = useResourceStore();
|
||||
// resourceStore.init();
|
||||
// // 修复三级以上路由页面无法缓存的问题
|
||||
// if (to.matched && to.matched.length > 2) {
|
||||
// to.matched.splice(1, to.matched.length - 2);
|
||||
// }
|
||||
// // 验证当前路由所有的匹配中是否需要有登录验证的
|
||||
// if (
|
||||
// to.matched.some((r) => {
|
||||
// return r.meta?.auth || r.meta?.permission;
|
||||
// })
|
||||
// ) {
|
||||
// const userStore = useUserStore();
|
||||
// // 这里暂时将cookie里是否存有token作为验证是否登录的条件
|
||||
// // 请根据自身业务需要修改
|
||||
// const token = userStore.getToken;
|
||||
// if (token) {
|
||||
// next();
|
||||
// } else {
|
||||
// // 没有登录的时候跳转到登录界面
|
||||
// // 携带上登陆成功之后需要跳转的页面完整路径
|
||||
// resourceStore.clear();
|
||||
// next({
|
||||
// name: "login",
|
||||
// query: {
|
||||
// redirect: to.fullPath
|
||||
// }
|
||||
// });
|
||||
// // https://github.com/d2-projects/d2-admin/issues/138
|
||||
// NProgress.done();
|
||||
// }
|
||||
// } else {
|
||||
// // 不需要身份校验 直接通过
|
||||
// next();
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// router.afterEach((to: any) => {
|
||||
// // 进度条
|
||||
// NProgress.done();
|
||||
// // 多页控制 打开新的页面
|
||||
// const pageStore = usePageStore();
|
||||
// // for (const item of to.matched) {
|
||||
// // pageStore.keepAlivePush(item.name);
|
||||
// // }
|
||||
// pageStore.open(to);
|
||||
// // 更改标题
|
||||
// const settingStore = useSettingStore();
|
||||
// site.title(to.meta.title, settingStore.siteInfo.title);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import LayoutPass from "/src/layout/layout-pass.vue";
|
||||
import * as _ from "lodash-es";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { outsideResource } from "./source/outside";
|
||||
import { headerResource } from "./source/header";
|
||||
import { frameworkResource } from "./source/framework";
|
||||
@@ -19,7 +19,7 @@ function transformOneResource(resource: any, parent: any) {
|
||||
if (meta.isMenu === false) {
|
||||
menu = null;
|
||||
} else {
|
||||
menu = _.cloneDeep(resource);
|
||||
menu = cloneDeep(resource);
|
||||
delete menu.component;
|
||||
if (menu.path?.startsWith("/")) {
|
||||
menu.fullPath = menu.path;
|
||||
@@ -28,11 +28,11 @@ function transformOneResource(resource: any, parent: any) {
|
||||
}
|
||||
}
|
||||
let route;
|
||||
if (meta.isRoute === false || resource.path == null || resource.path.startsWith("https://") || resource.path.startsWith("http://")) {
|
||||
if (meta.isRoute === false || resource.path == null) {
|
||||
//没有route
|
||||
route = null;
|
||||
} else {
|
||||
route = _.cloneDeep(resource);
|
||||
route = cloneDeep(resource);
|
||||
if (route.component && typeof route.component === "string") {
|
||||
const path = "/src/views" + route.component;
|
||||
route.component = modules[path];
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import LayoutFramework from "/src/layout/layout-framework.vue";
|
||||
//import { crudResources } from "/@/router/source/modules/crud";
|
||||
import { sysResources } from "/@/router/source/modules/sys";
|
||||
import { certdResources } from "/@/router/source/modules/certd";
|
||||
import LayoutBasic from "/@/layout/layout-basic.vue";
|
||||
|
||||
import type { RouteRecordRaw } from "vue-router";
|
||||
|
||||
import { mergeRouteModules } from "/@/vben/utils";
|
||||
const dynamicRouteFiles = import.meta.glob("./modules/**/*.ts*", {
|
||||
eager: true
|
||||
});
|
||||
|
||||
/** 动态路由 */
|
||||
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
|
||||
export const frameworkResource = [
|
||||
{
|
||||
title: "框架",
|
||||
name: "framework",
|
||||
name: "root",
|
||||
path: "/",
|
||||
redirect: "/index",
|
||||
component: LayoutFramework,
|
||||
component: LayoutBasic,
|
||||
meta: {
|
||||
icon: "ion:accessibility",
|
||||
auth: true
|
||||
hideInBreadcrumb: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -23,12 +29,13 @@ export const frameworkResource = [
|
||||
meta: {
|
||||
fixedAside: true,
|
||||
showOnHeader: false,
|
||||
icon: "ion:home-outline"
|
||||
icon: "ion:home-outline",
|
||||
auth: true
|
||||
}
|
||||
},
|
||||
//...crudResources,
|
||||
...certdResources,
|
||||
...sysResources
|
||||
// @ts-ignore
|
||||
|
||||
...dynamicRoutes
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
24
packages/ui/certd-client/src/router/source/modules/about.tsx
Normal file
24
packages/ui/certd-client/src/router/source/modules/about.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { IFrameView } from "/@/vben/layouts";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import { computed } from "vue";
|
||||
import TutorialButton from "/@/components/tutorial/index.vue";
|
||||
export const aboutResource = [
|
||||
{
|
||||
title: "文档",
|
||||
name: "document",
|
||||
path: "/about/doc",
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
icon: "lucide:book-open-text",
|
||||
link: "https://certd.docmirror.cn",
|
||||
title: "文档",
|
||||
order: 9999,
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
return !settingStore.isComm;
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default aboutResource;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import aboutResource from "/@/router/source/modules/about";
|
||||
|
||||
export const certdResources = [
|
||||
{
|
||||
@@ -8,7 +9,8 @@ export const certdResources = [
|
||||
redirect: "/certd/pipeline",
|
||||
meta: {
|
||||
icon: "ion:key-outline",
|
||||
auth: true
|
||||
auth: true,
|
||||
order: 0
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -64,7 +66,8 @@ export const certdResources = [
|
||||
{
|
||||
title: "设置",
|
||||
name: "MineSetting",
|
||||
path: "/certd/mine",
|
||||
path: "/certd/setting",
|
||||
redirect: "/certd/cname/record",
|
||||
meta: {
|
||||
icon: "ion:settings-outline",
|
||||
auth: true,
|
||||
@@ -142,6 +145,7 @@ export const certdResources = [
|
||||
title: "套餐",
|
||||
name: "SuiteProduct",
|
||||
path: "/certd/suite",
|
||||
redirect: "/certd/suite/mine",
|
||||
meta: {
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
@@ -220,3 +224,5 @@ export const certdResources = [
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export default certdResources;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import LayoutPass from "/@/layout/layout-pass.vue";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import aboutResource from "/@/router/source/modules/about";
|
||||
|
||||
export const sysResources = [
|
||||
{
|
||||
@@ -7,10 +8,10 @@ export const sysResources = [
|
||||
name: "SysRoot",
|
||||
path: "/sys",
|
||||
redirect: "/sys/settings",
|
||||
component: LayoutPass,
|
||||
meta: {
|
||||
icon: "ion:settings-outline",
|
||||
permission: "sys:settings:view"
|
||||
permission: "sys:settings:view",
|
||||
order: 10
|
||||
},
|
||||
children: [
|
||||
{
|
||||
@@ -175,6 +176,7 @@ export const sysResources = [
|
||||
title: "套餐管理",
|
||||
name: "SuiteManager",
|
||||
path: "/sys/suite",
|
||||
redirect: "/sys/suite/setting",
|
||||
meta: {
|
||||
icon: "ion:cart-outline",
|
||||
permission: "sys:settings:edit",
|
||||
@@ -231,3 +233,5 @@ export const sysResources = [
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export default sysResources;
|
||||
|
||||
@@ -1,437 +0,0 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { cloneDeep, get, uniq } from "lodash-es";
|
||||
import router from "/src/router";
|
||||
import { frameworkRoutes } from "/src/router/resolve";
|
||||
// @ts-ignore
|
||||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
import { useUserStore } from "/src/store/modules/user";
|
||||
const OPENED_CACHE_KEY = "TABS_OPENED";
|
||||
|
||||
interface PageState {
|
||||
// 可以在多页 tab 模式下显示的页面
|
||||
pool: Array<any>;
|
||||
// 当前显示的多页面列表
|
||||
opened: Array<any>;
|
||||
// 已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||
openedLoaded: boolean;
|
||||
// 当前页面
|
||||
current: string;
|
||||
// 需要缓存的页面 name
|
||||
keepAlive: Array<any>;
|
||||
inited: boolean;
|
||||
}
|
||||
// 判定是否需要缓存
|
||||
const isKeepAlive = (data: any) => get(data, "meta.cache", false);
|
||||
|
||||
export const usePageStore = defineStore({
|
||||
id: "app.page",
|
||||
state: (): PageState => ({
|
||||
// 可以在多页 tab 模式下显示的页面
|
||||
pool: [],
|
||||
// 当前显示的多页面列表
|
||||
opened: [
|
||||
{
|
||||
name: "index",
|
||||
fullPath: "/index",
|
||||
meta: {
|
||||
title: "首页",
|
||||
auth: false
|
||||
}
|
||||
}
|
||||
],
|
||||
// 已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||
openedLoaded: false,
|
||||
// 当前页面
|
||||
current: "",
|
||||
// 需要缓存的页面 name
|
||||
keepAlive: [],
|
||||
inited: false
|
||||
}),
|
||||
getters: {
|
||||
// @ts-ignore
|
||||
getOpened(): any {
|
||||
// @ts-ignore
|
||||
return this.opened;
|
||||
},
|
||||
getCurrent(): string {
|
||||
return this.current;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* @description 确认已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||
* @param {Object} context
|
||||
*/
|
||||
async isLoaded() {
|
||||
if (this.openedLoaded) {
|
||||
return true;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
const timer = setInterval(() => {
|
||||
if (this.openedLoaded) {
|
||||
resolve(clearInterval(timer));
|
||||
}
|
||||
}, 10);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 从持久化数据载入标签页列表
|
||||
* @param {Object} context
|
||||
*/
|
||||
async openedLoad() {
|
||||
// store 赋值
|
||||
const value = LocalStorage.get(this.getStorageKey());
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
// 在处理函数中进行数据优化 过滤掉现在已经失效的页签或者已经改变了信息的页签
|
||||
// 以 fullPath 字段为准
|
||||
// 如果页面过多的话可能需要优化算法
|
||||
// valid 有效列表 1, 1, 0, 1 => 有效, 有效, 失效, 有效
|
||||
const valid: Array<number> = [];
|
||||
// 处理数据
|
||||
this.opened = value
|
||||
.map((opened: any) => {
|
||||
// 忽略首页
|
||||
if (opened.fullPath === "/index") {
|
||||
valid.push(1);
|
||||
return opened;
|
||||
}
|
||||
// 尝试在所有的支持多标签页的页面里找到 name 匹配的页面
|
||||
const find = this.pool.find((item) => item.name === opened.name);
|
||||
// 记录有效或无效信息
|
||||
valid.push(find ? 1 : 0);
|
||||
// 返回合并后的数据 新的覆盖旧的
|
||||
// 新的数据中一般不会携带 params 和 query, 所以旧的参数会留存
|
||||
return Object.assign({}, opened, find);
|
||||
})
|
||||
.filter((opened: any, index: any) => valid[index] === 1);
|
||||
// 标记已经加载多标签页数据 https://github.com/d2-projects/d2-admin/issues/201
|
||||
this.openedLoaded = true;
|
||||
// 根据 opened 数据生成缓存设置
|
||||
this.keepAliveRefresh();
|
||||
},
|
||||
|
||||
getStorageKey() {
|
||||
const userStore = useUserStore();
|
||||
const userId = userStore.getUserInfo?.id ?? "anonymous";
|
||||
return OPENED_CACHE_KEY + ":" + userId;
|
||||
},
|
||||
/**
|
||||
* 将 opened 属性赋值并持久化 在这之前请先确保已经更新了 state.opened
|
||||
* @param {Object} context
|
||||
*/
|
||||
async opened2db() {
|
||||
// 设置数据
|
||||
|
||||
LocalStorage.set(this.getStorageKey(), this.opened);
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 更新页面列表上的某一项
|
||||
* @param {Object} context
|
||||
* @param {Object} payload { index, params, query, fullPath } 路由信息
|
||||
*/
|
||||
async openedUpdate({ index, params, query, fullPath }: any) {
|
||||
// 更新页面列表某一项
|
||||
const page = this.opened[index];
|
||||
page.params = params || page.params;
|
||||
page.query = query || page.query;
|
||||
page.fullPath = fullPath || page.fullPath;
|
||||
this.opened.splice(index, 1, page);
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 重排页面列表上的某一项
|
||||
* @param {Object} context
|
||||
* @param {Object} payload { oldIndex, newIndex } 位置信息
|
||||
*/
|
||||
async openedSort({ oldIndex, newIndex }: any) {
|
||||
// 重排页面列表某一项
|
||||
const page = this.opened[oldIndex];
|
||||
this.opened.splice(oldIndex, 1);
|
||||
this.opened.splice(newIndex, 0, page);
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 新增一个 tag (打开一个页面)
|
||||
* @param {Object} context
|
||||
* @param {Object} payload new tag info
|
||||
*/
|
||||
async add({ tag, params, query, fullPath }: any) {
|
||||
// 设置新的 tag 在新打开一个以前没打开过的页面时使用
|
||||
const newTag = tag;
|
||||
newTag.params = params || newTag.params;
|
||||
newTag.query = query || newTag.query;
|
||||
newTag.fullPath = fullPath || newTag.fullPath;
|
||||
// 添加进当前显示的页面数组
|
||||
this.opened.push(newTag);
|
||||
// 如果这个页面需要缓存 将其添加到缓存设置
|
||||
if (isKeepAlive(newTag)) {
|
||||
this.keepAlivePush(tag.name);
|
||||
}
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
},
|
||||
/**
|
||||
* @class current
|
||||
* @description 打开一个新的页面
|
||||
* @param {Object} context
|
||||
* @param {Object} payload 从路由钩子的 to 对象上获取 { name, params, query, fullPath, meta } 路由信息
|
||||
*/
|
||||
async open({ name, params, query, fullPath, meta }: any) {
|
||||
// 已经打开的页面
|
||||
const opened = this.opened;
|
||||
// 判断此页面是否已经打开 并且记录位置
|
||||
let pageOpendIndex = 0;
|
||||
const pageOpend = opened.find((page, index) => {
|
||||
const same = page.fullPath === fullPath;
|
||||
pageOpendIndex = same ? index : pageOpendIndex;
|
||||
return same;
|
||||
});
|
||||
if (pageOpend) {
|
||||
// 页面以前打开过
|
||||
await this.openedUpdate({
|
||||
index: pageOpendIndex,
|
||||
params,
|
||||
query,
|
||||
fullPath
|
||||
});
|
||||
} else {
|
||||
// 页面以前没有打开过
|
||||
const page = this.pool.find((t) => t.name === name);
|
||||
// 如果这里没有找到 page 代表这个路由虽然在框架内 但是不参与标签页显示
|
||||
if (page) {
|
||||
this.add({
|
||||
tag: Object.assign({}, page),
|
||||
params,
|
||||
query,
|
||||
fullPath
|
||||
});
|
||||
}
|
||||
}
|
||||
// 如果这个页面需要缓存 将其添加到缓存设置
|
||||
if (isKeepAlive({ meta })) {
|
||||
this.keepAlivePush(name);
|
||||
}
|
||||
// 设置当前的页面
|
||||
this.currentSet(fullPath);
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭一个 tag (关闭一个页面)
|
||||
* @param {Object} context
|
||||
* @param {Object} payload { tagName: 要关闭的标签名字 }
|
||||
*/
|
||||
async close({ tagName }: any) {
|
||||
// 预定下个新页面
|
||||
let newPage = {};
|
||||
const isCurrent = this.current === tagName;
|
||||
// 如果关闭的页面就是当前显示的页面
|
||||
if (isCurrent) {
|
||||
// 去找一个新的页面
|
||||
const len = this.opened.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (this.opened[i].fullPath === tagName) {
|
||||
newPage = i < len - 1 ? this.opened[i + 1] : this.opened[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 找到这个页面在已经打开的数据里是第几个
|
||||
const index = this.opened.findIndex((page) => page.fullPath === tagName);
|
||||
if (index >= 0) {
|
||||
// 如果这个页面是缓存的页面 将其在缓存设置中删除
|
||||
this.keepAliveRemove(this.opened[index].name);
|
||||
// 更新数据 删除关闭的页面
|
||||
this.opened.splice(index, 1);
|
||||
}
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
// 决定最后停留的页面
|
||||
if (isCurrent) {
|
||||
// @ts-ignore
|
||||
const { name = "index", params = {}, query = {} } = newPage;
|
||||
const routerObj = { name, params, query };
|
||||
await router.push(routerObj);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭当前标签左边的标签
|
||||
* @param opts
|
||||
*/
|
||||
async closeLeft(opts = {}) {
|
||||
await this.closeByCondition({
|
||||
condition({ i, currentIndex }: any) {
|
||||
return i >= currentIndex;
|
||||
},
|
||||
...opts
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭当前标签右边的标签
|
||||
* @param opts
|
||||
*/
|
||||
async closeRight(opts = {}) {
|
||||
await this.closeByCondition({
|
||||
condition({ i, currentIndex }: any) {
|
||||
return currentIndex >= i;
|
||||
},
|
||||
...opts
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭当前标签右边的标签
|
||||
* @param opts
|
||||
*/
|
||||
async closeByCondition(opts = {}) {
|
||||
// @ts-ignore
|
||||
const { pageSelect, condition } = opts;
|
||||
const pageAim = pageSelect || this.current;
|
||||
let currentIndex = 0;
|
||||
this.opened.forEach((page, index) => {
|
||||
if (page.fullPath === pageAim) currentIndex = index;
|
||||
});
|
||||
// 删除打开的页面 并在缓存设置中删除
|
||||
for (let i = this.opened.length - 1; i >= 0; i--) {
|
||||
if (this.opened[i].name === "index" || condition({ i, currentIndex })) {
|
||||
continue;
|
||||
}
|
||||
this.keepAliveRemove(this.opened[i].name);
|
||||
this.opened.splice(i, 1);
|
||||
}
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
// 设置当前的页面
|
||||
this.current = pageAim;
|
||||
// @ts-ignore
|
||||
if (router.currentRoute.fullPath !== pageAim) await router.push(pageAim);
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭当前激活之外的 tag
|
||||
* @param opts
|
||||
*/
|
||||
async closeOther(opts = {}) {
|
||||
await this.closeByCondition({
|
||||
condition({ i, currentIndex }: any) {
|
||||
return currentIndex === i;
|
||||
},
|
||||
...opts
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @class opened
|
||||
* @description 关闭所有 tag
|
||||
* @param {Object} context
|
||||
*/
|
||||
async closeAll() {
|
||||
// 删除打开的页面 并在缓存设置中删除
|
||||
for (let i = this.opened.length - 1; i >= 0; i--) {
|
||||
if (this.opened[i].name === "index") {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.keepAliveRemove(this.opened[i].name);
|
||||
this.opened.splice(i, 1);
|
||||
}
|
||||
// 持久化
|
||||
await this.opened2db();
|
||||
// 关闭所有的标签页后需要判断一次现在是不是在首页
|
||||
// @ts-ignore
|
||||
if (router.currentRoute.name !== "index") {
|
||||
await router.push({ name: "index" });
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @class keepAlive
|
||||
* @description 从已经打开的页面记录中更新需要缓存的页面记录
|
||||
* @param {Object} state state
|
||||
*/
|
||||
keepAliveRefresh() {
|
||||
this.keepAlive = this.opened.filter((item) => isKeepAlive(item)).map((e) => e.name);
|
||||
console.log("keepalive", this.keepAlive);
|
||||
},
|
||||
/**
|
||||
* @description 删除一个页面的缓存设置
|
||||
* @param {Object} state state
|
||||
* @param {String} name name
|
||||
*/
|
||||
keepAliveRemove(name: string) {
|
||||
const list = cloneDeep(this.keepAlive);
|
||||
const index = list.findIndex((item) => item === name);
|
||||
if (index !== -1) {
|
||||
list.splice(index, 1);
|
||||
this.keepAlive = list;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @description 增加一个页面的缓存设置
|
||||
* @param {Object} state state
|
||||
* @param {String} name name
|
||||
*/
|
||||
keepAlivePush(name: string) {
|
||||
const keep = cloneDeep(this.keepAlive);
|
||||
keep.push(name);
|
||||
this.keepAlive = uniq(keep);
|
||||
},
|
||||
/**
|
||||
* @description 清空页面缓存设置
|
||||
* @param {Object} state state
|
||||
*/
|
||||
keepAliveClean() {
|
||||
this.keepAlive = [];
|
||||
},
|
||||
/**
|
||||
* @class current
|
||||
* @description 设置当前激活的页面 fullPath
|
||||
* @param {Object} state state
|
||||
* @param {String} fullPath new fullPath
|
||||
*/
|
||||
currentSet(fullPath: string) {
|
||||
this.current = fullPath;
|
||||
},
|
||||
/**
|
||||
* @class pool
|
||||
* @description 保存 pool (候选池)
|
||||
* @param {Object} state state
|
||||
* @param {Array} routes routes
|
||||
*/
|
||||
async init(routes?: any) {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
if (routes == null) {
|
||||
//不能用全部的routes,只能是framework内的
|
||||
routes = frameworkRoutes;
|
||||
}
|
||||
|
||||
const pool: any = [];
|
||||
const push = function (routes: any) {
|
||||
routes.forEach((route: any) => {
|
||||
if (route.children && route.children.length > 0) {
|
||||
push(route.children);
|
||||
} else {
|
||||
if (!route.hidden) {
|
||||
const { meta, name, path } = route;
|
||||
// @ts-ignore
|
||||
pool.push({ meta, name, path });
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
push(routes);
|
||||
this.pool = pool;
|
||||
await this.openedLoad();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,148 +0,0 @@
|
||||
import { defineStore } from "pinia";
|
||||
// @ts-ignore
|
||||
import { frameworkMenus, headerMenus, filterMenus, findMenus } from "/src/router/resolve";
|
||||
import * as _ from "lodash-es";
|
||||
import { mitter } from "/src/utils/util.mitt";
|
||||
//监听注销事件
|
||||
mitter.on("app.logout", () => {
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.clear();
|
||||
});
|
||||
//
|
||||
// mitter.on("app.login", () => {
|
||||
// const resourceStore = useResourceStore();
|
||||
// resourceStore.clear();
|
||||
// resourceStore.init();
|
||||
// });
|
||||
|
||||
interface ResourceState {
|
||||
topMenus: Array<any>;
|
||||
authedTopMenus: Array<any>;
|
||||
headerMenus: Array<any>;
|
||||
asideMenus: Array<any>;
|
||||
fixedAsideMenus: Array<any>;
|
||||
inited: boolean;
|
||||
currentTopMenu?: string;
|
||||
currentAsidePath?: string;
|
||||
}
|
||||
|
||||
export const useResourceStore = defineStore({
|
||||
id: "app.resource",
|
||||
//@ts-ignore
|
||||
state: (): ResourceState => ({
|
||||
// user info
|
||||
topMenus: [],
|
||||
authedTopMenus: [],
|
||||
headerMenus: [],
|
||||
fixedAsideMenus: [],
|
||||
inited: false,
|
||||
currentTopMenu: undefined,
|
||||
currentAsidePath: undefined
|
||||
}),
|
||||
getters: {
|
||||
getAsideMenus() {
|
||||
let topMenu = this.currentTopMenu;
|
||||
if (!topMenu && this.authedTopMenus.length > 0) {
|
||||
topMenu = this.authedTopMenus[0];
|
||||
}
|
||||
let asideMenus = topMenu?.children || [];
|
||||
asideMenus = [...this.fixedAsideMenus, ...asideMenus];
|
||||
return asideMenus;
|
||||
},
|
||||
getHeaderMenus() {
|
||||
return this.headerMenus;
|
||||
},
|
||||
getFrameworkMenus() {
|
||||
return this.authedTopMenus;
|
||||
// const menus = _.cloneDeep(this.authedTopMenus);
|
||||
// for (const menu of menus) {
|
||||
// delete menu.children;
|
||||
// }
|
||||
// return menus;
|
||||
}
|
||||
} as any,
|
||||
actions: {
|
||||
clear() {
|
||||
this.inited = false;
|
||||
this.currentTopMenu = undefined;
|
||||
},
|
||||
/**
|
||||
* 初始化资源
|
||||
*/
|
||||
init() {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
|
||||
const allMenus = _.cloneDeep(frameworkMenus[0].children);
|
||||
this.topMenus = filterMenus(allMenus, (item: any) => {
|
||||
return item?.meta?.showOnHeader !== false;
|
||||
});
|
||||
|
||||
this.fixedAsideMenus = findMenus(allMenus, (item: any) => {
|
||||
return item?.meta?.fixedAside === true;
|
||||
});
|
||||
this.headerMenus = headerMenus;
|
||||
},
|
||||
setCurrentTopMenu(topMenu?: any) {
|
||||
if (this.topMenus.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (topMenu == null) {
|
||||
topMenu = this.topMenus[0];
|
||||
}
|
||||
this.currentTopMenu = topMenu;
|
||||
},
|
||||
setCurrentTopMenuByCurrentRoute(matched: any) {
|
||||
const menuHeader = this.authedTopMenus;
|
||||
if (matched?.length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
function findFromTree(tree: any, find: any) {
|
||||
tree = tree || [];
|
||||
const results: Array<any> = [];
|
||||
for (const item of tree) {
|
||||
if (find(item)) {
|
||||
results.push(item);
|
||||
return results;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found: any = findFromTree(item.children, find);
|
||||
if (found) {
|
||||
results.push(item);
|
||||
return results.concat(found);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const matchedPath = matched[1].path;
|
||||
const _side = findFromTree(menuHeader, (menu: any) => menu.path === matchedPath);
|
||||
if (_side?.length > 0) {
|
||||
if (this.currentAsidePath === _side[0]) {
|
||||
return;
|
||||
}
|
||||
this.currentAsidePath = _side[0];
|
||||
this.setCurrentTopMenu(_side[0]);
|
||||
}
|
||||
},
|
||||
filterByPermission(permissions: any) {
|
||||
this.authedTopMenus = this.filterChildrenByPermission(this.topMenus, permissions);
|
||||
},
|
||||
filterChildrenByPermission(list: any, permissions: any) {
|
||||
const menus = list.filter((item: any) => {
|
||||
if (item?.meta?.permission) {
|
||||
return permissions.includes(item.meta.permission);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
for (const menu of menus) {
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
menu.children = this.filterChildrenByPermission(menu.children, permissions);
|
||||
}
|
||||
}
|
||||
return menus;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { Modal, notification, theme } from "ant-design-vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import * as _ from "lodash-es";
|
||||
// @ts-ignore
|
||||
import { LocalStorage } from "/src/utils/util.storage";
|
||||
@@ -9,20 +9,11 @@ import { HeaderMenus, PlusInfo, SiteEnv, SiteInfo, SuiteSetting, SysInstallInfo,
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import { mitter } from "/@/utils/util.mitt";
|
||||
import { env } from "/@/utils/util.env";
|
||||
|
||||
export type ThemeToken = {
|
||||
token: {
|
||||
colorPrimary?: string;
|
||||
};
|
||||
algorithm: any;
|
||||
};
|
||||
export type ThemeConfig = {
|
||||
colorPrimary: string;
|
||||
mode: string;
|
||||
};
|
||||
import { updatePreferences } from "/@/vben/preferences";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
import { utils } from "/@/utils";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
export interface SettingState {
|
||||
themeConfig?: ThemeConfig;
|
||||
themeToken: ThemeToken;
|
||||
sysPublic?: SysPublicSetting;
|
||||
installInfo?: {
|
||||
siteId: string;
|
||||
@@ -40,11 +31,6 @@ export interface SettingState {
|
||||
suiteSetting?: SuiteSetting;
|
||||
}
|
||||
|
||||
const defaultThemeConfig = {
|
||||
colorPrimary: "#1890ff",
|
||||
mode: "light"
|
||||
};
|
||||
const SETTING_THEME_KEY = "SETTING_THEME";
|
||||
const defaultSiteInfo: SiteInfo = {
|
||||
title: env.TITLE || "Certd",
|
||||
slogan: env.SLOGAN || "让你的证书永不过期",
|
||||
@@ -56,11 +42,6 @@ const defaultSiteInfo: SiteInfo = {
|
||||
export const useSettingStore = defineStore({
|
||||
id: "app.setting",
|
||||
state: (): SettingState => ({
|
||||
themeConfig: null,
|
||||
themeToken: {
|
||||
token: {},
|
||||
algorithm: theme.defaultAlgorithm
|
||||
},
|
||||
plusInfo: {
|
||||
isPlus: false,
|
||||
vipType: "free",
|
||||
@@ -93,9 +74,6 @@ export const useSettingStore = defineStore({
|
||||
inited: false
|
||||
}),
|
||||
getters: {
|
||||
getThemeConfig(): any {
|
||||
return this.themeConfig || _.merge({}, defaultThemeConfig, LocalStorage.get(SETTING_THEME_KEY) || {});
|
||||
},
|
||||
getSysPublic(): SysPublicSetting {
|
||||
return this.sysPublic;
|
||||
},
|
||||
@@ -122,9 +100,23 @@ export const useSettingStore = defineStore({
|
||||
};
|
||||
return vipLabelMap[this.plusInfo?.vipType || "free"];
|
||||
},
|
||||
getHeaderMenus(): { menus: any[] } {
|
||||
getHeaderMenus(): any[] {
|
||||
// @ts-ignore
|
||||
return this.headerMenus?.menus || { menus: [] };
|
||||
let menus = this.headerMenus?.menus || [];
|
||||
menus = cloneDeep(menus);
|
||||
return utils.tree.treeMap(menus, (menu: any) => {
|
||||
return {
|
||||
...menu,
|
||||
name: menu.title,
|
||||
path: menu.path ?? "/" + menu.title,
|
||||
meta: {
|
||||
title: menu.title,
|
||||
icon: menu.icon,
|
||||
link: menu.path,
|
||||
order: 99999
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
isSuiteEnabled(): boolean {
|
||||
// @ts-ignore
|
||||
@@ -162,6 +154,22 @@ export const useSettingStore = defineStore({
|
||||
}
|
||||
}
|
||||
this.siteInfo = _.merge({}, defaultSiteInfo, siteInfo);
|
||||
|
||||
if (this.siteInfo.logo) {
|
||||
updatePreferences({
|
||||
logo: {
|
||||
source: this.siteInfo.logo
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.siteInfo.title) {
|
||||
updatePreferences({
|
||||
app: {
|
||||
name: this.siteInfo.title
|
||||
}
|
||||
});
|
||||
useTitle(this.siteInfo.title);
|
||||
}
|
||||
},
|
||||
async checkUrlBound() {
|
||||
const userStore = useUserStore();
|
||||
@@ -207,44 +215,7 @@ export const useSettingStore = defineStore({
|
||||
}
|
||||
}
|
||||
},
|
||||
persistThemeConfig() {
|
||||
LocalStorage.set(SETTING_THEME_KEY, this.getThemeConfig);
|
||||
},
|
||||
async setThemeConfig(themeConfig?: ThemeConfig) {
|
||||
this.themeConfig = _.merge({}, this.themeConfig, themeConfig);
|
||||
|
||||
this.persistThemeConfig();
|
||||
this.setPrimaryColor(this.themeConfig.colorPrimary);
|
||||
this.setDarkMode(this.themeConfig.mode);
|
||||
},
|
||||
setPrimaryColor(color: any) {
|
||||
this.themeConfig.colorPrimary = color;
|
||||
_.set(this.themeToken, "token.colorPrimary", color);
|
||||
this.persistThemeConfig();
|
||||
},
|
||||
setDarkMode(mode: string) {
|
||||
this.themeConfig.mode = mode;
|
||||
if (mode === "dark") {
|
||||
this.themeToken.algorithm = theme.darkAlgorithm;
|
||||
// const defaultSeed = theme.defaultSeed;
|
||||
// const mapToken = theme.darkAlgorithm(defaultSeed);
|
||||
// less.modifyVars(mapToken);
|
||||
// less.modifyVars({
|
||||
// "@colorPrimaryBg": "#111a2c",
|
||||
// colorPrimaryBg: "#111a2c"
|
||||
// });
|
||||
// less.refreshStyles();
|
||||
} else {
|
||||
this.themeToken.algorithm = theme.defaultAlgorithm;
|
||||
|
||||
// const defaultSeed = theme.defaultSeed;
|
||||
// const mapToken = theme.defaultAlgorithm(defaultSeed);
|
||||
// less.modifyVars(mapToken);
|
||||
}
|
||||
this.persistThemeConfig();
|
||||
},
|
||||
async init() {
|
||||
await this.setThemeConfig(this.getThemeConfig);
|
||||
await this.loadSysSettings();
|
||||
},
|
||||
async initOnce() {
|
||||
|
||||
@@ -11,6 +11,9 @@ import { message, Modal, notification } from "ant-design-vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
import { mitter } from "/src/utils/util.mitt";
|
||||
import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
||||
|
||||
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
||||
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfoRes>;
|
||||
@@ -39,12 +42,16 @@ export const useUserStore = defineStore({
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setToken(info: string, expire: number) {
|
||||
this.token = info;
|
||||
setToken(token: string, expire: number) {
|
||||
this.token = token;
|
||||
const accessStore = useAccessStore();
|
||||
accessStore.setAccessToken(token);
|
||||
LocalStorage.set(TOKEN_KEY, this.token, expire);
|
||||
},
|
||||
setUserInfo(info: UserInfoRes) {
|
||||
this.userInfo = info;
|
||||
const userStore = vbenUserStore();
|
||||
userStore.setUserInfo(info);
|
||||
LocalStorage.set(USER_INFO_KEY, info);
|
||||
},
|
||||
resetState() {
|
||||
@@ -78,6 +85,7 @@ export const useUserStore = defineStore({
|
||||
// get user info
|
||||
return await this.onLoginSuccess(loginRes);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -92,11 +100,10 @@ export const useUserStore = defineStore({
|
||||
},
|
||||
|
||||
async onLoginSuccess(loginData: any) {
|
||||
await this.getUserInfoAction();
|
||||
const userInfo = await this.getUserInfoAction();
|
||||
mitter.emit("app.login", { userInfo, token: loginData });
|
||||
// await this.getUserInfoAction();
|
||||
// const userInfo = await this.getUserInfoAction();
|
||||
mitter.emit("app.login", { token: loginData });
|
||||
await router.replace("/");
|
||||
return userInfo;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -104,6 +111,7 @@ export const useUserStore = defineStore({
|
||||
*/
|
||||
logout(goLogin = true) {
|
||||
this.resetState();
|
||||
resetAllStores();
|
||||
goLogin && router.push("/login");
|
||||
mitter.emit("app.logout");
|
||||
},
|
||||
|
||||
@@ -43,10 +43,20 @@
|
||||
}
|
||||
|
||||
|
||||
.ant-modal {
|
||||
max-width: calc(100% - 32px) !important ;
|
||||
//适配手机端
|
||||
.ant-tour{
|
||||
max-width: 90vw
|
||||
}
|
||||
|
||||
.fs-search .ant-row{
|
||||
flex-flow: row wrap !important;
|
||||
}
|
||||
.fs-page{
|
||||
.fs-page-header{
|
||||
background-color: hsl(var(--card));
|
||||
}
|
||||
.fs-crud-table{
|
||||
background-color: hsl(var(--card));
|
||||
}
|
||||
}
|
||||
|
||||
footer{
|
||||
background-color: hsl(var(--card)) !important;
|
||||
}
|
||||
|
||||
@@ -76,8 +76,16 @@
|
||||
|
||||
.fs-crud-table{
|
||||
.ant-table-body {
|
||||
height: 1000px !important;
|
||||
height: 60vh;
|
||||
table {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
body a{
|
||||
color: #1890ff;
|
||||
&:hover{
|
||||
color: #40a9ff;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.flex-inline {
|
||||
display: inline-flex;
|
||||
@@ -107,86 +106,86 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
}
|
||||
.m-0{
|
||||
margin:0
|
||||
margin:0 !important;
|
||||
}
|
||||
.m-2{
|
||||
margin:2px
|
||||
margin:2px !important;
|
||||
}
|
||||
.m-3{
|
||||
margin:3px
|
||||
margin:3px !important;
|
||||
}
|
||||
.m-5{
|
||||
margin:5px
|
||||
margin:5px !important;
|
||||
}
|
||||
.m-10 {
|
||||
margin: 10px;
|
||||
margin: 10px !important;;
|
||||
}
|
||||
|
||||
.m-20{
|
||||
margin:20px
|
||||
margin:20px !important;
|
||||
}
|
||||
.mb-2 {
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 2px !important;;
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 5px !important;;
|
||||
}
|
||||
.ml-5 {
|
||||
margin-left: 5px;
|
||||
margin-left: 5px !important;;
|
||||
}
|
||||
|
||||
.ml-10 {
|
||||
margin-left: 10px;
|
||||
margin-left: 10px !important;;
|
||||
}
|
||||
|
||||
.ml-20 {
|
||||
margin-left: 20px;
|
||||
margin-left: 20px !important;;
|
||||
}
|
||||
|
||||
.ml-15 {
|
||||
margin-left: 15px;
|
||||
margin-left: 15px !important;;
|
||||
}
|
||||
|
||||
.mr-5 {
|
||||
margin-right: 5px;
|
||||
margin-right: 5px !important;;
|
||||
}
|
||||
|
||||
.mr-10 {
|
||||
margin-right: 10px;
|
||||
margin-right: 10px !important;;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
margin-right: 20px !important;;
|
||||
}
|
||||
|
||||
.mr-15 {
|
||||
margin-right: 15px;
|
||||
margin-right: 15px !important;;
|
||||
}
|
||||
|
||||
.mt-5 {
|
||||
margin-top: 5px;
|
||||
margin-top: 5px !important;;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px;
|
||||
margin-top: 10px !important;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 10px !important;;
|
||||
}
|
||||
|
||||
|
||||
.p-5 {
|
||||
padding: 5px;
|
||||
padding: 5px !important;;
|
||||
}
|
||||
|
||||
.p-10 {
|
||||
padding: 10px;
|
||||
padding: 10px !important;;
|
||||
}
|
||||
|
||||
.p-20 {
|
||||
padding: 20px;
|
||||
padding: 20px !important;;
|
||||
}
|
||||
.ellipsis {
|
||||
white-space: nowrap;
|
||||
@@ -296,4 +295,9 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
.w-50\%{
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
.ant-drawer-content-wrapper {
|
||||
max-width: 90vw;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as _ from "lodash-es";
|
||||
import { isArray } from "lodash-es";
|
||||
export default {
|
||||
arrayToMap(array: any) {
|
||||
if (!array) {
|
||||
return {};
|
||||
}
|
||||
if (!_.isArray(array)) {
|
||||
if (!isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
const map: any = {};
|
||||
@@ -19,7 +19,7 @@ export default {
|
||||
if (!map) {
|
||||
return [];
|
||||
}
|
||||
if (_.isArray(map)) {
|
||||
if (isArray(map)) {
|
||||
return map;
|
||||
}
|
||||
const array: any = [];
|
||||
@@ -40,5 +40,8 @@ export default {
|
||||
return str.length > length ? str.slice(0, length) + "..." : str;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
transformLink(desc: string = "") {
|
||||
return desc.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// @ts-ignore
|
||||
import * as _ from "lodash-es";
|
||||
import {forEach} from "lodash-es";
|
||||
export function getEnvValue(key: string) {
|
||||
// @ts-ignore
|
||||
return import.meta.env["VITE_APP_" + key];
|
||||
}
|
||||
|
||||
export class EnvConfig {
|
||||
MODE: string = import.meta.env.MODE;
|
||||
|
||||
@@ -7,6 +7,17 @@ export function eachTree(tree: any[], callback: (item: any) => void) {
|
||||
});
|
||||
}
|
||||
|
||||
export function treeMap(tree: any[], mapFunc: (item: any) => {}) {
|
||||
return tree.map((item: any) => {
|
||||
const newItem: any = mapFunc(item);
|
||||
if (item.children) {
|
||||
newItem.children = treeMap(item.children, mapFunc);
|
||||
}
|
||||
return newItem;
|
||||
});
|
||||
}
|
||||
|
||||
export const treeUtils = {
|
||||
eachTree
|
||||
eachTree,
|
||||
treeMap
|
||||
};
|
||||
|
||||
47
packages/ui/certd-client/src/vben/access/access-control.vue
Normal file
47
packages/ui/certd-client/src/vben/access/access-control.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<!--
|
||||
Access control component for fine-grained access control.
|
||||
TODO: 可以扩展更完善的功能:
|
||||
1. 支持多个权限码,只要有一个权限码满足即可 或者 多个权限码全部满足
|
||||
2. 支持多个角色,只要有一个角色满足即可 或者 多个角色全部满足
|
||||
3. 支持自定义权限码和角色的判断逻辑
|
||||
-->
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
import { useAccess } from "./use-access";
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Specified codes is visible
|
||||
* @default []
|
||||
*/
|
||||
codes?: string[];
|
||||
|
||||
/**
|
||||
* 通过什么方式来控制组件,如果是 role,则传入角色,如果是 code,则传入权限码
|
||||
* @default 'role'
|
||||
*/
|
||||
type?: "code" | "role";
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: "AccessControl"
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
codes: () => [],
|
||||
type: "role"
|
||||
});
|
||||
|
||||
const { hasAccessByCodes, hasAccessByRoles } = useAccess();
|
||||
|
||||
const hasAuth = computed(() => {
|
||||
const { codes, type } = props;
|
||||
return type === "role" ? hasAccessByRoles(codes) : hasAccessByCodes(codes);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="!codes"></slot>
|
||||
<slot v-else-if="hasAuth"></slot>
|
||||
</template>
|
||||
84
packages/ui/certd-client/src/vben/access/accessible.ts
Normal file
84
packages/ui/certd-client/src/vben/access/accessible.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { AccessModeType, GenerateMenuAndRoutesOptions, RouteRecordRaw } from "/@/vben/types";
|
||||
|
||||
import { cloneDeep, generateMenus, generateRoutesByBackend, generateRoutesByFrontend, mapTree } from "/@/vben/utils";
|
||||
|
||||
async function generateAccessible(mode: AccessModeType, options: GenerateMenuAndRoutesOptions) {
|
||||
const { router } = options;
|
||||
|
||||
options.routes = cloneDeep(options.routes);
|
||||
// 生成路由
|
||||
const accessibleRoutes = await generateRoutes(mode, options);
|
||||
|
||||
const root = router.getRoutes().find((item: any) => item.path === "/");
|
||||
|
||||
// 动态添加到router实例内
|
||||
accessibleRoutes.forEach((route) => {
|
||||
if (root && !route.meta?.noBasicLayout) {
|
||||
// 为了兼容之前的版本用法,如果包含子路由,则将component移除,以免出现多层BasicLayout
|
||||
// 如果你的项目已经跟进了本次修改,移除了所有自定义菜单首级的BasicLayout,可以将这段if代码删除
|
||||
if (route.children && route.children.length > 0) {
|
||||
delete route.component;
|
||||
}
|
||||
root.children?.push(route);
|
||||
} else {
|
||||
router.addRoute(route);
|
||||
}
|
||||
});
|
||||
|
||||
if (root) {
|
||||
if (root.name) {
|
||||
router.removeRoute(root.name);
|
||||
}
|
||||
router.addRoute(root);
|
||||
}
|
||||
|
||||
// 生成菜单
|
||||
const accessibleMenus = await generateMenus(accessibleRoutes, options.router);
|
||||
|
||||
return { accessibleMenus, accessibleRoutes };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate routes
|
||||
* @param mode
|
||||
* @param options
|
||||
*/
|
||||
async function generateRoutes(mode: AccessModeType, options: GenerateMenuAndRoutesOptions) {
|
||||
const { forbiddenComponent, roles, routes } = options;
|
||||
|
||||
let resultRoutes: RouteRecordRaw[] = routes;
|
||||
switch (mode) {
|
||||
case "backend": {
|
||||
resultRoutes = await generateRoutesByBackend(options);
|
||||
break;
|
||||
}
|
||||
case "frontend": {
|
||||
resultRoutes = await generateRoutesByFrontend(routes, roles || [], forbiddenComponent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整路由树,做以下处理:
|
||||
* 1. 对未添加redirect的路由添加redirect
|
||||
*/
|
||||
resultRoutes = mapTree(resultRoutes, (route) => {
|
||||
// 如果有redirect或者没有子路由,则直接返回
|
||||
if (route.redirect || !route.children || route.children.length === 0) {
|
||||
return route;
|
||||
}
|
||||
const firstChild = route.children[0];
|
||||
|
||||
// 如果子路由不是以/开头,则直接返回,这种情况需要计算全部父级的path才能得出正确的path,这里不做处理
|
||||
if (!firstChild?.path || !firstChild.path.startsWith("/")) {
|
||||
return route;
|
||||
}
|
||||
|
||||
route.redirect = firstChild.path;
|
||||
return route;
|
||||
});
|
||||
|
||||
return resultRoutes;
|
||||
}
|
||||
|
||||
export { generateAccessible };
|
||||
42
packages/ui/certd-client/src/vben/access/directive.ts
Normal file
42
packages/ui/certd-client/src/vben/access/directive.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Global authority directive
|
||||
* Used for fine-grained control of component permissions
|
||||
* @Example v-access:role="[ROLE_NAME]" or v-access:role="ROLE_NAME"
|
||||
* @Example v-access:code="[ROLE_CODE]" or v-access:code="ROLE_CODE"
|
||||
*/
|
||||
import type { App, Directive, DirectiveBinding } from 'vue';
|
||||
|
||||
import { useAccess } from './use-access';
|
||||
|
||||
function isAccessible(
|
||||
el: Element,
|
||||
binding: DirectiveBinding<string | string[]>,
|
||||
) {
|
||||
const { accessMode, hasAccessByCodes, hasAccessByRoles } = useAccess();
|
||||
|
||||
const value = binding.value;
|
||||
|
||||
if (!value) return;
|
||||
const authMethod =
|
||||
accessMode.value === 'frontend' && binding.arg === 'role'
|
||||
? hasAccessByRoles
|
||||
: hasAccessByCodes;
|
||||
|
||||
const values = Array.isArray(value) ? value : [value];
|
||||
|
||||
if (!authMethod(values)) {
|
||||
el?.remove();
|
||||
}
|
||||
}
|
||||
|
||||
const mounted = (el: Element, binding: DirectiveBinding<string | string[]>) => {
|
||||
isAccessible(el, binding);
|
||||
};
|
||||
|
||||
const authDirective: Directive = {
|
||||
mounted,
|
||||
};
|
||||
|
||||
export function registerAccessDirective(app: App) {
|
||||
app.directive('access', authDirective);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user