mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-08-02 18:17:31 +08:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
ddf891e3bc | |||
7ae473d492 | |||
c20a6c369d | |||
57b71e1354 | |||
127f6e9f6c | |||
d1951dbcb5 | |||
5edcf63e24 | |||
b01b8b24ba | |||
d9c7085472 | |||
a01acddb9c | |||
e5208cd751 | |||
dc326c324c | |||
e0d15f8039 | |||
169ab08b88 | |||
3b8336105a | |||
7214a22ea5 | |||
3152a8d3e8 | |||
40e8eff5f3 | |||
47e67dd503 | |||
903ea1820a | |||
c08ea62064 | |||
4860299959 | |||
72f9d1a3ec | |||
98718a010c | |||
adfc8a7c74 | |||
427057b42d | |||
823efd4044 | |||
e43439a118 |
24
.github/workflows/nightly-build.yml
vendored
24
.github/workflows/nightly-build.yml
vendored
@ -18,8 +18,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
filter: tree:0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
# https://docs.github.com/zh/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
|
||||
@ -28,12 +27,9 @@ jobs:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('./src/frontend/admin/package.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm
|
||||
- name: Publish frontend
|
||||
working-directory: ./src/frontend/admin
|
||||
run:
|
||||
npm install && npm run build
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
- working-directory: ./src/frontend/admin
|
||||
run: npm install && npm run build
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
- uses: actions/cache@v3
|
||||
@ -41,15 +37,11 @@ jobs:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: ${{ runner.os }}-nuget
|
||||
- name: Publish backend
|
||||
working-directory: ./src/backend/NetAdmin.AdmServer.Host
|
||||
- working-directory: ./src/backend/NetAdmin.AdmServer.Host
|
||||
run: dotnet publish NetAdmin.AdmServer.Host.csproj -c Release
|
||||
- name: Build docker images
|
||||
run: docker build -t nsnail/netadmin .
|
||||
- name: Docker login
|
||||
uses: docker/login-action@v3
|
||||
- run: docker build -t nsnail/netadmin:nightly .
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
username: "nsnail"
|
||||
password: "${{secrets.DOCKER_PASSWORD}}"
|
||||
- name: Push docker images
|
||||
run: docker push nsnail/netadmin
|
||||
- run: docker push nsnail/netadmin:nightly
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -28,8 +28,7 @@ jobs:
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('./src/frontend/admin/package.json') }}
|
||||
restore-keys: ${{ runner.os }}-npm
|
||||
- working-directory: ./src/frontend/admin
|
||||
run:
|
||||
npm install && npm run build
|
||||
run: npm install && npm run build
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
@ -50,9 +49,10 @@ jobs:
|
||||
prerelease: false
|
||||
- id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/v}
|
||||
- run: docker build -t nsnail/netadmin:${{steps.get_version.outputs.VERSION}} .
|
||||
- run: docker build -t nsnail/netadmin -t nsnail/netadmin:${{steps.get_version.outputs.VERSION}} .
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
username: "nsnail"
|
||||
password: "${{secrets.DOCKER_PASSWORD}}"
|
||||
- run: docker push nsnail/netadmin
|
||||
- run: docker push nsnail/netadmin:${{steps.get_version.outputs.VERSION}}
|
55
CHANGELOG.md
55
CHANGELOG.md
@ -1,7 +1,58 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [1.2.0](https://github.com/nsnail/NetAdmin/compare/v1.1.1...v1.2.0) (2024-06-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 计划作业-上次执行耗时 ([#133](https://github.com/nsnail/NetAdmin/issues/133)) ([57b71e1](https://github.com/nsnail/NetAdmin/commit/57b71e1354ab8b0be995b5f563dd8c3fb7965d5f))
|
||||
* ✨ 框架代码同步 ([#129](https://github.com/nsnail/NetAdmin/issues/129)) ([b01b8b2](https://github.com/nsnail/NetAdmin/commit/b01b8b24ba574c08ba5605e103ff2ccf15e5830a))
|
||||
* ✨ 框架代码同步 ([#130](https://github.com/nsnail/NetAdmin/issues/130)) ([5edcf63](https://github.com/nsnail/NetAdmin/commit/5edcf63e24f6b13f5515e01ee8cf120b1a814d40))
|
||||
* ✨ 默认头像根据用户名绘制svg ([#132](https://github.com/nsnail/NetAdmin/issues/132)) ([127f6e9](https://github.com/nsnail/NetAdmin/commit/127f6e9f6c8c12974e5340e9697281250737bed3))
|
||||
* ✨ 手动执行计划作业 ([#122](https://github.com/nsnail/NetAdmin/issues/122)) ([3b83361](https://github.com/nsnail/NetAdmin/commit/3b8336105a908ba6bc300bec6ac4f49747ea66e9))
|
||||
* ✨ 增强作业执行记录页面 ([#135](https://github.com/nsnail/NetAdmin/issues/135)) ([7ae473d](https://github.com/nsnail/NetAdmin/commit/7ae473d492b9ba60cbb1c355894917d14f5ffa8f))
|
||||
* ✨ naColId组件 ([#118](https://github.com/nsnail/NetAdmin/issues/118)) ([47e67dd](https://github.com/nsnail/NetAdmin/commit/47e67dd503dd0ba6818e8b798e41c62420363f58))
|
||||
* **frontend:** ✨ 手机端分页控件显示总条数 ([#124](https://github.com/nsnail/NetAdmin/issues/124)) ([e0d15f8](https://github.com/nsnail/NetAdmin/commit/e0d15f8039a74a9826a0395983960ab620308899))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 字段顺序 ([#131](https://github.com/nsnail/NetAdmin/issues/131)) ([d1951db](https://github.com/nsnail/NetAdmin/commit/d1951dbcb5fa50a7ff308f6b6d554da5f791bcf2))
|
||||
* 🐛 字段长度 ([#134](https://github.com/nsnail/NetAdmin/issues/134)) ([c20a6c3](https://github.com/nsnail/NetAdmin/commit/c20a6c369d7b6d6dcfd07b3f3eaeab0fa309e766))
|
||||
* 🐛 take count ([c08ea62](https://github.com/nsnail/NetAdmin/commit/c08ea62064cc522d7cca9c90a5f15f23d833b6e3))
|
||||
* **backend:** 🐛 更新计划作业在sqlite数据库环境报错 ([#120](https://github.com/nsnail/NetAdmin/issues/120)) ([3152a8d](https://github.com/nsnail/NetAdmin/commit/3152a8d3e8054524470883c336fb6e93903a8426))
|
||||
|
||||
### [1.1.1](https://github.com/nsnail/NetAdmin/compare/v1.1.0...v1.1.1) (2024-04-29)
|
||||
|
||||
## [1.1.0](https://github.com/nsnail/NetAdmin/compare/v1.0.0...v1.1.0) (2024-04-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* ✨ 版本更新日志组件 ([#96](https://github.com/nsnail/NetAdmin/issues/96)) ([a37acc4](https://github.com/nsnail/NetAdmin/commit/a37acc4b55c91d57d51c7fa079da8700530412a5))
|
||||
* ✨ 计划作业 ([#87](https://github.com/nsnail/NetAdmin/issues/87)) ([8293ec0](https://github.com/nsnail/NetAdmin/commit/8293ec0297875ebc9ad75cce9465bd587929c0bf))
|
||||
* ✨ 计划作业执行记录 ([#89](https://github.com/nsnail/NetAdmin/issues/89)) ([6f89015](https://github.com/nsnail/NetAdmin/commit/6f890151989ad733e35653933b7597eec478cc3b))
|
||||
* ✨ 将数据库结构同步和种子数据初始化作为命令行开关 ([#78](https://github.com/nsnail/NetAdmin/issues/78)) ([05ed3d3](https://github.com/nsnail/NetAdmin/commit/05ed3d3746aa274a0f88f7afadfea12a3c8a80ff))
|
||||
* ✨ 快捷启用/禁用用户 ([#91](https://github.com/nsnail/NetAdmin/issues/91)) ([6c2d167](https://github.com/nsnail/NetAdmin/commit/6c2d1676e45b9f1ecf3be3ae5a172db49b62a81d))
|
||||
* ✨ 前端表格高级筛选 ([#100](https://github.com/nsnail/NetAdmin/issues/100)) ([3847d6f](https://github.com/nsnail/NetAdmin/commit/3847d6fdbbd27efb53921bcc8374157f0da47155))
|
||||
* ✨ 日志管理独立出来、增加登录日志界面 ([#65](https://github.com/nsnail/NetAdmin/issues/65)) ([9134c4f](https://github.com/nsnail/NetAdmin/commit/9134c4fe01165a87ebc7e2cbd0a2abff3c9fb3ea))
|
||||
* ✨ 首页仪表面板 ([#103](https://github.com/nsnail/NetAdmin/issues/103)) ([149e1af](https://github.com/nsnail/NetAdmin/commit/149e1afa533b142a3666a325ec84a091d53c1840))
|
||||
* ✨ cron表达式选择器 ([#92](https://github.com/nsnail/NetAdmin/issues/92)) ([bde9fb1](https://github.com/nsnail/NetAdmin/commit/bde9fb1ea264bd0b786ac68d590691892d7ce067))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 🐛 'Numbers' does not contain a definition for 'CACHE_SECS_DEFAULT' ([#102](https://github.com/nsnail/NetAdmin/issues/102)) ([8f69c29](https://github.com/nsnail/NetAdmin/commit/8f69c2907be282b1b39f4a179badb11502aa2403))
|
||||
* 🐛 低版本jetbrains.resharper.globaltools搞乱了代码 ([#97](https://github.com/nsnail/NetAdmin/issues/97)) ([c117ddf](https://github.com/nsnail/NetAdmin/commit/c117ddfe7a433215b3449cdd6b19318a1f3cbf37))
|
||||
* 🐛 前端样式问题 ([#84](https://github.com/nsnail/NetAdmin/issues/84)) ([6615df3](https://github.com/nsnail/NetAdmin/commit/6615df339934f6d19880c9822b44d5305c2f2a75))
|
||||
* 🐛 请求日志客户端IP显示不正确 ([#60](https://github.com/nsnail/NetAdmin/issues/60)) ([ec698ce](https://github.com/nsnail/NetAdmin/commit/ec698ce4db49861eaaeb8bf5080764939e6d7231))
|
||||
* 🐛 时区问题 ([#107](https://github.com/nsnail/NetAdmin/issues/107)) ([59c85ce](https://github.com/nsnail/NetAdmin/commit/59c85cef217c121b36d52993b6b5a774fe22df9e))
|
||||
* 🐛 小问题 ([#76](https://github.com/nsnail/NetAdmin/issues/76)) ([52ddf27](https://github.com/nsnail/NetAdmin/commit/52ddf273c856d8f7e363ce23e5886b9eedf4604f))
|
||||
* 🐛 在弹窗界面中引用的列表组件,点击重置搜索条件按钮时,会关闭弹窗的bug ([#95](https://github.com/nsnail/NetAdmin/issues/95)) ([8fee14c](https://github.com/nsnail/NetAdmin/commit/8fee14cd6ebd86456956fc59bbb61c545faa1fdd))
|
||||
* 🐛 tinymce editor css 加载路径错误 ([#93](https://github.com/nsnail/NetAdmin/issues/93)) ([5fe7387](https://github.com/nsnail/NetAdmin/commit/5fe73878a2a53dc5e7e2dcbcbf22f91ffb4376dd))
|
||||
* 🐛 tinymce editor css 加载路径错误 ([#94](https://github.com/nsnail/NetAdmin/issues/94)) ([802251e](https://github.com/nsnail/NetAdmin/commit/802251e42347bfe4fa0bcb4867b615d7c03abf19))
|
||||
|
||||
## 1.0.0 (2023-11-17)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0-preview.3 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.0-preview.4 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
RUN apt update
|
||||
|
79
README.md
79
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
通用后台权限管理系统、快速开发框架(基于C#12/.NET9、Vue3/Vite、Element Plus等现代技术构建,具有十分整洁、优雅的编码规范)
|
||||
|
||||
[](https://github.com/nsnail/NetAdmin/actions/workflows/ci.yml)
|
||||
[](https://github.com/nsnail/NetAdmin/actions/workflows/nightly-build.yml)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
[](https://github.com/nsnail/NetAdmin/blob/main/LICENSE)
|
||||
@ -19,64 +19,47 @@ docker run -p 8080:8080 nsnail/netadmin
|
||||
|
||||
## 构建步骤
|
||||
|
||||
- 后端
|
||||
1. 检查dotnet-sdk版本>=9.0.0
|
||||
``` shell
|
||||
dotnet --list-sdks
|
||||
```shell
|
||||
# 1. 克隆代码仓库
|
||||
# 下载 git https://git-scm.com/downloads
|
||||
git clone https://github.com/nsnail/NetAdmin.git && cd ./NetAdmin
|
||||
|
||||
# 下载 dotnet https://dotnet.microsoft.com/zh-cn/download/dotnet
|
||||
```
|
||||
2. 克隆代码仓库
|
||||
``` shell
|
||||
git clone https://github.com/nsnail/NetAdmin.git
|
||||
cd ./NetAdmin
|
||||
# 2. 检查dotnet-sdk版本>=9.0.0
|
||||
# 下载 dotnet https://dotnet.microsoft.com/zh-cn/download/dotnet
|
||||
dotnet --list-sdks
|
||||
|
||||
# 下载 git https://git-scm.com/downloads
|
||||
```
|
||||
3. 确保本机redis处于运行状态
|
||||
``` shell
|
||||
redis-cli
|
||||
# 3. 确保本机redis处于运行状态
|
||||
# 下载 redis for windows https://github.com/redis-windows/redis-windows/releases
|
||||
# 下载 redis for linux/mac https://redis.io/download
|
||||
redis-cli
|
||||
|
||||
# 下载 redis for windows https://github.com/redis-windows/redis-windows/releases
|
||||
# 下载 redis for linux/mac https://redis.io/download
|
||||
```
|
||||
4. 运行后端WebApi
|
||||
``` shell
|
||||
dotnet run --project ./src/backend/NetAdmin.AdmServer.Host/NetAdmin.AdmServer.Host.csproj --urls http://[::]:5010 -is
|
||||
```
|
||||
5. 体验WebApi程序
|
||||
- 浏览器打开 http://localhost:5010 ,将看到Swagger(Knife4jUI)界面
|
||||
# 4. 运行后端WebApi
|
||||
# 浏览器打开 http://localhost:5010 ,将看到Swagger(Knife4jUI)界面
|
||||
dotnet run --project ./src/backend/NetAdmin.AdmServer.Host/NetAdmin.AdmServer.Host.csproj --urls http://[::]:5010 -is
|
||||
|
||||
---
|
||||
# 5. 检查nodejs版本>=20
|
||||
# 下载 nodejs https://nodejs.org/en/download
|
||||
node -v
|
||||
|
||||
- 前端
|
||||
1. 检查nodejs版本>=20
|
||||
``` shell
|
||||
node -v
|
||||
# 6. 安装npm依赖包
|
||||
cd ./src/frontend/admin && npm install
|
||||
|
||||
# 下载 nodejs https://nodejs.org/en/download
|
||||
```
|
||||
2. 安装npm依赖包
|
||||
``` shell
|
||||
cd ./src/frontend/admin
|
||||
npm install
|
||||
```
|
||||
3. 运行前端项目
|
||||
``` shell
|
||||
npm run dev
|
||||
```
|
||||
4. 体验前端程序
|
||||
- 浏览器打开 http://localhost:5020 ,将看到管理界面(默认用户名:root,密码:1234qwer)
|
||||
# 7. 运行前端项目
|
||||
# 浏览器打开 http://localhost:5020 ,将看到管理界面(默认用户名:root,密码:1234qwer)
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 文件目录树
|
||||
|
||||
```
|
||||
+---.template.config # dotnet 项目模板配置目录
|
||||
+---assets # 程序运行需要的资源文件目录
|
||||
+---dist # 程序编译与分发的二进制文件目录
|
||||
+---docs # 项目文档目录
|
||||
+---refs # 引用的第三方项目仓库目录
|
||||
+---src # 项目源文件目录
|
||||
+---assets # 项目资源文件目录
|
||||
+---build # 构建相关的工程文件目录
|
||||
+---dist # 编译生成的二进制文件目录
|
||||
+---docs # 项目开发文档目录
|
||||
+---refs # 引用的第三方包的仓库目录
|
||||
+---scripts # 各种工具脚本文件目录
|
||||
+---src # 项目源码文件目录
|
||||
```
|
||||
|
||||
## 后端项目架构
|
||||
|
@ -70,13 +70,13 @@
|
||||
等于
|
||||
等待发送
|
||||
系统模块
|
||||
绑定手机号
|
||||
绑定手机号码
|
||||
结果非预期
|
||||
群众
|
||||
自定义
|
||||
范围
|
||||
菜单
|
||||
解绑手机号
|
||||
解绑手机号码
|
||||
警告
|
||||
调试
|
||||
跟踪
|
||||
|
@ -23,6 +23,7 @@ XML注释文件不存在
|
||||
学历不正确
|
||||
密码不能为空
|
||||
已处理完毕
|
||||
并发冲突请稍后重试
|
||||
开始事务
|
||||
性别不正确
|
||||
手机号码不正确
|
||||
@ -57,7 +58,7 @@ XML注释文件不存在
|
||||
父节点不存在
|
||||
用户不存在
|
||||
用户名不能为空
|
||||
用户名不能是手机号
|
||||
用户名不能是手机号码
|
||||
用户名或密码错误
|
||||
用户名长度4位以上
|
||||
用户头像不能为空
|
||||
|
@ -6,7 +6,7 @@
|
||||
"Id": 373837717815301,
|
||||
"Name": "home",
|
||||
"Path": "/home",
|
||||
"Sort": 100,
|
||||
"Sort": 999,
|
||||
"Title": "主控面板",
|
||||
"Type": 1
|
||||
},
|
||||
|
@ -14,5 +14,25 @@
|
||||
{
|
||||
"ApiId": "api/sys/user",
|
||||
"RoleId": 371729946431493
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/site.msg/unread.count",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/site.msg",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/site.msg/get.mine",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/site.msg/paged.query.mine",
|
||||
"RoleId": 371729946431493,
|
||||
},
|
||||
{
|
||||
"ApiId": "api/sys/site.msg/set.site.msg.status",
|
||||
"RoleId": 371729946431493,
|
||||
}
|
||||
]
|
@ -15,15 +15,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.12-preview">
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.12.2">
|
||||
<PackageReference Include="Roslynator.Analyzers" Version="4.12.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.24.0.89429">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.26.0.92422">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"version": "1.2.0",
|
||||
"devDependencies": {
|
||||
"cz-git": "^1.9.1",
|
||||
"cz-git": "^1.9.2",
|
||||
"commitizen": "^4.3.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier": "^3.3.0",
|
||||
"standard-version": "^9.5.0"
|
||||
},
|
||||
"config": {
|
||||
@ -11,4 +11,4 @@
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
"packages": [
|
||||
{
|
||||
"packageName": "Furion.Pure.NS",
|
||||
"version": "4.9.2.31-ns3"
|
||||
"version": "4.9.3-ns1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -4,6 +4,6 @@
|
||||
<ProjectReference Include="../NetAdmin.AdmServer.Host/NetAdmin.AdmServer.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -0,0 +1,29 @@
|
||||
using NetAdmin.Application.Repositories;
|
||||
using NetAdmin.Domain.DbMaps.Dependency;
|
||||
using RedLockNet;
|
||||
|
||||
namespace NetAdmin.Application.Services;
|
||||
|
||||
/// <summary>
|
||||
/// RedLocker Service Base
|
||||
/// </summary>
|
||||
public abstract class RedLockerService<T1, T2>(DefaultRepository<T1> rpo, RedLocker redLocker)
|
||||
: RepositoryService<T1, T2>(rpo)
|
||||
where T1 : EntityBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取锁
|
||||
/// </summary>
|
||||
/// <exception cref="NetAdminGetLockerException">NetAdminGetLockerException</exception>
|
||||
protected async Task<IRedLock> GetLockerAsync(string lockName)
|
||||
{
|
||||
// 加锁
|
||||
var redLock = await redLocker.RedLockFactory.CreateLockAsync( //
|
||||
lockName, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_EXPIRY)
|
||||
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_WAIT)
|
||||
, TimeSpan.FromSeconds(Numbers.SECS_RED_LOCK_RETRY))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return redLock.IsAcquired ? redLock : throw new NetAdminGetLockerException();
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
namespace NetAdmin.Domain.Attributes.DataValidation;
|
||||
|
||||
/// <summary>
|
||||
/// 手机号验证器
|
||||
/// 手机号码验证器
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
|
||||
public sealed class MobileAttribute : RegexAttribute
|
||||
|
@ -10,7 +10,7 @@ public sealed class PortAttribute : RangeAttribute
|
||||
/// Initializes a new instance of the <see cref="PortAttribute" /> class.
|
||||
/// </summary>
|
||||
public PortAttribute() //
|
||||
: base(1, 65535)
|
||||
: base(1, ushort.MaxValue)
|
||||
{
|
||||
ErrorMessageResourceName = nameof(Ln.无效端口号);
|
||||
ErrorMessageResourceType = typeof(Ln);
|
||||
|
@ -27,8 +27,8 @@ public sealed class UserNameAttribute : RegexAttribute
|
||||
return true;
|
||||
}
|
||||
|
||||
// 不能是手机号
|
||||
ErrorMessageResourceName = nameof(Ln.用户名不能是手机号);
|
||||
// 不能是手机号码
|
||||
ErrorMessageResourceName = nameof(Ln.用户名不能是手机号码);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -61,7 +61,11 @@ public record Sys_Dept : VersionEntity, IFieldEnabled, IFieldSummary, IFieldSort
|
||||
/// <summary>
|
||||
/// 部门描述
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
}
|
@ -37,6 +37,13 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
|
||||
[JsonIgnore]
|
||||
public virtual string JobName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次执行耗时
|
||||
/// </summary>
|
||||
[Column]
|
||||
[JsonIgnore]
|
||||
public virtual long? LastDuration { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 上次执行时间
|
||||
/// </summary>
|
||||
@ -68,21 +75,33 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
|
||||
/// <summary>
|
||||
/// 请求体
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestBody { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求头
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestHeader { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求的网络地址
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestUrl { get; init; }
|
||||
|
||||
@ -94,7 +113,11 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
|
||||
public virtual JobStatues Status { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldSummary.Summary" />
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
|
||||
|
@ -48,14 +48,22 @@ public record Sys_JobRecord : LiteImmutableEntity
|
||||
/// <summary>
|
||||
/// 请求体
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestBody { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求头
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestHeader { get; init; }
|
||||
|
||||
@ -69,14 +77,22 @@ public record Sys_JobRecord : LiteImmutableEntity
|
||||
/// <summary>
|
||||
/// 响应体
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ResponseBody { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 响应头
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ResponseHeader { get; init; }
|
||||
|
||||
|
@ -35,14 +35,22 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 创建者来源地址
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public string CreatedReferer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建者客户端用户代理
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(Position = -1, DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_1022)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string CreatedUserAgent { get; init; }
|
||||
|
||||
@ -63,14 +71,22 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 异常信息
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Exception { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 附加数据
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ExtraData { get; init; }
|
||||
|
||||
@ -91,14 +107,22 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 来源地址
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ReferUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 请求内容
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestBody { get; init; }
|
||||
|
||||
@ -112,7 +136,11 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 请求头信息
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string RequestHeaders { get; init; }
|
||||
|
||||
@ -126,7 +154,11 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 响应内容
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ResponseBody { get; init; }
|
||||
|
||||
@ -140,7 +172,11 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
|
||||
/// <summary>
|
||||
/// 响应头
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string ResponseHeaders { get; init; }
|
||||
|
||||
|
@ -85,7 +85,11 @@ public record Sys_Role : VersionEntity, IFieldSort, IFieldEnabled, IFieldSummary
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
|
||||
|
@ -14,7 +14,11 @@ public record Sys_SiteMsg : VersionEntity, IRegister, IFieldSummary
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Content { get; init; }
|
||||
|
||||
@ -55,14 +59,22 @@ public record Sys_SiteMsg : VersionEntity, IRegister, IFieldSummary
|
||||
/// <summary>
|
||||
/// 消息摘要
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息主题
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Title { get; init; }
|
||||
|
||||
|
@ -49,7 +49,7 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
|
||||
public virtual bool Enabled { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 手机号
|
||||
/// 手机号码
|
||||
/// </summary>
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
|
||||
[JsonIgnore]
|
||||
@ -85,7 +85,11 @@ public record Sys_User : VersionEntity, IFieldSummary, IFieldEnabled, IRegister
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public virtual string Summary { get; init; }
|
||||
|
||||
|
@ -80,7 +80,7 @@ public record Sys_UserProfile : VersionEntity, IRegister
|
||||
public int? EmergencyContactArea { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 紧急联系人手机号
|
||||
/// 紧急联系人手机号码
|
||||
/// </summary>
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_15)]
|
||||
[JsonIgnore]
|
||||
|
@ -33,7 +33,11 @@ public record Sys_VerifyCode : VersionEntity
|
||||
/// <summary>
|
||||
/// 发送报告
|
||||
/// </summary>
|
||||
#if DBTYPE_SQLITE
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
|
||||
#else
|
||||
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_MAX)]
|
||||
#endif
|
||||
[JsonIgnore]
|
||||
public string Report { get; init; }
|
||||
|
||||
|
@ -43,6 +43,10 @@ public sealed record QueryJobRsp : Sys_Job
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string JobName { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_Job.LastDuration" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override long? LastDuration { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_Job.LastExecTime" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override DateTime? LastExecTime { get; init; }
|
||||
|
@ -9,6 +9,10 @@ namespace NetAdmin.Domain.Dto.Sys.JobRecord;
|
||||
/// </summary>
|
||||
public sealed record QueryJobRecordRsp : Sys_JobRecord
|
||||
{
|
||||
/// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public new HttpStatusCode HttpStatusCode => (HttpStatusCode)base.HttpStatusCode;
|
||||
|
||||
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override DateTime CreatedTime { get; init; }
|
||||
@ -21,10 +25,6 @@ public sealed record QueryJobRecordRsp : Sys_JobRecord
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override HttpMethods HttpMethod { get; init; }
|
||||
|
||||
/// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override int HttpStatusCode { get; init; }
|
||||
|
||||
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
|
@ -4,7 +4,7 @@ using NetAdmin.Domain.DbMaps.Sys;
|
||||
namespace NetAdmin.Domain.Dto.Sys.User;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:检查手机号是否可用
|
||||
/// 请求:检查手机号码是否可用
|
||||
/// </summary>
|
||||
public sealed record CheckMobileAvailableReq : Sys_User
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
|
||||
public sealed record LoginByPwdReq : DataAbstraction
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户名、手机号、邮箱
|
||||
/// 用户名、手机号码、邮箱
|
||||
/// </summary>
|
||||
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.账号不能为空))]
|
||||
public string Account { get; init; }
|
||||
|
@ -21,7 +21,7 @@ public sealed record LoginRsp : DataAbstraction
|
||||
public void SetToRspHeader()
|
||||
{
|
||||
// 设置响应报文头
|
||||
App.HttpContext.Response.Headers[Chars.FLG_HTTP_HEADER_VALUE_ACCESS_TOKEN] = AccessToken;
|
||||
App.HttpContext.Response.Headers[Chars.FLG_HTTP_HEADER_KEY_ACCESS_TOKEN] = AccessToken;
|
||||
App.HttpContext.Response.Headers[Chars.FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN] = RefreshToken;
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using NetAdmin.Domain.Dto.Sys.VerifyCode;
|
||||
namespace NetAdmin.Domain.Dto.Sys.User;
|
||||
|
||||
/// <summary>
|
||||
/// 请求:设置手机号
|
||||
/// 请求:设置手机号码
|
||||
/// </summary>
|
||||
public sealed record SetMobileReq : DataAbstraction
|
||||
{
|
||||
|
@ -7,9 +7,9 @@ namespace NetAdmin.Domain.Enums.Sys;
|
||||
public enum VerifyCodeTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// 绑定手机号
|
||||
/// 绑定手机号码
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.绑定手机号))]
|
||||
[ResourceDescription<Ln>(nameof(Ln.绑定手机号码))]
|
||||
LinkMobile = 1
|
||||
|
||||
,
|
||||
@ -23,9 +23,9 @@ public enum VerifyCodeTypes
|
||||
,
|
||||
|
||||
/// <summary>
|
||||
/// 解绑手机号
|
||||
/// 解绑手机号码
|
||||
/// </summary>
|
||||
[ResourceDescription<Ln>(nameof(Ln.解绑手机号))]
|
||||
[ResourceDescription<Ln>(nameof(Ln.解绑手机号码))]
|
||||
UnlinkMobile = 3
|
||||
|
||||
,
|
||||
|
@ -1,4 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<DefineConstants>DBTYPE_SQLITE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)/build/code.quality.props"/>
|
||||
<ItemGroup>
|
||||
<Content Include="$(SolutionDir)/assets/seed-data/**" LinkBase="SeedData" CopyToOutputDirectory="PreserveNewest"/>
|
||||
|
@ -1,8 +1,16 @@
|
||||
using Furion.FriendlyException;
|
||||
using NetAdmin.Domain.Dto;
|
||||
|
||||
namespace NetAdmin.Host.Filters;
|
||||
|
||||
/// <inheritdoc cref="NetAdmin.Host.Filters.ApiResultHandler{T}" />
|
||||
/// <inheritdoc cref="ApiResultHandler{T}" />
|
||||
[SuppressSniffer]
|
||||
[UnifyModel(typeof(RestfulInfo<>))]
|
||||
public sealed class DefaultApiResultHandler : ApiResultHandler<RestfulInfo<object>>, IUnifyResultProvider;
|
||||
public sealed class DefaultApiResultHandler : ApiResultHandler<RestfulInfo<object>>, IUnifyResultProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -24,7 +24,12 @@ public sealed class RequestAuditMiddleware(
|
||||
// 跳过处理的情况:
|
||||
if (!context.Request.Path.StartsWithSegments(_defaultRoutePrefix) // 非api请求
|
||||
|| context.Request.Path.StartsWithSegments(_healthCheckRoutePrefix) // 健康检查
|
||||
|| context.Request.Method == Chars.FLG_HTTP_METHOD_OPTIONS) { // is options 请求
|
||||
|| context.Request.Method == Chars.FLG_HTTP_METHOD_OPTIONS // is options 请求
|
||||
|| (context.Request.ContentType?.StartsWith("multipart/form-data", true, CultureInfo.InvariantCulture) ??
|
||||
false) // 文件上传
|
||||
#pragma warning disable SA1009
|
||||
) {
|
||||
#pragma warning restore SA1009
|
||||
await next(context).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ public static class Chars
|
||||
public const string FLG_CONTEXT_OWNER_DEPT_ID = nameof(FLG_CONTEXT_OWNER_DEPT_ID);
|
||||
public const string FLG_CONTEXT_USER_ID = nameof(FLG_CONTEXT_USER_ID);
|
||||
public const string FLG_CONTEXT_USER_INFO = nameof(FLG_CONTEXT_USER_INFO);
|
||||
public const string FLG_CRON_PER_SECS = "* * * * * *";
|
||||
public const string FLG_DB_EXCEPTION_PRIVATE_KEY_CONFLICT = "PRIMARY KEY";
|
||||
public const string FLG_DB_FIELD_TYPE_NVARCHAR = "nvarchar";
|
||||
public const string FLG_DB_FIELD_TYPE_NVARCHAR_1022 = "nvarchar(1022)";
|
||||
@ -46,6 +47,7 @@ public static class Chars
|
||||
public const string FLG_FREE_SQL_GLOBAL_FILTER_MEMBER = nameof(FLG_FREE_SQL_GLOBAL_FILTER_MEMBER);
|
||||
public const string FLG_FREE_SQL_GLOBAL_FILTER_SELF = nameof(FLG_FREE_SQL_GLOBAL_FILTER_SELF);
|
||||
public const string FLG_FREE_SQL_GLOBAL_FILTER_TENANT = nameof(FLG_FREE_SQL_GLOBAL_FILTER_TENANT);
|
||||
public const string FLG_HTTP_HEADER_KEY_ACCESS_TOKEN = "ACCESS-TOKEN";
|
||||
public const string FLG_HTTP_HEADER_KEY_AUTHORIZATION = "Authorization";
|
||||
public const string FLG_HTTP_HEADER_KEY_REFERER = "Referer";
|
||||
public const string FLG_HTTP_HEADER_KEY_USER_AGENT = "User-Agent";
|
||||
@ -53,7 +55,6 @@ public static class Chars
|
||||
public const string FLG_HTTP_HEADER_KEY_X_ACCESS_TOKEN_HEADER_KEY = "X-Authorization";
|
||||
public const string FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR = "X-Forwarded-For";
|
||||
public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP";
|
||||
public const string FLG_HTTP_HEADER_VALUE_ACCESS_TOKEN = "ACCESS-TOKEN";
|
||||
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON = "application/json";
|
||||
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_URLENCODED = "application/x-www-form-urlencoded";
|
||||
public const string FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA = "Bearer";
|
||||
|
@ -8,11 +8,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cronos" Version="0.8.4"/>
|
||||
<PackageReference Include="FreeSql.DbContext.NS" Version="3.2.821-ns1"/>
|
||||
<PackageReference Include="FreeSql.Provider.SqlServer.NS" Version="3.2.821-ns1"/>
|
||||
<PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.821-ns1"/>
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.2.31"/>
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.2.31-ns1"/>
|
||||
<PackageReference Include="Furion.Pure.NS" Version="4.9.2.31-ns1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.3.24172.13"/>
|
||||
<PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.3"/>
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.3-ns1"/>
|
||||
<PackageReference Include="Furion.Pure.NS" Version="4.9.3-ns1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.0-preview.4.24267.6"/>
|
||||
<PackageReference Include="Minio" Version="6.0.2"/>
|
||||
<PackageReference Include="NSExt" Version="2.1.0"/>
|
||||
<PackageReference Include="RedLock.net" Version="2.3.2"/>
|
||||
|
@ -20,6 +20,11 @@ public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类
|
||||
/// </summary>
|
||||
Task<QueryJobRsp> EditAsync(UpdateJobReq req);
|
||||
|
||||
/// <summary>
|
||||
/// 执行作业
|
||||
/// </summary>
|
||||
Task ExecuteAsync(QueryJobReq req);
|
||||
|
||||
/// <summary>
|
||||
/// 获取作业记录条形图数据
|
||||
/// </summary>
|
||||
|
@ -15,7 +15,7 @@ public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建
|
||||
>
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查手机号是否可用
|
||||
/// 检查手机号码是否可用
|
||||
/// </summary>
|
||||
Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req);
|
||||
|
||||
@ -65,7 +65,7 @@ public interface IUserModule : ICrudModule<CreateUserReq, QueryUserRsp // 创建
|
||||
Task SetEnabledAsync(SetUserEnabledReq req);
|
||||
|
||||
/// <summary>
|
||||
/// 设置手机号
|
||||
/// 设置手机号码
|
||||
/// </summary>
|
||||
Task<UserInfoRsp> SetMobileAsync(SetMobileReq req);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Cronos;
|
||||
using FreeSql.Internal;
|
||||
using NetAdmin.Application.Repositories;
|
||||
using NetAdmin.Application.Services;
|
||||
using NetAdmin.Domain.DbMaps.Sys;
|
||||
@ -63,18 +64,60 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
|
||||
public async Task<QueryJobRsp> EditAsync(UpdateJobReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await Rpo.UpdateDiy.Set(a => a.ExecutionCron == req.ExecutionCron)
|
||||
.Set(a => a.HttpMethod == req.HttpMethod)
|
||||
.Set(a => a.JobName == req.JobName)
|
||||
.SetIf(req.RequestHeaders == null, a => a.RequestHeader, null)
|
||||
.SetIf(req.RequestHeaders != null, a => a.RequestHeader, req.RequestHeaders.Json())
|
||||
.Set(a => a.RequestBody == req.RequestBody)
|
||||
.Set(a => a.RequestUrl == req.RequestUrl)
|
||||
.Set(a => a.UserId == req.UserId)
|
||||
.Where(a => a.Id == req.Id)
|
||||
.ExecuteUpdatedAsync()
|
||||
.ConfigureAwait(false);
|
||||
return ret[0].Adapt<QueryJobRsp>();
|
||||
var update = Rpo.UpdateDiy.Set(a => a.ExecutionCron == req.ExecutionCron)
|
||||
.Set(a => a.HttpMethod == req.HttpMethod)
|
||||
.Set(a => a.JobName == req.JobName)
|
||||
.SetIf(req.RequestHeaders == null, a => a.RequestHeader, null)
|
||||
.SetIf(req.RequestHeaders != null, a => a.RequestHeader, req.RequestHeaders.Json())
|
||||
.Set(a => a.RequestBody == req.RequestBody)
|
||||
.Set(a => a.RequestUrl == req.RequestUrl)
|
||||
.Set(a => a.UserId == req.UserId)
|
||||
.Where(a => a.Id == req.Id);
|
||||
|
||||
#pragma warning disable IDE0046
|
||||
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
|
||||
#pragma warning restore IDE0046
|
||||
return await update.ExecuteAffrowsAsync().ConfigureAwait(false) <= 0
|
||||
? null
|
||||
: await GetAsync(new QueryJobReq { Id = req.Id }).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return (await update.ExecuteUpdatedAsync().ConfigureAwait(false))[0].Adapt<QueryJobRsp>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ExecuteAsync(QueryJobReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var df = new DynamicFilterInfo {
|
||||
Filters = [
|
||||
new DynamicFilterInfo {
|
||||
Field = nameof(QueryJobReq.Enabled)
|
||||
, Operator = DynamicFilterOperators.Eq
|
||||
, Value = true
|
||||
}
|
||||
, new DynamicFilterInfo {
|
||||
Field = nameof(QueryJobReq.Status)
|
||||
, Operator = DynamicFilterOperators.Eq
|
||||
, Value = JobStatues.Idle
|
||||
}
|
||||
]
|
||||
};
|
||||
var job = await QueryInternal(new QueryReq<QueryJobReq> { Count = 1, Filter = req, DynamicFilter = df })
|
||||
.ToOneAsync()
|
||||
.ConfigureAwait(false) ?? throw new NetAdminInvalidOperationException(Ln.未获取到待执行任务);
|
||||
|
||||
var nextExecTime = GetNextExecTime(Chars.FLG_CRON_PER_SECS);
|
||||
try {
|
||||
_ = await UpdateAsync(job.Adapt<UpdateJobReq>() with {
|
||||
NextExecTime = nextExecTime
|
||||
, NextTimeId = nextExecTime?.TimeUnixUtc()
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch (DbUpdateVersionException) {
|
||||
throw new NetAdminInvalidOperationException(Ln.并发冲突请稍后重试);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -127,14 +170,14 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService
|
||||
}
|
||||
]
|
||||
};
|
||||
var job
|
||||
= await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Count = 1, Order = Orders.Random })
|
||||
.Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
|
||||
.As("b")
|
||||
.Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
|
||||
.Any())
|
||||
.ToOneAsync()
|
||||
.ConfigureAwait(false);
|
||||
var job = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Order = Orders.Random })
|
||||
.Take(1)
|
||||
.Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
|
||||
.As("b")
|
||||
.Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
|
||||
.Any())
|
||||
.ToOneAsync()
|
||||
.ConfigureAwait(false);
|
||||
return job == null
|
||||
? null
|
||||
: await UpdateAsync(job.Adapt<UpdateJobReq>() with {
|
||||
|
@ -131,11 +131,15 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
|
||||
private ISelect<Sys_Menu> QueryInternal(QueryReq<QueryMenuReq> req)
|
||||
{
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
|
||||
return req.Order == Orders.Random
|
||||
? ret.OrderByRandom()
|
||||
: ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
|
||||
.OrderByDescending(a => a.Sort)
|
||||
.OrderBy(a => a.Name)
|
||||
.OrderBy(a => a.Id);
|
||||
#pragma warning disable IDE0072
|
||||
return req.Order switch {
|
||||
Orders.None => ret
|
||||
, Orders.Random => ret.OrderByRandom()
|
||||
, _ => ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
|
||||
.OrderByDescending(a => a.Sort)
|
||||
.OrderBy(a => a.Name)
|
||||
.OrderBy(a => a.Id)
|
||||
};
|
||||
#pragma warning restore IDE0072
|
||||
}
|
||||
}
|
@ -124,8 +124,11 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
|
||||
req.Keywords?.Length > 0
|
||||
, a => a.Id == req.Keywords.Int64Try(0) || a.Name.Contains(req.Keywords) ||
|
||||
a.Summary.Contains(req.Keywords));
|
||||
if (req.Order == Orders.Random) {
|
||||
return ret.OrderByRandom();
|
||||
switch (req.Order) {
|
||||
case Orders.None:
|
||||
return ret;
|
||||
case Orders.Random:
|
||||
return ret.OrderByRandom();
|
||||
}
|
||||
|
||||
ret = ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
|
||||
|
@ -152,7 +152,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
|
||||
private ISelect<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent> QueryInternal(
|
||||
QueryReq<QueryUserProfileReq> req)
|
||||
{
|
||||
#pragma warning disable CA1305
|
||||
#pragma warning disable CA1305,IDE0072
|
||||
var ret = Rpo.Orm.Select<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent>()
|
||||
.LeftJoin((a, b, _, __, ___) =>
|
||||
a.NationArea.ToString() == b.Value && b.CatalogId == Numbers.ID_DIC_CATALOG_GEO_AREA)
|
||||
@ -164,10 +164,13 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
|
||||
.LeftJoin((a, _, __, ___, e) => a.EmergencyContactArea.ToString() == e.Value &&
|
||||
e.CatalogId == Numbers.ID_DIC_CATALOG_GEO_AREA)
|
||||
.WhereDynamicFilter(req.DynamicFilter);
|
||||
return req.Order == Orders.Random
|
||||
? ret.OrderByRandom()
|
||||
: ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
|
||||
.OrderByDescending((a, _, __, ___, ____) => a.Id);
|
||||
#pragma warning restore CA1305
|
||||
|
||||
return req.Order switch {
|
||||
Orders.None => ret
|
||||
, Orders.Random => ret.OrderByRandom()
|
||||
, _ => ret.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending)
|
||||
.OrderByDescending((a, _, __, ___, ____) => a.Id)
|
||||
};
|
||||
#pragma warning restore CA1305,IDE0072
|
||||
}
|
||||
}
|
@ -134,9 +134,9 @@ public sealed class UserService(
|
||||
req.ThrowIfInvalid();
|
||||
|
||||
// ReSharper disable once MethodHasAsyncOverload
|
||||
#pragma warning disable VSTHRD103
|
||||
#pragma warning disable VSTHRD103,S6966
|
||||
return (await QueryInternal(new QueryReq<QueryUserReq> { Filter = req })
|
||||
#pragma warning restore VSTHRD103
|
||||
#pragma warning restore S6966, VSTHRD103
|
||||
.ForUpdate()
|
||||
.ToOneAsync()
|
||||
.ConfigureAwait(false)).Adapt<QueryUserRsp>();
|
||||
@ -257,7 +257,9 @@ public sealed class UserService(
|
||||
if (await Rpo.UpdateDiy
|
||||
.SetSource(req with {
|
||||
Id = UserToken.Id
|
||||
, Version = Rpo.Where(a => a.Id == UserToken.Id).ToOne(a => a.Version)
|
||||
, Version = await Rpo.Where(a => a.Id == UserToken.Id)
|
||||
.ToOneAsync(a => a.Version)
|
||||
.ConfigureAwait(false)
|
||||
})
|
||||
.UpdateColumns(a => a.Avatar)
|
||||
.ExecuteAffrowsAsync()
|
||||
@ -278,9 +280,11 @@ public sealed class UserService(
|
||||
public async Task<UserInfoRsp> SetEmailAsync(SetEmailReq req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var user = Rpo.Where(a => a.Id == UserToken.Id).ToOne(a => new { a.Mobile, a.Version, a.Email });
|
||||
var user = await Rpo.Where(a => a.Id == UserToken.Id)
|
||||
.ToOneAsync(a => new { a.Mobile, a.Version, a.Email })
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// 如果已绑定手机号、需要手机安全验证
|
||||
// 如果已绑定手机号码、需要手机安全验证
|
||||
if (!user.Mobile.NullOrEmpty()) {
|
||||
if (!await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
|
||||
throw new NetAdminInvalidOperationException(Ln.验证码不正确);
|
||||
@ -324,7 +328,7 @@ public sealed class UserService(
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (!user.Mobile.NullOrEmpty()) {
|
||||
// 已有手机号,需验证旧手机
|
||||
// 已有手机号码,需验证旧手机
|
||||
if (!await verifyCodeService.VerifyAsync(req.OriginVerifySmsCodeReq).ConfigureAwait(false)) {
|
||||
throw new NetAdminInvalidOperationException($"{Ln.旧手机号码验证码不正确}");
|
||||
}
|
||||
@ -334,7 +338,7 @@ public sealed class UserService(
|
||||
}
|
||||
}
|
||||
|
||||
// 验证新手机号
|
||||
// 验证新手机号码
|
||||
if (!await verifyCodeService.VerifyAsync(req.NewVerifySmsCodeReq).ConfigureAwait(false)) {
|
||||
throw new NetAdminInvalidOperationException($"{Ln.新手机号码验证码不正确}");
|
||||
}
|
||||
|
@ -173,8 +173,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
|
||||
private Task<Sys_VerifyCode> GetLastSentAsync(string destDevice)
|
||||
{
|
||||
return QueryInternal(new QueryReq<QueryVerifyCodeReq> {
|
||||
Count = 1
|
||||
, DynamicFilter
|
||||
DynamicFilter
|
||||
= new DynamicFilterInfo {
|
||||
Field = nameof(
|
||||
Sys_VerifyCode.DestDevice)
|
||||
@ -182,7 +181,8 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
|
||||
, Value = destDevice
|
||||
}
|
||||
})
|
||||
.ToOneAsync();
|
||||
.Take(1)
|
||||
.ToOneAsync();
|
||||
}
|
||||
|
||||
private ISelect<Sys_VerifyCode> QueryInternal(QueryReq<QueryVerifyCodeReq> req)
|
||||
|
@ -42,6 +42,12 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
|
||||
return Service.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task ExecuteAsync(QueryJobReq req)
|
||||
{
|
||||
return Service.ExecuteAsync(req);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<bool> ExistAsync(QueryReq<QueryJobReq> req)
|
||||
{
|
||||
|
@ -60,6 +60,14 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
|
||||
return Cache.EditAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行作业
|
||||
/// </summary>
|
||||
public Task ExecuteAsync(QueryJobReq req)
|
||||
{
|
||||
return Cache.ExecuteAsync(req);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计划作业是否存在
|
||||
/// </summary>
|
||||
|
@ -27,7 +27,7 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查手机号是否可用
|
||||
/// 检查手机号码是否可用
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
public Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
|
||||
@ -192,7 +192,7 @@ public sealed class UserController(IUserCache cache, IConfigCache configCache)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置手机号
|
||||
/// 设置手机号码
|
||||
/// </summary>
|
||||
[Transaction]
|
||||
public Task<UserInfoRsp> SetMobileAsync(SetMobileReq req)
|
||||
|
@ -68,7 +68,7 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
||||
var request = BuildRequest(job);
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var rsp = await request.SendAsync(cancelToken).ConfigureAwait(false);
|
||||
var rsp = await request.SendAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
if (rsp.StatusCode == HttpStatusCode.Unauthorized) {
|
||||
var loginRsp = await _userService.LoginByUserIdAsync(job.UserId).ConfigureAwait(false);
|
||||
#pragma warning disable S2696
|
||||
@ -76,12 +76,13 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
||||
_refreshToken = loginRsp.RefreshToken;
|
||||
#pragma warning restore S2696
|
||||
request = BuildRequest(job);
|
||||
rsp = await request.SendAsync(cancelToken).ConfigureAwait(false);
|
||||
rsp = await request.SendAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
await UowManager.AtomicOperateAsync(async () => {
|
||||
var rspBody = await rsp.Content.ReadAsStringAsync(cancelToken).ConfigureAwait(false);
|
||||
var rspBody = await rsp.Content.ReadAsStringAsync(CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
var jobRecord = new CreateJobRecordReq //
|
||||
{
|
||||
Duration = sw.ElapsedMilliseconds
|
||||
@ -97,7 +98,10 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
||||
};
|
||||
_ = await _jobRecordService.CreateAsync(jobRecord).ConfigureAwait(false);
|
||||
await _jobService
|
||||
.FinishJobAsync(job.Adapt<UpdateJobReq>() with { LastStatusCode = rsp.StatusCode })
|
||||
.FinishJobAsync(job.Adapt<UpdateJobReq>() with {
|
||||
LastStatusCode = rsp.StatusCode
|
||||
, LastDuration = jobRecord.Duration
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
@ -128,7 +132,7 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
|
||||
ret = ret.SetBody(job.RequestBody);
|
||||
}
|
||||
|
||||
return ret.OnResponsing(GetRequestHeader).OnException(GetRequestHeader);
|
||||
return ret.SetLog(_logger).OnResponsing(GetRequestHeader).OnException(GetRequestHeader);
|
||||
}
|
||||
|
||||
private void GetRequestHeader(HttpClient _, HttpResponseMessage rsp, string __)
|
||||
|
@ -3,9 +3,9 @@
|
||||
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.8.0"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0-preview.3.24172.13"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
|
||||
<PackageReference Include="xunit" Version="2.8.1"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0-preview.4.24267.6"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
@ -11,14 +11,14 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@tinymce/tinymce-vue": "^5.1.1",
|
||||
"ace-builds": "^1.33.1",
|
||||
"axios": "^1.6.8",
|
||||
"ace-builds": "^1.34.2",
|
||||
"axios": "^1.7.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"core-js": "^3.37.0",
|
||||
"core-js": "^3.37.1",
|
||||
"cropperjs": "^1.6.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.7.1",
|
||||
"element-plus": "^2.7.4",
|
||||
"json-bigint": "^1.0.0",
|
||||
"json5-to-table": "^0.1.8",
|
||||
"markdown-it": "^14.1.0",
|
||||
@ -29,23 +29,23 @@
|
||||
"sortablejs": "^1.15.2",
|
||||
"tinymce": "^6.8.3",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"vue": "^3.4.25",
|
||||
"vue": "^3.4.27",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-router": "^4.3.2",
|
||||
"vue3-ace-editor": "^2.2.4",
|
||||
"vue3-json-viewer": "^2.2.2",
|
||||
"vuedraggable": "^4.0.3",
|
||||
"vuex": "^4.1.0",
|
||||
"xgplayer": "^3.0.16",
|
||||
"xgplayer-hls": "^3.0.16"
|
||||
"xgplayer": "^3.0.18",
|
||||
"xgplayer-hls": "^3.0.18"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"prettier": "^3.2.5",
|
||||
"@vitejs/plugin-vue": "^5.0.5",
|
||||
"prettier": "^3.3.0",
|
||||
"prettier-plugin-organize-attributes": "^1.0.0",
|
||||
"sass": "^1.75.0",
|
||||
"terser": "^5.30.4",
|
||||
"vite": "^5.2.10"
|
||||
"sass": "^1.77.4",
|
||||
"terser": "^5.31.0",
|
||||
"vite": "^5.2.12"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
@ -60,6 +60,17 @@ export default {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 执行作业
|
||||
*/
|
||||
execute: {
|
||||
url: `${config.API_URL}/api/sys/job/execute`,
|
||||
name: `执行作业`,
|
||||
post: async function (data = {}, config = {}) {
|
||||
return await http.post(this.url, data, config)
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 计划作业是否存在
|
||||
*/
|
||||
|
7
src/frontend/admin/src/assets/icons/Email.vue
Normal file
7
src/frontend/admin/src/assets/icons/Email.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg class="icon" height="256" p-id="2661" t="1716619036537" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M874.666667 181.333333H149.333333c-40.533333 0-74.666667 34.133333-74.666666 74.666667v512c0 40.533333 34.133333 74.666667 74.666666 74.666667h725.333334c40.533333 0 74.666667-34.133333 74.666666-74.666667V256c0-40.533333-34.133333-74.666667-74.666666-74.666667z m-725.333334 64h725.333334c6.4 0 10.666667 4.266667 10.666666 10.666667v25.6L512 516.266667l-373.333333-234.666667V256c0-6.4 4.266667-10.666667 10.666666-10.666667z m725.333334 533.333334H149.333333c-6.4 0-10.666667-4.266667-10.666666-10.666667V356.266667l356.266666 224c4.266667 4.266667 10.666667 4.266667 17.066667 4.266666s12.8-2.133333 17.066667-4.266666l356.266666-224V768c0 6.4-4.266667 10.666667-10.666666 10.666667z"
|
||||
p-id="2662"></path>
|
||||
</svg>
|
||||
</template>
|
13
src/frontend/admin/src/assets/icons/MailCode.vue
Normal file
13
src/frontend/admin/src/assets/icons/MailCode.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg class="icon" height="256" p-id="9218" t="1716619196030" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M642.0992 501.5552a33.28 33.28 0 0 1 52.4288 40.6528l-3.328 4.2496-154.624 168.96a33.28 33.28 0 0 1-41.6256 6.144l-4.3008-3.072-128-107.008a33.28 33.28 0 0 1 38.1952-54.1696l4.4544 3.072 103.5776 86.6304 133.2224-145.4592z"
|
||||
p-id="9219"></path>
|
||||
<path
|
||||
d="M793.6 153.6a102.4 102.4 0 0 1 102.4 102.4v512a102.4 102.4 0 0 1-102.4 102.4h-563.2a102.4 102.4 0 0 1-102.4-102.4V256a102.4 102.4 0 0 1 102.4-102.4h563.2z m0 66.56h-563.2a35.84 35.84 0 0 0-35.5328 30.976L194.56 256v512a35.84 35.84 0 0 0 30.976 35.5328l4.864 0.3072h563.2a35.84 35.84 0 0 0 35.5328-30.976L829.44 768V256a35.84 35.84 0 0 0-30.976-35.5328L793.6 220.16z"
|
||||
p-id="9220"></path>
|
||||
<path
|
||||
d="M821.248 242.176a33.28 33.28 0 0 1 36.352 55.3984l-4.5056 2.9696-300.7488 164.096a84.48 84.48 0 0 1-72.9088 3.84l-7.68-3.6864-304.128-164.1472a33.28 33.28 0 0 1 26.7264-60.7744l4.9152 2.2016 304.128 164.1984a17.92 17.92 0 0 0 13.7728 1.3312l3.2768-1.3824 300.8-164.096z"
|
||||
p-id="9221"></path>
|
||||
</svg>
|
||||
</template>
|
7
src/frontend/admin/src/assets/icons/Mobile.vue
Normal file
7
src/frontend/admin/src/assets/icons/Mobile.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<svg class="icon" height="256" p-id="3820" t="1716618918266" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M736 0h-448C235.2 0 192 43.2 192 96v832c0 52.8 43.2 96 96 96h448c52.8 0 96-43.2 96-96v-832c0-52.8-43.2-96-96-96zM384 48h256v32H384v-32zM512 960a64 64 0 1 1 0-128 64 64 0 0 1 0 128z m256-192H256V128h512v640z"
|
||||
p-id="3821"></path>
|
||||
</svg>
|
||||
</template>
|
@ -1,15 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
class="icon"
|
||||
height="128"
|
||||
p-id="17757"
|
||||
t="1708507211716"
|
||||
version="1.1"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="128"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="icon" height="256" p-id="5451" t="1716619102648" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M179.2 742.4v102.4c0 26.368 21.6576 48.128 49.408 50.8928l6.0416 0.3072h554.7008c28.5696 0 52.1216-19.968 55.1424-45.6192l0.3072-5.5808v-102.4h51.2v102.4c0 54.1184-44.9536 98.4064-101.888 102.144l-7.8336 0.256H237.7216c-57.9584 0-105.472-41.984-109.4656-95.0784L128 844.8v-102.4h51.2z m682.5984-399.36v45.6192l-65.8944 78.336c49.7664 2.7648 78.336 40.0896 78.336 97.2288 0 61.2864-35.0208 106.9056-102.2976 106.9056-67.7376 0-102.7584-45.1584-102.7584-106.4448v-0.9216l56.6784-3.6864v3.6864c0 40.0896 18.8928 58.5216 46.08 58.5216 26.7264 0 45.6192-18.432 45.6192-58.5216s-17.9712-58.5216-47.0016-58.5216a50.3296 50.3296 0 0 0-22.1184 5.0688l-27.648-30.4128 74.1888-88.0128h-115.6608V343.04h182.4768z m-570.2144 0v273.2544h61.7472V665.6H172.2368v-49.3056h62.6688V403.4048l-62.6688 25.8048V375.7568L243.6608 343.04h47.9232z m220.8256-5.5296c60.8256 0 99.9936 42.3936 99.9936 97.6896 0 32.7168-11.52 52.5312-31.7952 76.032l-92.6208 105.5232h124.416V665.6H412.416v-41.0112l123.9552-142.848c14.7456-17.0496 19.3536-29.952 19.3536-46.08 0-32.256-17.9712-49.3056-43.3152-49.3056-25.8048 0-43.3152 17.0496-43.3152 49.3056v11.0592l-56.6784-3.6864V435.2c0-55.296 37.3248-97.6896 99.9936-97.6896z m273.92-260.7104c57.9072 0 105.3696 41.984 109.4144 95.0784l0.256 7.3216v102.4h-51.2v-102.4c0-26.368-21.6576-48.128-49.408-50.8928l-6.0416-0.3072H234.6496c-28.5696 0-52.1216 19.968-55.1424 45.6192L179.2 179.2v102.4h-51.2v-102.4c0-54.1184 44.9536-98.4064 101.888-102.144l7.8336-0.256h548.5568z"
|
||||
p-id="17758"></path>
|
||||
d="M932.8 1024H495.274667c-50.304 0-91.242667-41.088-91.242667-91.562667V588.032a21.333333 21.333333 0 1 1 42.666667 0v344.405333A48.789333 48.789333 0 0 0 495.274667 981.333333h437.525333A48.768 48.768 0 0 0 981.333333 932.437333V310.208a48.768 48.768 0 0 0-48.533333-48.874667H771.2a21.333333 21.333333 0 1 1 0-42.666666h161.578667c50.282667 0 91.2 41.066667 91.2 91.541333v622.229333C1024 982.912 983.082667 1024 932.8 1024z"
|
||||
p-id="5452"></path>
|
||||
<path
|
||||
d="M930.133333 876.394667H497.941333a21.333333 21.333333 0 0 1-21.333333-21.333334V599.786667a21.333333 21.333333 0 1 1 42.666667 0v233.941333H908.8V342.656h-121.92a21.333333 21.333333 0 1 1 0-42.666667H930.133333a21.333333 21.333333 0 0 1 21.333334 21.333334v533.738666a21.333333 21.333333 0 0 1-21.333334 21.333334zM745.770667 951.04H682.24a21.333333 21.333333 0 1 1 0-42.666667h63.530667a21.333333 21.333333 0 1 1 0 42.666667z"
|
||||
p-id="5453"></path>
|
||||
<path
|
||||
d="M780.352 699.285333a21.269333 21.269333 0 0 1-5.973333-0.853333l-334.421334-97.621333c-13.866667 1.024-26.688 1.514667-39.104 1.514666C179.818667 602.325333 0 467.221333 0 301.162667S179.818667 0 400.832 0s400.832 135.104 400.832 301.162667c0 6.656-0.32 13.312-0.874667 19.861333-7.36 85.205333-63.552 164.522667-152.170666 216.938667l145.536 123.733333a21.312 21.312 0 0 1-13.802667 37.589333z m-338.154667-141.376c2.026667 0 4.032 0.298667 5.973334 0.853334l242.197333 70.677333-93.141333-79.168a21.312 21.312 0 0 1 4.416-35.392c91.264-44.821333 149.824-118.656 156.650666-197.482667 0.448-5.333333 0.704-10.794667 0.704-16.256C758.997333 158.634667 598.336 42.666667 400.832 42.666667 203.349333 42.666667 42.666667 158.634667 42.666667 301.162667s160.682667 258.496 358.186666 258.496c12.437333 0 25.386667-0.533333 39.658667-1.685334 0.554667-0.042667 1.130667-0.064 1.685333-0.064z"
|
||||
p-id="5454"></path>
|
||||
<path
|
||||
d="M190.528 355.946667a77.802667 77.802667 0 0 0 39.488 11.157333c22.528 0 35.690667-11.904 35.690667-29.141333 0-15.914667-9.130667-25.066667-32.149334-33.877334-27.84-9.898667-45.056-24.32-45.056-48.362666 0-26.581333 22.016-46.314667 55.168-46.314667 17.450667 0 30.144 4.032 37.717334 8.362667l-6.08 17.962666a67.776 67.776 0 0 0-32.405334-8.106666c-23.296 0-32.128 13.930667-32.128 25.557333 0 15.957333 10.368 23.786667 33.898667 32.938667 28.864 11.114667 43.541333 25.024 43.541333 50.069333 0 26.346667-19.498667 49.130667-59.733333 49.130667-16.448 0-34.410667-4.821333-43.541333-10.88l5.589333-18.496zM462.186667 307.882667c-1.258667-23.829333-2.773333-52.394667-2.538667-73.664h-0.768a1007.829333 1007.829333 0 0 1-21.482667 64.789333l-30.144 82.773333h-16.682666l-27.584-81.237333c-8.106667-24.064-14.954667-46.08-19.754667-66.325333h-0.512c-0.490667 21.269333-1.770667 49.834667-3.285333 75.434666l-4.544 73.130667h-21.013334l11.904-170.602667h28.096l29.098667 82.517334c7.082667 21.013333 12.885333 39.722667 17.216 57.450666h0.768c4.309333-17.194667 10.389333-35.946667 17.962667-57.450666l30.378666-82.517334h28.096l10.645334 170.602667h-21.525334l-4.330666-74.901333zM518.997333 355.946667a77.76 77.76 0 0 0 39.509334 11.157333c22.506667 0 35.669333-11.904 35.669333-29.141333 0-15.914667-9.130667-25.066667-32.128-33.877334-27.882667-9.898667-45.077333-24.32-45.077333-48.362666 0-26.581333 22.037333-46.314667 55.146666-46.314667 17.450667 0 30.122667 4.032 37.738667 8.362667l-6.08 17.962666a67.776 67.776 0 0 0-32.405333-8.106666c-23.296 0-32.170667 13.930667-32.170667 25.557333 0 15.957333 10.368 23.786667 33.941333 32.938667 28.864 11.114667 43.541333 25.024 43.541334 50.069333 0 26.346667-19.477333 49.130667-59.733334 49.130667-16.448 0-34.432-4.821333-43.562666-10.88l5.610666-18.496z"
|
||||
p-id="5455"></path>
|
||||
</svg>
|
||||
</template>
|
@ -44,7 +44,6 @@ export { default as Robot } from './Robot.vue'
|
||||
export { default as Role } from './Role.vue'
|
||||
export { default as ScheduledJob } from './ScheduledJob.vue'
|
||||
export { default as Send } from './Send.vue'
|
||||
export { default as SmsCode } from './SmsCode.vue'
|
||||
export { default as Stats } from './Stats.vue'
|
||||
export { default as Sync } from './Sync.vue'
|
||||
export { default as Task } from './Task.vue'
|
||||
@ -67,4 +66,8 @@ export { default as Collect } from './Collect.vue'
|
||||
export { default as FreeSql } from './FreeSql.vue'
|
||||
export { default as Performance } from './Performance.vue'
|
||||
export { default as Proxy } from './Proxy.vue'
|
||||
export { default as ECharts } from './ECharts.vue'
|
||||
export { default as ECharts } from './ECharts.vue'
|
||||
export { default as Mobile } from './Mobile.vue'
|
||||
export { default as Email } from './Email.vue'
|
||||
export { default as SmsCode } from './SmsCode.vue'
|
||||
export { default as MailCode } from './MailCode.vue'
|
@ -2,7 +2,7 @@
|
||||
<el-table-column :label="label" :prop="prop" sortable="custom">
|
||||
<template #default="scope">
|
||||
<div class="avatar">
|
||||
<el-avatar :src="getAvatar(scope)" size="small"></el-avatar>
|
||||
<el-avatar :src="getAvatar(scope, prop)" size="small"></el-avatar>
|
||||
<span>{{ tool.getNestedProperty(scope.row, prop) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@ -36,8 +36,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
//获取头像
|
||||
getAvatar(scope) {
|
||||
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR
|
||||
getAvatar(scope, prop) {
|
||||
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR(tool.getNestedProperty(scope.row, prop))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
33
src/frontend/admin/src/components/naColId/index.vue
Normal file
33
src/frontend/admin/src/components/naColId/index.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<el-table-column v-bind="$attrs">
|
||||
<template #default="scope">
|
||||
<el-text @click="click(scope.row)" style="cursor: pointer" tag="ins">
|
||||
{{ tool.getNestedProperty(scope.row, $attrs.prop) }}
|
||||
</el-text>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
<script>
|
||||
import tool from '@/utils/tool'
|
||||
export default {
|
||||
emits: ['click'],
|
||||
props: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
mounted() {},
|
||||
created() {},
|
||||
components: {},
|
||||
computed: {
|
||||
tool() {
|
||||
return tool
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async click(row) {
|
||||
this.$emit('click', row)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
@ -2,7 +2,10 @@
|
||||
<el-table-column v-bind="$attrs">
|
||||
<template #default="scope">
|
||||
<div @click="click(tool.getNestedProperty(scope.row, $attrs.prop))" class="avatar">
|
||||
<el-avatar v-if="tool.getNestedProperty(scope.row, $attrs.nestProp)" :src="getAvatar(scope)" size="small"></el-avatar>
|
||||
<el-avatar
|
||||
v-if="tool.getNestedProperty(scope.row, $attrs.nestProp)"
|
||||
:src="getAvatar(scope, $attrs.nestProp)"
|
||||
size="small"></el-avatar>
|
||||
<div>
|
||||
<p>{{ tool.getNestedProperty(scope.row, $attrs.nestProp) }}</p>
|
||||
<p v-if="$attrs.nestProp2">{{ tool.getNestedProperty(scope.row, $attrs.nestProp2) }}</p>
|
||||
@ -54,8 +57,8 @@ export default {
|
||||
await this.$refs.saveDialog.open('view', { id: id })
|
||||
},
|
||||
//获取头像
|
||||
getAvatar(scope) {
|
||||
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR
|
||||
getAvatar(scope, prop) {
|
||||
return scope.row.avatar ? scope.row.avatar : this.$CONFIG.DEFAULT_AVATAR(tool.getNestedProperty(scope.row, prop))
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -60,6 +60,7 @@
|
||||
:layout="paginationLayout"
|
||||
:page-size="scPageSize"
|
||||
:page-sizes="pageSizes"
|
||||
:pager-count="pagerCount"
|
||||
:small="true"
|
||||
:total="total"
|
||||
@current-change="paginationChange"
|
||||
@ -230,6 +231,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pagerCount: 10,
|
||||
current: {
|
||||
row: null,
|
||||
column: null,
|
||||
@ -258,6 +260,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.pagerCount = document.body.clientWidth < 1000 ? 3 : 10
|
||||
//判断是否开启自定义列
|
||||
if (this.column) {
|
||||
this.getCustomColumn()
|
||||
@ -309,7 +312,7 @@ export default {
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
await this.vue.deleteRow(this.current.row)
|
||||
await this.vue.rowDel(this.current.row)
|
||||
return
|
||||
}
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,5 +1,7 @@
|
||||
import MY_CONFIG from './myConfig'
|
||||
import APP_CONFIG from './appConfig'
|
||||
import avatar from '@/utils/avatar'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
//标题
|
||||
@ -74,8 +76,14 @@ const DEFAULT_CONFIG = {
|
||||
},
|
||||
|
||||
//默认头像
|
||||
DEFAULT_AVATAR:
|
||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAIAAAC0Ujn1AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAflJREFUeNqs1u1P2kAYAHB6B+21UmZbatEAki2+MKIR42TGaHRLjBr/Yj/7wWRb3If5gjHbfIlBjIqCUCq0PmaJLqG9IwdP+qV3vV+v1/Z5Ttg/Ke39tm+q7VD/wlBx/j0J7xzWbccL9TVgosCivrv/Atgw8yI9JuZGVXNQjCkROL2tOpc39q8/D7bj0gcy6NSQvDoTR4LwdidVhOPDyMD29/Jd9YkyFlH6pAhanjb+d19DFvHSlOHX0x09kYpGcOAFMPf0kMxJJ02ZvlyWRjhpWcJ0moiIk240Gf9Rw2lz0o92i04/Nnhpl/Hhhtqux0mnLcZrzCQUTrrV9lh/M++CFM9rtOXyvL+lOif98+T++r4Z1Ht0Vju9anDSEKXbQBqSFH0sgz6+qMGDd7Y/1J96pav1FuTPzvZvxYrrej3REOWK09l4XWkyB7Jp30RBRNwH2jf/MZMimzZi/kk5l1HFMOKnFYK/zMaDqsxqPo6QwENDtd2YtwakwOI5rJO1OZOS0/3p8VR0s2CpMqMoJzSytWAldKmrig7cQk4fMUiX+w14rPVPVvGi9qNYcVpuIP1xVM2PvaOU2sACnYwmTbJ7cHdefssqSJFeXkUYCysz8flJjcN9nf7XvFnIahi/gMCiwphMROFzVstYSu/7sWxaXczpAAL7LMAA6FWV/DBrhrIAAAAASUVORK5CYII=',
|
||||
DEFAULT_AVATAR(name) {
|
||||
return (
|
||||
'data:image/svg+xml,' +
|
||||
encodeURIComponent(
|
||||
avatar.createSVG(`#${Math.abs(tool.crypto.hashCode(name)).toString(16).substring(0, 6)}`, name.slice(0, 1).toUpperCase()).outerHTML,
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
//合并业务配置
|
||||
|
@ -49,6 +49,7 @@ import naArea from '@/components/naArea/index.vue'
|
||||
import naButtonAdd from '@/components/naButtonAdd/index.vue'
|
||||
import naButtonBatchDel from '@/components/naButtonBatchDel/index.vue'
|
||||
import naColAvatar from '@/components/naColAvatar'
|
||||
import naColId from '@/components/naColId/index.vue'
|
||||
import naColIndicator from '@/components/naColIndicator/index.vue'
|
||||
import naColOperation from '@/components/naColOperation'
|
||||
import naColTags from '@/components/naColTags/index.vue'
|
||||
@ -89,6 +90,7 @@ export default {
|
||||
app.component('naButtonAdd', naButtonAdd)
|
||||
app.component('naButtonBatchDel', naButtonBatchDel)
|
||||
app.component('naColAvatar', naColAvatar)
|
||||
app.component('naColId', naColId)
|
||||
app.component('naColIndicator', naColIndicator)
|
||||
app.component('naColOperation', naColOperation)
|
||||
app.component('naColTags', naColTags)
|
||||
|
@ -132,14 +132,13 @@ export default {
|
||||
<style scoped>
|
||||
.mobile-nav-button {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
left: 1rem;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
background: #409eff;
|
||||
box-shadow: 0 0.2rem 1rem 0 rgba(64, 158, 255, 1);
|
||||
border-radius: 50%;
|
||||
background: var(--el-color-primary);
|
||||
box-shadow: 0 0.2rem 1rem 0 var(--el-color-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
@ -39,7 +39,20 @@
|
||||
</div>
|
||||
<el-dropdown @command="handleUser" class="user panel-item" trigger="click">
|
||||
<div class="user-avatar">
|
||||
<el-avatar :size="30" :src="user.avatar ? user.avatar : $CONFIG.DEFAULT_AVATAR"></el-avatar>
|
||||
<el-avatar
|
||||
:size="30"
|
||||
:src="
|
||||
user.avatar
|
||||
? user.avatar
|
||||
: 'data:image/svg+xml,' +
|
||||
encodeURIComponent(
|
||||
avatar.createSVG(
|
||||
`#${Math.abs(this.$TOOL.crypto.hashCode(user.userName)).toString(16).substring(0, 6)}`,
|
||||
user.userName.slice(0, 1).toUpperCase(),
|
||||
).outerHTML,
|
||||
)
|
||||
"></el-avatar>
|
||||
|
||||
<label>{{ user.userName }}</label>
|
||||
<el-icon class="el-icon--right">
|
||||
<el-icon-arrow-down />
|
||||
@ -68,7 +81,14 @@
|
||||
import search from './search.vue'
|
||||
import tasks from './tasks.vue'
|
||||
import message from '@/views/profile/message/components/list.vue'
|
||||
import avatar from '../../utils/avatar'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
avatar() {
|
||||
return avatar
|
||||
},
|
||||
},
|
||||
components: {
|
||||
search,
|
||||
tasks,
|
||||
|
@ -25,6 +25,10 @@
|
||||
--el-menu-horizontal-height: 4rem;
|
||||
}
|
||||
|
||||
.el-menu--inline {
|
||||
--el-menu-active-color: var(--el-color-primary) !important;
|
||||
}
|
||||
|
||||
.el-form-item--default {
|
||||
--font-size: 1rem;
|
||||
}
|
||||
@ -205,18 +209,14 @@
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.el-table.el-table--large {
|
||||
.el-table * {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.el-table.el-table--large * {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.el-table.el-table--small {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.el-radio-button__inner {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
@ -47,10 +47,20 @@
|
||||
padding: 0 0.4rem !important;
|
||||
}
|
||||
|
||||
.el-pagination__jump {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-pagination__total,
|
||||
.el-pagination__jump,
|
||||
.el-pagination__sizes {
|
||||
display: none !important;
|
||||
.el-select {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.scTable-do {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
42
src/frontend/admin/src/utils/avatar.js
Normal file
42
src/frontend/admin/src/utils/avatar.js
Normal file
@ -0,0 +1,42 @@
|
||||
export default {
|
||||
//生成svg矩形
|
||||
createSVG(color, name) {
|
||||
const svg = document.createElement('svg')
|
||||
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
|
||||
|
||||
svg.setAttribute('width', 50)
|
||||
svg.setAttribute('height', 50)
|
||||
|
||||
// <rect> background
|
||||
const rect = document.createElement('rect')
|
||||
rect.setAttribute('fill', color)
|
||||
rect.setAttribute('x', 0)
|
||||
rect.setAttribute('y', 0)
|
||||
rect.setAttribute('width', '100%')
|
||||
rect.setAttribute('height', '100%')
|
||||
|
||||
svg.appendChild(rect)
|
||||
|
||||
// <text> name
|
||||
const text = document.createElement('text')
|
||||
|
||||
text.setAttribute('fill', '#fff')
|
||||
text.setAttribute('x', '50%')
|
||||
text.setAttribute('y', '50%')
|
||||
text.setAttribute('text-anchor', 'middle')
|
||||
text.setAttribute('font-size', '16')
|
||||
text.setAttribute('font-weight', '900')
|
||||
text.setAttribute('font-family', 'monospace')
|
||||
|
||||
// IE/Edge don't support alignment-baseline
|
||||
// @see https://msdn.microsoft.com/en-us/library/gg558060(v=vs.85).aspx
|
||||
if (document.documentMode || /Edge/.test(navigator.userAgent)) {
|
||||
text.setAttribute('dy', '0.35em')
|
||||
} else {
|
||||
text.setAttribute('alignment-baseline', 'middle')
|
||||
}
|
||||
text.textContent = name
|
||||
svg.appendChild(text)
|
||||
return svg
|
||||
},
|
||||
}
|
@ -15,7 +15,9 @@
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :loading="isLoading" @click="login" round style="width: 100%" type="primary">{{ $t('登录') }}</el-button>
|
||||
<el-button :loading="isLoading" @click="login" round style="width: 100%" type="primary"
|
||||
>{{ starred ? $t('登录') : $t('Star 后可登录') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<div class="login-reg">
|
||||
{{ $t('还没有账号?') }}
|
||||
@ -28,6 +30,7 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
starred: false,
|
||||
autoLogin: false,
|
||||
form: {
|
||||
account: 'root',
|
||||
@ -50,6 +53,12 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
if (!this.starred) {
|
||||
window.open('https://github.com/nsnail/NetAdmin')
|
||||
this.starred = true
|
||||
return
|
||||
}
|
||||
|
||||
const validate = await this.$refs.loginForm.validate().catch(() => {})
|
||||
if (!validate) {
|
||||
return false
|
||||
|
@ -4,6 +4,7 @@
|
||||
<img alt="" src="@/assets/img/logo.png" />
|
||||
<h2>{{ packageJson.name }}</h2>
|
||||
<p>{{ ver }}</p>
|
||||
<el-link href="https://github.com/nsnail/NetAdmin" target="_blank">喜欢就点个 Star⭐️ 吧!</el-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<el-container>
|
||||
<el-header>
|
||||
<div class="user-info-top">
|
||||
<el-avatar :size="70" :src="user.avatar ? user.avatar : $CONFIG.DEFAULT_AVATAR"></el-avatar>
|
||||
<el-avatar :size="70" :src="user.avatar ? user.avatar : $CONFIG.DEFAULT_AVATAR(user.userName)"></el-avatar>
|
||||
<h2>{{ user.userName }}</h2>
|
||||
<p>
|
||||
<el-tag v-for="(item, i) in user.roles" :key="i" effect="dark" round size="large">{{ item.name }}</el-tag>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<template>
|
||||
<el-skeleton v-if="loading" :rows="5" animated />
|
||||
<div v-if="loading" style="padding: 1rem">
|
||||
<el-skeleton :rows="5" animated />
|
||||
</div>
|
||||
<template v-else>
|
||||
<el-container v-if="msgList.length > 0" class="nopadding">
|
||||
<el-header style="border: none">
|
||||
@ -30,9 +32,12 @@
|
||||
<div class="msg-title">
|
||||
<div>
|
||||
<el-badge v-if="msg.myFlags.userSiteMsgStatus === 0" is-dot type="primary">
|
||||
<el-avatar :size="40" :src="msg.sender.avatar ?? $CONFIG.DEFAULT_AVATAR"></el-avatar>
|
||||
<el-avatar :size="40" :src="msg.sender.avatar ?? $CONFIG.DEFAULT_AVATAR(msg.sender.userName)"></el-avatar>
|
||||
</el-badge>
|
||||
<el-avatar v-else :size="40" :src="msg.sender.avatar ?? $CONFIG.DEFAULT_AVATAR"></el-avatar>
|
||||
<el-avatar
|
||||
v-else
|
||||
:size="40"
|
||||
:src="msg.sender.avatar ?? $CONFIG.DEFAULT_AVATAR(msg.sender.userName)"></el-avatar>
|
||||
</div>
|
||||
<div>
|
||||
<h2>{{ msg.title }}</h2>
|
||||
|
@ -39,23 +39,22 @@
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column align="center" type="selection"></el-table-column>
|
||||
<el-table-column :label="$t('配置编号')" align="center" prop="id"></el-table-column>
|
||||
<el-table-column :label="$t('配置编号')" align="center" prop="id" width="170"></el-table-column>
|
||||
<el-table-column :label="$t('用户注册')" align="center">
|
||||
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name"></el-table-column>
|
||||
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name"></el-table-column>
|
||||
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm">
|
||||
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150"></el-table-column>
|
||||
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150"></el-table-column>
|
||||
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.userRegisterConfirm" @change="changeSwitch($event, scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('启用')" align="center" prop="enabled">
|
||||
<el-table-column :label="$t('启用')" align="center" prop="enabled" width="100">
|
||||
<template #default="scope">
|
||||
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('创建时间')" align="center" prop="createdTime"></el-table-column>
|
||||
<el-table-column :label="$t('创建时间')" align="center" prop="createdTime" width="170"></el-table-column>
|
||||
<na-col-operation
|
||||
:buttons="
|
||||
naColOperation.buttons.concat({
|
||||
@ -66,7 +65,8 @@
|
||||
type: 'danger',
|
||||
})
|
||||
"
|
||||
:vue="this" />
|
||||
:vue="this"
|
||||
width="170" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
</el-container>
|
||||
|
@ -109,20 +109,33 @@
|
||||
"
|
||||
prop="httpMethod"
|
||||
width="100" />
|
||||
<el-table-column :label="$t('上次执行时间')" align="right" prop="lastExecTime" sortable="custom" width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.lastExecTime" v-time.tip="scope.row.lastExecTime"></span>
|
||||
</template>
|
||||
<el-table-column :label="$t('上次执行')" align="center">
|
||||
<el-table-column :label="$t('状态')" align="center" prop="lastExecTime" sortable="custom" width="150">
|
||||
<template #default="scope">
|
||||
<div class="indicator">
|
||||
<sc-status-indicator :type="scope.row.lastStatusCode === 'ok' ? 'success' : 'danger'" />
|
||||
<span>{{
|
||||
this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode]
|
||||
? this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode][1]
|
||||
: scope.row.lastStatusCode
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('时间')" align="right" prop="lastExecTime" sortable="custom" width="100">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.lastExecTime" v-time.tip="scope.row.lastExecTime"></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:formatter="(row) => (row.lastDuration ? `${tool.groupSeparator(row.lastDuration.toFixed(0))} ms` : `-`)"
|
||||
:label="$t('耗时')"
|
||||
align="right"
|
||||
prop="lastDuration"
|
||||
sortable="custom"
|
||||
width="100">
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<na-col-indicator
|
||||
:label="$t('上次执行状态')"
|
||||
:options="
|
||||
Object.entries(this.$GLOBAL.enums.httpStatusCodes).map((x) => {
|
||||
return { value: x[0], text: x[1][1], type: x[0] === 'ok' ? 'success' : null }
|
||||
})
|
||||
"
|
||||
prop="lastStatusCode"
|
||||
width="120" />
|
||||
|
||||
<el-table-column :label="$t('下次执行时间')" align="right" prop="nextExecTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="80">
|
||||
@ -137,15 +150,22 @@
|
||||
</el-table-column>
|
||||
<na-col-operation
|
||||
:buttons="
|
||||
naColOperation.buttons.concat({
|
||||
icon: 'el-icon-delete',
|
||||
confirm: true,
|
||||
type: 'danger',
|
||||
title: $t('删除作业'),
|
||||
click: rowDel,
|
||||
})
|
||||
naColOperation.buttons.concat(
|
||||
{
|
||||
icon: 'el-icon-video-play',
|
||||
click: execute,
|
||||
},
|
||||
{
|
||||
icon: 'el-icon-delete',
|
||||
confirm: true,
|
||||
type: 'danger',
|
||||
title: $t('删除作业'),
|
||||
click: rowDel,
|
||||
},
|
||||
)
|
||||
"
|
||||
:vue="this" />
|
||||
:vue="this"
|
||||
width="180" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
</el-container>
|
||||
@ -162,6 +182,7 @@ import saveDialog from './save'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
import ScSelectFilter from '@/components/scSelectFilter/index.vue'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -171,6 +192,7 @@ export default {
|
||||
inject: ['reload'],
|
||||
data() {
|
||||
return {
|
||||
timer: null,
|
||||
loading: false,
|
||||
query: {
|
||||
dynamicFilter: {
|
||||
@ -192,6 +214,9 @@ export default {
|
||||
},
|
||||
watch: {},
|
||||
computed: {
|
||||
tool() {
|
||||
return tool
|
||||
},
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
@ -220,6 +245,31 @@ export default {
|
||||
}
|
||||
this.$refs.table.refresh()
|
||||
},
|
||||
async execute(row) {
|
||||
try {
|
||||
await this.$API.sys_job.execute.post({ id: row.id })
|
||||
this.$refs.table.refresh()
|
||||
this.$notify.success({
|
||||
dangerouslyUseHTMLString: true,
|
||||
message: `<div id="countdown">已发起执行请求,5 秒后弹出执行结果</div>`,
|
||||
onClose: async () => {
|
||||
clearInterval(this.timer)
|
||||
this.loading = true
|
||||
this.dialog.save = true
|
||||
await this.$nextTick()
|
||||
await this.$refs.saveDialog.open('view', row, 1)
|
||||
this.loading = false
|
||||
},
|
||||
})
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
const countdown = new RegExp('\\d+').exec(document.getElementById('countdown').innerText)[0]
|
||||
document.getElementById('countdown').innerText = document
|
||||
.getElementById('countdown')
|
||||
.innerText.replace(countdown, `${parseInt(countdown) - 1}`)
|
||||
}, 1000)
|
||||
} catch {}
|
||||
},
|
||||
//删除
|
||||
async rowDel(row) {
|
||||
try {
|
||||
|
@ -57,11 +57,36 @@
|
||||
row-key="id"
|
||||
stripe>
|
||||
<el-table-column :label="$t('唯一编码')" prop="id" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('执行耗时(毫秒)')" align="right" prop="duration" sortable="custom" width="150" />
|
||||
<el-table-column :label="$t('请求方法')" prop="httpMethod" sortable="custom" width="100" />
|
||||
<el-table-column :label="$t('HTTP 状态码')" align="right" prop="httpStatusCode" sortable="custom" width="150" />
|
||||
<el-table-column
|
||||
:formatter="(row) => `${tool.groupSeparator(row.duration.toFixed(0))} ms`"
|
||||
:label="$t('执行耗时')"
|
||||
align="right"
|
||||
prop="duration"
|
||||
sortable="custom"
|
||||
width="150" />
|
||||
<na-col-indicator
|
||||
:label="$t('请求方式')"
|
||||
:options="
|
||||
Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
|
||||
return { value: x[0], text: x[1][1] }
|
||||
})
|
||||
"
|
||||
prop="httpMethod"
|
||||
width="100" />
|
||||
<el-table-column :label="$t('响应状态码')" align="center" prop="httpStatusCode" sortable="custom" width="200">
|
||||
<template #default="scope">
|
||||
<div class="indicator">
|
||||
<sc-status-indicator :type="scope.row.httpStatusCode === 'ok' ? 'success' : 'danger'" />
|
||||
<span>{{
|
||||
this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode]
|
||||
? this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode][1]
|
||||
: scope.row.httpStatusCode
|
||||
}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('请求的网络地址')" prop="requestUrl" sortable="custom" />
|
||||
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
|
||||
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" width="170" />
|
||||
<na-col-operation :buttons="[naColOperation.buttons[0]]" :vue="this" width="100" />
|
||||
</sc-table>
|
||||
</el-main>
|
||||
@ -78,6 +103,7 @@
|
||||
import saveDialog from './save'
|
||||
import table from '@/config/table'
|
||||
import naColOperation from '@/config/naColOperation'
|
||||
import tool from '@/utils/tool'
|
||||
|
||||
export default {
|
||||
props: ['keywords'],
|
||||
@ -101,6 +127,9 @@ export default {
|
||||
},
|
||||
watch: {},
|
||||
computed: {
|
||||
tool() {
|
||||
return tool
|
||||
},
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
<el-form-item :label="$t('唯一编码')" prop="id"><el-input v-model="form.id" clearable /></el-form-item
|
||||
><el-form-item :label="$t('执行耗时(毫秒)')" prop="duration"><el-input v-model="form.duration" clearable /></el-form-item
|
||||
><el-form-item :label="$t('请求方法')" prop="httpMethod"><el-input v-model="form.httpMethod" clearable /></el-form-item
|
||||
><el-form-item :label="$t('HTTP 状态码')" prop="httpStatusCode"><el-input v-model="form.httpStatusCode" clearable /></el-form-item
|
||||
><el-form-item :label="$t('响应状态码')" prop="httpStatusCode"><el-input v-model="form.httpStatusCode" clearable /></el-form-item
|
||||
><el-form-item :label="$t('作业编号')" prop="jobId"><el-input v-model="form.jobId" clearable /></el-form-item
|
||||
><el-form-item :label="$t('请求体')" prop="requestBody"
|
||||
><el-input v-model="form.requestBody" clearable rows="5" type="textarea" /></el-form-item
|
||||
|
@ -27,6 +27,9 @@
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('上次执行时间')" prop="lastExecTime">
|
||||
<el-input v-model="form.lastExecTime" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('上次执行耗时(毫秒)')" prop="lastExecTime">
|
||||
<el-input v-model="form.lastDuration" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="mode === 'view'" :label="$t('下次执行时间')" prop="nextExecTime">
|
||||
<el-input v-model="form.nextExecTime" clearable />
|
||||
</el-form-item>
|
||||
@ -189,7 +192,7 @@ export default {
|
||||
mounted() {},
|
||||
methods: {
|
||||
//显示
|
||||
async open(mode = 'add', data) {
|
||||
async open(mode = 'add', data, tabIndex = 0) {
|
||||
this.visible = true
|
||||
this.loading = true
|
||||
this.mode = mode
|
||||
@ -198,6 +201,7 @@ export default {
|
||||
Object.assign(this.form, res.data)
|
||||
}
|
||||
this.loading = false
|
||||
this.tabIndex = tabIndex
|
||||
return this
|
||||
},
|
||||
|
||||
|
Reference in New Issue
Block a user