mirror of
https://github.com/certd/certd.git
synced 2026-04-04 23:10:56 +08:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f47f86b669 | ||
|
|
95eeb93822 | ||
|
|
367f807313 | ||
|
|
a954629ff9 | ||
|
|
3bbbc41062 | ||
|
|
bf63b0d73f | ||
|
|
5362df55f4 | ||
|
|
59897c4cea | ||
|
|
a9717b9a0d | ||
|
|
680941af11 | ||
|
|
1cf8d4e5e7 | ||
|
|
70ce6be0bf | ||
|
|
9187e87419 | ||
|
|
6ed1e18c7d | ||
|
|
8d27f07213 | ||
|
|
e4f4570b29 | ||
|
|
d86fc9569a | ||
|
|
fa7a983bcb | ||
|
|
9ac908ebee | ||
|
|
6e594ee66e | ||
|
|
c26d3e9c38 | ||
|
|
5db5607faa | ||
|
|
728f27e0a0 | ||
|
|
3d8f329e2d | ||
|
|
351fb70d5d | ||
|
|
b5cbeb9bde | ||
|
|
e7e89b8de7 | ||
|
|
225894d15c | ||
|
|
64ba485b0f | ||
|
|
3a666db36c | ||
|
|
ce7e5a2461 | ||
|
|
b22f94b079 | ||
|
|
3408465df6 | ||
|
|
e97dfb456b | ||
|
|
439c6c8b6c | ||
|
|
afa2b0307a | ||
|
|
56867fa777 | ||
|
|
9c2e33fa39 | ||
|
|
2ca72f838b | ||
|
|
37a9e6aae0 | ||
|
|
6a8a02dae5 | ||
|
|
eaee5db69e | ||
|
|
25d06904c6 | ||
|
|
fa14f87a80 | ||
|
|
4404f99642 | ||
|
|
bafab905b4 | ||
|
|
44d5e54550 | ||
|
|
a23c13d7d9 | ||
|
|
17a7a1432f | ||
|
|
26e8932b85 | ||
|
|
32beb02d40 | ||
|
|
af7177e6bb | ||
|
|
69ac0fd0a8 | ||
|
|
e0998f35e8 | ||
|
|
6d371b38c3 | ||
|
|
587f11138a | ||
|
|
a7b8bac4c8 | ||
|
|
b7b5df0587 |
16
.github/workflows/build-image.yml
vendored
16
.github/workflows/build-image.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
@@ -33,6 +33,17 @@ jobs:
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
# - name: Use Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 18
|
||||
# cache: 'npm'
|
||||
# working-directory: ./packages/ui/certd-client
|
||||
- run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm run build
|
||||
working-directory: ./packages/ui/certd-client
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -50,8 +61,9 @@ jobs:
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6.5.0
|
||||
with:
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
context: ./packages/ui/
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
|
||||
|
||||
55
.github/workflows/deploy-demo.yml
vendored
Normal file
55
.github/workflows/deploy-demo.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: deploy-demo
|
||||
on:
|
||||
push:
|
||||
branches: ['v2']
|
||||
paths:
|
||||
- "deploy.trigger"
|
||||
workflow_run:
|
||||
workflows: [ "build-image" ]
|
||||
types:
|
||||
- completed
|
||||
|
||||
# schedule:
|
||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||
# - cron: '17 19 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy-certd-demo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v4
|
||||
- name: get_certd_version
|
||||
id: get_certd_version
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||
const pkg = JSON.parse(jsonContent)
|
||||
console.log("certd_version:",pkg.version);
|
||||
return pkg.version
|
||||
- uses: GuillaumeFalourd/wait-sleep-action@v1
|
||||
with:
|
||||
time: '10' # for 60 seconds
|
||||
- name: Send HTTP request
|
||||
id: request
|
||||
uses: tyrrrz/action-http-request@master
|
||||
with:
|
||||
url: http://flow-openapi.aliyun.com/pipeline/webhook/lzCzlGrLCOHQaTMMt0mG
|
||||
method: POST
|
||||
headers: |
|
||||
Content-Type: application/json
|
||||
body: |
|
||||
{
|
||||
"CERTD_VERSION": "${{steps.get_certd_version.outputs.result}}"
|
||||
}
|
||||
retry-count: 3
|
||||
retry-delay: 5000
|
||||
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -3,6 +3,38 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复保存配置报id不能为空的bug ([367f807](https://github.com/certd/certd/commit/367f80731396003416665c22853dfbc09c2c03a0))
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复在相同的cron时偶尔无法触发定时任务的bug ([680941a](https://github.com/certd/certd/commit/680941af119619006b592e3ab6fb112cb5556a8b))
|
||||
* 修复pg下pipeline title 类型问题 ([a9717b9](https://github.com/certd/certd/commit/a9717b9a0df7b5a64d4fe03314fecad4f59774cc))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||
* 腾讯云clb支持更多大区选择 ([e4f4570](https://github.com/certd/certd/commit/e4f4570b29f26c60f1ee9660a4c507cbeaba3d7e))
|
||||
* 优化前置任务输出为空的提示 ([6ed1e18](https://github.com/certd/certd/commit/6ed1e18c7d9c46d964ecc6abc90f3908297b7632))
|
||||
|
||||
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复用户管理无法添加用户的bug ([e7e89b8](https://github.com/certd/certd/commit/e7e89b8de7386e84c0d6b8e217e2034909657d68))
|
||||
|
||||
## [1.22.4](https://github.com/certd/certd/compare/v1.22.3...v1.22.4) (2024-07-26)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 证书申请支持反向代理,letsencrypt无法访问时的备用方案 ([b7b5df0](https://github.com/certd/certd/commit/b7b5df0587e0f7ea288c1b2af6f87211f207395f))
|
||||
* 支持arm64 ([fa14f87](https://github.com/certd/certd/commit/fa14f87a8093ef3addc5e5f3315ce1bfc9982782))
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
78
README.md
78
README.md
@@ -8,17 +8,14 @@ CertD 是一个免费全自动申请和自动部署更新SSL证书的工具。
|
||||
## 一、特性
|
||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||
|
||||
* 全自动申请证书(支持阿里云、腾讯云、华为云、Cloudflare注册的域名)
|
||||
* 全自动部署更新证书(目前支持服务器上传部署、部署到阿里云、腾讯云等)
|
||||
* 支持通配符域名
|
||||
* 支持多个域名打到一个证书上
|
||||
* 全自动申请证书(支持阿里云、腾讯云、华为云、Cloudflare等各种途径注册的域名)
|
||||
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等)
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||
* 邮件通知
|
||||
* 证书自动更新
|
||||
* 私有化部署,安全
|
||||
* 私有化部署,保障安全
|
||||
* 免费、免费、免费([阿里云单个通配符域名证书最便宜也要1800/年](https://yundun.console.aliyun.com/?p=cas#/certExtend/buy/cn-hangzhou))
|
||||
|
||||
|
||||
|
||||
## 二、在线体验
|
||||
|
||||
官方Demo地址,自助注册后体验
|
||||
@@ -41,7 +38,9 @@ https://certd.handsfree.work/
|
||||
-------> [点我查看详细使用步骤演示](./step.md) <--------
|
||||
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
||||
|
||||
## 四、本地docker部署
|
||||
## 四、私有化部署
|
||||
|
||||
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全
|
||||
|
||||
### 1. 安装docker、docker-compose
|
||||
|
||||
@@ -50,14 +49,16 @@ https://certd.handsfree.work/
|
||||
* 【腾讯云】云服务器2核2G,新老用户同享,99元/年,续费同价!【 [立即购买](https://cloud.tencent.com/act/cps/redirect?redirect=6094&cps_key=b3ef73330335d7a6efa4a4bbeeb6b2c9&from=console)】
|
||||
|
||||
|
||||
1.2 安装docker
|
||||
https://docs.docker.com/engine/install/
|
||||
选择对应的操作系统,按照官方文档执行命令即可
|
||||
1.2 安装docker
|
||||
|
||||
https://docs.docker.com/engine/install/
|
||||
选择对应的操作系统,按照官方文档执行命令即可
|
||||
|
||||
### 2. 运行certd
|
||||
|
||||
[docker-compose.yaml下载](https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml)
|
||||
[docker-compose.yaml 下载](https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml)
|
||||
|
||||
当前版本号: 
|
||||
|
||||
```bash
|
||||
# 随便创建一个目录
|
||||
@@ -77,10 +78,18 @@ vi docker-compose.yaml # 【可选】
|
||||
docker compose up -d
|
||||
|
||||
```
|
||||
当前版本号: 
|
||||
> 如果提示 没有compose命令,请安装docker-compose
|
||||
> https://docs.docker.com/compose/install/linux/
|
||||
|
||||
#### 镜像说明:
|
||||
* certd镜像地址:
|
||||
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
|
||||
* 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||
* [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml)
|
||||
|
||||

|
||||
|
||||
如果提示 没有compose命令,请安装docker-compose
|
||||
https://docs.docker.com/compose/install/linux/
|
||||
|
||||
### 3. 访问
|
||||
|
||||
@@ -89,14 +98,22 @@ http://your_server_ip:7001
|
||||
记得修改密码
|
||||
|
||||
|
||||
### 4. 升级
|
||||
## 五、 升级
|
||||
如果使用固定版本号
|
||||
1. 修改`docker-compose.yaml`中的镜像版本号
|
||||
2. 运行 `docker compose up -d` 即可
|
||||
|
||||
* 修改`docker-compose.yaml`中的镜像版本号
|
||||
* 重新运行 `docker compose up -d` 即可
|
||||
* 数据存在`/data/certd`目录下,不用担心数据丢失
|
||||
如果使用`latest`版本
|
||||
1. 重新拉取镜像 `docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||
2. 重新启动容器 `docker compose restart`
|
||||
|
||||
> 数据默认存在`/data/certd`目录下,不用担心数据丢失
|
||||
|
||||
|
||||
## 五、一些说明
|
||||
更新日志: [CHANGELOG](./CHANGELOG.md)
|
||||
|
||||
|
||||
## 六、一些说明
|
||||
* 本项目ssl证书提供商为letencrypt
|
||||
* 申请过程遵循acme协议
|
||||
* 需要验证域名所有权,一般有两种方式(目前本项目仅支持dns-01)
|
||||
@@ -108,14 +125,15 @@ http://your_server_ip:7001
|
||||
* 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
||||
* 设置每天自动运行,当证书过期前20天,会自动重新申请证书并部署
|
||||
|
||||
## 六、不同平台的设置说明
|
||||
|
||||
## 七、不同平台的设置说明
|
||||
|
||||
* [Cloudflare](./doc/cf/cf.md)
|
||||
* [腾讯云](./doc/tencent/tencent.md)
|
||||
* [windows主机](./doc/host/host.md)
|
||||
|
||||
|
||||
## 七、问题处理
|
||||
## 八、问题处理
|
||||
### 7.1 忘记管理员密码
|
||||
解决方法如下:
|
||||
1. 修改docker-compose.yaml文件,将环境变量`certd_system_resetAdminPassword`改为`true`
|
||||
@@ -138,7 +156,7 @@ docker compose up -d
|
||||
```
|
||||
5. 使用`admin/123456`登录系统,请及时修改管理员密码
|
||||
|
||||
## 八、联系作者
|
||||
## 九、联系作者
|
||||
如有疑问,欢迎加入群聊(请备注certd)
|
||||
* QQ群:141236433
|
||||
* 微信群:
|
||||
@@ -150,7 +168,7 @@ docker compose up -d
|
||||
<img height="230" src="./doc/images/me.png">
|
||||
</p>
|
||||
|
||||
## 九、捐赠
|
||||
## 十、捐赠
|
||||
媳妇儿说:“一天到晚搞开源,也不管管老婆孩子!😡😡😡”
|
||||
拜托各位捐赠支持一下,让媳妇儿开心开心,我也能有更多时间进行开源项目,感谢🙏🙏🙏
|
||||
<p align="center">
|
||||
@@ -158,16 +176,20 @@ docker compose up -d
|
||||
</p>
|
||||
|
||||
|
||||
## 十、贡献代码
|
||||
## 十一、贡献代码
|
||||
|
||||
[贡献插件教程](./plugin.md)
|
||||
|
||||
|
||||
## 十一、我的其他项目(求Star)
|
||||
## 十二、我的其他项目(求Star)
|
||||
* [袖手GPT](https://ai.handsfree.work/) ChatGPT,国内可用,无需FQ,每日免费额度
|
||||
* [fast-crud](https://gitee.com/fast-crud/fast-crud/) 基于vue3的crud快速开发框架
|
||||
* [dev-sidecar](https://github.com/docmirror/dev-sidecar/) 直连访问github工具,无需FQ,解决github无法访问的问题
|
||||
|
||||
|
||||
## 十二、版本更新日志
|
||||
https://github.com/certd/certd/blob/v2/CHANGELOG.md
|
||||
|
||||
## 十三、更新日志
|
||||
|
||||
更新日志:[CHANGELOG](./CHANGELOG.md)
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
4
|
||||
02:55
|
||||
|
||||
1
deploy.trigger
Normal file
1
deploy.trigger
Normal file
@@ -0,0 +1 @@
|
||||
5
|
||||
BIN
doc/images/action-build.jpg
Normal file
BIN
doc/images/action-build.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -11,6 +11,12 @@ services:
|
||||
ports: # 端口映射
|
||||
# ↓↓↓↓ ----------------------------------------------------------3、如果端口有冲突,可以修改第一个7001为其他不冲突的端口号【可选】
|
||||
- "7001:7001"
|
||||
dns:
|
||||
# 如果出现getaddrinfo ENOTFOUND等错误,可以尝试修改或注释dns配置
|
||||
- 223.5.5.5
|
||||
- 223.6.6.6
|
||||
- 8.8.8.8
|
||||
- 8.8.4.4
|
||||
environment: # 环境变量
|
||||
- TZ=Asia/Shanghai
|
||||
- certd_system_resetAdminPassword=false
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.22.3"
|
||||
"version": "1.22.7"
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"scripts": {
|
||||
"start": "lerna bootstrap --hoist",
|
||||
"i-all": "lerna link && lerna exec npm install ",
|
||||
"publish": "npm run prepublishOnly1 && lerna publish --conventional-commits --create-release github && npm run afterpublishOnly && npm run deploy1",
|
||||
"afterpublishOnly": "",
|
||||
"publish": "npm run prepublishOnly1 && lerna publish --conventional-commits --create-release github && npm run afterpublishOnly",
|
||||
"afterpublishOnly": "time /t >build.trigger && git add ./build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push",
|
||||
"prepublishOnly1": "npm run check && npm run before-build && lerna run build ",
|
||||
"before-build": "cd ./packages/core/acme-client && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
||||
"before-build": "cd ./packages/core/pipeline && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
||||
"deploy1": "node --experimental-json-modules deploy.js ",
|
||||
"check": "node --experimental-json-modules publish-check.js",
|
||||
"init": "lerna run build"
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.6](https://github.com/publishlab/node-acme-client/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.22.4](https://github.com/publishlab/node-acme-client/compare/v1.22.3...v1.22.4) (2024-07-26)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 证书申请支持反向代理,letsencrypt无法访问时的备用方案 ([b7b5df0](https://github.com/publishlab/node-acme-client/commit/b7b5df0587e0f7ea288c1b2af6f87211f207395f))
|
||||
|
||||
## [1.22.3](https://github.com/publishlab/node-acme-client/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -1 +1 @@
|
||||
22:32
|
||||
20:55
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.6",
|
||||
"main": "src/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -100,7 +100,7 @@ class AcmeClient {
|
||||
max: this.opts.backoffMax,
|
||||
};
|
||||
|
||||
this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding);
|
||||
this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding, this.opts.urlMapping);
|
||||
this.api = new AcmeApi(this.http, this.opts.accountUrl);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,11 @@ const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
||||
let httpsAgent = null;
|
||||
if (httpsProxy) {
|
||||
httpsAgent = new HttpsProxyAgent(httpsProxy);
|
||||
log(`use https_proxy:${httpsProxy}`);
|
||||
}
|
||||
const axios = axios1.create({
|
||||
proxy: false,
|
||||
httpsAgent
|
||||
httpsAgent,
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,7 @@ const axios = axios1.create({
|
||||
*/
|
||||
|
||||
class HttpClient {
|
||||
constructor(directoryUrl, accountKey, externalAccountBinding = {}) {
|
||||
constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {}) {
|
||||
this.directoryUrl = directoryUrl;
|
||||
this.accountKey = accountKey;
|
||||
this.externalAccountBinding = externalAccountBinding;
|
||||
@@ -41,6 +42,7 @@ class HttpClient {
|
||||
this.directoryCache = null;
|
||||
this.directoryMaxAge = 86400;
|
||||
this.directoryTimestamp = 0;
|
||||
this.urlMapping = urlMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +55,16 @@ class HttpClient {
|
||||
*/
|
||||
|
||||
async request(url, method, opts = {}) {
|
||||
if (this.urlMapping && this.urlMapping.enabled === true && this.urlMapping.mappings) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const key in this.urlMapping.mappings) {
|
||||
if (url.includes(key)) {
|
||||
const newUrl = url.replace(key, this.urlMapping.mappings[key]);
|
||||
log(`use reverse proxy: ${newUrl}`);
|
||||
url = newUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
opts.url = url;
|
||||
opts.method = method;
|
||||
opts.validateStatus = null;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": ["es6"],
|
||||
|
||||
6
packages/core/acme-client/types/index.d.ts
vendored
6
packages/core/acme-client/types/index.d.ts
vendored
@@ -27,6 +27,11 @@ export interface Authorization extends rfc8555.Authorization {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type UrlMapping={
|
||||
enabled: boolean
|
||||
mappings: Record<string, string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
*/
|
||||
@@ -39,6 +44,7 @@ export interface ClientOptions {
|
||||
backoffAttempts?: number;
|
||||
backoffMin?: number;
|
||||
backoffMax?: number;
|
||||
urlMapping?: UrlMapping;
|
||||
}
|
||||
|
||||
export interface ClientExternalAccountBindingOptions {
|
||||
|
||||
2
packages/core/pipeline/.gitignore
vendored
2
packages/core/pipeline/.gitignore
vendored
@@ -24,3 +24,5 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
test/user.secret.*
|
||||
test/**/*.js
|
||||
src/**/*.spec.ts
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"extension": ["ts"],
|
||||
"spec": "test/**/*.test.ts",
|
||||
"require": "ts-node/register"
|
||||
}
|
||||
"spec": "src/**/*.spec.ts"
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
src
|
||||
src
|
||||
dist/**/*.spec.*
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复在相同的cron时偶尔无法触发定时任务的bug ([680941a](https://github.com/certd/certd/commit/680941af119619006b592e3ab6fb112cb5556a8b))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||
* 优化前置任务输出为空的提示 ([6ed1e18](https://github.com/certd/certd/commit/6ed1e18c7d9c46d964ecc6abc90f3908297b7632))
|
||||
|
||||
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
1
packages/core/pipeline/build.md
Normal file
1
packages/core/pipeline/build.md
Normal file
@@ -0,0 +1 @@
|
||||
22:30
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.7",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -10,10 +10,10 @@
|
||||
"build": "tsc --skipLibCheck",
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"test": "mocha --loader=ts-node/esm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"axios": "^1.7.2",
|
||||
"fix-path": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -28,6 +28,7 @@
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"@rollup/plugin-typescript": "^11.0.0",
|
||||
"@types/chai": "^4.3.10",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/node-forge": "^1.3.2",
|
||||
"@types/uuid": "^9.0.2",
|
||||
|
||||
@@ -213,7 +213,12 @@ export class Executor {
|
||||
if (contextKey != null) {
|
||||
const value = this.runtime.context[contextKey];
|
||||
if (value == null) {
|
||||
currentLogger.warn(`[step init] input ${define.title} is null`);
|
||||
currentLogger.warn(`[step init] input ${define.title} is null,前置任务步骤输出值为空,请按如下步骤排查:`);
|
||||
currentLogger.log(`1、检查前置任务(证书申请任务)是否是配置了成功后跳过,如果是,请改为正常执行`);
|
||||
currentLogger.log(
|
||||
`2、是否曾经删除过前置任务(证书申请任务),然后又重新添加了,如果是,请重新编辑当前任务,重新选择一下前置任务输出的参数(域名证书那一栏)`
|
||||
);
|
||||
currentLogger.log(`3、以上都不是,联系作者吧`);
|
||||
}
|
||||
step.input[key] = value;
|
||||
}
|
||||
@@ -241,18 +246,26 @@ export class Executor {
|
||||
|
||||
await instance.onInstance();
|
||||
await instance.execute();
|
||||
|
||||
//执行结果处理
|
||||
if (instance._result.clearLastStatus) {
|
||||
//是否需要清除所有状态
|
||||
this.lastStatusMap.clear();
|
||||
}
|
||||
//输出到output context
|
||||
//输出上下文变量到output context
|
||||
_.forEach(define.output, (item: any, key: any) => {
|
||||
step.status!.output[key] = instance[key];
|
||||
const stepOutputKey = `step.${step.id}.${key}`;
|
||||
this.runtime.context[stepOutputKey] = instance[key];
|
||||
});
|
||||
|
||||
step.status!.files = instance.getFiles();
|
||||
|
||||
//更新pipeline vars
|
||||
if (Object.keys(instance._result.pipelineVars).length > 0) {
|
||||
// 判断 pipelineVars 有值时更新
|
||||
const vars = this.pipelineContext.getObj("vars");
|
||||
_.merge(vars, instance._result.pipelineVars);
|
||||
await this.pipelineContext.setObj("vars", vars);
|
||||
}
|
||||
}
|
||||
|
||||
async notification(when: NotificationWhen, error?: any) {
|
||||
|
||||
@@ -3,3 +3,4 @@ export * from "./run-history.js";
|
||||
export * from "./context.js";
|
||||
export * from "./storage.js";
|
||||
export * from "./file-store.js";
|
||||
export * from "./license.js";
|
||||
|
||||
15
packages/core/pipeline/src/core/license.spec.ts
Normal file
15
packages/core/pipeline/src/core/license.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { isPlus, verify } from "./license.js";
|
||||
import { equal } from "assert";
|
||||
describe("license", function () {
|
||||
it("#license", async function () {
|
||||
const req = {
|
||||
appKey: "z4nXOeTeSnnpUpnmsV",
|
||||
subjectId: "999",
|
||||
license: "",
|
||||
};
|
||||
const plus = isPlus();
|
||||
equal(plus, false);
|
||||
const res = await verify(req);
|
||||
equal(res, true);
|
||||
});
|
||||
});
|
||||
85
packages/core/pipeline/src/core/license.ts
Normal file
85
packages/core/pipeline/src/core/license.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { createVerify } from "node:crypto";
|
||||
import { logger } from "../utils/index.js";
|
||||
|
||||
const SecreteKey =
|
||||
"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQXY3TGtMaUp1dGM0NzhTU3RaTExjajVGZXh1YjJwR2NLMGxwa0hwVnlZWjhMY29rRFhuUlAKUGQ5UlJSTVRTaGJsbFl2Mzd4QUhOV1ZIQ0ZsWHkrQklVU001bUlBU1NDQTV0azlJNmpZZ2F4bEFDQm1BY0lGMwozKzBjeGZIYVkrVW9YdVluMkZ6YUt2Ym5GdFZIZ0lkMDg4a3d4clZTZzlCT3BDRVZIR1pxR2I5TWN5MXVHVXhUClFTVENCbmpoTWZlZ0p6cXVPYWVOY0ZPSE5tbmtWRWpLTythbTBPeEhNS1lyS3ZnQnVEbzdoVnFENlBFMUd6V3AKZHdwZUV4QXZDSVJxL2pWTkdRK3FtMkRWOVNJZ3U5bmF4MktmSUtFeU50dUFFS1VpekdqL0VmRFhDM1cxMExhegpKaGNYNGw1SUFZU1o3L3JWVmpGbExWSVl0WDU1T054L1Z3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
|
||||
const appKey = "z4nXOeTeSnnpUpnmsV";
|
||||
export type LicenseVerifyReq = {
|
||||
subjectId: string;
|
||||
license: string;
|
||||
};
|
||||
|
||||
type License = {
|
||||
appKey: string;
|
||||
code: string;
|
||||
subjectId: string;
|
||||
expireTime: number;
|
||||
activeTime: number;
|
||||
duration: number;
|
||||
version: number;
|
||||
secret: string;
|
||||
signature: string;
|
||||
};
|
||||
|
||||
class LicenseHolder {
|
||||
isPlus = false;
|
||||
}
|
||||
const holder = new LicenseHolder();
|
||||
holder.isPlus = false;
|
||||
|
||||
class LicenseVerifier {
|
||||
checked = false;
|
||||
licenseReq?: LicenseVerifyReq = undefined;
|
||||
async reVerify(req: LicenseVerifyReq) {
|
||||
this.checked = false;
|
||||
return await this.verify(req);
|
||||
}
|
||||
|
||||
setPlus(value: boolean) {
|
||||
holder.isPlus = value;
|
||||
return value;
|
||||
}
|
||||
async verify(req: LicenseVerifyReq) {
|
||||
this.licenseReq = req;
|
||||
if (this.checked) {
|
||||
return this.setPlus(false);
|
||||
}
|
||||
const license = req?.license;
|
||||
if (!license) {
|
||||
this.checked = true;
|
||||
return this.setPlus(false);
|
||||
}
|
||||
|
||||
const licenseJson = Buffer.from(Buffer.from(license, "hex").toString(), "base64").toString();
|
||||
const json: License = JSON.parse(licenseJson);
|
||||
if (json.expireTime < Date.now()) {
|
||||
logger.warn("授权已过期");
|
||||
return this.setPlus(false);
|
||||
}
|
||||
const content = `${appKey},${this.licenseReq.subjectId},${json.code},${json.secret},${json.activeTime},${json.duration},${json.expireTime},${json.version}`;
|
||||
const publicKey = Buffer.from(SecreteKey, "base64").toString();
|
||||
const res = this.verifySignature(content, json.signature, publicKey);
|
||||
this.checked = true;
|
||||
if (!res) {
|
||||
logger.warn("授权校验失败");
|
||||
return this.setPlus(false);
|
||||
}
|
||||
return this.setPlus(true);
|
||||
}
|
||||
|
||||
verifySignature(content: string, signature: any, publicKey: string) {
|
||||
const verify = createVerify("RSA-SHA256");
|
||||
verify.update(content);
|
||||
return verify.verify(publicKey, signature, "base64");
|
||||
}
|
||||
}
|
||||
|
||||
const verifier = new LicenseVerifier();
|
||||
|
||||
export function isPlus() {
|
||||
return holder.isPlus;
|
||||
}
|
||||
|
||||
export async function verify(req: LicenseVerifyReq) {
|
||||
return await verifier.reVerify(req);
|
||||
}
|
||||
@@ -51,6 +51,7 @@ export type ITaskPlugin = {
|
||||
export type TaskResult = {
|
||||
clearLastStatus?: boolean;
|
||||
files?: FileItem[];
|
||||
pipelineVars: Record<string, any>;
|
||||
};
|
||||
export type TaskInstanceContext = {
|
||||
pipeline: Pipeline;
|
||||
@@ -66,7 +67,7 @@ export type TaskInstanceContext = {
|
||||
};
|
||||
|
||||
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
||||
_result: TaskResult = { clearLastStatus: false, files: [] };
|
||||
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {} };
|
||||
ctx!: TaskInstanceContext;
|
||||
clearLastStatus() {
|
||||
this._result.clearLastStatus = true;
|
||||
@@ -83,12 +84,6 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
||||
randomFileId() {
|
||||
return Math.random().toString(36).substring(2, 9);
|
||||
}
|
||||
linkFile(file: FileItem) {
|
||||
this._result.files?.push({
|
||||
...file,
|
||||
id: this.randomFileId(),
|
||||
});
|
||||
}
|
||||
saveFile(filename: string, file: Buffer) {
|
||||
const filePath = this.ctx.fileStore.writeFile(filename, file);
|
||||
logger.info(`saveFile:${filePath}`);
|
||||
|
||||
@@ -5,4 +5,3 @@ export default function (timeout: number) {
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from "chai";
|
||||
import "mocha";
|
||||
import { EchoPlugin } from "./echo-plugin";
|
||||
import { EchoPlugin } from "./echo-plugin.js";
|
||||
describe("task_plugin", function () {
|
||||
it("#taskplugin", function () {
|
||||
console.log("before new plugin");
|
||||
|
||||
1
packages/core/pipeline/test/pipeline/.gitignore
vendored
Normal file
1
packages/core/pipeline/test/pipeline/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
license.*
|
||||
@@ -34,6 +34,7 @@
|
||||
"exclude": [
|
||||
"*.js",
|
||||
"*.ts",
|
||||
"*.spec.ts",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"test"
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.7",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -17,7 +17,7 @@
|
||||
"shelljs": "^0.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/pipeline": "^1.22.3",
|
||||
"@certd/pipeline": "^1.22.7",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.6",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||
|
||||
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.22.4](https://github.com/certd/certd/compare/v1.22.3...v1.22.4) (2024-07-26)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 证书申请支持反向代理,letsencrypt无法访问时的备用方案 ([b7b5df0](https://github.com/certd/certd/commit/b7b5df0587e0f7ea288c1b2af6f87211f207395f))
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.7",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -13,8 +13,8 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.22.3",
|
||||
"@certd/pipeline": "^1.22.3",
|
||||
"@certd/acme-client": "^1.22.6",
|
||||
"@certd/pipeline": "^1.22.7",
|
||||
"jszip": "^3.10.1",
|
||||
"node-forge": "^0.10.0",
|
||||
"psl": "^1.9.0"
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Logger } from "log4js";
|
||||
import { IContext } from "@certd/pipeline";
|
||||
import { IDnsProvider } from "../../dns-provider/index.js";
|
||||
import psl from "psl";
|
||||
import { ClientExternalAccountBindingOptions } from "@certd/acme-client";
|
||||
import { ClientExternalAccountBindingOptions, UrlMapping } from "@certd/acme-client";
|
||||
|
||||
export type CertInfo = {
|
||||
crt: string;
|
||||
@@ -14,19 +14,24 @@ export type CertInfo = {
|
||||
csr: string;
|
||||
};
|
||||
export type SSLProvider = "letsencrypt" | "buypass" | "zerossl";
|
||||
type AcmeServiceOptions = {
|
||||
userContext: IContext;
|
||||
logger: Logger;
|
||||
sslProvider: SSLProvider;
|
||||
eab?: ClientExternalAccountBindingOptions;
|
||||
skipLocalVerify?: boolean;
|
||||
useMappingProxy?: boolean;
|
||||
};
|
||||
|
||||
export class AcmeService {
|
||||
options: AcmeServiceOptions;
|
||||
userContext: IContext;
|
||||
logger: Logger;
|
||||
sslProvider: SSLProvider;
|
||||
skipLocalVerify = true;
|
||||
eab?: ClientExternalAccountBindingOptions;
|
||||
constructor(options: {
|
||||
userContext: IContext;
|
||||
logger: Logger;
|
||||
sslProvider: SSLProvider;
|
||||
eab?: ClientExternalAccountBindingOptions;
|
||||
skipLocalVerify?: boolean;
|
||||
}) {
|
||||
constructor(options: AcmeServiceOptions) {
|
||||
this.options = options;
|
||||
this.userContext = options.userContext;
|
||||
this.logger = options.logger;
|
||||
this.sslProvider = options.sslProvider || "letsencrypt";
|
||||
@@ -61,6 +66,13 @@ export class AcmeService {
|
||||
} else {
|
||||
directoryUrl = acme.directory[this.sslProvider].production;
|
||||
}
|
||||
const urlMapping: UrlMapping = { enabled: false, mappings: {} };
|
||||
if (this.options.useMappingProxy) {
|
||||
urlMapping.enabled = true;
|
||||
urlMapping.mappings = {
|
||||
"acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
|
||||
};
|
||||
}
|
||||
const client = new acme.Client({
|
||||
directoryUrl: directoryUrl,
|
||||
accountKey: conf.key,
|
||||
@@ -69,6 +81,7 @@ export class AcmeService {
|
||||
backoffAttempts: 30,
|
||||
backoffMin: 5000,
|
||||
backoffMax: 10000,
|
||||
urlMapping,
|
||||
});
|
||||
|
||||
if (conf.accountUrl == null) {
|
||||
|
||||
@@ -138,6 +138,8 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
||||
const cert: CertInfo = certReader.toCertInfo();
|
||||
this.cert = cert;
|
||||
|
||||
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.validity.notAfter).valueOf();
|
||||
|
||||
if (isNew) {
|
||||
const applyTime = dayjs(certReader.detail.validity.notBefore).format("YYYYMMDD_HHmmss");
|
||||
await this.zipCert(cert, applyTime);
|
||||
|
||||
@@ -80,6 +80,17 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
})
|
||||
dnsProviderAccess!: string;
|
||||
|
||||
@TaskInput({
|
||||
title: "使用代理",
|
||||
default: false,
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked",
|
||||
},
|
||||
helper: "如果acme-v02.api.letsencrypt.org被墙无法连接访问,请尝试开启此选项",
|
||||
})
|
||||
useProxy = false;
|
||||
|
||||
@TaskInput({
|
||||
title: "跳过本地校验DNS",
|
||||
default: false,
|
||||
@@ -104,6 +115,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
sslProvider: this.sslProvider,
|
||||
eab,
|
||||
skipLocalVerify: this.skipLocalVerify,
|
||||
useMappingProxy: this.useProxy,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
FROM node:18-alpine as builder
|
||||
FROM node:18-alpine AS builder
|
||||
EXPOSE 7001
|
||||
WORKDIR /workspace/
|
||||
COPY . /workspace/
|
||||
RUN npm install -g pnpm@8.15.7
|
||||
|
||||
RUN cd /workspace/certd-client && pnpm install && npm run build
|
||||
#RUN cd /workspace/certd-client && pnpm install && npm run build
|
||||
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
||||
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
||||
|
||||
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
||||
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app/
|
||||
COPY --from=builder /workspace/certd-server/ /app/
|
||||
RUN chmod +x /workspace/certd-server/tools/linux/*
|
||||
RUN chmod +x /app/tools/linux/*
|
||||
ENV TZ=Asia/Shanghai
|
||||
ENV NODE_ENV=production
|
||||
ENV MIDWAY_SERVER_ENV=production
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复在相同的cron时偶尔无法触发定时任务的bug ([680941a](https://github.com/certd/certd/commit/680941af119619006b592e3ab6fb112cb5556a8b))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||
|
||||
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.22.3",
|
||||
"version": "1.22.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -24,8 +24,6 @@
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@aws-sdk/client-s3": "^3.535.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.535.0",
|
||||
"@fast-crud/fast-crud": "^1.21.1",
|
||||
"@fast-crud/fast-extends": "^1.21.1",
|
||||
"@fast-crud/ui-antdv4": "^1.21.1",
|
||||
@@ -59,7 +57,7 @@
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/pipeline": "^1.22.3",
|
||||
"@certd/pipeline": "^1.22.7",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -2,7 +2,7 @@ import _ from "lodash-es";
|
||||
function copyList(originList: any, newList: any, options: any, parentId?: any) {
|
||||
for (const item of originList) {
|
||||
const newItem: any = _.cloneDeep(item);
|
||||
if(parentId!= null && newItem.parentId == null){
|
||||
if (parentId != null && newItem.parentId == null) {
|
||||
newItem.parentId = parentId;
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ const mockUtil: any = {
|
||||
handle(req: any): any {
|
||||
const item = findById(req.body.id, list);
|
||||
if (item) {
|
||||
_.mergeWith(item, req.body, (objValue, srcValue) => {
|
||||
_.mergeWith(item, req.body, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -305,7 +305,7 @@ const mockUtil: any = {
|
||||
console.log("req", req);
|
||||
let item = findById(req.body.id, list);
|
||||
if (item) {
|
||||
_.mergeWith(item, { [req.body.key]: req.body.value }, (objValue, srcValue) => {
|
||||
_.mergeWith(item, { [req.body.key]: req.body.value }, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -336,7 +336,7 @@ const mockUtil: any = {
|
||||
for (const item of req.body) {
|
||||
const item2 = findById(item.id, list);
|
||||
if (item2) {
|
||||
_.mergeWith(item2, item, (objValue, srcValue) => {
|
||||
_.mergeWith(item2, item, (objValue: any, srcValue: any) => {
|
||||
if (srcValue == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ const list: any = [];
|
||||
_.forEach(commonMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
_.forEach(apiMocks, (value) => {
|
||||
_.forEach(apiMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
_.forEach(viewMocks, (value) => {
|
||||
_.forEach(viewMocks, (value: any) => {
|
||||
list.push(value.default);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,15 +1,37 @@
|
||||
import { request, requestForMock } from "/src/api/service";
|
||||
// import "/src/mock";
|
||||
import { ColumnCompositionProps, CrudOptions, FastCrud, PageQuery, PageRes, setLogger, TransformResProps, useColumns, UseCrudProps, UserPageQuery, useTypes, utils } from "@fast-crud/fast-crud";
|
||||
import {
|
||||
ColumnCompositionProps,
|
||||
CrudOptions,
|
||||
FastCrud,
|
||||
PageQuery,
|
||||
PageRes,
|
||||
setLogger,
|
||||
TransformResProps,
|
||||
useColumns,
|
||||
UseCrudProps,
|
||||
UserPageQuery,
|
||||
useTypes,
|
||||
utils
|
||||
} from "@fast-crud/fast-crud";
|
||||
import "@fast-crud/fast-crud/dist/style.css";
|
||||
import { FsExtendsCopyable, FsExtendsEditor, FsExtendsJson, FsExtendsTime, FsExtendsUploader, FsExtendsInput, FsUploaderS3SignedUrlType, FsUploaderGetAuthContext, FsUploaderAliossSTS } from "@fast-crud/fast-extends";
|
||||
import {
|
||||
FsExtendsCopyable,
|
||||
FsExtendsEditor,
|
||||
FsExtendsJson,
|
||||
FsExtendsTime,
|
||||
FsExtendsUploader,
|
||||
FsExtendsInput,
|
||||
FsUploaderS3SignedUrlType,
|
||||
FsUploaderGetAuthContext,
|
||||
FsUploaderAliossSTS
|
||||
} from "@fast-crud/fast-extends";
|
||||
import "@fast-crud/fast-extends/dist/style.css";
|
||||
import UiAntdv from "@fast-crud/ui-antdv4";
|
||||
import "@fast-crud/ui-antdv4/dist/style.css";
|
||||
import _ from "lodash-es";
|
||||
import { useCrudPermission } from "../permission";
|
||||
import { App } from "vue";
|
||||
import { GetSignedUrl } from "/@/views/crud/component/uploader/s3/api";
|
||||
import { notification } from "ant-design-vue";
|
||||
|
||||
function install(app: App, options: any = {}) {
|
||||
@@ -39,7 +61,7 @@ function install(app: App, options: any = {}) {
|
||||
},
|
||||
size: "small",
|
||||
pagination: false,
|
||||
onResizeColumn: (w: number, col: any) => {
|
||||
onResizeColumn: (w: number | string, col: any) => {
|
||||
if (crudBinding.value?.table?.columnsMap && crudBinding.value?.table?.columnsMap[col.key]) {
|
||||
crudBinding.value.table.columnsMap[col.key].width = w;
|
||||
}
|
||||
@@ -149,6 +171,7 @@ function install(app: App, options: any = {}) {
|
||||
// fast-extends里面的扩展组件均为异步组件,只有在使用时才会被加载,并不会影响首页加载速度
|
||||
//安装uploader 公共参数
|
||||
|
||||
// @ts-ignore
|
||||
app.use(FsExtendsUploader, {
|
||||
// @ts-ignore
|
||||
defaultType: "cos",
|
||||
@@ -231,33 +254,6 @@ function install(app: App, options: any = {}) {
|
||||
},
|
||||
domain: "http://d2p.file.handsfree.work/"
|
||||
},
|
||||
s3: {
|
||||
keepName: true,
|
||||
//同时也支持minio
|
||||
bucket: "fast-crud",
|
||||
sdkOpts: {
|
||||
s3ForcePathStyle: true,
|
||||
signatureVersion: "v4",
|
||||
region: "us-east-1",
|
||||
forcePathStyle: true,
|
||||
//minio与s3完全适配
|
||||
endpoint: "https://play.min.io",
|
||||
credentials: {
|
||||
//不建议在客户端使用secretAccessKey来上传
|
||||
accessKeyId: "Q3AM3UQ867SPQQA43P2F", //访问登录名
|
||||
secretAccessKey: "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG" //访问密码
|
||||
}
|
||||
},
|
||||
//预签名配置,向后端获取上传的预签名连接
|
||||
async getSignedUrl(bucket: string, key: string, options: any, type: FsUploaderS3SignedUrlType = "put") {
|
||||
return await GetSignedUrl(bucket, key, type);
|
||||
},
|
||||
successHandle(ret: any) {
|
||||
// 上传完成后可以在此处处理结果,修改url什么的
|
||||
console.log("success handle:", ret);
|
||||
return ret;
|
||||
}
|
||||
},
|
||||
form: {
|
||||
keepName: true,
|
||||
action: "http://www.docmirror.cn:7070/api/upload/form/upload",
|
||||
@@ -340,6 +336,23 @@ function install(app: App, options: any = {}) {
|
||||
return columnProps;
|
||||
}
|
||||
});
|
||||
|
||||
registerMergeColumnPlugin({
|
||||
name: "resize-column-plugin",
|
||||
order: 2,
|
||||
handle: (columnProps: ColumnCompositionProps) => {
|
||||
if (!columnProps.column) {
|
||||
columnProps.column = {};
|
||||
}
|
||||
columnProps.column.resizable = true;
|
||||
if (!columnProps.column.width) {
|
||||
columnProps.column.width = 100;
|
||||
} else if (typeof columnProps.column?.width === "string" && columnProps.column.width.indexOf("px") > -1) {
|
||||
columnProps.column.width = parseInt(columnProps.column.width.replace("px", ""));
|
||||
}
|
||||
return columnProps;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,784 +0,0 @@
|
||||
export const crudResources = [
|
||||
{
|
||||
title: "CRUD示例",
|
||||
name: "crud",
|
||||
path: "/crud",
|
||||
redirect: "/crud/basis",
|
||||
meta: {
|
||||
icon: "ion:apps-sharp"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "debug",
|
||||
name: "debug",
|
||||
path: "/crud/debug",
|
||||
component: "/crud/debug/index.vue",
|
||||
meta: {
|
||||
isMenu: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "基本特性",
|
||||
name: "basis",
|
||||
path: "/crud/basis",
|
||||
redirect: "/crud/basis/i18n",
|
||||
meta: {
|
||||
icon: "ion:disc-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "FirstDemo",
|
||||
name: "FsCrudFirst",
|
||||
path: "/crud/basis/first",
|
||||
component: "/crud/basis/first/index.vue"
|
||||
},
|
||||
{
|
||||
title: "HelloWorld",
|
||||
name: "FsCrudHelloWorld",
|
||||
path: "/crud/basis/helloworld",
|
||||
component: "/crud/basis/helloworld/index.vue"
|
||||
},
|
||||
{
|
||||
title: "动态计算",
|
||||
name: "BasisCompute",
|
||||
path: "/crud/basis/compute",
|
||||
component: "/crud/basis/compute/index.vue"
|
||||
},
|
||||
{
|
||||
title: "动态计算-更多示例",
|
||||
name: "BasisComputeMore",
|
||||
path: "/crud/basis/compute-more",
|
||||
component: "/crud/basis/compute-more/index.vue"
|
||||
},
|
||||
{
|
||||
title: "国际化",
|
||||
name: "BasisI18n",
|
||||
path: "/crud/basis/i18n",
|
||||
component: "/crud/basis/i18n/index.vue"
|
||||
},
|
||||
{
|
||||
title: "ValueChange",
|
||||
name: "BasisValueChange",
|
||||
path: "/crud/basis/value-change",
|
||||
component: "/crud/basis/value-change/index.vue"
|
||||
},
|
||||
{
|
||||
title: "Card布局",
|
||||
name: "BasisLayoutCard",
|
||||
path: "/crud/basis/layout-card",
|
||||
component: "/crud/basis/layout-card/index.vue"
|
||||
},
|
||||
{
|
||||
title: "自定义布局",
|
||||
name: "BasisLayoutCustom",
|
||||
path: "/crud/basis/layout-custom",
|
||||
component: "/crud/basis/layout-custom/index.vue"
|
||||
},
|
||||
{
|
||||
title: "自定义组件",
|
||||
name: "BasisCustom",
|
||||
path: "/crud/basis/custom",
|
||||
component: "/crud/basis/custom/index.vue"
|
||||
},
|
||||
{
|
||||
title: "列设置",
|
||||
name: "BasisColumnsSet",
|
||||
path: "/crud/basis/columns-set",
|
||||
component: "/crud/basis/columns-set/index.vue"
|
||||
},
|
||||
{
|
||||
title: "字段合并插件",
|
||||
name: "BasisColumnMergePlugin",
|
||||
path: "/crud/basis/column-merge-plugin",
|
||||
component: "/crud/basis/column-merge-plugin/index.vue"
|
||||
},
|
||||
{
|
||||
title: "ResetCrudOptions",
|
||||
name: "BasisReset",
|
||||
path: "/crud/basis/reset",
|
||||
component: "/crud/basis/reset/index.vue",
|
||||
meta: {
|
||||
cache: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "CrudOptions插件",
|
||||
name: "BasisPlugin",
|
||||
path: "/crud/basis/plugin",
|
||||
component: "/crud/basis/plugin/index.vue"
|
||||
},
|
||||
{
|
||||
title: "Ts定义测试",
|
||||
name: "BasisTsTest",
|
||||
path: "/crud/basis/ts",
|
||||
component: "/crud/basis/ts/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "数据字典",
|
||||
name: "dict",
|
||||
path: "/crud/dict",
|
||||
redirect: "/crud/dict/single",
|
||||
meta: {
|
||||
icon: "ion:book-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "单例",
|
||||
name: "DictSingle",
|
||||
path: "/crud/dict/single",
|
||||
component: "/crud/dict/single/index.vue"
|
||||
},
|
||||
{
|
||||
title: "分发复制",
|
||||
name: "DictCloneable",
|
||||
path: "/crud/dict/cloneable",
|
||||
component: "/crud/dict/cloneable/index.vue"
|
||||
},
|
||||
{
|
||||
title: "原型复制",
|
||||
name: "DictPrototype",
|
||||
path: "/crud/dict/prototype",
|
||||
component: "/crud/dict/prototype/index.vue"
|
||||
},
|
||||
{
|
||||
title: "页面间共享",
|
||||
name: "DictShared",
|
||||
path: "/crud/dict/shared",
|
||||
children: [
|
||||
{
|
||||
title: "共享字典数据管理",
|
||||
name: "DictSharedManager",
|
||||
path: "/crud/dict/shared/manager",
|
||||
component: "/crud/dict/shared/manager/index.vue"
|
||||
},
|
||||
{
|
||||
title: "共享字典使用",
|
||||
name: "DictSharedUse",
|
||||
path: "/crud/dict/shared/use",
|
||||
component: "/crud/dict/shared/use/index.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "操作列",
|
||||
name: "row-handle",
|
||||
path: "/crud/row-handle",
|
||||
redirect: "/crud/row-handle/tooltip",
|
||||
meta: {
|
||||
icon: "ion:build-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "Tooltip",
|
||||
name: "RowHandleTooltip",
|
||||
path: "/crud/row-handle/tooltip",
|
||||
component: "/crud/row-handle/tooltip/index.vue"
|
||||
},
|
||||
{
|
||||
title: "按钮折叠",
|
||||
name: "RowHandleDropdown",
|
||||
path: "/crud/row-handle/dropdown",
|
||||
component: "/crud/row-handle/dropdown/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "组件示例",
|
||||
name: "component",
|
||||
path: "/crud/component",
|
||||
redirect: "/crud/component/text",
|
||||
meta: {
|
||||
icon: "ion:cube-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "文本输入(input)",
|
||||
name: "ComponentText",
|
||||
path: "/crud/component/text",
|
||||
component: "/crud/component/text/index.vue"
|
||||
},
|
||||
{
|
||||
title: "选择(select)",
|
||||
name: "ComponentSelect",
|
||||
path: "/crud/component/select",
|
||||
component: "/crud/component/select/index.vue"
|
||||
},
|
||||
{
|
||||
title: " 表格选择(table-select)",
|
||||
name: "ComponentTableSelect",
|
||||
path: "/crud/component/table-select",
|
||||
component: "/crud/component/table-select/index.vue"
|
||||
},
|
||||
{
|
||||
title: "级联(cascader)",
|
||||
name: "ComponentCascader",
|
||||
path: "/crud/component/cascader",
|
||||
component: "/crud/component/cascader/index.vue"
|
||||
},
|
||||
{
|
||||
title: "多选(checkbox)",
|
||||
name: "ComponentCheckbox",
|
||||
path: "/crud/component/checkbox",
|
||||
component: "/crud/component/checkbox/index.vue"
|
||||
},
|
||||
{
|
||||
title: "单选(radio)",
|
||||
name: "ComponentRadio",
|
||||
path: "/crud/component/radio",
|
||||
component: "/crud/component/radio/index.vue"
|
||||
},
|
||||
{
|
||||
title: "开关(switch)",
|
||||
name: "ComponentSwitch",
|
||||
path: "/crud/component/switch",
|
||||
component: "/crud/component/switch/index.vue"
|
||||
},
|
||||
{
|
||||
title: "日期时间(date)",
|
||||
name: "ComponentDate",
|
||||
path: "/crud/component/date",
|
||||
component: "/crud/component/date/index.vue"
|
||||
},
|
||||
{
|
||||
title: "按钮链接",
|
||||
name: "ComponentButton",
|
||||
path: "/crud/component/button",
|
||||
component: "/crud/component/button/index.vue"
|
||||
},
|
||||
{
|
||||
title: "数字",
|
||||
name: "ComponentNumber",
|
||||
path: "/crud/component/number",
|
||||
component: "/crud/component/number/index.vue"
|
||||
},
|
||||
{
|
||||
title: "树形选择",
|
||||
name: "ComponentTree",
|
||||
path: "/crud/component/tree",
|
||||
component: "/crud/component/tree/index.vue"
|
||||
},
|
||||
{
|
||||
title: "图片裁剪上传",
|
||||
name: "ComponentUploaderCropper",
|
||||
path: "/crud/component/uploader/cropper",
|
||||
component: "/crud/component/uploader/cropper/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单本地上传",
|
||||
name: "ComponentUploaderForm",
|
||||
path: "/crud/component/uploader/form",
|
||||
component: "/crud/component/uploader/form/index.vue"
|
||||
},
|
||||
{
|
||||
title: "阿里云oss上传",
|
||||
name: "ComponentUploaderAlioss",
|
||||
path: "/crud/component/uploader/alioss",
|
||||
component: "/crud/component/uploader/alioss/index.vue"
|
||||
},
|
||||
{
|
||||
title: "腾讯云cos上传",
|
||||
name: "ComponentUploaderCos",
|
||||
path: "/crud/component/uploader/cos",
|
||||
component: "/crud/component/uploader/cos/index.vue"
|
||||
},
|
||||
{
|
||||
title: "七牛云上传",
|
||||
name: "ComponentUploaderQiniu",
|
||||
path: "/crud/component/uploader/qiniu",
|
||||
component: "/crud/component/uploader/qiniu/index.vue"
|
||||
},
|
||||
{
|
||||
title: "s3上传",
|
||||
name: "ComponentUploaderS3",
|
||||
path: "/crud/component/uploader/s3",
|
||||
component: "/crud/component/uploader/s3/index.vue"
|
||||
},
|
||||
{
|
||||
title: "富文本编辑器",
|
||||
name: "ComponentEditor",
|
||||
path: "/crud/component/editor",
|
||||
component: "/crud/component/editor/index.vue"
|
||||
},
|
||||
{
|
||||
title: "图标",
|
||||
name: "ComponentIcon",
|
||||
path: "/crud/component/icon",
|
||||
component: "/crud/component/icon/index.vue"
|
||||
},
|
||||
{
|
||||
title: "JsonEditor",
|
||||
name: "ComponentJson",
|
||||
path: "/crud/component/json",
|
||||
component: "/crud/component/json/index.vue"
|
||||
},
|
||||
{
|
||||
title: "手机号输入框",
|
||||
name: "ComponentPhone",
|
||||
path: "/crud/component/phone",
|
||||
component: "/crud/component/phone/index.vue"
|
||||
},
|
||||
{
|
||||
title: "组件独立使用",
|
||||
name: "ComponentIndependent",
|
||||
path: "/crud/component/independent",
|
||||
component: "/crud/component/independent/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Form表单",
|
||||
name: "form",
|
||||
path: "/crud/form",
|
||||
redirect: "/crud/form/layout",
|
||||
meta: {
|
||||
icon: "ion:document-text-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "基本表单",
|
||||
name: "FormBase",
|
||||
path: "/crud/form/base",
|
||||
component: "/crud/form/base/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单Grid布局",
|
||||
name: "FormLayoutGrid",
|
||||
path: "/crud/form/layout-grid",
|
||||
component: "/crud/form/layout-grid/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单Flex布局",
|
||||
name: "FormLayoutFlex",
|
||||
path: "/crud/form/layout-flex",
|
||||
component: "/crud/form/layout-flex/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单动态布局",
|
||||
name: "FormLayout",
|
||||
path: "/crud/form/layout",
|
||||
component: "/crud/form/layout/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单单列模式",
|
||||
name: "FormSingleColumn",
|
||||
path: "/crud/form/single-column",
|
||||
component: "/crud/form/single-column/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单校验",
|
||||
name: "FormValidation",
|
||||
path: "/crud/form/validation",
|
||||
component: "/crud/form/validation/index.vue"
|
||||
},
|
||||
{
|
||||
title: "抽屉表单",
|
||||
name: "FormDrawer",
|
||||
path: "/crud/form/drawer",
|
||||
component: "/crud/form/drawer/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单分组",
|
||||
name: "FormGroup",
|
||||
path: "/crud/form/group",
|
||||
component: "/crud/form/group/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单分组(tabs)",
|
||||
name: "FormGroupTabs",
|
||||
path: "/crud/form/group-tabs",
|
||||
component: "/crud/form/group-tabs/index.vue"
|
||||
},
|
||||
{
|
||||
title: "自定义表单",
|
||||
name: "FormCustomForm",
|
||||
path: "/crud/form/custom-form",
|
||||
component: "/crud/form/custom-form/index.vue"
|
||||
},
|
||||
{
|
||||
title: "字段帮助说明",
|
||||
name: "FormHelper",
|
||||
path: "/crud/form/helper",
|
||||
component: "/crud/form/helper/index.vue"
|
||||
},
|
||||
{
|
||||
title: "页面内部弹出表单",
|
||||
name: "FormInner",
|
||||
path: "/crud/form/inner",
|
||||
component: "/crud/form/inner/index.vue",
|
||||
meta: {
|
||||
cache: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "地区字典管理",
|
||||
name: "FormInnerArea",
|
||||
path: "/crud/form/inner/area",
|
||||
component: "/crud/form/inner/area/index.vue",
|
||||
meta: {
|
||||
isMenu: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "新页面编辑",
|
||||
name: "FormNewPage",
|
||||
path: "/crud/form/new-page",
|
||||
component: "/crud/form/new-page/index.vue",
|
||||
meta: {
|
||||
cache: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "新页面编辑表单",
|
||||
name: "FormNewPageEdit",
|
||||
path: "/crud/form/new-page/edit",
|
||||
component: "/crud/form/new-page/edit.vue",
|
||||
meta: {
|
||||
isMenu: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "独立使用表单",
|
||||
name: "FormIndependent",
|
||||
path: "/crud/form/independent",
|
||||
component: "/crud/form/independent/index.vue"
|
||||
},
|
||||
{
|
||||
title: "重置表单",
|
||||
name: "FormReset",
|
||||
path: "/crud/form/reset",
|
||||
component: "/crud/form/reset/index.vue"
|
||||
},
|
||||
{
|
||||
title: "嵌套数据结构",
|
||||
name: "FormNest",
|
||||
path: "/crud/form/nest",
|
||||
component: "/crud/form/nest/index.vue"
|
||||
},
|
||||
{
|
||||
title: "字段组件render",
|
||||
name: "FormRender",
|
||||
path: "/crud/form/render",
|
||||
component: "/crud/form/render/index.vue"
|
||||
},
|
||||
{
|
||||
title: "查看表单使用单元格组件",
|
||||
name: "FormView",
|
||||
path: "/crud/form/view",
|
||||
component: "/crud/form/view/index.vue"
|
||||
},
|
||||
{
|
||||
title: "initialForm",
|
||||
name: "FormInitial",
|
||||
path: "/crud/form/initial",
|
||||
component: "/crud/form/initial/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单Watch",
|
||||
name: "FormWatch",
|
||||
path: "/crud/form/watch",
|
||||
component: "/crud/form/watch/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "表格特性",
|
||||
path: "/crud/feature",
|
||||
meta: {
|
||||
icon: "ion:beer-outline"
|
||||
},
|
||||
redirect: "/crud/feature/dropdown",
|
||||
children: [
|
||||
{
|
||||
title: "部件显隐",
|
||||
name: "FeatureHide",
|
||||
path: "/crud/feature/hide",
|
||||
component: "/crud/feature/hide/index.vue"
|
||||
},
|
||||
{
|
||||
title: "多选&批量删除",
|
||||
name: "FeatureSelection",
|
||||
path: "/crud/feature/selection",
|
||||
component: "/crud/feature/selection/index.vue"
|
||||
},
|
||||
{
|
||||
title: "单选",
|
||||
name: "FeatureSelectionRadio",
|
||||
path: "/crud/feature/selection-radio",
|
||||
component: "/crud/feature/selection-radio/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表头过滤",
|
||||
name: "FeatureFilter",
|
||||
path: "/crud/feature/filter",
|
||||
component: "/crud/feature/filter/index.vue"
|
||||
},
|
||||
{
|
||||
title: "行展开",
|
||||
name: "FeatureExpand",
|
||||
path: "/crud/feature/expand",
|
||||
component: "/crud/feature/expand/index.vue"
|
||||
},
|
||||
{
|
||||
title: "树形表格",
|
||||
name: "FeatureTree",
|
||||
path: "/crud/feature/tree",
|
||||
component: "/crud/feature/tree/index.vue"
|
||||
},
|
||||
{
|
||||
title: "多级表头",
|
||||
name: "FeatureHeaderGroup",
|
||||
path: "/crud/feature/header-group",
|
||||
component: "/crud/feature/header-group/index.vue"
|
||||
},
|
||||
{
|
||||
title: "自定义表头",
|
||||
name: "FeatureHeader",
|
||||
path: "/crud/feature/header",
|
||||
component: "/crud/feature/header/index.vue"
|
||||
},
|
||||
{
|
||||
title: "合并单元格",
|
||||
name: "FeatureMerge",
|
||||
path: "/crud/feature/merge",
|
||||
component: "/crud/feature/merge/index.vue"
|
||||
},
|
||||
{
|
||||
title: "序号",
|
||||
name: "FeatureIndex",
|
||||
path: "/crud/feature/index",
|
||||
component: "/crud/feature/index/index.vue"
|
||||
},
|
||||
{
|
||||
title: "排序",
|
||||
name: "FeatureSortable",
|
||||
path: "/crud/feature/sortable",
|
||||
component: "/crud/feature/sortable/index.vue"
|
||||
},
|
||||
{
|
||||
title: "固定列",
|
||||
name: "FeatureFixed",
|
||||
path: "/crud/feature/fixed",
|
||||
component: "/crud/feature/fixed/index.vue"
|
||||
},
|
||||
{
|
||||
title: "不固定高度",
|
||||
name: "FeatureHeight",
|
||||
path: "/crud/feature/height",
|
||||
component: "/crud/feature/height/index.vue"
|
||||
},
|
||||
{
|
||||
title: "查询框",
|
||||
name: "FeatureSearch",
|
||||
path: "/crud/feature/search",
|
||||
component: "/crud/feature/search/index.vue"
|
||||
},
|
||||
{
|
||||
title: "查询框多行模式",
|
||||
name: "FeatureSearchMulti",
|
||||
path: "/crud/feature/search-multi",
|
||||
component: "/crud/feature/search-multi/index.vue"
|
||||
},
|
||||
{
|
||||
title: "Tabs快捷查询",
|
||||
name: "FeatureTabs",
|
||||
path: "/crud/feature/tabs",
|
||||
component: "/crud/feature/tabs/index.vue"
|
||||
},
|
||||
{
|
||||
title: "字段排序",
|
||||
name: "FeatureColumnSort",
|
||||
path: "/crud/feature/column-sort",
|
||||
component: "/crud/feature/column-sort/index.vue"
|
||||
},
|
||||
{
|
||||
title: "ValueBuilder",
|
||||
name: "FeatureValueBuilder",
|
||||
path: "/crud/feature/value-builder",
|
||||
component: "/crud/feature/value-builder/index.vue"
|
||||
},
|
||||
{
|
||||
title: "列设置",
|
||||
name: "FeatureColumnsSet",
|
||||
path: "/crud/feature/columns-set",
|
||||
component: "/crud/feature/columns-set/index.vue"
|
||||
},
|
||||
{
|
||||
title: "本地化编辑",
|
||||
name: "FeatureLocal",
|
||||
path: "/crud/feature/local",
|
||||
component: "/crud/feature/local/index.vue"
|
||||
},
|
||||
{
|
||||
title: "v-model",
|
||||
name: "FeatureVModel",
|
||||
path: "/crud/feature/local-v-model",
|
||||
component: "/crud/feature/local-v-model/index.vue"
|
||||
},
|
||||
{
|
||||
title: "导入",
|
||||
name: "FeatureImport",
|
||||
path: "/crud/feature/local-import",
|
||||
component: "/crud/feature/local-import/index.vue"
|
||||
},
|
||||
{
|
||||
title: "导出",
|
||||
name: "FeatureExport",
|
||||
path: "/crud/feature/export",
|
||||
component: "/crud/feature/export/index.vue"
|
||||
},
|
||||
{
|
||||
title: "自定义删除",
|
||||
name: "FeatureRemove",
|
||||
path: "/crud/feature/remove",
|
||||
component: "/crud/feature/remove/index.vue"
|
||||
},
|
||||
{
|
||||
title: "调整列宽",
|
||||
name: "FeatureColumnResize",
|
||||
path: "/crud/feature/column-resize",
|
||||
component: "/crud/feature/column-resize/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "可编辑",
|
||||
name: "Editable",
|
||||
path: "/crud/editable",
|
||||
redirect: "/crud/editable/free",
|
||||
meta: {
|
||||
icon: "ion:create-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "自由编辑",
|
||||
name: "EditableFree",
|
||||
path: "/crud/editable/free",
|
||||
component: "/crud/editable/free/index.vue"
|
||||
},
|
||||
{
|
||||
title: "行编辑",
|
||||
name: "EditableRow",
|
||||
path: "/crud/editable/row",
|
||||
component: "/crud/editable/row/index.vue"
|
||||
},
|
||||
{
|
||||
title: "单元格编辑",
|
||||
name: "EditableCell",
|
||||
path: "/crud/editable/cell",
|
||||
component: "/crud/editable/cell/index.vue"
|
||||
},
|
||||
{
|
||||
title: "子表格编辑",
|
||||
name: "EditableVModel",
|
||||
path: "/crud/editable/vmodel",
|
||||
component: "/crud/editable/vmodel/index.vue"
|
||||
},
|
||||
{
|
||||
title: "子CRUD",
|
||||
name: "EditableSubCrud",
|
||||
path: "/crud/editable/sub-crud",
|
||||
component: "/crud/editable/sub-crud/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "插槽",
|
||||
name: "Slots",
|
||||
path: "/crud/slots",
|
||||
redirect: "/crud/slots/layout",
|
||||
meta: {
|
||||
icon: "ion:extension-puzzle-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "页面占位插槽",
|
||||
name: "SlotsLayout",
|
||||
path: "/crud/slots/layout",
|
||||
component: "/crud/slots/layout/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单占位插槽",
|
||||
name: "SlotsForm",
|
||||
path: "/crud/slots/form",
|
||||
component: "/crud/slots/form/index.vue"
|
||||
},
|
||||
{
|
||||
title: "查询字段插槽",
|
||||
name: "SlotsSearch",
|
||||
path: "/crud/slots/search",
|
||||
component: "/crud/slots/search/index.vue"
|
||||
},
|
||||
{
|
||||
title: "单元格插槽",
|
||||
name: "SlotsCell",
|
||||
path: "/crud/slots/cell",
|
||||
component: "/crud/slots/cell/index.vue"
|
||||
},
|
||||
{
|
||||
title: "表单字段插槽",
|
||||
name: "SlotsFormItem",
|
||||
path: "/crud/slots/form-item",
|
||||
component: "/crud/slots/form-item/index.vue"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "复杂需求",
|
||||
name: "Advanced",
|
||||
path: "/crud/advanced",
|
||||
redirect: "/crud/advanced/linkage",
|
||||
meta: {
|
||||
icon: "ion:flame-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "选择联动",
|
||||
name: "AdvancedLinkage",
|
||||
path: "/crud/advanced/linkage",
|
||||
component: "/crud/advanced/linkage/index.vue"
|
||||
},
|
||||
{
|
||||
title: "后台加载crud",
|
||||
name: "AdvancedFormBackend",
|
||||
path: "/crud/advanced/from-backend",
|
||||
component: "/crud/advanced/from-backend/index.vue"
|
||||
},
|
||||
{
|
||||
title: "本地分页",
|
||||
name: "AdvancedLocalPagination",
|
||||
path: "/crud/advanced/local-pagination",
|
||||
component: "/crud/advanced/local-pagination/index.vue"
|
||||
},
|
||||
{
|
||||
title: "嵌套子表格",
|
||||
name: "AdvancedNest",
|
||||
path: "/crud/advanced/nest",
|
||||
component: "/crud/advanced/nest/index.vue"
|
||||
},
|
||||
{
|
||||
title: "对话框中显示crud",
|
||||
name: "AdvancedInDialog",
|
||||
path: "/crud/advanced/in-dialog",
|
||||
component: "/crud/advanced/in-dialog/index.vue"
|
||||
},
|
||||
{
|
||||
title: "抽屉中显示crud",
|
||||
name: "AdvancedInDrawer",
|
||||
path: "/crud/advanced/in-drawer",
|
||||
component: "/crud/advanced/in-drawer/index.vue"
|
||||
},
|
||||
{
|
||||
title: "大量数据",
|
||||
name: "AdvancedBigData",
|
||||
path: "/crud/advanced/big-data",
|
||||
component: "/crud/advanced/big-data/index.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -1,22 +0,0 @@
|
||||
export const integrationResources = [
|
||||
{
|
||||
title: "集成",
|
||||
name: "integration",
|
||||
path: "/integration",
|
||||
redirect: "/integration/bpmn",
|
||||
meta: {
|
||||
icon: "ion:apps-sharp"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "FsBpmn",
|
||||
name: "FsBpmn",
|
||||
path: "/integration/bpmn",
|
||||
component: "/integration/bpmn/index.vue",
|
||||
meta: {
|
||||
icon: "ion:disc-outline"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -1,30 +0,0 @@
|
||||
export const uiResources = [
|
||||
{
|
||||
title: "UI示例",
|
||||
name: "ui",
|
||||
path: "/ui",
|
||||
redirect: "/ui/form",
|
||||
meta: {
|
||||
icon: "ion:apps-sharp"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "表单组件",
|
||||
name: "UIForm",
|
||||
path: "/ui/form",
|
||||
redirect: "/ui/form/input",
|
||||
meta: {
|
||||
icon: "ion:disc-outline"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
title: "input",
|
||||
name: "UIFormInput",
|
||||
path: "/ui/form/input",
|
||||
component: "/ui/form/input/index.vue"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -6,12 +6,10 @@ export function useReference(form: any) {
|
||||
return;
|
||||
}
|
||||
for (const reference of form.reference) {
|
||||
debugger;
|
||||
_.set(
|
||||
form,
|
||||
reference.dest,
|
||||
compute<any>((scope) => {
|
||||
debugger;
|
||||
return _.get(scope, reference.src);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { nanoid } from "nanoid";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { env } from "/@/utils/util.env";
|
||||
import { useUserStore } from "/@/store/modules/user";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
@@ -125,6 +126,8 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
minWidth: 200,
|
||||
fixed: "right",
|
||||
buttons: {
|
||||
view: {
|
||||
click({ row }) {
|
||||
@@ -200,6 +203,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
},
|
||||
column: {
|
||||
width: 300,
|
||||
sorter: true,
|
||||
component: {
|
||||
on: {
|
||||
// 注意:必须要on前缀
|
||||
@@ -210,11 +214,35 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}
|
||||
}
|
||||
},
|
||||
lastVars: {
|
||||
title: "到期剩余",
|
||||
type: "number",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
cellRender({ row }) {
|
||||
if (!row.lastVars?.certExpiresTime) {
|
||||
return "-";
|
||||
}
|
||||
const leftDays = dayjs(row.lastVars.certExpiresTime).diff(dayjs(), "day");
|
||||
const color = leftDays < 20 ? "red" : "#389e0d";
|
||||
const percent = (leftDays / 90) * 100;
|
||||
return <a-progress percent={percent} strokeColor={color} format={(percent: number) => `${leftDays} 天`} />;
|
||||
},
|
||||
width: 110
|
||||
}
|
||||
},
|
||||
lastHistoryTime: {
|
||||
title: "最后运行",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 120,
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
status: {
|
||||
@@ -225,6 +253,11 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}),
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 80,
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -242,6 +275,9 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 80,
|
||||
align: "center",
|
||||
component: {
|
||||
name: "fs-dict-switch",
|
||||
vModel: "checked"
|
||||
@@ -254,12 +290,25 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
keepHistoryCount: {
|
||||
title: "历史记录保持数",
|
||||
type: "number",
|
||||
form: {
|
||||
value: 30,
|
||||
helper: "历史记录保持条数,多余的会被删除"
|
||||
},
|
||||
column: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
order: {
|
||||
title: "排序号",
|
||||
type: "number",
|
||||
column: {
|
||||
sorter: true,
|
||||
align: "center",
|
||||
width: 80
|
||||
}
|
||||
},
|
||||
createTime: {
|
||||
@@ -267,6 +316,11 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 125,
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
updateTime: {
|
||||
@@ -274,6 +328,9 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedBigData";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
output: {},
|
||||
crudOptions: {
|
||||
//大量数据的crud配置
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
table: {
|
||||
scroll: {
|
||||
//启用横向滚动条,设置一个大于所有列宽之和的值,一般大于表格宽度
|
||||
x: 2400
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
pageSize: 100
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right"
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
text: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
dict1: {
|
||||
title: "字典1",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict1"
|
||||
})
|
||||
},
|
||||
dict2: {
|
||||
title: "字典2",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict2"
|
||||
})
|
||||
},
|
||||
dict3: {
|
||||
title: "字典3",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict3"
|
||||
})
|
||||
},
|
||||
dict4: {
|
||||
title: "字典4",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict4"
|
||||
})
|
||||
},
|
||||
dict5: {
|
||||
title: "字典5",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict5"
|
||||
})
|
||||
},
|
||||
dict6: {
|
||||
title: "字典6",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict6"
|
||||
})
|
||||
},
|
||||
dict7: {
|
||||
title: "字典7",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict7"
|
||||
})
|
||||
},
|
||||
dict8: {
|
||||
title: "字典8",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict8"
|
||||
})
|
||||
},
|
||||
dict9: {
|
||||
title: "字典9",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict9"
|
||||
})
|
||||
},
|
||||
dict10: {
|
||||
title: "字典10",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/ManyOpenStatusEnum?from=dict10"
|
||||
})
|
||||
},
|
||||
text1: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text2: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text3: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text4: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text5: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text6: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text7: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text8: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text9: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
},
|
||||
text10: {
|
||||
title: "文本",
|
||||
type: "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">大量数据</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud.js";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AdvancedBigData",
|
||||
setup() {
|
||||
const { crudBinding, crudRef, crudExpose, output } = useFs({ createCrudOptions });
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
...output
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,128 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedBigData",
|
||||
idGenerator: 0,
|
||||
//此处copy多次,模拟大量数据
|
||||
copyTimes: 1000
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
},
|
||||
{
|
||||
text: "测试文本",
|
||||
dict1: "1",
|
||||
dict2: "1",
|
||||
dict3: "2",
|
||||
dict4: "1",
|
||||
dict5: "2",
|
||||
dict6: "1",
|
||||
dict7: "1",
|
||||
dict8: "1",
|
||||
text1: "测试文本1",
|
||||
text2: "测试文本2",
|
||||
text3: "测试文本3",
|
||||
text4: "测试文本4",
|
||||
text5: "测试文本5",
|
||||
text6: "测试文本6",
|
||||
text7: "测试文本7",
|
||||
text8: "测试文本8",
|
||||
dict9: "2",
|
||||
dict10: "1",
|
||||
dict11: "2",
|
||||
dict12: "1"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,48 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedFromBackend";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
export function GetCrud() {
|
||||
return request({
|
||||
url: apiPrefix + "/crud",
|
||||
method: "get"
|
||||
});
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//此处演示从后台获取crudOptions配置字符串
|
||||
export const crudOptions = `
|
||||
({crudExpose,dict}) => {
|
||||
return {
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
radio: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
`;
|
||||
@@ -1,51 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes, dict } from "@fast-crud/fast-crud";
|
||||
import { GetCrud } from "./api";
|
||||
import _ from "lodash-es";
|
||||
|
||||
/**
|
||||
* 异步创建options
|
||||
* @param props
|
||||
*/
|
||||
export default async function (props: CreateCrudOptionsProps): Promise<CreateCrudOptionsRet> {
|
||||
const { crudExpose } = props;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
const localCrudOptions = {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
}
|
||||
};
|
||||
// 上面是本地crudOptions
|
||||
|
||||
// 下面从后台获取crudOptions
|
||||
const ret = await GetCrud();
|
||||
// 编译
|
||||
const crudBackend = eval(ret);
|
||||
// 本示例返回的是一个方法字符串,所以要先执行这个方法,获取options
|
||||
const remoteCrudOptions = crudBackend({ crudExpose, dict });
|
||||
// 与本地options合并
|
||||
const crudOptions = _.merge(localCrudOptions, remoteCrudOptions);
|
||||
|
||||
return {
|
||||
crudOptions
|
||||
};
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">CrudOptions从后台加载</div>
|
||||
<div class="more">
|
||||
<a target="_blank" href="http://fast-crud.docmirror.cn/api/use.html#usefsasync">文档</a>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud v-if="crudBinding" ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, Ref } from "vue";
|
||||
import { CrudBinding, useFsAsync } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AdvancedFromBackend",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef: Ref = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding: Ref<CrudBinding> = ref();
|
||||
|
||||
const customValue: any = {}; //自定义变量,传给createCrudOptions的额外参数
|
||||
|
||||
// 初始化crud配置
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
const customValue = {};
|
||||
//异步初始化fs,createCrudOptions为异步方法
|
||||
const { crudExpose, context } = await useFsAsync({ crudRef, crudBinding, createCrudOptions, context: customValue });
|
||||
// 刷新数据
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,36 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
import { crudOptions } from "./crud-backend";
|
||||
const options: any = {
|
||||
name: "AdvancedFromBackend",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
radio: "1"
|
||||
},
|
||||
{
|
||||
radio: "2"
|
||||
},
|
||||
{
|
||||
radio: "0"
|
||||
}
|
||||
];
|
||||
|
||||
options.list = list;
|
||||
options.copyTimes = 1000;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
|
||||
mock.push({
|
||||
path: "/AdvancedFromBackend/crud",
|
||||
method: "get",
|
||||
handle(req: any) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: crudOptions
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default mock;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedInDialog";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import {
|
||||
AddReq,
|
||||
CreateCrudOptionsProps,
|
||||
CreateCrudOptionsRet,
|
||||
DelReq,
|
||||
EditReq,
|
||||
UserPageQuery,
|
||||
UserPageRes,
|
||||
dict,
|
||||
utils
|
||||
} from "@fast-crud/fast-crud";
|
||||
import { SearchOutlined } from "@ant-design/icons-vue";
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
name: {
|
||||
title: "姓名",
|
||||
type: "text", //虽然不写也能正确显示组件,但不建议省略它
|
||||
search: { show: true },
|
||||
form: {
|
||||
component: {
|
||||
maxlength: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
search: {
|
||||
title: "搜索",
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
addonAfter: "后置",
|
||||
suffix: "suffix",
|
||||
children: {
|
||||
addonBefore() {
|
||||
return <SearchOutlined />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
password: {
|
||||
title: "密码",
|
||||
type: "password",
|
||||
column: {
|
||||
//一般密码不显示在列里面
|
||||
show: false
|
||||
}
|
||||
},
|
||||
intro: {
|
||||
title: "简介",
|
||||
type: "textarea",
|
||||
form: {
|
||||
component: { showWordLimit: true, maxlength: 200 }
|
||||
},
|
||||
column: {
|
||||
ellipsis: true
|
||||
}
|
||||
},
|
||||
render: {
|
||||
title: "复杂输入(render)",
|
||||
type: "text",
|
||||
form: {
|
||||
title: "复杂输入",
|
||||
component: {
|
||||
render(context: any) {
|
||||
utils.logger.info("context scope", context);
|
||||
return (
|
||||
<a-input-group compact>
|
||||
<a-input placeholder={"render1 input"} style="width: 50%" v-model={[context.form.render, "value"]} />
|
||||
<a-input placeholder={"render2 input"} style="width: 50%" v-model={[context.form.render2, "value"]} />
|
||||
</a-input-group>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
render2: {
|
||||
title: "我的值是由复杂输入列输入的",
|
||||
type: "text",
|
||||
column: {
|
||||
width: "300px"
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "FsInDialog",
|
||||
setup() {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,41 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedInDialog",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
name: "王小虎",
|
||||
date: "2016-05-02",
|
||||
status: "0",
|
||||
province: "1",
|
||||
avatar: "https://alicdn.antdv.com/vue.png",
|
||||
show: true,
|
||||
city: "sz",
|
||||
address: "123123",
|
||||
zip: "518000",
|
||||
intro: "王小虎是element-plus的table示例出现的名字"
|
||||
},
|
||||
{
|
||||
name: "张三",
|
||||
date: "2016-05-04",
|
||||
status: "1",
|
||||
province: "2"
|
||||
},
|
||||
{
|
||||
name: "李四",
|
||||
date: 2232433534511,
|
||||
status: "1",
|
||||
province: "0"
|
||||
},
|
||||
{
|
||||
name: "王五",
|
||||
date: "2016-05-03",
|
||||
status: "2",
|
||||
province: "wh,gz"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,37 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">对话框中显示crud</div>
|
||||
</template>
|
||||
<div style="padding: 50px">
|
||||
<a-button type="primary" @click="openDialog">打开对话框</a-button>
|
||||
</div>
|
||||
|
||||
<a-modal v-model:open="dialogShow" width="80%" title="fs-crud in dialog">
|
||||
<div style="height: 400px; position: relative">
|
||||
<!-- 在此处显示fs-crud页面 -->
|
||||
<fs-in-dialog></fs-in-dialog>
|
||||
</div>
|
||||
</a-modal>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
//将fs-crud做成的页面在此处引入
|
||||
import FsInDialog from "./crud/index.vue";
|
||||
export default defineComponent({
|
||||
name: "InDialog",
|
||||
components: { FsInDialog },
|
||||
setup() {
|
||||
const dialogShow = ref(false);
|
||||
function openDialog() {
|
||||
dialogShow.value = true;
|
||||
}
|
||||
return {
|
||||
dialogShow,
|
||||
openDialog
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,43 +0,0 @@
|
||||
//@ts-ignore
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedInDrawer";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
//从context中获取子组件的ref
|
||||
const drawerClassTimeRef = context.drawerClassTimeRef;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
width: 300,
|
||||
buttons: {
|
||||
editClassTime: {
|
||||
text: "录入课时",
|
||||
click: ({ row }) => {
|
||||
//调用子组件暴露的open方法,打开抽屉对话框
|
||||
drawerClassTimeRef.value.open({
|
||||
textbook: row
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
textbookCategory: {
|
||||
title: "教材分类",
|
||||
type: "text", //虽然不写也能正确显示组件,但不建议省略它
|
||||
search: { show: true }
|
||||
},
|
||||
textbookVersion: {
|
||||
title: "教材版本",
|
||||
type: "text"
|
||||
},
|
||||
textbookName: {
|
||||
title: "教材名称",
|
||||
type: "text"
|
||||
},
|
||||
totalWords: {
|
||||
title: "总词汇数",
|
||||
type: "number"
|
||||
},
|
||||
classTimeNumber: {
|
||||
title: "课时数量",
|
||||
type: "number",
|
||||
column: {
|
||||
cellRender({ value }) {
|
||||
return `${value}课时`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//@ts-ignore
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedInDrawerClassTime";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { computed } from "vue";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const textbookRef = context.textbookRef;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
textbookId: {
|
||||
title: "教材ID",
|
||||
type: "text",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
form: {
|
||||
value: computed(() => {
|
||||
//动态设置初始值
|
||||
return textbookRef.value.id;
|
||||
})
|
||||
}
|
||||
},
|
||||
textbookCategory: {
|
||||
title: "教材分类",
|
||||
type: "text",
|
||||
form: { show: false },
|
||||
column: {
|
||||
//本字段禁止条件render,因为此字段没有值,是从父组件传过来显示的
|
||||
conditionalRender: false,
|
||||
cellRender() {
|
||||
return textbookRef.value.textbookCategory;
|
||||
}
|
||||
}
|
||||
},
|
||||
textbookVersion: {
|
||||
title: "教材版本",
|
||||
type: "text",
|
||||
form: { show: false },
|
||||
column: {
|
||||
//本字段禁止条件render,因为此字段没有值,是从父组件传过来显示的
|
||||
conditionalRender: false,
|
||||
cellRender() {
|
||||
return textbookRef.value.textbookVersion;
|
||||
}
|
||||
}
|
||||
},
|
||||
textbookName: {
|
||||
title: "教材名称",
|
||||
type: "text",
|
||||
form: { show: false },
|
||||
column: {
|
||||
//本字段禁止条件render,因为此字段没有值,是从父组件传过来显示的
|
||||
conditionalRender: false,
|
||||
cellRender() {
|
||||
return textbookRef.value.textbookName;
|
||||
}
|
||||
}
|
||||
},
|
||||
classTimeName: {
|
||||
title: "课时名称",
|
||||
type: "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<template>
|
||||
<a-drawer v-model:open="drawerOpened" width="1000px">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"></fs-crud>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
const textbookRef = ref();
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { textbookRef } });
|
||||
|
||||
//课时子表组件名称定义
|
||||
defineOptions({
|
||||
name: "AdvancedInDrawerClassTime"
|
||||
});
|
||||
const drawerOpened = ref(false);
|
||||
|
||||
//定义抽屉打开方法
|
||||
const open = async ({ textbook }) => {
|
||||
textbookRef.value = textbook;
|
||||
drawerOpened.value = true;
|
||||
await nextTick(); //等待crud初始化完成
|
||||
//设置查询条件,只查询当前选中的教材id
|
||||
crudExpose.setSearchFormData({ form: { textbookId: textbook.id } });
|
||||
//刷新课时表
|
||||
await crudExpose.doRefresh();
|
||||
};
|
||||
|
||||
//暴露出去,父组件通过ref可以调用open方法
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
@@ -1,61 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedInDrawerClassTime",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
textbookId: 1,
|
||||
classTimeName: "1",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 1,
|
||||
classTimeName: "2",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 1,
|
||||
classTimeName: "3",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 1,
|
||||
classTimeName: "4",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 1,
|
||||
classTimeName: "5",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 2,
|
||||
classTimeName: "1",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 2,
|
||||
classTimeName: "2",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 2,
|
||||
classTimeName: "3",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 2,
|
||||
classTimeName: "4",
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
textbookId: 2,
|
||||
classTimeName: "5",
|
||||
order: 1
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
<advanced-in-drawer-class-time ref="drawerClassTimeRef" />
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, onMounted, ref } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import AdvancedInDrawerClassTime from "./drawer-class-time/index.vue";
|
||||
//保留子组件的ref引用
|
||||
const drawerClassTimeRef = ref();
|
||||
//通过context传递到crud.tsx中
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { drawerClassTimeRef } });
|
||||
|
||||
defineOptions({
|
||||
name: "AdvancedInDrawer"
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
@@ -1,25 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedInDrawer",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
textbookCategory: "初中英语",
|
||||
textbookVersion: "初中人教版",
|
||||
textbookName: "初一上学期",
|
||||
totalWords: 200,
|
||||
classTimeNumber: 40
|
||||
},
|
||||
{
|
||||
textbookCategory: "初中英语",
|
||||
textbookVersion: "初中人教版",
|
||||
textbookName: "初一上学期",
|
||||
totalWords: 200,
|
||||
classTimeNumber: 40
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/FormLinkage";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, ScopeContext, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
align: "center"
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
province: {
|
||||
title: "省",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true,
|
||||
valueChange({ form, value, getComponentRef }: any) {
|
||||
form.city = undefined; // 将“city”的值置空
|
||||
form.county = undefined; // 将“county”的值置空
|
||||
if (value) {
|
||||
getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典
|
||||
}
|
||||
}
|
||||
},
|
||||
dict: dict({
|
||||
url: "/mock/linkage/province",
|
||||
value: "id",
|
||||
cache: true
|
||||
}),
|
||||
form: {
|
||||
valueChange({ form, value, getComponentRef }) {
|
||||
form.city = ""; // 将“city”的值置空
|
||||
form.county = ""; // 将“county”的值置空
|
||||
if (value) {
|
||||
getComponentRef("city").reloadDict(); // 执行city的select组件的reloadDict()方法,触发“city”重新加载字典
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
city: {
|
||||
title: "市",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
dict: dict({
|
||||
cache: true,
|
||||
prototype: true,
|
||||
// url() 改成构建url,返回一个url
|
||||
url({ form }: any) {
|
||||
if (form && form.province != null) {
|
||||
// 本数据字典的url是通过前一个select的选项决定的
|
||||
return `/mock/linkage/city?province=${form.province}`;
|
||||
}
|
||||
return undefined; // 返回undefined 将不加载字典
|
||||
},
|
||||
value: "id"
|
||||
}),
|
||||
form: {
|
||||
// 注释同上
|
||||
valueChange({ value, form, getComponentRef }: ScopeContext) {
|
||||
if (value) {
|
||||
form.county = ""; // 将county的value置空
|
||||
const countySelect = getComponentRef("county");
|
||||
if (form && form.province && form.city) {
|
||||
countySelect.reloadDict(); // 重新加载字典项
|
||||
} else {
|
||||
countySelect.clearDict(); // 清空选项
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
county: {
|
||||
title: "区",
|
||||
type: "dict-select",
|
||||
search: {
|
||||
show: true
|
||||
},
|
||||
dict: dict({
|
||||
value: "id",
|
||||
cache: true,
|
||||
prototype: true,
|
||||
url({ form }: any) {
|
||||
if (form && form.province != null && form.city != null) {
|
||||
return `/mock/linkage/county?province=${form.province} &city=${form.city}`;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud.js";
|
||||
|
||||
export default defineComponent({
|
||||
name: "FormLinkage",
|
||||
setup() {
|
||||
const customValue: any = {}; //自定义变量,传给createCrudOptions的额外参数(可以任意命名,任意多个)
|
||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions, context: customValue });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,151 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
import _ from "lodash-es";
|
||||
const options: any = {
|
||||
name: "FormLinkage",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
province: 10000,
|
||||
city: 100003,
|
||||
county: 100004
|
||||
},
|
||||
{
|
||||
province: 10010,
|
||||
city: 100113,
|
||||
county: 100115
|
||||
}
|
||||
];
|
||||
const tree = [
|
||||
{
|
||||
id: 10000,
|
||||
label: "北京市",
|
||||
children: [
|
||||
{
|
||||
id: 100003,
|
||||
label: "北京市辖区",
|
||||
children: [
|
||||
{ id: 100004, label: "东城区" },
|
||||
{ id: 100005, label: "西城区" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 100103,
|
||||
label: "北京郊区",
|
||||
children: [
|
||||
{ id: 100104, label: "东郊" },
|
||||
{ id: 100105, label: "西郊" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 10010,
|
||||
label: "天津市",
|
||||
children: [
|
||||
{
|
||||
id: 100013,
|
||||
label: "天津市辖区",
|
||||
children: [
|
||||
{ id: 100014, label: "天津湾" },
|
||||
{ id: 100015, label: "渤海湾" }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 100113,
|
||||
label: "天津市郊区",
|
||||
children: [
|
||||
{ id: 100114, label: "天津湾郊区" },
|
||||
{ id: 100115, label: "渤海湾郊区" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
options.list = list;
|
||||
options.copyTimes = 100;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
|
||||
function omitChildren(originalList: any) {
|
||||
const list: any = [];
|
||||
originalList.forEach((item: any) => {
|
||||
list.push(_.omit(item, "children"));
|
||||
});
|
||||
return list;
|
||||
}
|
||||
mock.push({
|
||||
path: "/mock/linkage/province",
|
||||
method: "get",
|
||||
handle() {
|
||||
const list = omitChildren(tree);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
mock.push({
|
||||
path: "/mock/linkage/city",
|
||||
method: "get",
|
||||
handle(req: any) {
|
||||
const province = parseInt(req.params.province);
|
||||
const a = tree.filter((item) => {
|
||||
return item.id === province;
|
||||
});
|
||||
if (a == null || a.length === 0) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: []
|
||||
};
|
||||
}
|
||||
const list = omitChildren(a[0].children);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
mock.push({
|
||||
path: "/mock/linkage/county",
|
||||
method: "get",
|
||||
handle(req: any) {
|
||||
const province = parseInt(req.params.province);
|
||||
const a = tree.filter((item) => {
|
||||
return item.id === province;
|
||||
});
|
||||
if (a == null || a.length === 0) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: []
|
||||
};
|
||||
}
|
||||
const city = parseInt(req.params.city);
|
||||
const b = a[0].children.filter((item) => {
|
||||
return item.id === city;
|
||||
});
|
||||
if (b == null || b.length === 0) {
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: []
|
||||
};
|
||||
}
|
||||
|
||||
const list = omitChildren(b[0].children);
|
||||
return {
|
||||
code: 0,
|
||||
msg: "success",
|
||||
data: list
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default mock;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedLocalPagination";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import * as api from "./api.js";
|
||||
import _ from "lodash-es";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const localDataRef = context.localDataRef;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
const { page } = query;
|
||||
//总数据
|
||||
let data = localDataRef.value;
|
||||
//获取请求参数
|
||||
const limit = page.limit;
|
||||
const offset = page.offset;
|
||||
data = data.filter((item: any) => {
|
||||
// 根据你的业务,编写你的本地查询逻辑
|
||||
// text改成你的查询字段
|
||||
if (query.status && item.status !== query.status) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// 本地分页
|
||||
const start = offset;
|
||||
let end = offset + limit;
|
||||
if (data.length < end) {
|
||||
end = data.length;
|
||||
}
|
||||
const records = data.slice(start, end);
|
||||
|
||||
// 构造返回结果
|
||||
return {
|
||||
offset,
|
||||
limit,
|
||||
total: localDataRef.value.length,
|
||||
records
|
||||
};
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
await api.UpdateObj(form);
|
||||
//更新本地数据
|
||||
const tableData = localDataRef.value;
|
||||
for (const item of tableData) {
|
||||
if (item.id === form.id) {
|
||||
_.merge(item, form);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
const { id } = await api.AddObj(form);
|
||||
//本地添加
|
||||
form.id = id;
|
||||
localDataRef.value.unshift(form);
|
||||
return form;
|
||||
};
|
||||
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
await api.DelObj(row.id);
|
||||
//本地删除那一条记录
|
||||
const tableData = localDataRef.value;
|
||||
let index = 0;
|
||||
for (const item of tableData) {
|
||||
if (item.id === row.id) {
|
||||
localDataRef.value.splice(index, 1);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
output: {},
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
status: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">本地分页</div>
|
||||
</template>
|
||||
<fs-crud v-if="crudBinding" ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="先从后台获取全部数据,然后本地分页展示" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import { useCrud, useExpose, useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { GetList } from "./api";
|
||||
|
||||
/**
|
||||
* 本示例演示如何本地分页
|
||||
* 主要就是将pageRequest修改为从本地获取数据就行了
|
||||
*/
|
||||
export default defineComponent({
|
||||
name: "AdvanceLocalPagination",
|
||||
setup() {
|
||||
// crud组件的ref
|
||||
const crudRef = ref();
|
||||
// crud 配置的ref
|
||||
const crudBinding = ref();
|
||||
|
||||
const localDataRef = ref();
|
||||
|
||||
onMounted(async () => {
|
||||
//先加载后台数据
|
||||
const ret = await GetList({ page: { offset: 0, limit: 99999999 }, query: {}, sort: {} });
|
||||
localDataRef.value = ret.records;
|
||||
|
||||
const { crudExpose } = useFs({ crudBinding, crudRef, createCrudOptions, context: { localDataRef } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,20 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedLocalPagination",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
status: "1"
|
||||
},
|
||||
{
|
||||
status: "2"
|
||||
},
|
||||
{
|
||||
status: "0"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,50 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedNest";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids: any[]) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedAside";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids: any[]) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
pagination: {
|
||||
showSizeChanger: false, // antdv
|
||||
showQuickJumper: false // antdv
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
toolbar: {
|
||||
compact: false
|
||||
},
|
||||
rowHandle: {
|
||||
width: "230px"
|
||||
},
|
||||
table: {},
|
||||
columns: {
|
||||
gradeId: {
|
||||
title: "年级Id",
|
||||
search: { show: true },
|
||||
type: "number",
|
||||
column: {
|
||||
width: 80,
|
||||
align: "center",
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
class: {
|
||||
title: "班级",
|
||||
search: { show: false },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="左侧表格点击行可以触发这里的查询" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud.js";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AsideTable",
|
||||
setup() {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
setSearchFormData: crudExpose.setSearchFormData,
|
||||
doRefresh: crudExpose.doRefresh
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,27 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedAside",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
class: "一班",
|
||||
gradeId: 1
|
||||
},
|
||||
{
|
||||
class: "二班",
|
||||
gradeId: 1
|
||||
},
|
||||
{
|
||||
class: "三班",
|
||||
gradeId: 2
|
||||
},
|
||||
{
|
||||
class: "四班",
|
||||
gradeId: 2
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,110 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { ref, shallowRef } from "vue";
|
||||
import SubTable from "./sub-table/index.vue";
|
||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context: { asideTableRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
const currentRow = ref();
|
||||
|
||||
const onCurrentRowChange = (id: number) => {
|
||||
currentRow.value = id;
|
||||
asideTableRef.value.crudBinding.search.initialForm = { gradeId: id };
|
||||
asideTableRef.value.setSearchFormData({ form: { gradeId: id } });
|
||||
asideTableRef.value.doRefresh();
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
table: {
|
||||
customRow(record: any, index: number) {
|
||||
const clazz = record.id === currentRow.value ? "fs-current-row" : "";
|
||||
return {
|
||||
onClick() {
|
||||
onCurrentRowChange(record.id);
|
||||
},
|
||||
class: clazz
|
||||
};
|
||||
}
|
||||
},
|
||||
pagination: {
|
||||
showSizeChanger: false, // antdv
|
||||
showQuickJumper: false // antdv
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
is: "a-drawer"
|
||||
}
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
rowHandle: {
|
||||
width: "240px"
|
||||
},
|
||||
toolbar: {
|
||||
compact: false
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
grade: {
|
||||
title: "年级",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
},
|
||||
nestId: {
|
||||
title: "嵌套表格",
|
||||
//复合字段类型
|
||||
type: ["number", "colspan"],
|
||||
form: {
|
||||
// 嵌套表格字段
|
||||
rules: [{ required: true, message: "请选择用户" }],
|
||||
component: {
|
||||
//局部引用子表格,要用shallowRef包裹
|
||||
name: shallowRef(SubTable),
|
||||
vModel: "modelValue",
|
||||
gradeId: compute(({ form }) => {
|
||||
return form.id;
|
||||
})
|
||||
}
|
||||
// antdv 的跨列配置,需要配置如下三个, 可以通过colspan简化
|
||||
// col: { span: 24 },
|
||||
// labelCol: { span: 2 },
|
||||
// wrapperCol: { span: 21 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<template>
|
||||
<a-row class="demo-nest" :gutter="0">
|
||||
<a-col :span="12">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<a-alert type="warning" class="ml-1" message="<--对话框内嵌套子表格" />
|
||||
</template>
|
||||
</fs-crud>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<aside-table ref="asideTableRef" />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from "vue";
|
||||
import createCrudOptions from "./crud.js";
|
||||
import AsideTable from "./aside-table/index.vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "FeatureNest",
|
||||
// eslint-disable-next-line vue/no-unused-components
|
||||
components: { AsideTable },
|
||||
setup() {
|
||||
const asideTableRef = ref();
|
||||
|
||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions, context: { asideTableRef } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
asideTableRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.demo-nest {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,23 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedNest",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
grade: "一年级",
|
||||
nestId: 1
|
||||
},
|
||||
{
|
||||
grade: "二年级",
|
||||
nestId: 2
|
||||
},
|
||||
{
|
||||
grade: "三年级",
|
||||
nestId: 3
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,50 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/AdvancedSubTable";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids: any[]) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose, context: { props, ctx } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
table: {
|
||||
customRow(record: any, index: number) {
|
||||
const clazz = record.id === props.modelValue ? "fs-current-row" : "";
|
||||
return {
|
||||
onClick() {
|
||||
ctx.emit("update:modelValue", record.id);
|
||||
},
|
||||
class: clazz
|
||||
};
|
||||
}
|
||||
},
|
||||
request: {
|
||||
pageRequest: api.GetList,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
search: { show: false },
|
||||
form: {
|
||||
wrapper: {
|
||||
is: "a-drawer"
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
name: {
|
||||
title: "用户姓名",
|
||||
search: { show: true },
|
||||
type: "text",
|
||||
column: {
|
||||
sortable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>年级id:{{ gradeId }},当前选中值:{{ modelValue }}</div>
|
||||
<div style="height: 400px">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, watch } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import {useFs, utils} from "@fast-crud/fast-crud";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SubTable",
|
||||
props: {
|
||||
modelValue: {},
|
||||
gradeId: {} //年级id,接收其他参数
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, ctx) {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { props, ctx } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
//你的业务代码
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue;
|
||||
},
|
||||
(value) => {
|
||||
utils.logger.info("modelValue changed", value);
|
||||
}
|
||||
);
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/deep/.fs-crud-container.compact .el-table--border {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "AdvancedSubTable",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
name: "张三"
|
||||
},
|
||||
{
|
||||
name: "李四"
|
||||
},
|
||||
{
|
||||
name: "王五"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,42 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisColumnMergePlugin";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import * as api from "./api";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
viewFormUseCellComponent: true
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
text: {
|
||||
title: "text",
|
||||
type: "text"
|
||||
},
|
||||
readonly: {
|
||||
title: "只读字段",
|
||||
type: "text",
|
||||
readonly: true
|
||||
},
|
||||
useCell: {
|
||||
title: "查看使用cell组件",
|
||||
type: "dict-select",
|
||||
readonly: true,
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum"
|
||||
}),
|
||||
viewForm: {
|
||||
component: {
|
||||
vModel: "modelValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">可以实现类似dict的公共属性</div>
|
||||
<div class="more"><a target="_blank" href="http://fast-crud.docmirror.cn/guide/advance/column-type.html#修改官方字段类型配置"> 字段合并插件帮助文档</a></div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding">
|
||||
<template #actionbar-right>
|
||||
<span class="fs-desc">此示例实现只需配置readonly: true,即可关闭添加和编辑时该字段的显示,更多说明请点击右上角帮助</span>
|
||||
</template>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useFs, UseFsProps } from "@fast-crud/fast-crud";
|
||||
export default defineComponent({
|
||||
name: "BasisColumnMergePlugin",
|
||||
setup() {
|
||||
const context: any = {}; //自定义变量,传给createCrudOptions的额外参数(可以任意命名,任意多个)
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,26 +0,0 @@
|
||||
// @ts-ignore
|
||||
import mockUtil from "/src/mock/base";
|
||||
const options: any = {
|
||||
name: "BasisColumnMergePlugin",
|
||||
idGenerator: 0
|
||||
};
|
||||
const list = [
|
||||
{
|
||||
text: "点击右边查看按钮看效果",
|
||||
readonly: "我是只读",
|
||||
useCell: "1"
|
||||
},
|
||||
{
|
||||
text: "点击编辑按钮查看效果",
|
||||
readonly: "我是只读",
|
||||
useCell: "2"
|
||||
},
|
||||
{
|
||||
text: "正常字段",
|
||||
readonly: "我是只读",
|
||||
useCell: "0"
|
||||
}
|
||||
];
|
||||
options.list = list;
|
||||
const mock = mockUtil.buildMock(options);
|
||||
export default mock;
|
||||
@@ -1,50 +0,0 @@
|
||||
import { requestForMock } from "/src/api/service";
|
||||
const request = requestForMock;
|
||||
const apiPrefix = "/mock/BasisColumnsSet";
|
||||
export function GetList(query: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "get",
|
||||
data: query
|
||||
});
|
||||
}
|
||||
|
||||
export function AddObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateObj(obj: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
|
||||
export function DelObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function GetObj(id: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/get",
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
}
|
||||
|
||||
export function BatchDelete(ids: any) {
|
||||
return request({
|
||||
url: apiPrefix + "/batchDelete",
|
||||
method: "post",
|
||||
data: { ids }
|
||||
});
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import * as api from "./api.js";
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { message } from "ant-design-vue";
|
||||
import { computed } from "vue";
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const { crudBinding } = crudExpose;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const editRequest = async ({ form, row }: EditReq) => {
|
||||
if (form.id == null) {
|
||||
form.id = row.id;
|
||||
}
|
||||
return await api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async ({ row }: DelReq) => {
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async ({ form }: AddReq) => {
|
||||
return await api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
toolbar: {
|
||||
columnsFilter: {
|
||||
mode: "default",
|
||||
container: {
|
||||
width: "500px"
|
||||
}
|
||||
}
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
toggleMode: {
|
||||
text: "切换简单模式",
|
||||
click() {
|
||||
crudBinding.value.toolbar.columnsFilter.mode = crudBinding.value.toolbar.columnsFilter.mode === "simple" ? "default" : "simple";
|
||||
message.info("当前列设置组件的模式为:" + crudBinding.value.toolbar.columnsFilter.mode);
|
||||
}
|
||||
},
|
||||
toggleColumnSetShow: {
|
||||
text: "切换列设置项显隐",
|
||||
click() {
|
||||
crudBinding.value.toolbar.columnsFilter.originalColumns.hidden.columnSetShow = !crudBinding.value.toolbar.columnsFilter.originalColumns.hidden.columnSetShow;
|
||||
message.info("切换第4列的列设置显隐");
|
||||
}
|
||||
},
|
||||
toggleColumnSetDisabled: {
|
||||
text: "切换列设置项禁用",
|
||||
click() {
|
||||
crudBinding.value.toolbar.columnsFilter.originalColumns.disabled.columnSetDisabled = !crudBinding.value.toolbar.columnsFilter.originalColumns.disabled.columnSetDisabled;
|
||||
message.info("切换第3列的列设置禁用启用");
|
||||
}
|
||||
},
|
||||
desc: {
|
||||
text: "点击左侧按钮后,再点最右侧的列设置按钮查看效果"
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
radio: {
|
||||
title: "状态",
|
||||
search: { show: true },
|
||||
type: "dict-radio",
|
||||
dict: dict({
|
||||
url: "/mock/dicts/OpenStatusEnum?single"
|
||||
}),
|
||||
column: {
|
||||
show: computed(() => {
|
||||
return true;
|
||||
})
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
title: "列设置禁用",
|
||||
type: "text",
|
||||
column: {
|
||||
columnSetDisabled: true
|
||||
}
|
||||
},
|
||||
hidden: {
|
||||
title: "列设置隐藏",
|
||||
type: "text",
|
||||
column: {
|
||||
columnSetShow: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user