mirror of
https://github.com/certd/certd.git
synced 2026-04-14 12:30:54 +08:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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/*/.idea
|
||||||
/packages/ui/*/node_modules
|
/packages/ui/*/node_modules
|
||||||
/packages/*/node_modules
|
/packages/*/node_modules
|
||||||
/pnpm-lock.yaml
|
#/pnpm-lock.yaml
|
||||||
|
|
||||||
|
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|||||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,6 +3,25 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
|
|||||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||||
|
|
||||||
* 全自动申请证书(支持所有注册商注册的域名)
|
* 全自动申请证书(支持所有注册商注册的域名)
|
||||||
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持40+部署插件)
|
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件)
|
||||||
* 支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
|
* 支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
|
||||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
|
* 支持通配符域名/泛域名,支持多个域名打到一个证书上,支持pem、pfx、der、jks等多种证书格式
|
||||||
* 邮件通知、webhook通知
|
* 邮件通知、webhook通知
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
15:10
|
01:08
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [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)
|
## [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
|
**Note:** Version bump only for package root
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ packages:
|
|||||||
### 安装依赖和初始化:
|
### 安装依赖和初始化:
|
||||||
```shell
|
```shell
|
||||||
# 安装pnpm,如果提示npm命令不存在,就需要先安装nodejs
|
# 安装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
|
pnpm config set registry https://registry.npmmirror.com
|
||||||
@@ -36,19 +36,19 @@ pnpm config set registry https://registry.npmmirror.com
|
|||||||
pnpm install
|
pnpm install
|
||||||
|
|
||||||
# 初始化构建
|
# 初始化构建
|
||||||
npm run init
|
pnpm init
|
||||||
```
|
```
|
||||||
|
|
||||||
### 启动 server:
|
### 启动 server:
|
||||||
```shell
|
```shell
|
||||||
cd packages/ui/certd-server
|
cd packages/ui/certd-server
|
||||||
npm run dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### 启动 client:
|
### 启动 client:
|
||||||
```shell
|
```shell
|
||||||
cd packages/ui/certd-client
|
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
|
```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换成最新版本号
|
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时,可以尝试此命令,1.x.x换成最新版本号
|
||||||
cd certd
|
cd certd
|
||||||
# 启动服务
|
# 启动服务
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ features:
|
|||||||
- title: 全自动申请证书
|
- title: 全自动申请证书
|
||||||
details: 支持所有注册商注册的域名
|
details: 支持所有注册商注册的域名
|
||||||
- title: 全自动部署证书
|
- title: 全自动部署证书
|
||||||
details: 支持部署到主机、阿里云、腾讯云等,目前已支持30+部署插件
|
details: 支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件
|
||||||
- title: 多域名、泛域名打到一个证书上
|
- title: 多域名、泛域名打到一个证书上
|
||||||
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||||
- title: 多证书格式支持
|
- title: 多证书格式支持
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /
|
Allow: /
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.30.6"
|
"version": "1.31.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,5 +41,8 @@
|
|||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/**"
|
"packages/**"
|
||||||
]
|
],
|
||||||
|
"pnpm": {
|
||||||
|
"neverBuiltDependencies": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "scr/index.js",
|
"module": "scr/index.js",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
@@ -65,5 +65,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
01:03
|
01:30
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -44,5 +44,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ import { nanoid } from 'nanoid';
|
|||||||
import * as id from './util.id.js';
|
import * as id from './util.id.js';
|
||||||
import { locker } from './util.lock.js';
|
import { locker } from './util.lock.js';
|
||||||
import { mitter } from './util.mitter.js';
|
import { mitter } from './util.mitter.js';
|
||||||
|
|
||||||
|
import * as request from './util.request.js';
|
||||||
export const utils = {
|
export const utils = {
|
||||||
sleep,
|
sleep,
|
||||||
http,
|
http,
|
||||||
@@ -52,4 +54,5 @@ export const utils = {
|
|||||||
locker,
|
locker,
|
||||||
mitter,
|
mitter,
|
||||||
amount: amountUtils,
|
amount: amountUtils,
|
||||||
|
request,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import crypto from 'crypto';
|
import crypto, { BinaryToTextEncoding } from 'crypto';
|
||||||
|
|
||||||
function md5(data: string) {
|
function md5(data: string, digest: BinaryToTextEncoding = 'hex') {
|
||||||
return crypto.createHash('md5').update(data).digest('hex');
|
return crypto.createHash('md5').update(data).digest(digest);
|
||||||
}
|
}
|
||||||
function sha256(data: string) {
|
function sha256(data: string, digest: BinaryToTextEncoding = 'hex') {
|
||||||
return crypto.createHash('sha256').update(data).digest('hex');
|
return crypto.createHash('sha256').update(data).digest(digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
function base64(data: string) {
|
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,12 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [1.30.6](https://github.com/certd/certd/compare/v1.30.5...v1.30.6) (2025-02-24)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
"test": "mocha --loader=ts-node/esm"
|
"test": "mocha --loader=ts-node/esm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@certd/plus-core": "^1.30.6",
|
"@certd/plus-core": "^1.31.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
@@ -43,5 +43,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
|
import type { ResultError } from "../dt/index.js";
|
||||||
|
|
||||||
export class CancelError extends Error {
|
export class CancelError extends Error {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(message);
|
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 { RunHistory, RunnableCollection } from "./run-history.js";
|
||||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js";
|
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext, UserInfo } from "../plugin/index.js";
|
||||||
import { ContextFactory, IContext } from "./context.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 { cloneDeep, forEach, merge } from "lodash-es";
|
||||||
import { INotificationService } from "../notification/index.js";
|
import { INotificationService } from "../notification/index.js";
|
||||||
import { taskEmitterCreate } from "../service/emit.js";
|
import { taskEmitterCreate } from "../service/emit.js";
|
||||||
|
import { RunnableError } from "./exceptions.js";
|
||||||
|
|
||||||
export type SysInfo = {
|
export type SysInfo = {
|
||||||
//系统标题
|
//系统标题
|
||||||
@@ -192,6 +193,8 @@ export class Executor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resList: ResultType[] = [];
|
let resList: ResultType[] = [];
|
||||||
|
const errorList: ResultError[] = [];
|
||||||
|
let errorMessage = "";
|
||||||
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
||||||
//并行
|
//并行
|
||||||
const pList = [];
|
const pList = [];
|
||||||
@@ -200,10 +203,25 @@ export class Executor {
|
|||||||
}
|
}
|
||||||
resList = await Promise.all(pList);
|
resList = await Promise.all(pList);
|
||||||
} else {
|
} else {
|
||||||
//串行
|
//串行且报错继续
|
||||||
for (let i = 0; i < runnerList.length; i++) {
|
for (let i = 0; i < runnerList.length; i++) {
|
||||||
const runner = runnerList[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);
|
return this.compositionResultType(resList);
|
||||||
@@ -402,7 +420,16 @@ export class Executor {
|
|||||||
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}`;
|
content = `流水线ID:${this.pipeline.id},运行ID:${this.runtime.id}`;
|
||||||
} else if (when === "error") {
|
} else if (when === "error") {
|
||||||
subject = `执行失败,${this.pipeline.title}【${this.pipeline.id}】`;
|
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 {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,12 @@ export type Log = {
|
|||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ResultError = {
|
||||||
|
e: any;
|
||||||
|
returnType: ResultType;
|
||||||
|
runnable: Runnable;
|
||||||
|
};
|
||||||
|
|
||||||
export enum ResultType {
|
export enum ResultType {
|
||||||
start = "start",
|
start = "start",
|
||||||
success = "success",
|
success = "success",
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -21,5 +21,5 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -30,5 +30,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,13 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -27,18 +27,18 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.30.6",
|
"@certd/acme-client": "^1.31.0",
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@certd/pipeline": "^1.30.6",
|
"@certd/pipeline": "^1.31.0",
|
||||||
"@certd/plus-core": "^1.30.6",
|
"@certd/plus-core": "^1.31.0",
|
||||||
"@midwayjs/cache": "~3.14.0",
|
"@midwayjs/cache": "~3.14.0",
|
||||||
"@midwayjs/core": "~3.17.1",
|
"@midwayjs/core": "~3.20.3",
|
||||||
"@midwayjs/i18n": "~3.17.3",
|
"@midwayjs/i18n": "~3.20.3",
|
||||||
"@midwayjs/info": "~3.17.3",
|
"@midwayjs/info": "~3.20.3",
|
||||||
"@midwayjs/koa": "~3.17.1",
|
"@midwayjs/koa": "~3.20.3",
|
||||||
"@midwayjs/logger": "~3.4.2",
|
"@midwayjs/logger": "~3.4.2",
|
||||||
"@midwayjs/typeorm": "~3.17.1",
|
"@midwayjs/typeorm": "~3.20.3",
|
||||||
"@midwayjs/upload": "^3.17.3",
|
"@midwayjs/upload": "^3.20.3",
|
||||||
"better-sqlite3": "^11.1.2",
|
"better-sqlite3": "^11.1.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export class SysPublicSettings extends BaseSettings {
|
|||||||
limitUserPipelineCount = 0;
|
limitUserPipelineCount = 0;
|
||||||
managerOtherUserPipeline = false;
|
managerOtherUserPipeline = false;
|
||||||
icpNo?: string;
|
icpNo?: string;
|
||||||
|
robots?: boolean = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SysPrivateSettings extends BaseSettings {
|
export class SysPrivateSettings extends BaseSettings {
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -25,9 +25,9 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@midwayjs/core": "~3.17.1",
|
"@midwayjs/core": "~3.20.3",
|
||||||
"@midwayjs/logger": "~3.4.2",
|
"@midwayjs/logger": "~3.4.2",
|
||||||
"@midwayjs/typeorm": "~3.17.1",
|
"@midwayjs/typeorm": "~3.20.3",
|
||||||
"better-sqlite3": "^11.1.2"
|
"better-sqlite3": "^11.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -46,5 +46,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.30.6",
|
"@certd/acme-client": "^1.31.0",
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@certd/pipeline": "^1.30.6",
|
"@certd/pipeline": "^1.31.0",
|
||||||
"@certd/plugin-lib": "^1.30.6",
|
"@certd/plugin-lib": "^1.31.0",
|
||||||
"@google-cloud/publicca": "^1.3.0",
|
"@google-cloud/publicca": "^1.3.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
@@ -41,5 +41,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||||
|
//@ts-ignore
|
||||||
import psl from "psl";
|
import psl from "psl";
|
||||||
import { dnsProviderRegistry } from "./registry.js";
|
import { dnsProviderRegistry } from "./registry.js";
|
||||||
import { Decorator } from "@certd/pipeline";
|
import { Decorator } from "@certd/pipeline";
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
|||||||
},
|
},
|
||||||
required: false,
|
required: false,
|
||||||
order: 100,
|
order: 100,
|
||||||
helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456",
|
helper: "PFX、jks格式证书是否加密\njks必须设置密码,不传则默认123456\npfx不传则为空密码",
|
||||||
})
|
})
|
||||||
pfxPassword!: string;
|
pfxPassword!: string;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -16,8 +16,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@certd/basic": "^1.30.6",
|
"@certd/basic": "^1.31.0",
|
||||||
"@certd/pipeline": "^1.30.6",
|
"@certd/pipeline": "^1.31.0",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
"ali-oss": "^6.21.0",
|
"ali-oss": "^6.21.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
@@ -48,5 +48,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "4e750bdbe109e300a5a17a783aa00d2f0eb6daac"
|
"gitHead": "5d6a9457630d620e842b876286a48227b9ae0b4b"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
|||||||
VITE_APP_LOGO=/static/images/logo/logo.svg
|
VITE_APP_LOGO=/static/images/logo/logo.svg
|
||||||
VITE_APP_LOGIN_LOGO=/static/images/logo/rect-black.svg
|
VITE_APP_LOGIN_LOGO=/static/images/logo/rect-black.svg
|
||||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||||
|
VITE_APP_NAMESPACE=fs
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"printWidth": 160
|
"printWidth": 220
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,15 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
# [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)
|
## [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
|
**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-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示例
|
* [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",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.30.6",
|
"version": "1.31.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"debug": "vite --mode debug --open",
|
"debug": "vite --mode debug --open",
|
||||||
"debug:pm": "vite --mode debugpm",
|
"debug:pm": "vite --mode debugpm",
|
||||||
"debug:force": "vite --force --mode debug",
|
"debug:force": "vite --force --mode debug",
|
||||||
"build": " vite build ",
|
"build": "vite build ",
|
||||||
"dev-build": "echo 1",
|
"dev-build": "echo 1",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest",
|
||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
@@ -21,53 +21,82 @@
|
|||||||
"circle:check": "pnpm dependency-cruise --validate --output-type err-html -f dependency-report.html src",
|
"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"
|
"afterPubPush": "git add . && git commit -m \"build: publish success\" && git push"
|
||||||
},
|
},
|
||||||
"author": "Greper",
|
"author": "greper",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^7.0.2",
|
"@ant-design/colors": "^7.0.2",
|
||||||
"@ant-design/icons-vue": "^6.1.0",
|
"@ant-design/icons-vue": "^7.0.1",
|
||||||
"@fast-crud/fast-crud": "^1.25.0",
|
"@aws-sdk/client-s3": "^3.535.0",
|
||||||
"@fast-crud/fast-extends": "^1.25.0",
|
"@aws-sdk/s3-request-presigner": "^3.535.0",
|
||||||
"@fast-crud/ui-antdv4": "^1.25.0",
|
"@ctrl/tinycolor": "^4.1.0",
|
||||||
"@fast-crud/ui-interface": "^1.25.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",
|
"@iconify/vue": "^4.1.1",
|
||||||
|
"@manypkg/get-packages": "^2.2.2",
|
||||||
"@soerenmartius/vue3-clipboard": "^0.1.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",
|
"@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",
|
"async-validator": "^4.2.5",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"axios-mock-adapter": "^1.22.0",
|
"axios-mock-adapter": "^1.22.0",
|
||||||
"base64-js": "^1.5.1",
|
"base64-js": "^1.5.1",
|
||||||
"better-scroll": "^2.5.1",
|
"better-scroll": "^2.5.1",
|
||||||
"china-division": "^2.7.0",
|
"china-division": "^2.7.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.36.0",
|
"core-js": "^3.36.0",
|
||||||
"cos-js-sdk-v5": "^1.7.0",
|
"cos-js-sdk-v5": "^1.7.0",
|
||||||
"cron-parser": "^4.9.0",
|
"cron-parser": "^4.9.0",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
|
"cssnano": "^7.0.6",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
"defu": "^6.1.4",
|
||||||
"echarts": "^5.5.1",
|
"echarts": "^5.5.1",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"humanize-duration": "^3.27.3",
|
"humanize-duration": "^3.27.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"lucide-vue-next": "^0.477.0",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^4.0.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
"pinia": "2.1.7",
|
"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",
|
"psl": "^1.9.0",
|
||||||
"qiniu-js": "^3.4.2",
|
"qiniu-js": "^3.4.2",
|
||||||
"qrcode": "^1.5.4",
|
"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": "^3.4.21",
|
||||||
"vue-cropperjs": "^5.0.0",
|
"vue-cropperjs": "^5.0.0",
|
||||||
"vue-echarts": "^7.0.3",
|
"vue-echarts": "^7.0.3",
|
||||||
"vue-i18n": "^9.10.2",
|
"vue-i18n": "^9.10.2",
|
||||||
"vue-router": "^4.3.0",
|
"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": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.30.6",
|
"@certd/lib-iframe": "^1.31.0",
|
||||||
"@certd/pipeline": "^1.30.6",
|
"@certd/pipeline": "^1.31.0",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
@@ -83,7 +112,7 @@
|
|||||||
"@vue/compiler-sfc": "^3.4.21",
|
"@vue/compiler-sfc": "^3.4.21",
|
||||||
"@vue/eslint-config-typescript": "^13.0.0",
|
"@vue/eslint-config-typescript": "^13.0.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"autoprefixer": "^10.4.18",
|
"autoprefixer": "^10.4.20",
|
||||||
"caller-path": "^4.0.0",
|
"caller-path": "^4.0.0",
|
||||||
"chai": "^5.1.0",
|
"chai": "^5.1.0",
|
||||||
"dependency-cruiser": "^16.2.3",
|
"dependency-cruiser": "^16.2.3",
|
||||||
@@ -105,7 +134,7 @@
|
|||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"stylelint": "^15.11.0",
|
"stylelint": "^15.11.0",
|
||||||
"stylelint-order": "^6.0.4",
|
"stylelint-order": "^6.0.4",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.14",
|
||||||
"terser": "^5.29.2",
|
"terser": "^5.29.2",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslint": "^6.1.3",
|
"tslint": "^6.1.3",
|
||||||
@@ -114,6 +143,7 @@
|
|||||||
"vite": "^5.3.1",
|
"vite": "^5.3.1",
|
||||||
"vite-plugin-compression": "^0.5.1",
|
"vite-plugin-compression": "^0.5.1",
|
||||||
"vite-plugin-html": "^3.2.2",
|
"vite-plugin-html": "^3.2.2",
|
||||||
|
"vite-plugin-theme": "^0.8.6",
|
||||||
"vite-plugin-windicss": "^1.9.3",
|
"vite-plugin-windicss": "^1.9.3",
|
||||||
"vitest": "^2.1.2",
|
"vitest": "^2.1.2",
|
||||||
"vue-eslint-parser": "^9.4.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>
|
<template>
|
||||||
<a-config-provider :locale="locale" :theme="settingStore.themeToken">
|
<AConfigProvider :locale="locale" :theme="tokenTheme">
|
||||||
<contextHolder />
|
<contextHolder />
|
||||||
<fs-form-provider>
|
<fs-form-provider>
|
||||||
<router-view v-if="routerEnabled" />
|
<router-view />
|
||||||
</fs-form-provider>
|
</fs-form-provider>
|
||||||
</a-config-provider>
|
</AConfigProvider>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
||||||
import enUS from "ant-design-vue/es/locale/en_US";
|
import enUS from "ant-design-vue/es/locale/en_US";
|
||||||
import { provide, ref } from "vue";
|
import { computed, provide, ref } from "vue";
|
||||||
import { usePageStore } from "/src/store/modules/page";
|
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
|
||||||
import "dayjs/locale/zh-cn";
|
import "dayjs/locale/zh-cn";
|
||||||
import "dayjs/locale/en";
|
import "dayjs/locale/en";
|
||||||
import dayjs from "dayjs";
|
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";
|
import { Modal } from "ant-design-vue";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -24,13 +26,12 @@ defineOptions({
|
|||||||
const [modal, contextHolder] = Modal.useModal();
|
const [modal, contextHolder] = Modal.useModal();
|
||||||
provide("modal", modal);
|
provide("modal", modal);
|
||||||
//刷新页面方法
|
//刷新页面方法
|
||||||
const routerEnabled = ref(true);
|
|
||||||
const locale = ref(zhCN);
|
const locale = ref(zhCN);
|
||||||
async function reload() {
|
async function reload() {}
|
||||||
// routerEnabled.value = false;
|
localeChanged("zh-cn");
|
||||||
// await nextTick();
|
provide("fn:router.reload", reload);
|
||||||
// routerEnabled.value = true;
|
provide("fn:locale.changed", localeChanged);
|
||||||
}
|
//刷新页面方法
|
||||||
function localeChanged(value: any) {
|
function localeChanged(value: any) {
|
||||||
console.log("locale changed:", value);
|
console.log("locale changed:", value);
|
||||||
if (value === "zh-cn") {
|
if (value === "zh-cn") {
|
||||||
@@ -45,10 +46,27 @@ localeChanged("zh-cn");
|
|||||||
provide("fn:router.reload", reload);
|
provide("fn:router.reload", reload);
|
||||||
provide("fn:locale.changed", localeChanged);
|
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();
|
// const resourceStore = useResourceStore();
|
||||||
// resourceStore.init();
|
// resourceStore.init();
|
||||||
const pageStore = usePageStore();
|
// const pageStore = usePageStore();
|
||||||
pageStore.init();
|
// pageStore.init();
|
||||||
const settingStore = useSettingStore();
|
// const settingStore = useSettingStore();
|
||||||
|
// settingStore.init();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export type SysPublicSetting = {
|
|||||||
limitUserPipelineCount?: number;
|
limitUserPipelineCount?: number;
|
||||||
managerOtherUserPipeline?: boolean;
|
managerOtherUserPipeline?: boolean;
|
||||||
icpNo?: string;
|
icpNo?: string;
|
||||||
|
robots?: boolean;
|
||||||
};
|
};
|
||||||
export type SuiteSetting = {
|
export type SuiteSetting = {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export interface UserInfoRes {
|
|||||||
id: string | number;
|
id: string | number;
|
||||||
username: string;
|
username: string;
|
||||||
nickName: string;
|
nickName: string;
|
||||||
avatar: string;
|
avatar?: string;
|
||||||
roleIds: number[];
|
roleIds: number[];
|
||||||
isWeak?: boolean;
|
isWeak?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ function createService() {
|
|||||||
return dataAxios.data;
|
return dataAxios.data;
|
||||||
default:
|
default:
|
||||||
// 不是正确的 code
|
// 不是正确的 code
|
||||||
const errorMessage = dataAxios.msg;
|
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (response?.config?.onError) {
|
if (response?.config?.onError) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@@ -62,5 +62,6 @@ export default defineComponent({
|
|||||||
.fs-highlight {
|
.fs-highlight {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<td style="width: 100px; text-align: center">记录类型</td>
|
<td style="width: 100px; text-align: center">记录类型</td>
|
||||||
<td style="width: 250px">请设置CNAME记录(验证成功以后不要删除)</td>
|
<td style="width: 250px">请设置CNAME记录(验证成功以后不要删除)</td>
|
||||||
<td style="width: 120px" class="center">状态</td>
|
<td style="width: 120px" class="center">状态</td>
|
||||||
<td style="width: 80px" class="center">操作</td>
|
<td style="width: 90px" class="center">操作</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<template v-for="key in domains" :key="key">
|
<template v-for="key in domains" :key="key">
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import TutorialSteps from "/@/components/tutorial/tutorial-steps.vue";
|
import TutorialSteps from "/@/components/tutorial/tutorial-steps.vue";
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
|
||||||
|
const props = defineProps<{
|
||||||
|
showIcon?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
const openedRef = ref(false);
|
const openedRef = ref(false);
|
||||||
function open() {
|
function open() {
|
||||||
openedRef.value = true;
|
openedRef.value = true;
|
||||||
@@ -12,8 +16,8 @@ const slots = defineSlots();
|
|||||||
<template>
|
<template>
|
||||||
<div class="tutorial-button pointer" @click="open">
|
<div class="tutorial-button pointer" @click="open">
|
||||||
<template v-if="!slots.default">
|
<template v-if="!slots.default">
|
||||||
<fs-icon icon="ant-design:question-circle-outlined"></fs-icon>
|
<fs-icon v-if="showIcon" icon="ant-design:question-circle-outlined"></fs-icon>
|
||||||
<div class="ml-5">使用教程</div>
|
<div class="hidden md:block">使用教程</div>
|
||||||
</template>
|
</template>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
|
<a-modal v-model:open="openedRef" class="tutorial-modal" width="90%">
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
<contextHolder />
|
<contextHolder />
|
||||||
<fs-icon icon="mingcute:vip-1-line" :title="text.title" />
|
<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>
|
<a-tooltip>
|
||||||
<template #title> {{ text.title }}</template>
|
<template #title> {{ text.title }}</template>
|
||||||
<span>{{ text.name }}</span>
|
<span class="">{{ text.name }}</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,7 +238,7 @@ function openUpgrade() {
|
|||||||
title: "专业版",
|
title: "专业版",
|
||||||
desc: "开源需要您的赞助支持",
|
desc: "开源需要您的赞助支持",
|
||||||
type: "plus",
|
type: "plus",
|
||||||
privilege: ["可加VIP群,您的需求将优先实现", "站点证书监控无限制", "更多通知方式", "更多强大的部署插件,宝塔、群晖、1Panel等"],
|
privilege: ["可加VIP群,您的需求将优先实现", "站点证书监控无限制", "更多通知方式", "插件全开放,更多强大的部署插件,宝塔、群晖、1Panel等"],
|
||||||
trial: {
|
trial: {
|
||||||
title: "点击获取7天试用",
|
title: "点击获取7天试用",
|
||||||
click: () => {
|
click: () => {
|
||||||
@@ -390,7 +390,6 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
margin-left: 5px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
import { createI18n } from "vue-i18n";
|
|
||||||
import en from "./locale/en";
|
import en from "./locale/en";
|
||||||
import zh from "./locale/zh_CN";
|
import zh from "./locale/zh_CN";
|
||||||
const messages = {
|
import { SupportedLanguagesType } from "/@/vben/locales";
|
||||||
en: {
|
export const messages = {
|
||||||
|
"en-US": {
|
||||||
label: "English",
|
label: "English",
|
||||||
...en
|
...en
|
||||||
},
|
},
|
||||||
"zh-cn": {
|
"zh-CN": {
|
||||||
label: "简体中文",
|
label: "简体中文",
|
||||||
...zh
|
...zh
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createI18n({
|
// export default createI18n({
|
||||||
legacy: false,
|
// legacy: false,
|
||||||
locale: "zh-cn",
|
// locale: "zh-cn",
|
||||||
fallbackLocale: "zh-cn",
|
// fallbackLocale: "zh-cn",
|
||||||
messages
|
// 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>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<template v-if="siteInfo.licenseTo">
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<a :href="siteInfo.licenseToUrl || ''">{{ siteInfo.licenseTo }}</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-if="sysPublic.icpNo">
|
||||||
|
<a-divider type="vertical" />
|
||||||
|
<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">
|
<script lang="ts">
|
||||||
import i18n from "../../../i18n";
|
import i18n from "../../../i18n";
|
||||||
import { computed, inject } from "vue";
|
import { computed, inject } from "vue";
|
||||||
import * as _ from "lodash-es";
|
import { forEach } from "lodash-es";
|
||||||
export default {
|
export default {
|
||||||
name: "FsLocale",
|
name: "FsLocale",
|
||||||
setup() {
|
setup() {
|
||||||
const languages = computed(() => {
|
const languages = computed(() => {
|
||||||
const map: any = i18n.global.messages?.value || {};
|
const map: any = i18n.global.messages?.value || {};
|
||||||
const list: any = [];
|
const list: any = [];
|
||||||
_.forEach(map, (item, key) => {
|
forEach(map, (item, key) => {
|
||||||
list.push({
|
list.push({
|
||||||
key,
|
key,
|
||||||
label: item.label
|
label: item.label
|
||||||
|
|||||||
81
packages/ui/certd-client/src/layout/layout-basic.vue
Normal file
81
packages/ui/certd-client/src/layout/layout-basic.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<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">
|
||||||
|
<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>-->
|
<!-- </button>-->
|
||||||
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="settingStore.getHeaderMenus" />
|
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="settingStore.getHeaderMenus" />
|
||||||
|
|
||||||
<fs-menu
|
<fs-menu v-if="!settingStore?.isAgent && !settingStore.isComm" class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="headerMenus" />
|
||||||
v-if="!settingStore?.isAgent && !settingStore.isComm"
|
|
||||||
class="header-menu"
|
|
||||||
mode="horizontal"
|
|
||||||
:expand-selected="false"
|
|
||||||
:selectable="false"
|
|
||||||
:menus="headerMenus"
|
|
||||||
/>
|
|
||||||
<div class="header-btn">
|
<div class="header-btn">
|
||||||
<fs-locale />
|
<fs-locale />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ const sysPublic: Ref<SysPublicSetting> = computed(() => {
|
|||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
//padding: 50px 0 84px;
|
//padding: 50px 0 84px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.user-layout-content {
|
.user-layout-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,22 +1,44 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import App from "./App.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 "./style/common.less";
|
||||||
|
import { loadMessages } from "./i18n";
|
||||||
import i18n from "./i18n";
|
import { i18n } from "/@/vben/locales";
|
||||||
import store from "./store";
|
|
||||||
import components from "./components";
|
import components from "./components";
|
||||||
|
import router from "./router";
|
||||||
import plugin from "./plugin/";
|
import plugin from "./plugin/";
|
||||||
// 正式项目请删除mock,避免影响性能
|
// 正式项目请删除mock,避免影响性能
|
||||||
//import "./mock";
|
//import "./mock";
|
||||||
|
import { setupVben } from "./vben";
|
||||||
|
import { util } from "/@/utils";
|
||||||
|
import { initPreferences } from "/@/vben/preferences";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const app = createApp(App);
|
async function bootstrap() {
|
||||||
app.use(Antd);
|
const app = createApp(App);
|
||||||
app.use(router);
|
// app.use(Antd);
|
||||||
app.use(i18n);
|
app.use(Antd);
|
||||||
app.use(store);
|
await setupVben(app, { loadMessages, router });
|
||||||
app.use(components);
|
app.use(router);
|
||||||
app.use(plugin, { i18n });
|
// app.use(i18n);
|
||||||
app.mount("#app");
|
// 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) {
|
function copyList(originList: any, newList: any, options: any, parentId?: any) {
|
||||||
for (const item of originList) {
|
for (const item of originList) {
|
||||||
const newItem: any = _.cloneDeep(item);
|
const newItem: any = cloneDeep(item);
|
||||||
if (parentId != null && newItem.parentId == null) {
|
if (parentId != null && newItem.parentId == null) {
|
||||||
newItem.parentId = parentId;
|
newItem.parentId = parentId;
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ const mockUtil: any = {
|
|||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
msg: "success",
|
msg: "success",
|
||||||
data: _.cloneDeep(req.body)
|
data: cloneDeep(req.body)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -228,12 +228,12 @@ const mockUtil: any = {
|
|||||||
handle(req: any): any {
|
handle(req: any): any {
|
||||||
const item = findById(req.body.id, list);
|
const item = findById(req.body.id, list);
|
||||||
if (item) {
|
if (item) {
|
||||||
_.mergeWith(item, req.body, (objValue: any, srcValue: any) => {
|
mergeWith(item, req.body, (objValue: any, srcValue: any) => {
|
||||||
if (srcValue == null) {
|
if (srcValue == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||||
if (_.isArray(objValue)) {
|
if (isArray(objValue)) {
|
||||||
return srcValue;
|
return srcValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -305,12 +305,12 @@ const mockUtil: any = {
|
|||||||
console.log("req", req);
|
console.log("req", req);
|
||||||
let item = findById(req.body.id, list);
|
let item = findById(req.body.id, list);
|
||||||
if (item) {
|
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) {
|
if (srcValue == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||||
if (_.isArray(objValue)) {
|
if (isArray(objValue)) {
|
||||||
return srcValue;
|
return srcValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -336,12 +336,12 @@ const mockUtil: any = {
|
|||||||
for (const item of req.body) {
|
for (const item of req.body) {
|
||||||
const item2 = findById(item.id, list);
|
const item2 = findById(item.id, list);
|
||||||
if (item2) {
|
if (item2) {
|
||||||
_.mergeWith(item2, item, (objValue: any, srcValue: any) => {
|
mergeWith(item2, item, (objValue: any, srcValue: any) => {
|
||||||
if (srcValue == null) {
|
if (srcValue == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
// 如果被合并对象为数组,则直接被覆盖对象覆盖,只要覆盖对象不为空
|
||||||
if (_.isArray(objValue)) {
|
if (isArray(objValue)) {
|
||||||
return srcValue;
|
return srcValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import cascaderData from "./cascader-data";
|
|||||||
import pcaDataLittle from "./pca-data-little";
|
import pcaDataLittle from "./pca-data-little";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { TreeNodesLazyLoader, getPcaData } from "./pcas-data";
|
import { TreeNodesLazyLoader, getPcaData } from "./pcas-data";
|
||||||
import * as _ from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
const openStatus = [
|
const openStatus = [
|
||||||
{ value: "1", label: "打开", color: "success", icon: "ion:radio-button-on" },
|
{ value: "1", label: "打开", color: "success", icon: "ion:radio-button-on" },
|
||||||
{ value: "2", label: "停止", color: "cyan" },
|
{ value: "2", label: "停止", color: "cyan" },
|
||||||
@@ -29,7 +29,7 @@ let manyStatus = [
|
|||||||
];
|
];
|
||||||
let tempManyStatus: any[] = [];
|
let tempManyStatus: any[] = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
tempManyStatus = tempManyStatus.concat(_.cloneDeep(manyStatus));
|
tempManyStatus = tempManyStatus.concat(cloneDeep(manyStatus));
|
||||||
}
|
}
|
||||||
manyStatus = tempManyStatus;
|
manyStatus = tempManyStatus;
|
||||||
let idIndex = 0;
|
let idIndex = 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mock } from "../api/service";
|
import { mock } from "../api/service";
|
||||||
import * as tools from "../api/tools";
|
import * as tools from "../api/tools";
|
||||||
import * as _ from "lodash-es";
|
import { forEach } from "lodash-es";
|
||||||
import { utils } from "@fast-crud/fast-crud";
|
import { utils } from "@fast-crud/fast-crud";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const commonMocks: any = import.meta.glob("./common/mock.*.[j|t]s", { eager: true });
|
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 viewMocks: any = import.meta.glob("../views/**/mock.[j|t]s", { eager: true });
|
||||||
|
|
||||||
const list: any = [];
|
const list: any = [];
|
||||||
_.forEach(commonMocks, (value: any) => {
|
forEach(commonMocks, (value: any) => {
|
||||||
list.push(value.default);
|
list.push(value.default);
|
||||||
});
|
});
|
||||||
_.forEach(apiMocks, (value: any) => {
|
forEach(apiMocks, (value: any) => {
|
||||||
list.push(value.default);
|
list.push(value.default);
|
||||||
});
|
});
|
||||||
_.forEach(viewMocks, (value: any) => {
|
forEach(viewMocks, (value: any) => {
|
||||||
list.push(value.default);
|
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 { request, requestForMock } from "/src/api/service";
|
||||||
// import "/src/mock";
|
// import "/src/mock";
|
||||||
import {
|
import { ColumnCompositionProps, CrudOptions, FastCrud, PageQuery, PageRes, setLogger, TransformResProps, useColumns, UseCrudProps, UserPageQuery, useTypes, utils } from "@fast-crud/fast-crud";
|
||||||
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 "@fast-crud/fast-crud/dist/style.css";
|
||||||
import {
|
import { FsExtendsCopyable, FsExtendsEditor, FsExtendsJson, FsExtendsTime, FsExtendsUploader, FsExtendsInput, FsUploaderS3SignedUrlType, FsUploaderGetAuthContext, FsUploaderAliossSTS } from "@fast-crud/fast-extends";
|
||||||
FsExtendsCopyable,
|
|
||||||
FsExtendsEditor,
|
|
||||||
FsExtendsJson,
|
|
||||||
FsExtendsTime,
|
|
||||||
FsExtendsUploader,
|
|
||||||
FsExtendsInput,
|
|
||||||
FsUploaderS3SignedUrlType,
|
|
||||||
FsUploaderGetAuthContext,
|
|
||||||
FsUploaderAliossSTS
|
|
||||||
} from "@fast-crud/fast-extends";
|
|
||||||
import "@fast-crud/fast-extends/dist/style.css";
|
import "@fast-crud/fast-extends/dist/style.css";
|
||||||
import UiAntdv from "@fast-crud/ui-antdv4";
|
import UiAntdv from "@fast-crud/ui-antdv4";
|
||||||
import "@fast-crud/ui-antdv4/dist/style.css";
|
import "@fast-crud/ui-antdv4/dist/style.css";
|
||||||
import * as _ from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
import { useCrudPermission } from "../permission";
|
import { useCrudPermission } from "../permission";
|
||||||
import { App } from "vue";
|
import { App } from "vue";
|
||||||
import { notification } from "ant-design-vue";
|
import { notification } from "ant-design-vue";
|
||||||
|
import { usePreferences } from "/@/vben/preferences";
|
||||||
|
|
||||||
function install(app: App, options: any = {}) {
|
function install(app: App, options: any = {}) {
|
||||||
app.use(UiAntdv);
|
app.use(UiAntdv);
|
||||||
@@ -54,7 +32,18 @@ function install(app: App, options: any = {}) {
|
|||||||
commonOptions(props: UseCrudProps): CrudOptions {
|
commonOptions(props: UseCrudProps): CrudOptions {
|
||||||
utils.logger.debug("commonOptions:", props);
|
utils.logger.debug("commonOptions:", props);
|
||||||
const crudBinding = props.crudExpose?.crudBinding;
|
const crudBinding = props.crudExpose?.crudBinding;
|
||||||
|
const { isMobile } = usePreferences();
|
||||||
const opts: CrudOptions = {
|
const opts: CrudOptions = {
|
||||||
|
settings: {
|
||||||
|
plugins: {
|
||||||
|
mobile: {
|
||||||
|
enabled: true,
|
||||||
|
props: {
|
||||||
|
isMobile: isMobile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
table: {
|
table: {
|
||||||
scroll: {
|
scroll: {
|
||||||
x: 960
|
x: 960
|
||||||
@@ -90,6 +79,7 @@ function install(app: App, options: any = {}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
|
fixed: "right",
|
||||||
buttons: {
|
buttons: {
|
||||||
view: { type: "link", text: null, icon: "ion:eye-outline" },
|
view: { type: "link", text: null, icon: "ion:eye-outline" },
|
||||||
copy: { show: true, type: "link", text: null, icon: "ion:copy-outline" },
|
copy: { show: true, type: "link", text: null, icon: "ion:copy-outline" },
|
||||||
@@ -163,6 +153,7 @@ function install(app: App, options: any = {}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
|
//最后一列空白,用于自动伸缩列宽
|
||||||
__blank__: {
|
__blank__: {
|
||||||
title: "",
|
title: "",
|
||||||
type: "text",
|
type: "text",
|
||||||
@@ -170,9 +161,10 @@ function install(app: App, options: any = {}) {
|
|||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
show: true,
|
order: 99999,
|
||||||
order: 999999,
|
width: -1,
|
||||||
width: -1
|
columnSetShow: false,
|
||||||
|
resizable: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +190,11 @@ function install(app: App, options: any = {}) {
|
|||||||
action: "/basic/file/upload",
|
action: "/basic/file/upload",
|
||||||
name: "file",
|
name: "file",
|
||||||
withCredentials: false,
|
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
|
// @ts-ignore
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
data.append("file", file);
|
data.append("file", file);
|
||||||
@@ -268,7 +264,7 @@ function install(app: App, options: any = {}) {
|
|||||||
// 比如你可以定义一个readonly的公共属性,处理该字段只读,不能编辑
|
// 比如你可以定义一个readonly的公共属性,处理该字段只读,不能编辑
|
||||||
if (columnProps.readonly) {
|
if (columnProps.readonly) {
|
||||||
// 合并column配置
|
// 合并column配置
|
||||||
_.merge(columnProps, {
|
merge(columnProps, {
|
||||||
form: { show: false },
|
form: { show: false },
|
||||||
viewForm: { show: true }
|
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({
|
registerMergeColumnPlugin({
|
||||||
name: "resize-column-plugin",
|
name: "resize-column-plugin",
|
||||||
order: 2,
|
order: 2,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { message } from "ant-design-vue";
|
|||||||
import NProgress from "nprogress";
|
import NProgress from "nprogress";
|
||||||
export function registerRouterHook() {
|
export function registerRouterHook() {
|
||||||
// 注册路由beforeEach钩子,在第一次加载路由页面时,加载权限
|
// 注册路由beforeEach钩子,在第一次加载路由页面时,加载权限
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from) => {
|
||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
if (permissionStore.isInited) {
|
if (permissionStore.isInited) {
|
||||||
if (to.meta.permission) {
|
if (to.meta.permission) {
|
||||||
@@ -20,26 +20,23 @@ export function registerRouterHook() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const token = userStore.getToken;
|
const token = userStore.getToken;
|
||||||
if (!token || token === "undefined") {
|
if (!token || token === "undefined") {
|
||||||
next();
|
return true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化权限列表
|
// 初始化权限列表
|
||||||
try {
|
try {
|
||||||
console.log("permission is enabled");
|
console.log("permission is enabled");
|
||||||
await permissionStore.loadFromRemote();
|
await permissionStore.loadFromRemote();
|
||||||
console.log("PM load success");
|
console.log("PM load success");
|
||||||
next({ ...to, replace: true });
|
return { ...to, replace: true };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("加载动态路由失败", e);
|
console.error("加载动态路由失败", e);
|
||||||
next();
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,28 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { useResourceStore } from "/src/store/modules/resource";
|
// import { useResourceStore } from "/src/store/modules/resource";
|
||||||
import { getPermissions } from "./api";
|
import { getPermissions } from "./api";
|
||||||
import { mitter } from "/@/utils/util.mitt";
|
import { mitter } from "/@/utils/util.mitt";
|
||||||
import { env } from "/@/utils/util.env";
|
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", () => {
|
mitter.on("app.logout", () => {
|
||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
permissionStore.clear();
|
permissionStore.clear();
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
accessStore.setIsAccessChecked(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
mitter.on("app.login", () => {
|
mitter.on("app.login", () => {
|
||||||
const permissionStore = useResourceStore();
|
const accessStore = useAccessStore();
|
||||||
|
accessStore.setIsAccessChecked(false);
|
||||||
|
const permissionStore = usePermissionStore();
|
||||||
permissionStore.clear();
|
permissionStore.clear();
|
||||||
permissionStore.init();
|
// const accessStore = useAccessStore();
|
||||||
|
// accessStore.setAccessCode([]);
|
||||||
|
// permissionStore.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
interface PermissionState {
|
interface PermissionState {
|
||||||
@@ -27,7 +36,7 @@ interface PermissionState {
|
|||||||
* @param permissionList
|
* @param permissionList
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
function formatPermissions(menuTree: Array<any>, permissionList: any[] = []) {
|
export function formatPermissions(menuTree: Array<any>, permissionList: any[] = []) {
|
||||||
if (menuTree == null) {
|
if (menuTree == null) {
|
||||||
menuTree = [];
|
menuTree = [];
|
||||||
}
|
}
|
||||||
@@ -75,8 +84,8 @@ export const usePermissionStore = defineStore({
|
|||||||
this.init({ permissions });
|
this.init({ permissions });
|
||||||
|
|
||||||
//过滤没有权限的菜单
|
//过滤没有权限的菜单
|
||||||
const resourceStore = useResourceStore();
|
const accessStore = useAccessStore();
|
||||||
resourceStore.filterByPermission(permissions);
|
accessStore.setAccessCodes(permissions);
|
||||||
},
|
},
|
||||||
async loadFromRemote() {
|
async loadFromRemote() {
|
||||||
let permissionTree = [];
|
let permissionTree = [];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { usePermission } from "/@/plugin/permission";
|
import { usePermission } from "/@/plugin/permission";
|
||||||
import * as _ from "lodash-es";
|
import { merge as LodashMerge } from "lodash-es";
|
||||||
|
|
||||||
export type UseCrudPermissionExtraProps = {
|
export type UseCrudPermissionExtraProps = {
|
||||||
hasActionPermission: (action: string) => boolean;
|
hasActionPermission: (action: string) => boolean;
|
||||||
@@ -30,7 +30,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
|||||||
return hasPermissions(prefix + ":" + action);
|
return hasPermissions(prefix + ":" + action);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildCrudPermission() {
|
function buildCrudPermission(): any {
|
||||||
if (permission == null) {
|
if (permission == null) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _.merge(
|
return LodashMerge(
|
||||||
{
|
{
|
||||||
actionbar: {
|
actionbar: {
|
||||||
buttons: {
|
buttons: {
|
||||||
@@ -64,7 +64,7 @@ export function useCrudPermission({ permission }: UseCrudPermissionProps) {
|
|||||||
|
|
||||||
function merge(userOptions: any) {
|
function merge(userOptions: any) {
|
||||||
const permissionOptions = buildCrudPermission();
|
const permissionOptions = buildCrudPermission();
|
||||||
_.merge(permissionOptions, userOptions);
|
LodashMerge(permissionOptions, userOptions);
|
||||||
return permissionOptions;
|
return permissionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const util = {
|
|||||||
const permissionStore = usePermissionStore();
|
const permissionStore = usePermissionStore();
|
||||||
const userPermissionList = permissionStore.getPermissions;
|
const userPermissionList = permissionStore.getPermissions;
|
||||||
return userPermissionList.some((permission: any) => {
|
return userPermissionList.some((permission: any) => {
|
||||||
|
if (permission === "*") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return need.includes(permission);
|
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 { createRouter, createWebHashHistory } from "vue-router";
|
||||||
// 进度条
|
// 进度条
|
||||||
import NProgress from "nprogress";
|
|
||||||
import "nprogress/nprogress.css";
|
import "nprogress/nprogress.css";
|
||||||
import { usePageStore } from "../store/modules/page";
|
|
||||||
import { site } from "../utils/util.site";
|
|
||||||
import { routes } from "./resolve";
|
import { routes } from "./resolve";
|
||||||
import { useResourceStore } from "../store/modules/resource";
|
import { createRouterGuard } from "/@/router/guard";
|
||||||
import { useUserStore } from "../store/modules/user";
|
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes
|
routes
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 路由拦截
|
|
||||||
*/
|
*/
|
||||||
router.beforeEach(async (to, from, next) => {
|
createRouterGuard(router);
|
||||||
// 进度条
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
export default 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 LayoutPass from "/src/layout/layout-pass.vue";
|
||||||
import * as _ from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
import { outsideResource } from "./source/outside";
|
import { outsideResource } from "./source/outside";
|
||||||
import { headerResource } from "./source/header";
|
import { headerResource } from "./source/header";
|
||||||
import { frameworkResource } from "./source/framework";
|
import { frameworkResource } from "./source/framework";
|
||||||
@@ -19,7 +19,7 @@ function transformOneResource(resource: any, parent: any) {
|
|||||||
if (meta.isMenu === false) {
|
if (meta.isMenu === false) {
|
||||||
menu = null;
|
menu = null;
|
||||||
} else {
|
} else {
|
||||||
menu = _.cloneDeep(resource);
|
menu = cloneDeep(resource);
|
||||||
delete menu.component;
|
delete menu.component;
|
||||||
if (menu.path?.startsWith("/")) {
|
if (menu.path?.startsWith("/")) {
|
||||||
menu.fullPath = menu.path;
|
menu.fullPath = menu.path;
|
||||||
@@ -28,11 +28,11 @@ function transformOneResource(resource: any, parent: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let route;
|
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
|
||||||
route = null;
|
route = null;
|
||||||
} else {
|
} else {
|
||||||
route = _.cloneDeep(resource);
|
route = cloneDeep(resource);
|
||||||
if (route.component && typeof route.component === "string") {
|
if (route.component && typeof route.component === "string") {
|
||||||
const path = "/src/views" + route.component;
|
const path = "/src/views" + route.component;
|
||||||
route.component = modules[path];
|
route.component = modules[path];
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
import LayoutFramework from "/src/layout/layout-framework.vue";
|
import LayoutBasic from "/@/layout/layout-basic.vue";
|
||||||
//import { crudResources } from "/@/router/source/modules/crud";
|
|
||||||
import { sysResources } from "/@/router/source/modules/sys";
|
|
||||||
import { certdResources } from "/@/router/source/modules/certd";
|
|
||||||
|
|
||||||
|
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 = [
|
export const frameworkResource = [
|
||||||
{
|
{
|
||||||
title: "框架",
|
title: "框架",
|
||||||
name: "framework",
|
name: "root",
|
||||||
path: "/",
|
path: "/",
|
||||||
redirect: "/index",
|
redirect: "/index",
|
||||||
component: LayoutFramework,
|
component: LayoutBasic,
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:accessibility",
|
icon: "ion:accessibility",
|
||||||
auth: true
|
hideInBreadcrumb: true
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -23,12 +29,13 @@ export const frameworkResource = [
|
|||||||
meta: {
|
meta: {
|
||||||
fixedAside: true,
|
fixedAside: true,
|
||||||
showOnHeader: false,
|
showOnHeader: false,
|
||||||
icon: "ion:home-outline"
|
icon: "ion:home-outline",
|
||||||
|
auth: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//...crudResources,
|
// @ts-ignore
|
||||||
...certdResources,
|
|
||||||
...sysResources
|
...dynamicRoutes
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
41
packages/ui/certd-client/src/router/source/modules/about.tsx
Normal file
41
packages/ui/certd-client/src/router/source/modules/about.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "教程",
|
||||||
|
name: "help",
|
||||||
|
path: "/help",
|
||||||
|
meta: {
|
||||||
|
icon: "lucide:copyright",
|
||||||
|
order: 9999,
|
||||||
|
show: () => {
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
return !settingStore.isComm;
|
||||||
|
},
|
||||||
|
slot() {
|
||||||
|
return <TutorialButton className="flex-center" show-icon={false} />;
|
||||||
|
},
|
||||||
|
click() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default aboutResource;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useSettingStore } from "/@/store/modules/settings";
|
import { useSettingStore } from "/@/store/modules/settings";
|
||||||
|
import aboutResource from "/@/router/source/modules/about";
|
||||||
|
|
||||||
export const certdResources = [
|
export const certdResources = [
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,8 @@ export const certdResources = [
|
|||||||
redirect: "/certd/pipeline",
|
redirect: "/certd/pipeline",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:key-outline",
|
icon: "ion:key-outline",
|
||||||
auth: true
|
auth: true,
|
||||||
|
order: 0
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -64,7 +66,8 @@ export const certdResources = [
|
|||||||
{
|
{
|
||||||
title: "设置",
|
title: "设置",
|
||||||
name: "MineSetting",
|
name: "MineSetting",
|
||||||
path: "/certd/mine",
|
path: "/certd/setting",
|
||||||
|
redirect: "/certd/cname/record",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:settings-outline",
|
icon: "ion:settings-outline",
|
||||||
auth: true,
|
auth: true,
|
||||||
@@ -142,6 +145,7 @@ export const certdResources = [
|
|||||||
title: "套餐",
|
title: "套餐",
|
||||||
name: "SuiteProduct",
|
name: "SuiteProduct",
|
||||||
path: "/certd/suite",
|
path: "/certd/suite",
|
||||||
|
redirect: "/certd/suite/mine",
|
||||||
meta: {
|
meta: {
|
||||||
show: () => {
|
show: () => {
|
||||||
const settingStore = useSettingStore();
|
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 LayoutPass from "/@/layout/layout-pass.vue";
|
||||||
import { useSettingStore } from "/@/store/modules/settings";
|
import { useSettingStore } from "/@/store/modules/settings";
|
||||||
|
import aboutResource from "/@/router/source/modules/about";
|
||||||
|
|
||||||
export const sysResources = [
|
export const sysResources = [
|
||||||
{
|
{
|
||||||
@@ -7,10 +8,10 @@ export const sysResources = [
|
|||||||
name: "SysRoot",
|
name: "SysRoot",
|
||||||
path: "/sys",
|
path: "/sys",
|
||||||
redirect: "/sys/settings",
|
redirect: "/sys/settings",
|
||||||
component: LayoutPass,
|
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:settings-outline",
|
icon: "ion:settings-outline",
|
||||||
permission: "sys:settings:view"
|
permission: "sys:settings:view",
|
||||||
|
order: 10
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -175,6 +176,7 @@ export const sysResources = [
|
|||||||
title: "套餐管理",
|
title: "套餐管理",
|
||||||
name: "SuiteManager",
|
name: "SuiteManager",
|
||||||
path: "/sys/suite",
|
path: "/sys/suite",
|
||||||
|
redirect: "/sys/suite/setting",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:cart-outline",
|
icon: "ion:cart-outline",
|
||||||
permission: "sys:settings:edit",
|
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 { defineStore } from "pinia";
|
||||||
import { Modal, notification, theme } from "ant-design-vue";
|
import { Modal, notification } from "ant-design-vue";
|
||||||
import * as _ from "lodash-es";
|
import * as _ from "lodash-es";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { LocalStorage } from "/src/utils/util.storage";
|
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 { useUserStore } from "/@/store/modules/user";
|
||||||
import { mitter } from "/@/utils/util.mitt";
|
import { mitter } from "/@/utils/util.mitt";
|
||||||
import { env } from "/@/utils/util.env";
|
import { env } from "/@/utils/util.env";
|
||||||
|
import { updatePreferences } from "/@/vben/preferences";
|
||||||
export type ThemeToken = {
|
import { useTitle } from "@vueuse/core";
|
||||||
token: {
|
import { utils } from "/@/utils";
|
||||||
colorPrimary?: string;
|
import { cloneDeep } from "lodash-es";
|
||||||
};
|
|
||||||
algorithm: any;
|
|
||||||
};
|
|
||||||
export type ThemeConfig = {
|
|
||||||
colorPrimary: string;
|
|
||||||
mode: string;
|
|
||||||
};
|
|
||||||
export interface SettingState {
|
export interface SettingState {
|
||||||
themeConfig?: ThemeConfig;
|
|
||||||
themeToken: ThemeToken;
|
|
||||||
sysPublic?: SysPublicSetting;
|
sysPublic?: SysPublicSetting;
|
||||||
installInfo?: {
|
installInfo?: {
|
||||||
siteId: string;
|
siteId: string;
|
||||||
@@ -40,11 +31,6 @@ export interface SettingState {
|
|||||||
suiteSetting?: SuiteSetting;
|
suiteSetting?: SuiteSetting;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultThemeConfig = {
|
|
||||||
colorPrimary: "#1890ff",
|
|
||||||
mode: "light"
|
|
||||||
};
|
|
||||||
const SETTING_THEME_KEY = "SETTING_THEME";
|
|
||||||
const defaultSiteInfo: SiteInfo = {
|
const defaultSiteInfo: SiteInfo = {
|
||||||
title: env.TITLE || "Certd",
|
title: env.TITLE || "Certd",
|
||||||
slogan: env.SLOGAN || "让你的证书永不过期",
|
slogan: env.SLOGAN || "让你的证书永不过期",
|
||||||
@@ -56,11 +42,6 @@ const defaultSiteInfo: SiteInfo = {
|
|||||||
export const useSettingStore = defineStore({
|
export const useSettingStore = defineStore({
|
||||||
id: "app.setting",
|
id: "app.setting",
|
||||||
state: (): SettingState => ({
|
state: (): SettingState => ({
|
||||||
themeConfig: null,
|
|
||||||
themeToken: {
|
|
||||||
token: {},
|
|
||||||
algorithm: theme.defaultAlgorithm
|
|
||||||
},
|
|
||||||
plusInfo: {
|
plusInfo: {
|
||||||
isPlus: false,
|
isPlus: false,
|
||||||
vipType: "free",
|
vipType: "free",
|
||||||
@@ -93,9 +74,6 @@ export const useSettingStore = defineStore({
|
|||||||
inited: false
|
inited: false
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getThemeConfig(): any {
|
|
||||||
return this.themeConfig || _.merge({}, defaultThemeConfig, LocalStorage.get(SETTING_THEME_KEY) || {});
|
|
||||||
},
|
|
||||||
getSysPublic(): SysPublicSetting {
|
getSysPublic(): SysPublicSetting {
|
||||||
return this.sysPublic;
|
return this.sysPublic;
|
||||||
},
|
},
|
||||||
@@ -122,9 +100,23 @@ export const useSettingStore = defineStore({
|
|||||||
};
|
};
|
||||||
return vipLabelMap[this.plusInfo?.vipType || "free"];
|
return vipLabelMap[this.plusInfo?.vipType || "free"];
|
||||||
},
|
},
|
||||||
getHeaderMenus(): { menus: any[] } {
|
getHeaderMenus(): any[] {
|
||||||
// @ts-ignore
|
// @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 {
|
isSuiteEnabled(): boolean {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -162,6 +154,22 @@ export const useSettingStore = defineStore({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.siteInfo = _.merge({}, defaultSiteInfo, siteInfo);
|
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() {
|
async checkUrlBound() {
|
||||||
const userStore = useUserStore();
|
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() {
|
async init() {
|
||||||
await this.setThemeConfig(this.getThemeConfig);
|
|
||||||
await this.loadSysSettings();
|
await this.loadSysSettings();
|
||||||
},
|
},
|
||||||
async initOnce() {
|
async initOnce() {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import { message, Modal, notification } from "ant-design-vue";
|
|||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
import { mitter } from "/src/utils/util.mitt";
|
import { mitter } from "/src/utils/util.mitt";
|
||||||
|
import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
||||||
|
|
||||||
|
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
userInfo: Nullable<UserInfoRes>;
|
userInfo: Nullable<UserInfoRes>;
|
||||||
@@ -39,12 +42,16 @@ export const useUserStore = defineStore({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setToken(info: string, expire: number) {
|
setToken(token: string, expire: number) {
|
||||||
this.token = info;
|
this.token = token;
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
accessStore.setAccessToken(token);
|
||||||
LocalStorage.set(TOKEN_KEY, this.token, expire);
|
LocalStorage.set(TOKEN_KEY, this.token, expire);
|
||||||
},
|
},
|
||||||
setUserInfo(info: UserInfoRes) {
|
setUserInfo(info: UserInfoRes) {
|
||||||
this.userInfo = info;
|
this.userInfo = info;
|
||||||
|
const userStore = vbenUserStore();
|
||||||
|
userStore.setUserInfo(info);
|
||||||
LocalStorage.set(USER_INFO_KEY, info);
|
LocalStorage.set(USER_INFO_KEY, info);
|
||||||
},
|
},
|
||||||
resetState() {
|
resetState() {
|
||||||
@@ -78,6 +85,7 @@ export const useUserStore = defineStore({
|
|||||||
// get user info
|
// get user info
|
||||||
return await this.onLoginSuccess(loginRes);
|
return await this.onLoginSuccess(loginRes);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -92,11 +100,10 @@ export const useUserStore = defineStore({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async onLoginSuccess(loginData: any) {
|
async onLoginSuccess(loginData: any) {
|
||||||
await this.getUserInfoAction();
|
// await this.getUserInfoAction();
|
||||||
const userInfo = await this.getUserInfoAction();
|
// const userInfo = await this.getUserInfoAction();
|
||||||
mitter.emit("app.login", { userInfo, token: loginData });
|
mitter.emit("app.login", { token: loginData });
|
||||||
await router.replace("/");
|
await router.replace("/");
|
||||||
return userInfo;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,6 +111,7 @@ export const useUserStore = defineStore({
|
|||||||
*/
|
*/
|
||||||
logout(goLogin = true) {
|
logout(goLogin = true) {
|
||||||
this.resetState();
|
this.resetState();
|
||||||
|
resetAllStores();
|
||||||
goLogin && router.push("/login");
|
goLogin && router.push("/login");
|
||||||
mitter.emit("app.logout");
|
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{
|
.fs-page{
|
||||||
flex-flow: row wrap !important;
|
.fs-page-header{
|
||||||
}
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
.fs-crud-table{
|
||||||
|
background-color: hsl(var(--card));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer{
|
||||||
|
background-color: hsl(var(--card)) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,3 +81,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body a{
|
||||||
|
color: #1890ff;
|
||||||
|
&:hover{
|
||||||
|
color: #40a9ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,7 +77,6 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
.flex-inline {
|
.flex-inline {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -107,86 +106,86 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
.m-0{
|
.m-0{
|
||||||
margin:0
|
margin:0 !important;
|
||||||
}
|
}
|
||||||
.m-2{
|
.m-2{
|
||||||
margin:2px
|
margin:2px !important;
|
||||||
}
|
}
|
||||||
.m-3{
|
.m-3{
|
||||||
margin:3px
|
margin:3px !important;
|
||||||
}
|
}
|
||||||
.m-5{
|
.m-5{
|
||||||
margin:5px
|
margin:5px !important;
|
||||||
}
|
}
|
||||||
.m-10 {
|
.m-10 {
|
||||||
margin: 10px;
|
margin: 10px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m-20{
|
.m-20{
|
||||||
margin:20px
|
margin:20px !important;
|
||||||
}
|
}
|
||||||
.mb-2 {
|
.mb-2 {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-5 {
|
.mb-5 {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px !important;;
|
||||||
}
|
}
|
||||||
.ml-5 {
|
.ml-5 {
|
||||||
margin-left: 5px;
|
margin-left: 5px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml-10 {
|
.ml-10 {
|
||||||
margin-left: 10px;
|
margin-left: 10px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml-20 {
|
.ml-20 {
|
||||||
margin-left: 20px;
|
margin-left: 20px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ml-15 {
|
.ml-15 {
|
||||||
margin-left: 15px;
|
margin-left: 15px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-5 {
|
.mr-5 {
|
||||||
margin-right: 5px;
|
margin-right: 5px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-10 {
|
.mr-10 {
|
||||||
margin-right: 10px;
|
margin-right: 10px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-20 {
|
.mr-20 {
|
||||||
margin-right: 20px;
|
margin-right: 20px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-15 {
|
.mr-15 {
|
||||||
margin-right: 15px;
|
margin-right: 15px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-5 {
|
.mt-5 {
|
||||||
margin-top: 5px;
|
margin-top: 5px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mt-10 {
|
.mt-10 {
|
||||||
margin-top: 10px;
|
margin-top: 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mb-10 {
|
.mb-10 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.p-5 {
|
.p-5 {
|
||||||
padding: 5px;
|
padding: 5px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-10 {
|
.p-10 {
|
||||||
padding: 10px;
|
padding: 10px !important;;
|
||||||
}
|
}
|
||||||
|
|
||||||
.p-20 {
|
.p-20 {
|
||||||
padding: 20px;
|
padding: 20px !important;;
|
||||||
}
|
}
|
||||||
.ellipsis {
|
.ellipsis {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as _ from "lodash-es";
|
import { isArray } from "lodash-es";
|
||||||
export default {
|
export default {
|
||||||
arrayToMap(array: any) {
|
arrayToMap(array: any) {
|
||||||
if (!array) {
|
if (!array) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!_.isArray(array)) {
|
if (!isArray(array)) {
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
const map: any = {};
|
const map: any = {};
|
||||||
@@ -19,7 +19,7 @@ export default {
|
|||||||
if (!map) {
|
if (!map) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (_.isArray(map)) {
|
if (isArray(map)) {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
const array: any = [];
|
const array: any = [];
|
||||||
@@ -40,5 +40,8 @@ export default {
|
|||||||
return str.length > length ? str.slice(0, length) + "..." : str;
|
return str.length > length ? str.slice(0, length) + "..." : str;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
},
|
||||||
|
transformLink(desc: string = "") {
|
||||||
|
return desc.replace(/\[(.*)\]\((.*)\)/g, '<a href="$2" target="_blank">$1</a>');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
// @ts-ignore
|
import {forEach} from "lodash-es";
|
||||||
import * as _ from "lodash-es";
|
export function getEnvValue(key: string) {
|
||||||
|
// @ts-ignore
|
||||||
|
return import.meta.env["VITE_APP_" + key];
|
||||||
|
}
|
||||||
|
|
||||||
export class EnvConfig {
|
export class EnvConfig {
|
||||||
MODE: string = import.meta.env.MODE;
|
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 = {
|
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);
|
||||||
|
}
|
||||||
4
packages/ui/certd-client/src/vben/access/index.ts
Normal file
4
packages/ui/certd-client/src/vben/access/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export { default as AccessControl } from './access-control.vue';
|
||||||
|
export * from './accessible';
|
||||||
|
export * from './directive';
|
||||||
|
export * from './use-access';
|
||||||
52
packages/ui/certd-client/src/vben/access/use-access.ts
Normal file
52
packages/ui/certd-client/src/vben/access/use-access.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
import { preferences, updatePreferences } from "@vben/preferences";
|
||||||
|
import { useAccessStore, useUserStore } from "@vben/stores";
|
||||||
|
|
||||||
|
function useAccess() {
|
||||||
|
const accessStore = useAccessStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const accessMode = computed(() => {
|
||||||
|
return preferences.app.accessMode;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于角色判断是否有权限
|
||||||
|
* @description: Determine whether there is permission,The role is judged by the user's role
|
||||||
|
* @param roles
|
||||||
|
*/
|
||||||
|
function hasAccessByRoles(roles: string[]) {
|
||||||
|
const userRoleSet = new Set(userStore.userRoles);
|
||||||
|
const intersection = roles.filter((item) => userRoleSet.has(item));
|
||||||
|
return intersection.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于权限码判断是否有权限
|
||||||
|
* @description: Determine whether there is permission,The permission code is judged by the user's permission code
|
||||||
|
* @param codes
|
||||||
|
*/
|
||||||
|
function hasAccessByCodes(codes: string[]) {
|
||||||
|
const userCodesSet = new Set(accessStore.accessCodes);
|
||||||
|
|
||||||
|
const intersection = codes.filter((item) => userCodesSet.has(item));
|
||||||
|
return intersection.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleAccessMode() {
|
||||||
|
updatePreferences({
|
||||||
|
app: {
|
||||||
|
accessMode: preferences.app.accessMode === "frontend" ? "backend" : "frontend"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessMode,
|
||||||
|
hasAccessByCodes,
|
||||||
|
hasAccessByRoles,
|
||||||
|
toggleAccessMode
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useAccess };
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user