Compare commits

..

No commits in common. "master" and "normal" have entirely different histories.

88 changed files with 931 additions and 1044 deletions

View File

@ -1,54 +0,0 @@
# 定义CI/CD流水线的阶段
stages:
- build # 第一阶段:构建应用程序
- build-docker # 第二阶段构建Docker镜像
- deploy # 第三阶段:部署应用程序
cache: # 缓存内容
paths:
- node_modules/
- docker/dist/
# 定义全局变量
variables:
CONTAINER_NAME: 'auth-web' # Docker容器名称
DOCKER_TAG: '1.0.0' # Docker镜像标签版本
# 构建任务
build-job:
stage: build # 指定此任务属于build阶段
script:
# 打印编译开始信息
- echo "Compiling the code..."
# 使用Maven编译Java项目跳过测试
- npm i -g pnpm && pnpm i && pnpm build
# 打印编译完成信息
- echo "Compile complete."
# 从Docker Hub拉取OpenJDK基础镜像
- docker pull nginx:1.27.3
# 打印拉取完成信息
- echo "docker pull complete."
# 使用Dockerfile构建Docker镜像并打上标签
- cd docker && docker build -f Dockerfile -t $CONTAINER_NAME:$DOCKER_TAG .
# 打印构建成功信息
- echo "Application successfully deployed."
# 部署任务
deploy-job:
stage: deploy # 指定此任务属于deploy阶段
environment: production # 指定部署环境为production
script:
# 打印部署开始信息
- echo "Deploying application..."
# 停止正在运行的容器(如果存在),|| true确保命令失败不会中断脚本
- docker stop $CONTAINER_NAME || true
# 删除容器(如果存在)
- docker rm $CONTAINER_NAME || true
# 运行新的Docker容器
# -d: 后台运行
# -p: 端口映射7070和8000
# --name: 容器名称
# --restart always: 总是自动重启
- docker run -d -p 8800:80 --name $CONTAINER_NAME --restart always $CONTAINER_NAME:$DOCKER_TAG
# 打印部署成功信息
- echo "Application successfully deployed."

View File

@ -1,8 +0,0 @@
{
"hash": "3178506e",
"configHash": "718798cd",
"lockfileHash": "88017af1",
"browserHash": "cfdd5370",
"optimized": {},
"chunks": {}
}

View File

@ -1,3 +0,0 @@
{
"type": "module"
}

View File

@ -1,283 +0,0 @@
# BunnyAuth Dynamic Permission Control Introduction
![GitHub Stars](https://img.shields.io/github/stars/BunnyMaster/bunny-admin-server?style=social) ![GitHub forks](https://img.shields.io/github/forks/BunnyMaster/bunny-admin-server) ![GitHub contributors](https://img.shields.io/github/contributors/BunnyMaster/bunny-admin-server) <img alt="GitHub License" src="https://img.shields.io/github/license/BunnyMaster/bunny-admin-server"> ![GitHub top language](https://img.shields.io/github/languages/top/BunnyMaster/bunny-admin-server) ![GitHub Repo Size](https://img.shields.io/github/repo-size/BunnyMaster/bunny-admin-server)
> [!IMPORTANT]
>
> Open-source permission template: [Pure-admin](https://pure-admin.github.io/vue-pure-admin/)
>
> **Pure-admin Documentation**: https://pure-admin.github.io/pure-admin-doc
>
> **Default Credentials**
>
> The project includes a default administrator with user `id` `1` in the database:
>
> Username: `Administrator`
>
> Password: `admin123`
> [!WARNING]
>
> If the password for the administrator [`Administrator`] is incorrect during initial login:
>
> Locate the `sys_user` table in the database and replace the password for `Administrator` with the following:
>
> `$2a$10$h5BUwmMaVcEuu7Bz0TPPy.PQV8JP6CFJlbHTgT78G1s0YPIu2kfXe`
Flexible permission control with multi-platform file upload support.
## 📽️ Video Tutorials
**Introduction Videos**
- [RBAC URL Permission Database and Backend Design](https://www.bilibili.com/video/BV1nGVazrEKf/)
- [Bunny-Admin Configuration Guide](https://www.bilibili.com/video/BV177VazMEiM/)
- [Bunny-Admin User Operations](https://www.bilibili.com/video/BV1B7VazME72/)
- [Bunny-Admin Role Permissions](https://www.bilibili.com/video/BV1ELVazzEnC/)
- [Bunny-Admin Remaining Business Logic](https://www.bilibili.com/video/BV1ELVazzE7S/)
- [Code Generator](https://www.bilibili.com/video/BV1d4Lxz9E3j/?vd_source=d42b5b664efb958be39eef8ee1196a7e)
**GitHub Repositories**
- Permission Backend: https://github.com/BunnyMaster/bunny-admin-server
- Permission Frontend: https://github.com/BunnyMaster/bunny-admin-web
- Code Generator: https://github.com/BunnyMaster/generator-code-server
**Gitee Repositories**
- Permission Backend: https://gitee.com/BunnyBoss/bunny-admin-server
- Permission Frontend: https://gitee.com/BunnyBoss/bunny-admin-web
- Code Generator: https://gitee.com/BunnyBoss/generator-code-server
## 🚀 Project Overview
A modern dynamic permission control system based on Spring Security 6, providing a complete RBAC permission management solution. Supports frontend-backend separation architecture and flexible fine-grained permission control.
## 😋 Controller Annotation Guide
The project is URL-based for easy permission interface definition. Permissions can be added or deleted via URLs, even if the interface does not exist in the project.
For example, if a role needs access to all interfaces under `dept`, the URL can be written as `api/dept/**`. For specific interfaces like `/api/dept/aaa/bbb`, the URL can be customized accordingly.
For paginated queries with URL parameters, use `/api/dept/*/*` for flexible permission control.
In scenarios where interfaces directly represent permissions, manually adding each URL is tedious. Instead, Swagger annotations and the custom `PermissionTag` annotation can be used. Reflection-based permission addition is also supported. Refer to the `ReadMe` in the controller directory for details.
## ✨ Major Updates
### Core Improvements
**v4.0.0**
- **Complete Refactoring**: Backend interfaces, entity classes, and frontend optimizations.
- **Batch Operations Support**:
- ✅ Menu Management: Enhanced attribute content.
- ✅ Permission Management: JSON/Excel import/export.
- ✅ Role Management: Excel batch updates.
- ✅ Multi-language Configuration: JSON/Excel updates (full replacement mode).
**v4.0.1**
- File system supports multiple platforms with manual configuration.
- Reference documentation: https://x-file-storage.xuyanwu.cn/#/
- File deletion and download require implementing the `FileRecorder` interface. The code and controllers are in the `file` directory. Modify as needed based on the [x-file-storage] documentation.
## 🧠 Usage Tips
> [!TIP]
>
> Multi-language Usage Tips:
>
> While direct JSON file manipulation may be challenging for some users, JSON offers unique advantages in multi-language projects:
>
> 1. Structured format for easy AI parsing.
> 2. Efficient translation workflow:
> - Developers only need to complete the Chinese version.
> - Upload JSON to AI translation tools.
> - Simple commands generate English/Traditional Chinese/Korean versions.
> 3. Saves significant development time with a "write once, adapt for multiple languages" approach.
## 🔐 Permission Control System
![image-20250428225337843](./images/image-20250428225337843-1745854181492-5.png)
### Access Rules Configuration
Configured via `WebSecurityConfig`:
| Path Type | Example | Access Requirement | Configuration Method |
| ----------------- | ----------------- | ------------------ | ------------------------------- |
| Public Interface | `/api/public/**` | No authentication | Path contains `public` keyword |
| Private Interface | `/api/private/**` | Requires login | Path contains `private` keyword |
### Path Matching Strategy
```java
public static String[] annotations = { ... };
// Configuration Example
http.authorizeHttpRequests(auth -> auth
.authorizeHttpRequests(authorize -> authorize
.requestMatchers(annotations).permitAll()
);
```
### Maven Project Structure
```
bunny-auth/
├── auth-system # System Module
├── core-common # Core and base...
│ ├── context # Context
│ └── exception # Exception
│ └── ...... # And more...
├── domain # Domain
└─services # Service and Mapper
```
## 🛠️ Use Cases
### 1. Frontend-Only Control Mode
Frontend details: https://pure-admin.cn/pages/RBAC/#%E5%A6%82%E4%BD%95%E9%85%8D%E7%BD%AE
![image-20250428230444403](./images/image-20250428230444403-1745854157395-3.png)
- **Page Control**:
1. Assign roles to route menus.
2. Assign roles to users.
- **Button Control**:
```ts
// Frontend Permission Codes
const auth = {
add: ['i18nType::add'],
update: ['i18nType::update'],
delete: ['i18nType::delete'],
};
```
### 2. Backend-Only Control Mode
- Interface-level permissions: For pagination, use `/api/permission/*/*`.
```java
@Tag(name = "System Permissions")
@PermissionTag(permission = "permission::*")
@RestController
@RequestMapping("api/permission")
public class PermissionController {
@Operation(summary = "Pagination Query")
@PermissionTag(permission = "permission::query")
@GetMapping("{page}/{limit}")
public Result<PageResult<PermissionVo>> getPermissionPage(
@PathVariable Integer page,
@PathVariable Integer limit) {
// ...
}
}
```
### 3. Full-Stack Control Mode
Combine the above two approaches.
## 🛡️ Security Configuration
### Path Matching Strategy
AntPath details: https://juejin.cn/spost/7498247273660743732
| Pattern | Example | Description |
| --------------- | --------------- | ----------------------- |
| Exact Match | `/api/user` | Matches exact path |
| Single Wildcard | `/api/user/*` | Matches single level |
| Multi Wildcard | `/api/user/**` | Matches multiple levels |
| Method Specific | `GET /api/user` | Matches HTTP method |
## 🧰 Technology Stack
### 😄 Frontend
- Vue 3 + PureAdmin Template
- Custom Permission Components
- Internationalization Support
### 😃 Backend
- Spring Boot 3 + Spring Security 6
- JDK 17
- MySQL + Redis + MinIO
- Swagger + Knife4j Documentation
### 😀 Development Environment
Docker startup varies by version:
```bash
# Start dependency services with one command
docker-compose up -d
# For newer Docker versions
docker compose up -d
```
## 📚 Best Practices
1. **Annotation Standards**:
```java
@Tag(name = "Module Name", description = "Module Description")
@Operation(summary = "Interface Summary", tags = {"Permission Code"})
// Or
@Operation(summary = "Interface Summary", tags = "Permission Code")
```
2. **Permission Code Design**:
- Module::Operation (e.g., `user::create`).
- Hierarchical design (e.g., `system:user:update`).
3. **Batch Operations**:
- Manage permissions via Excel/JSON.
- Regularly backup permission configurations.
## 🌟 Project Advantages
1. **True Dynamic Control** No hardcoded permission logic.
2. **Flexible Data Import** Supports multiple file formats.
3. **Fine-Grained Control** Multi-level permissions from pages to buttons.
4. **Modern Tech Stack** Based on the latest Spring ecosystem.
5. **Out-of-the-Box** Complete Docker deployment solution.
## 📌 Notes
1. Multi-language updates fully replace existing configurations.
2. Disable Swagger endpoints in production.
3. Use Excel for complex permission management.
## 📈 Future Plans
None at the moment.
## 📏 Frontend-Backend Interface Standards
### 🌐 Frontend Example Standards
| **Action** | **API Layer** | **Pinia Layer** |
| :----------- | :------------ | :-------------- |
| Query Single | `getUser` | `loadUser` |
| Query List | `getUserList` | `loadUserList` |
| Pagination | `getUserPage` | `fetchUserPage` |
| Add Data | `createUser` | `addUser` |
| Update Data | `updateUser` | `editUser` |
| Delete Data | `deleteUser` | `removeUser` |
### 🛟 Backend Interface Example Standards
Follows RESTful standards.
| **Action** | **RESTful** |
| :----------- | :-------------------------- |
| Query List | `GET /users` |
| Pagination | `GET /users/{page}/{limit}` |
| Query Single | `GET /users/{id}` |
| Add | `POST /users` |
| Update | `PUT /users/{id}` |
| Delete | `DELETE /users/{id}` |
![wx_alipay](./images/wx_alipay.png)

110
ReadMe.md
View File

@ -1,15 +1,14 @@
# BunnyAuth动态权限控制简介
![GitHub Stars](https://img.shields.io/github/stars/BunnyMaster/bunny-admin-server?style=social)![GitHub forks](https://img.shields.io/github/forks/BunnyMaster/bunny-admin-server)![GitHub contributors](https://img.shields.io/github/contributors/BunnyMaster/bunny-admin-server)<img alt="GitHub License" src="https://img.shields.io/github/license/BunnyMaster/bunny-admin-server">![GitHub top language](https://img.shields.io/github/languages/top/BunnyMaster/bunny-admin-server)![GitHub Repo Size](https://img.shields.io/github/repo-size/BunnyMaster/bunny-admin-server)
> [!IMPORTANT]
>
> 开源权限模板[Pure-admin](https://pure-admin.github.io/vue-pure-admin/)
> 前端项目整体都由此模板开发
>
> 项目由[小铭](https://github.com/xiaoxian521)开源权限模板[Pure-admin](https://pure-admin.github.io/vue-pure-admin/)
>
> **Pure-admin文档**https://pure-admin.github.io/pure-admin-doc
>
> **默认凭证**
> [!TIP]
>
> 项目中有一个默认管理员,数据库中用户`id`是`1`
>
@ -25,17 +24,14 @@
>
> `$2a$10$h5BUwmMaVcEuu7Bz0TPPy.PQV8JP6CFJlbHTgT78G1s0YPIu2kfXe`
可粗可细的权限控制,多平台文件上传。
## 📽️视频说明地址
## 视频说明地址
**介绍视频视频**
- [RBAC中URL的权限数据库、后端设计](https://www.bilibili.com/video/BV1nGVazrEKf/)
- [bunny-admin 配置说明](https://www.bilibili.com/video/BV177VazMEiM/)
- [Bunny-Admin 用户相关操作](https://www.bilibili.com/video/BV1B7VazME72/)
- [Bunny-Admin 角色权限](https://www.bilibili.com/video/BV1ELVazzEnC/)
- [Bunny-Admin 剩下的业务逻辑](https://www.bilibili.com/video/BV1ELVazzE7S/)
- [环境搭建](https://www.bilibili.com/video/BV17odHY6E3S/?spm_id_from=333.1387.homepage.video_card.click&vd_source=d42b5b664efb958be39eef8ee1196a7e)
- [运行项目](https://www.bilibili.com/video/BV1qodHYzErA/?spm_id_from=333.1387.homepage.video_card.click&vd_source=d42b5b664efb958be39eef8ee1196a7e)
- [前端部署](https://www.bilibili.com/video/BV1BddHYgEPq/?spm_id_from=333.1387.homepage.video_card.click&vd_source=d42b5b664efb958be39eef8ee1196a7e)
- [后端部署](https://www.bilibili.com/video/BV1BddHYgEFt/?spm_id_from=333.1387.homepage.video_card.click&vd_source=d42b5b664efb958be39eef8ee1196a7e)
- [代码生成器](https://www.bilibili.com/video/BV1d4Lxz9E3j/?vd_source=d42b5b664efb958be39eef8ee1196a7e)
**Github地址**
@ -54,24 +50,10 @@
一个基于 Spring Security 6 的现代化动态权限控制系统,提供完整的 RBAC 权限管理解决方案。支持前后端分离架构,可灵活配置细粒度权限控制。
## 😋控制器上注解说明
整个项目是基于URL方便定义权限接口即使项目中接口不存在通过URL的方式增删权限。
比如项目需要为`dept`分配了角色,这个角色可以访问`dept`下所有的接口,那么就可以写成`api/dept/**`,如果需要`dept`下某个接口,`/api/dept/aaa/bbb`,这种形式。
如果针对分页查询分页参数写在URL上的可以这样做`/api/dept/*/*`这样就可以做到灵活的权限控制。
但是在部分场景下接口就是我们的权限这时如果手动一个一个添加URL很麻烦所以使用的swagger自带的注解和`PermissionTag`,自定义的注解的方式,如果这时的项目需求是,为整个接口添加权限,就可以用反射的方式添加。
使用放射的方式已经放在controller的目录下看`ReadMe`文档即可。
## ✨重大更新
## ✨ v4.0.0 重大更新
### 核心改进
**v4.0.0**
- **全面重构**后端接口、实体类等重构前端重构部分j+优化操作体验
- **批量操作支持**
- ✅菜单管理:完善属性内容
@ -79,13 +61,27 @@
- ✅ 角色管理:支持 Excel 批量更新
- ✅ 多语言配置:支持 JSON/Excel 更新(全量替换模式)
**v4.0.1**
### 技术亮点
- 文件系统支持多平台,只需要手动配置即可。
- 有需要参考文档https://x-file-storage.xuyanwu.cn/#/
- 文件删除和下载等需要实现对应接口`FileRecorder `,目前以实现,对应代码和控制器都在文件夹`file`下如有需要修改可以参考【x-file-storage】文档修改。
- **注解扫描**:通过 `AnnotationScanner.java` 自动扫描想要的注解
## 🧠用法提示
```java
// 示例:扫描特定注解的类
public static Set<Class<?>> getClassesWithAnnotation(Class<?> annotation) {
// 实现细节...
}
```
- **应用场景**
- 定时任务配置
- 权限接口发现
### 界面优化
![权限管理界面](./images/image-20250428223816172.png)
![角色管理界面](./images/image-20250428223843974.png)
## :tipping_hand_man:用法提示
> [!TIP]
>
@ -127,15 +123,17 @@ http.authorizeHttpRequests(auth -> auth
### Maven工程结构
```
bunny-auth/
├── auth-system # 系统模块
├── core-common # 配置和基础模块
│ ├── exception # exception
│ └── context # context
│ └── ...... # 还要很多...
├── domain # domain
└─services # 服务和mapper
```mermaid
graph TD
父工程 -->|主项目| auth-api
父工程 -->|代码生成器| generator-code
auth-api -->|启动项、控制器| service
service -->|mapper| dao
service -->|包含domain、配置等| auth-core
dao -->|包含domain、配置等| auth-code
```
## 🛠️ 应用场景
@ -165,12 +163,10 @@ bunny-auth/
```java
@Tag(name = "系统权限")
@PermissionTag(permission = "permission::*")
@RestController
@RequestMapping("api/permission")
public class PermissionController {
@Operation(summary = "分页查询")
@PermissionTag(permission = "permission::query")
@Operation(summary = "分页查询", tags = {"permission::page"})
@GetMapping("{page}/{limit}")
public Result<PageResult<PermissionVo>> getPermissionPage(
@PathVariable Integer page,
@ -199,20 +195,20 @@ AntPath详情https://juejin.cn/spost/7498247273660743732
## 🧰 技术栈
### 😄前端
### 前端
- Vue 3 + PureAdmin 模板
- 自定义权限组件
- 国际化支持
### 😃后端
### 后端
- Spring Boot 3 + Spring Security 6
- JDK 17
- MySQL + Redis + MinIO
- Swagger + Knife4j 文档
### 😀开发环境
### 开发环境
根据不同docker 启动方式不一样
@ -257,11 +253,19 @@ docker compose up -d
## 📈 后续规划
暂无
- [x] 权限级别拖拽
- [x] 权限树型结构动态添加、更新、删除
- [ ] 用户设置持久化存储到数据库
- [x] 权限弹窗页面优化
- [x] 后端文档注释完善
- [x] 系统监控后端返回403停止请求
- [x] 优化用户配置权限逻辑,配置后热更新逻辑等
- [x] 完善后端注释有需要添加ReadMe文档
- [x] 获取Redis中活跃用户
## 📏前后端接口规范
## 前后端接口规范
### 🌐前端示例规范
### 前端示例规范
| **操作** | **API 层** | **Pinia 层** |
| :------- | :------------ | :-------------- |
@ -272,7 +276,7 @@ docker compose up -d
| 更新数据 | `updateUser` | `editUser` |
| 删除数据 | `deleteUser` | `removeUser` |
### 🛟后端接口示例规范
### 后端接口示例规范
遵循Restful

View File

@ -3,7 +3,7 @@ import type { BaseResult } from '@/api/service/types';
/** 读取web配置文件并返回给前端 */
export const getWebConfig = () => {
return http.request<any>('get', '/config/public/web-config');
return http.request<any>('get', '/config/public/webConfig');
};
/** 更新web配置文件 */

View File

@ -3,27 +3,27 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 邮件模板表---分页查询邮件模板 */
export const getEmailTemplatePage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `email-template/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `emailTemplate/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 邮件模板表---添加邮件模板 */
export const createEmailTemplate = (data: any) => {
return http.request<BaseResult<object>>('post', 'email-template', { data });
return http.request<BaseResult<object>>('post', 'emailTemplate', { data });
};
/** 邮件模板表---更新邮件模板 */
export const updateEmailTemplate = (data: any) => {
return http.request<BaseResult<object>>('put', 'email-template', { data });
return http.request<BaseResult<object>>('put', 'emailTemplate', { data });
};
/** 邮件模板表---删除邮件模板 */
export const deleteEmailTemplate = (data: any) => {
return http.request<BaseResult<object>>('delete', 'email-template', { data });
return http.request<BaseResult<object>>('delete', 'emailTemplate', { data });
};
/** 邮件模板表---获取全部邮件类型列表 */
export const getEmailTypeList = () => {
return http.request<BaseResult<any>>('get', 'email-template/private');
return http.request<BaseResult<any>>('get', 'emailTemplate/private');
};

View File

@ -3,27 +3,27 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 邮箱用户发送配置管理---分页查询邮箱用户发送配置 */
export const getEmailUserPage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `email-users/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `emailUsers/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 邮箱用户发送配置管理---添加邮箱用户发送配置 */
export const createEmailUsers = (data: any) => {
return http.request<BaseResult<object>>('post', 'email-users', { data });
return http.request<BaseResult<object>>('post', 'emailUsers', { data });
};
/** 邮箱用户发送配置管理---更新邮箱用户发送配置 */
export const updateEmailUsers = (data: any) => {
return http.request<BaseResult<object>>('put', 'email-users', { data });
return http.request<BaseResult<object>>('put', 'emailUsers', { data });
};
/** 邮箱用户发送配置管理---删除邮箱用户 */
export const deleteEmailUsers = (data: any) => {
return http.request<BaseResult<object>>('delete', 'email-users', { data });
return http.request<BaseResult<object>>('delete', 'emailUsers', { data });
};
/** 邮箱用户发送配置管理---获取全部邮件用户配置 */
export const getEmailUserList = () => {
return http.request<BaseResult<any>>('get', 'email-users/private');
return http.request<BaseResult<any>>('get', 'emailUsers/private');
};

View File

@ -29,7 +29,12 @@ export const downloadFilesByFileId = (data: any) => {
});
};
/** 系统文件管理---获取所有文件类型 */
export const getMediaTypeList = () => {
return http.request<BaseResult<any>>('get', `files/private/getMediaTypeList`);
};
/** 系统文件管理---获取所有文件存储基础路径 */
export const getFilesStoragePath = () => {
return http.request<BaseResult<any>>('get', `files/private/file-storage-paths`);
return http.request<BaseResult<any>>('get', `files/private/getAllFilesStoragePath`);
};

View File

@ -44,22 +44,22 @@ export const uploadI18nFile = (data: any) => {
/** 多语言类型管理---添加多语言类型 */
export const createI18Type = (data: any) => {
return http.request<BaseResult<object>>('post', 'i18n-type', { data });
return http.request<BaseResult<object>>('post', 'i18nType', { data });
};
/** 多语言类型管理---更新多语言类型 */
export const updateI18nType = (data: any) => {
return http.request<BaseResult<object>>('put', 'i18n-type', { data });
return http.request<BaseResult<object>>('put', 'i18nType', { data });
};
/** 多语言类型管理---删除多语言类型 */
export const deleteI18nType = (data: any) => {
return http.request<BaseResult<object>>('delete', 'i18n-type', { data });
return http.request<BaseResult<object>>('delete', 'i18nType', { data });
};
/** 多语言类型管理---获取全部多语言类型列表 */
export const getI18nTypeList = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', 'i18n-type/public', {
return http.request<BaseResult<ResultTable>>('get', 'i18nType/public', {
params: data,
});
};

View File

@ -3,14 +3,14 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 调度任务执行日志---分页查询调度任务执行日志 */
export const getScheduleExecuteLogPage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `schedule-execute-log/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `scheduleExecuteLog/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 调度任务执行日志---删除调度任务执行日志 */
export const deleteScheduleExecuteLog = (data: any) => {
return http.request<BaseResult<object>>('delete', 'schedule-execute-log', {
return http.request<BaseResult<object>>('delete', 'scheduleExecuteLog', {
data,
});
};

View File

@ -3,17 +3,17 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 用户登录日志---获取用户登录日志列表 */
export const getUserLoginLogPageByAdmin = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `user-login-log/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `userLoginLog/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 用户登录日志---删除用户登录日志 */
export const deleteUserLoginLog = (data: any) => {
return http.request<BaseResult<object>>('delete', 'user-login-log', { data });
return http.request<BaseResult<object>>('delete', 'userLoginLog', { data });
};
/** 用户登录日志---获取用户登录日志列表 */
export const getUserLoginLogPageByUser = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `user-login-log/private/${data.currentPage}/${data.pageSize}`);
return http.request<BaseResult<ResultTable>>('get', `userLoginLog/private/${data.currentPage}/${data.pageSize}`);
};

View File

@ -3,7 +3,12 @@ import type { BaseResult } from '@/api/service/types';
/** 菜单管理-列表 */
export const getRouterList = () => {
return http.request<BaseResult<any>>('get', `router/routers`);
return http.request<BaseResult<any>>('get', `router/routerList`);
};
/* 根据路由id获取所有角色 */
export const getRoleListByRouterId = (data: any) => {
return http.request<BaseResult<any>>('get', `routerRole/private/getRoleListByRouterId`, { params: data });
};
/** 菜单管理-添加菜单 */
@ -11,6 +16,13 @@ export const createRouter = (data?: any) => {
return http.request<BaseResult<any>>('post', `router`, { data });
};
/** 菜单管理-清除选中菜单所有角色 */
export const clearRouterRole = (data: any) => {
return http.request<BaseResult<any>>('delete', `routerRole/clearRouterRole`, {
data,
});
};
/** 菜单管理-更新菜单 */
export const updateRouter = (data?: any) => {
return http.request<BaseResult<any>>('put', `router`, { data });
@ -20,15 +32,3 @@ export const updateRouter = (data?: any) => {
export const deletedRouterByIds = (data?: any) => {
return http.request<BaseResult<any>>('delete', `router`, { data });
};
/* 根据路由id获取所有角色 */
export const getRoleListByRouterId = (data: any) => {
return http.request<BaseResult<any>>('get', `router-role/private/roles/${data.routerId}`);
};
/** 菜单管理-清除选中菜单所有角色 */
export const clearRouterRole = (data: any) => {
return http.request<BaseResult<any>>('delete', `router-role/batch`, {
data,
});
};

View File

@ -3,29 +3,29 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 系统菜单图标---分页查询系统菜单图标 */
export const getMenuIconPage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `menu-icon/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `menuIcon/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 系统菜单图标---添加系统菜单图标 */
export const createMenuIcon = (data: any) => {
return http.request<BaseResult<object>>('post', 'menu-icon', { data });
return http.request<BaseResult<object>>('post', 'menuIcon', { data });
};
/** 系统菜单图标---更新系统菜单图标 */
export const updateMenuIcon = (data: any) => {
return http.request<BaseResult<object>>('put', 'menu-icon', { data });
return http.request<BaseResult<object>>('put', 'menuIcon', { data });
};
/** 系统菜单图标---删除系统菜单图标 */
export const deleteMenuIcon = (data: any) => {
return http.request<BaseResult<object>>('delete', 'menu-icon', { data });
return http.request<BaseResult<object>>('delete', 'menuIcon', { data });
};
/** 系统菜单图标---根据名称搜索图标 */
export const getIconNameListByIconName = (data: any) => {
return http.request<BaseResult<object>>('get', 'menu-icon/public', {
return http.request<BaseResult<object>>('get', 'menuIcon/public', {
params: data,
});
};

View File

@ -3,36 +3,36 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 管理员操作用户消息---管理员分页查询用户消息 */
export const getMessageReceivedPage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `message-received/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `messageReceived/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 管理员操作用户消息---管理员将用户消息标为已读 */
export const updateMessageReceivedByAdmin = (data: any) => {
return http.request<BaseResult<object>>('put', 'message-received', { data });
return http.request<BaseResult<object>>('put', 'messageReceived', { data });
};
/** 管理员操作用户消息---管理删除用户消息 */
export const deleteMessageReceivedByAdmin = (data: any) => {
return http.request<BaseResult<object>>('delete', 'message-received', {
return http.request<BaseResult<object>>('delete', 'messageReceived', {
data,
});
};
/** 用户系统消息---用户获取系统消息列表 */
export const getMessageReceivedPageByUser = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `message-received/private/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `messageReceived/private/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 系统消息---用户将消息标为已读 */
export const updateMessageByUser = (data: any) => {
return http.request<BaseResult<object>>('put', 'message-received/private/user/messages/read-status', { data });
return http.request<BaseResult<object>>('put', 'messageReceived/private/markAsRead', { data });
};
/** 系统消息---用户删除系统消息 */
export const deleteMessageReceivedByUser = (data: any) => {
return http.request<BaseResult<object>>('delete', 'message-received/private/user/messages', { data });
return http.request<BaseResult<object>>('delete', 'messageReceived/private/deleteMessage', { data });
};

View File

@ -25,10 +25,10 @@ export const deleteMessage = (data: any) => {
/** 用户系统消息---根据消息id查询消息详情 */
export const getMessageDetailById = (data: any) => {
return http.request<BaseResult<any>>('get', `message/private/message/${data.id}`);
return http.request<BaseResult<any>>('get', `message/private/getMessageDetailById`, { params: data });
};
/** 系统消息---根据消息id获取接收人信息 */
export const getReceivedUserinfoByMessageId = (data: any) => {
return http.request<BaseResult<any>>('get', `message/private/messages/${data.messageId}/recipients`);
return http.request<BaseResult<any>>('get', `message/private/getReceivedUserinfoByMessageId`, { params: data });
};

View File

@ -25,5 +25,5 @@ export const deleteMessageType = (data: any) => {
/** 系统消息类型---获取所有消息列表 */
export const getMessageTypeList = () => {
return http.request<BaseResult<ResultTable>>('get', '/messageType/private/messages');
return http.request<BaseResult<ResultTable>>('get', '/messageType/private/getMessageList');
};

View File

@ -3,29 +3,29 @@ import type { BaseResult, ResultTable } from '@/api/service/types';
/** 任务调度分组---分页查询任务调度分组 */
export const getSchedulersGroupPage = (data: any) => {
return http.request<BaseResult<ResultTable>>('get', `schedulers-group/${data.currentPage}/${data.pageSize}`, {
return http.request<BaseResult<ResultTable>>('get', `schedulersGroup/${data.currentPage}/${data.pageSize}`, {
params: data,
});
};
/** 任务调度分组---添加任务调度分组 */
export const createSchedulersGroup = (data: any) => {
return http.request<BaseResult<object>>('post', 'schedulers-group', { data });
return http.request<BaseResult<object>>('post', 'schedulersGroup', { data });
};
/** 任务调度分组---更新任务调度分组 */
export const updateSchedulersGroup = (data: any) => {
return http.request<BaseResult<object>>('put', 'schedulers-group', { data });
return http.request<BaseResult<object>>('put', 'schedulersGroup', { data });
};
/** 任务调度分组---删除任务调度分组 */
export const deleteSchedulersGroup = (data: any) => {
return http.request<BaseResult<object>>('delete', 'schedulers-group', {
return http.request<BaseResult<object>>('delete', 'schedulersGroup', {
data,
});
};
/** 任务调度分组---获取所有任务调度分组 */
export const getSchedulersGroupList = () => {
return http.request<BaseResult<ResultTable>>('get', 'schedulers-group/scheduler-groups');
return http.request<BaseResult<ResultTable>>('get', 'schedulersGroup/getSchedulersGroupList');
};

View File

@ -56,18 +56,20 @@ export const deleteUserByAdmin = (data: any) => {
};
/** 用户管理---根据用户id查询 */
export const loadUserinfoById = (data?: any) => {
return http.request<BaseResult<UserResult>>('get', `user/private/users/${data.id}`);
export const loadUserinfoById = (data?: object) => {
return http.request<BaseResult<UserResult>>('get', 'user/private/getUserinfoById', { params: data });
};
/** 用户信息---根据用户名查询用户列表 */
export const getUserListByKeyword = (data: any) => {
return http.request<BaseResult<object>>('get', 'user/private/users/search', { params: data });
return http.request<BaseResult<object>>('get', 'user/private/getUserListByKeyword', { params: data });
};
/** 用户管理---强制用户下线 */
export const forcedOfflineByAdmin = (data: any) => {
return http.request<BaseResult<UserResult>>('put', `user/${data.id}/force-logout`);
return http.request<BaseResult<UserResult>>('put', 'user/forcedOffline', {
data,
});
};
// -----------------------------------------
@ -81,12 +83,12 @@ export const userLogin = (data?: object) => {
/** 刷新`token` */
export const refreshTokenApi = (data?: object) => {
return http.request<BaseResult<RefreshTokenResult>>('post', 'user/private/refresh-token', { data });
return http.request<BaseResult<RefreshTokenResult>>('post', 'user/public/refreshToken', { data });
};
/** 发送邮件 */
export const sendLoginEmail = (data: any) => {
return http.request<BaseResult<any>>('post', '/user/public/email-code', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
return http.request<BaseResult<any>>('post', '/user/public/sendLoginEmail', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/** 获取用户信息,根据当前token获取 */
@ -111,5 +113,5 @@ export const updateUserPassword = (data: any) => {
/* 查询缓存中的已登录的用户 */
export const getCacheLoggedInPage = (data: any) => {
return http.request<BaseResult<any>>('get', `user/users/logged-in/${data.currentPage}/${data.pageSize}`, { data });
return http.request<BaseResult<any>>('get', `user/getCacheUserPage/${data.currentPage}/${data.pageSize}`, { data });
};

View File

@ -25,5 +25,5 @@ export const deleteDept = (data: any) => {
/** 部门管理---获取所有部门管理列表 */
export const getDeptList = () => {
return http.request<BaseResult<object>>('get', 'dept/private/departments');
return http.request<BaseResult<object>>('get', 'dept/private/getDeptList');
};

View File

@ -35,25 +35,27 @@ export const importPermission = (data: any) => {
/** 权限---获取所有权限 */
export const getPermissionList = () => {
return http.request<BaseResult<any>>('get', `permission/private/permissions`);
return http.request<BaseResult<any>>('get', `permission/private/getPermissionList`);
};
/* 权限---获取系统API信息 */
export const getSystemApiInfoList = () => {
return http.request<BaseResult<any>>('get', 'permission/private/system/apis');
return http.request<BaseResult<any>>('get', 'permission/private/getSystemApiInfoList');
};
/** 权限---批量修改权限父级 */
export const updatePermissionListByParentId = (data: any) => {
return http.request<BaseResult<object>>('patch', 'permission/update/permissions/parent', { data });
return http.request<BaseResult<object>>('patch', 'permission/update/permissionListByParentId', { data });
};
/** 权限---批量更新权限 */
export const updatePermissionBatch = (data: any) => {
return http.request<BaseResult<object>>('patch', 'permission/update/permissions/batch', { data });
return http.request<BaseResult<object>>('patch', 'permission/update/permissionBatch', { data });
};
/** 角色和权限---根据角色id获取权限内容 */
export const getPowerListByRoleId = (data: any) => {
return http.request<BaseResult<object>>('get', `rolePermission/private/permissions/${data.id}`);
return http.request<BaseResult<object>>('get', 'rolePermission/private/getPermissionListByRoleId', {
params: data,
});
};

View File

@ -41,12 +41,12 @@ export const updateRoleByFile = (data: any) => {
/** 为用户分配角色---根据用户id获取所有角色 */
export const getRoleListByUserId = (data: any) => {
return http.request<BaseResult<any>>('get', `user-role/private/roles/${data.userId}`);
return http.request<BaseResult<any>>('get', `userRole/private/getRoleListByUserId`, { params: data });
};
/** 为用户分配角色---为用户分配角色 */
export const createUserRole = (data: object) => {
return http.request<BaseResult<any>>('post', 'user-role', { data });
return http.request<BaseResult<any>>('post', 'userRole', { data });
};
/** 角色和权限---为角色分配权限 */

View File

@ -3,15 +3,10 @@ import type { BaseResult } from '@/api/service/types';
/** 系统管理-用户路由获取 */
export const fetchRouterAsync = () => {
return http.request<BaseResult<any>>('get', 'router/private/router-async');
return http.request<BaseResult<any>>('get', 'router/private/routerAsync');
};
/** 上传文件 */
export const uploadFile = (data: any) => {
return http.request<BaseResult<any>>('post', '/files/private/file', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};
/** 上传文件 */
export const uploadImage = (data: any) => {
return http.request<BaseResult<any>>('post', '/files/private/image', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
return http.request<BaseResult<any>>('post', '/files/private/upload', { data }, { headers: { 'Content-Type': 'multipart/form-data' } });
};

View File

@ -17,7 +17,7 @@ import { Plus } from '@element-plus/icons-vue';
import { ElMessage, UploadRawFile, UploadRequestOptions } from 'element-plus';
import { onMounted, ref } from 'vue';
import { SystemEnum } from '@/enums/upload';
import { uploadImage } from '@/api/v1/system/system';
import { uploadFile } from '@/api/v1/system/system';
import ImageLoading from '@/components/Upload/ImageLoading.vue';
const props = defineProps({
@ -28,7 +28,10 @@ const emits = defineEmits(['uploadCallback']);
const imageSrc = ref('');
/* 上传图片 */
/**
* * 上传时
* @param options
*/
const onUpload = async (options: UploadRequestOptions) => {
//
const file = options.file;
@ -36,12 +39,14 @@ const onUpload = async (options: UploadRequestOptions) => {
const data = { file, type };
//
const result: any = await uploadImage(data);
imageSrc.value = result.data.thUrl;
const result: any = await uploadFile(data);
imageSrc.value = result.data.url;
emits('uploadCallback', result);
};
/* 删除图片 */
/**
* * 删除图片
*/
const onRemoveImage = () => {
//
imageSrc.value = '';
@ -58,7 +63,9 @@ const onRemoveImage = () => {
emits('uploadCallback', data);
};
/* 上传之前 */
/**
* * 上传之前
*/
const beforeUpload = (file: UploadRawFile) => {
if (file.size > SystemEnum.IMAGE_SIZE) {
ElMessage.error(SystemEnum.IMAGE_MESSAGE);
@ -66,7 +73,9 @@ const beforeUpload = (file: UploadRawFile) => {
}
};
/* 初始化图片地址 */
/**
* * 初始化图片地址
*/
const handleInitiateImageSrc = () => {
const value = props.imageUrl;
if (value) imageSrc.value = value;
@ -76,3 +85,17 @@ onMounted(() => {
handleInitiateImageSrc();
});
</script>
<style lang="scss" scoped>
//.el-upload {
// width: 128px;
// height: 128px;
//}
//
//.el-upload-dragger {
// display: flex;
// align-items: center;
// justify-content: center;
// height: 100%;
//}
</style>

View File

@ -32,7 +32,7 @@ export const getPlatformConfig = async (app: App): Promise<undefined> => {
return axios({
method: 'get',
// TODO 後端讀取 platform-config.json
url: `/api/config/public/web-config`,
url: `/api/config/public/webConfig`,
})
.then(({ data: config }) => {
let $config = app.config.globalProperties.$config;

View File

@ -44,6 +44,7 @@ export const useMessageUserStore = defineStore('messageUserStore', {
const data = { ...this.pagination, ...this.form };
delete data.pageSizes;
delete data.total;
delete data.background;
// 获取系统消息列表
const result = await getMessageReceivedPageByUser(data);
@ -61,7 +62,7 @@ export const useMessageUserStore = defineStore('messageUserStore', {
this.messageDetail = result.data;
// 解码消息内容
(this.messageDetail as any).content = decode((this.messageDetail as any)?.content);
this.messageDetail.content = decode(this.messageDetail?.content);
}
},

View File

@ -63,6 +63,7 @@ export const useAdminUserStore = defineStore('adminUserStore', {
const data = { ...this.pagination, ...this.form };
delete data.pageSizes;
delete data.total;
delete data.background;
// 获取用户信息列表
const result = await getUserPageByAdmin(data);
@ -92,7 +93,7 @@ export const useAdminUserStore = defineStore('adminUserStore', {
/** 强制用户下线 */
async forcedOffline(data: any) {
const result = await forcedOfflineByAdmin({ data });
const result = await forcedOfflineByAdmin(data);
return storeMessage(result);
},

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia';
import { deleteFiles, fetchAddFiles, getFilesPage, getFilesStoragePath, updateFiles } from '@/api/v1/files';
import { deleteFiles, fetchAddFiles, getFilesPage, getFilesStoragePath, getMediaTypeList, updateFiles } from '@/api/v1/files';
import { pageSizes } from '@/enums/baseConstant';
import { storeMessage } from '@/utils/message';
import { storePagination } from '@/store/useStorePagination';
@ -12,6 +12,8 @@ export const useFilesStore = defineStore('filesStore', {
return {
// 系统文件表列表
datalist: [],
// 所有文件类型
allMediaTypes: [],
// 所有文件存储路径
allFilesStoragePath: [],
// 查询表单
@ -21,11 +23,7 @@ export const useFilesStore = defineStore('filesStore', {
// 文件在服务器上的存储路径
filepath: undefined,
// 文件的MIME类型
contentType: undefined,
// 扩展名
ext: undefined,
// 存储平台
platform: undefined,
fileType: undefined,
// 下载数量
downloadCount: undefined,
},
@ -48,6 +46,7 @@ export const useFilesStore = defineStore('filesStore', {
const data = { ...this.pagination, ...this.form };
delete data.pageSizes;
delete data.total;
delete data.background;
// 获取系统文件表列表
const result = await getFilesPage(data);
@ -75,6 +74,14 @@ export const useFilesStore = defineStore('filesStore', {
return storeMessage(result);
},
/** 获取所有文件类型 */
async loadMediaTypeList() {
const result = await getMediaTypeList();
if (result.code === 200) {
this.allMediaTypes = result.data;
}
},
/** 获取所有文件类型 */
async loadFilesStoragePath() {
const result = await getFilesStoragePath();

View File

@ -10,7 +10,7 @@ import {
updatePermission,
updatePermissionBatch,
updatePermissionListByParentId,
} from '@/api/v1/system/permission';
} from '@/api/v1/system/power';
import { pageSizes } from '@/enums/baseConstant';
import { storeMessage } from '@/utils/message';
import { storePagination } from '@/store/useStorePagination';

View File

@ -22,17 +22,6 @@ export const useUserStore = defineStore('system-user', {
isRemembered: true,
// 登录页的免登录存储几天默认7天
readMeDay: 7,
// 用户修改信息列表
userinfoForm: {
avatar: '',
username: '',
nickname: '',
email: '',
phone: '',
summary: '',
password: '',
sex: '',
},
}),
actions: {
/** 登入 */

View File

@ -44,14 +44,4 @@ export type userType = {
permissions?: Array<string>;
isRemembered?: boolean;
readMeDay?: number;
userinfoForm?: {
avatar: string;
username: string;
nickname: string;
email: string;
phone: string;
summary: string;
password: string;
sex: string;
};
};

View File

@ -4,19 +4,14 @@ import { sexConstant } from '@/enums/baseConstant';
import { $t } from '@/plugins/i18n';
import { useAdminUserStore } from '@/store/system/adminUser';
import { message } from '@/utils/message';
import { rules } from '@/views/account-settings/utils/columns';
import { cropperBlob, handleSubmitImage, isShow, onSearchByUserinfo, rules, uploadAvatarSrc, userInfos } from '@/views/account-settings/utils';
import uploadLine from '~icons/ri/upload-line';
import { createFormData, deviceDetection } from '@pureadmin/utils';
import { deviceDetection } from '@pureadmin/utils';
import type { FormInstance } from 'element-plus';
import { onMounted, ref } from 'vue';
import { useUserStore } from '@/store/system/user';
import { uploadImage } from '@/api/v1/system/system';
import { storeToRefs } from 'pinia';
const adminUserStore = useAdminUserStore();
const userStore = useUserStore();
const { userinfoForm } = storeToRefs(userStore);
// Ref
const userInfoFormRef = ref<FormInstance>();
// Ref
@ -25,12 +20,6 @@ const uploadRef = ref();
const cropRef = ref();
// base64
const imgBase64Src = ref('');
//
const isShow = ref(false);
//
const cropperBlob = ref();
//
const uploadAvatarSrc = ref();
/** 关闭弹窗 */
const handleClose = () => {
@ -52,50 +41,16 @@ const onChange = (uploadFile: any) => {
reader.readAsDataURL(uploadFile.raw);
};
/** 获取用户信息内容 */
const onSearchByUserinfo = async () => {
const data = await userStore.loadUserinfo();
userinfoForm.value.summary = data.personDescription;
userinfoForm.value.avatar = data.avatar;
userinfoForm.value.username = data.username;
userinfoForm.value.nickname = data.nickname;
userinfoForm.value.email = data.email;
userinfoForm.value.phone = data.phone;
userinfoForm.value.nickname = data.nickname;
userinfoForm.value.password = data.password;
userinfoForm.value.sex = data.sex;
};
/** 修改头像 */
const handleSubmitImage = async () => {
//
const formData = createFormData({
files: new File([cropperBlob.value], 'avatar'),
type: 'avatar',
});
//
const result = await uploadImage(formData);
//
if (result.code === 200) {
uploadAvatarSrc.value = result.data.url;
userinfoForm.value.avatar = result.data.thUrl;
message($t('upload_success'), { type: 'success' });
isShow.value = false;
}
};
/** 提交表单 */
const onSubmit = async (formEl: FormInstance) => {
await formEl.validate(async (valid) => {
if (valid) {
//
const avatar = uploadAvatarSrc.value;
if (avatar) userinfoForm.value.avatar = avatar;
if (avatar) userInfos.avatar = avatar;
//
const result = await adminUserStore.editUserinfo(userinfoForm.value);
const result = await adminUserStore.editUserinfo(userInfos);
if (!result) return;
//
@ -116,9 +71,9 @@ onMounted(() => {
<h3 class="my-8">个人信息</h3>
<!-- 头像 -->
<el-form ref="userInfoFormRef" :model="userinfoForm" :rules="rules" label-position="top">
<el-form ref="userInfoFormRef" :model="userInfos" :rules="rules" label-position="top">
<el-form-item :label="$t('avatar')">
<el-avatar :size="80" :src="userinfoForm.avatar" />
<el-avatar :size="80" :src="userInfos.avatar" />
<el-upload ref="uploadRef" :auto-upload="false" :limit="1" :on-change="onChange" :show-file-list="false" accept="image/*">
<el-button class="ml-4" plain>
<IconifyIconOffline :icon="uploadLine" />
@ -134,27 +89,27 @@ onMounted(() => {
<!-- 用户名 -->
<el-form-item :label="$t('adminUser_username')" prop="username">
<el-input v-model="userinfoForm.username" :placeholder="$t('adminUser_username')" autocomplete="off" disabled type="text" />
<el-input v-model="userInfos.username" :placeholder="$t('adminUser_username')" autocomplete="off" disabled type="text" />
</el-form-item>
<!-- 昵称 -->
<el-form-item :label="$t('adminUser_nickname')" prop="nickname">
<el-input v-model="userinfoForm.nickname" :placeholder="$t('adminUser_nickname')" autocomplete="off" type="text" />
<el-input v-model="userInfos.nickname" :placeholder="$t('adminUser_nickname')" autocomplete="off" type="text" />
</el-form-item>
<!-- 邮箱 -->
<el-form-item :label="$t('adminUser_email')" prop="email">
<el-input v-model="userinfoForm.email" :placeholder="$t('adminUser_email')" autocomplete="off" type="text" />
<el-input v-model="userInfos.email" :placeholder="$t('adminUser_email')" autocomplete="off" type="text" />
</el-form-item>
<!-- 手机号 -->
<el-form-item :label="$t('adminUser_phone')" prop="phone">
<el-input v-model="userinfoForm.phone" :placeholder="$t('adminUser_phone')" autocomplete="off" type="text" />
<el-input v-model="userInfos.phone" :placeholder="$t('adminUser_phone')" autocomplete="off" type="text" />
</el-form-item>
<!-- 性别 -->
<el-form-item :label="$t('adminUser_sex')" prop="sex">
<el-select v-model="userinfoForm.sex" :placeholder="$t('adminUser_sex')" clearable filterable>
<el-select v-model="userInfos.sex" :placeholder="$t('adminUser_sex')" clearable filterable>
<el-option v-for="(item, index) in sexConstant" :key="index" :label="item.label" :navigationBar="false" :value="item.value" />
</el-select>
</el-form-item>
@ -162,7 +117,7 @@ onMounted(() => {
<!-- 用户简介 -->
<el-form-item :label="$t('adminUser_summary')" prop="summary">
<el-input
v-model="userinfoForm.summary"
v-model="userInfos.summary"
:autosize="{ minRows: 3, maxRows: 6 }"
:placeholder="$t('adminUser_summary')"
autocomplete="off"

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { $t } from '@/plugins/i18n';
import { useUserLoginLogStore } from '@/store/monitor/userLoginLog';
import { columns } from '@/views/account-settings/utils/columns';
import { columns } from '@/views/account-settings/utils';
import { PureTable } from '@pureadmin/table';
import { deviceDetection } from '@pureadmin/utils';
import { onMounted, reactive } from 'vue';

View File

@ -3,18 +3,14 @@ import Text from '@/components/ReText';
import LaySidebarTopCollapse from '@/layout/components/lay-sidebar/components/SidebarTopCollapse.vue';
import { useDataThemeChange } from '@/layout/hooks/useDataThemeChange';
import { $t } from '@/plugins/i18n';
import { panes } from '@/views/account-settings/utils/columns';
import { panes, userInfos } from '@/views/account-settings/utils';
import leftLine from '~icons/ri/arrow-left-s-line';
import { deviceDetection, useGlobal } from '@pureadmin/utils';
import { onBeforeMount, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/store/system/user';
import { storeToRefs } from 'pinia';
const router = useRouter();
const isOpen = ref(!deviceDetection());
const userStore = useUserStore();
const { userinfoForm } = storeToRefs(userStore);
const { $storage } = useGlobal<GlobalPropertiesApi>();
const witchPane = ref('profile');
@ -40,13 +36,13 @@ onBeforeMount(() => {
</el-menu-item>
<div class="flex items-center ml-8 mt-4 mb-4">
<el-avatar :size="48" :src="userinfoForm.avatar" />
<el-avatar :size="48" :src="userInfos.avatar" />
<div class="ml-4 flex flex-col max-w-[130px]">
<Text class="font-bold !self-baseline">
{{ userinfoForm.nickname }}
{{ userInfos.nickname }}
</Text>
<Text class="!self-baseline" type="info">
{{ userinfoForm.username }}
{{ userInfos.username }}
</Text>
</div>
</div>

View File

@ -10,7 +10,7 @@ import AccountManagementIcon from '~icons/ri/profile-line';
import AccountManagement from '@/views/account-settings/components/account-management.vue';
export const columns: TableColumnList = [
{ type: 'index', index: (index: number) => index + 1, label: $t("index"), width: 60 },
{ type: 'index', index: (index: number) => index + 1, label: $t('index'), width: 60 },
// 用户名
{ label: $t('userLoginLog_username'), prop: 'username', width: 180 },
// 登录Ip

View File

@ -0,0 +1,62 @@
import { useUserStore } from '@/store/system/user';
import { reactive, ref } from 'vue';
import { createFormData } from '@pureadmin/utils';
import { uploadFile } from '@/api/v1/system/system';
import { message } from '@/utils/message';
import { $t } from '@/plugins/i18n';
const userStore = useUserStore();
// 剪裁完成后头像数据
export const cropperBlob = ref();
// 上传地址路径
export const uploadAvatarSrc = ref();
// 剪裁头像是否显示
export const isShow = ref(false);
// 用户信息内容
export const userInfos = reactive({
avatar: '',
username: '',
nickname: '',
email: '',
phone: '',
summary: '',
password: '',
sex: '',
});
/** 获取用户信息内容 */
export const onSearchByUserinfo = async () => {
const data = await userStore.loadUserinfo();
userInfos.summary = data.personDescription;
userInfos.avatar = data.avatar;
userInfos.username = data.username;
userInfos.nickname = data.nickname;
userInfos.email = data.email;
userInfos.phone = data.phone;
userInfos.nickname = data.nickname;
userInfos.password = data.password;
userInfos.sex = data.sex;
};
/** 修改头像 */
export const handleSubmitImage = async () => {
// 上传头像表单
const formData = createFormData({
files: new File([cropperBlob.value], 'avatar'),
type: 'avatar',
});
// 上传头像
const result = await uploadFile(formData);
// 上传成功后关闭弹窗
if (result.code === 200) {
uploadAvatarSrc.value = result.data.filepath;
userInfos.avatar = result.data.url;
message($t('upload_success'), { type: 'success' });
isShow.value = false;
}
};

View File

@ -0,0 +1,2 @@
export * from './hooks';
export * from './columns';

View File

@ -21,22 +21,34 @@ export async function onSearch() {
/** 添加邮件模板表 */
export function onAdd() {
const formRef = ref();
const formInline = {
templateName: undefined,
emailUser: undefined,
subject: undefined,
isDefault: false,
body: undefined,
type: undefined,
};
addDialog({
title: `${$t('addNew')}${$t('emailTemplate')}`,
props: { formInline },
props: {
formInline: {
templateName: undefined,
emailUser: undefined,
subject: undefined,
isDefault: false,
body: undefined,
type: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(EmailTemplateDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(EmailTemplateDialog, {
ref: formRef,
formInline: {
templateName: undefined,
emailUser: undefined,
subject: undefined,
isDefault: false,
body: undefined,
type: undefined,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -54,22 +66,34 @@ export function onAdd() {
/* 更新邮件模板表 */
export function onUpdate(row: any) {
const formRef = ref();
const formInline = {
templateName: row.templateName,
emailUser: row.emailUser,
subject: row.subject,
isDefault: row.isDefault,
body: row.body,
type: row.type,
};
addDialog({
title: `${$t('modify')}${$t('emailTemplate')}`,
props: { formInline },
props: {
formInline: {
templateName: row.templateName,
emailUser: row.emailUser,
subject: row.subject,
isDefault: row.isDefault,
body: row.body,
type: row.type,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(EmailTemplateDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(EmailTemplateDialog, {
ref: formRef,
formInline: {
templateName: row.templateName,
emailUser: row.emailUser,
subject: row.subject,
isDefault: row.isDefault,
body: row.body,
type: row.type,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;

View File

@ -8,7 +8,8 @@ import { usePublicHooks } from '@/views/hooks';
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
//
email: '',
email: undefined,
emailTemplate: undefined,
//
password: undefined,
// Host
@ -17,7 +18,6 @@ const props = withDefaults(defineProps<FormProps>(), {
port: undefined,
//
smtpAgreement: undefined,
openSSL: false,
//
isDefault: false,
}),

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 邮箱

View File

@ -22,23 +22,24 @@ export async function onSearch() {
/** 添加邮箱用户发送配置 */
export function onAdd() {
const formInline = {
email: undefined,
password: undefined,
host: undefined,
port: undefined,
smtpAgreement: undefined,
openSSL: true,
isDefault: false,
};
addDialog({
title: `${$t('addNew')}${$t('emailUsers')}`,
props: { formInline },
props: {
formInline: {
email: undefined,
password: undefined,
host: undefined,
port: undefined,
smtpAgreement: undefined,
openSSL: true,
isDefault: false,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(EmailUsersDialog, { ref: formRef, formInline }),
contentRenderer: () => h(EmailUsersDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -55,23 +56,24 @@ export function onAdd() {
/* 更新邮箱用户发送配置 */
export function onUpdate(row: any) {
const formInline = {
email: row.email,
password: row.password,
host: row.host,
port: row.port,
smtpAgreement: row.smtpAgreement,
openSSL: row.openSSL,
isDefault: row.isDefault,
};
addDialog({
title: `${$t('modify')}${$t('emailUsers')}`,
props: { formInline },
props: {
formInline: {
email: row.email,
password: row.password,
host: row.host,
port: row.port,
smtpAgreement: row.smtpAgreement,
openSSL: row.openSSL,
isDefault: row.isDefault,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(EmailUsersDialog, { ref: formRef, formInline }),
contentRenderer: () => h(EmailUsersDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -1,10 +0,0 @@
export const auth = {
// 分页查询
query: 'menuIcon::query',
// 添加操作
add: ['menuIcon::add'],
// 更新操作
update: ['menuIcon::update'],
// 删除操作
delete: ['menuIcon::delete'],
};

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// icon 类名

View File

@ -19,15 +19,14 @@ export async function onSearch() {
/** 添加系统菜单图标 */
export function onAdd() {
const formInline = { iconCode: undefined, iconName: undefined };
addDialog({
title: `${$t('addNew')} ${$t('menuIcon')}`,
props: { formInline },
props: { formInline: { confirmText: '' } },
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(MenuIconDialog, { ref: formRef, formInline }),
contentRenderer: () => h(MenuIconDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -44,15 +43,19 @@ export function onAdd() {
/* 更新系统菜单图标 */
export function onUpdate(row: any) {
const formInline = { iconCode: row.iconCode, iconName: row.iconName };
addDialog({
title: `${$t('modify')} ${$t('menuIcon')}`,
props: { formInline },
props: {
formInline: {
iconCode: row.iconCode,
iconName: row.iconName,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(MenuIconDialog, { ref: formRef, formInline }),
contentRenderer: () => h(MenuIconDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -3,10 +3,9 @@ import { $t } from '@/plugins/i18n';
import { onMounted, ref } from 'vue';
import { FormInstance } from 'element-plus';
import ReCol from '@/components/ReCol';
import { auth, form, onSearch, rules, submitForm } from '@/views/configuration/web-configuration/utils';
import { form, onSearch, rules, submitForm } from '@/views/configuration/web-configuration/utils';
import { userI18nTypeStore } from '@/store/i18n/i18nType';
import { usePublicHooks } from '@/views/hooks';
import { hasAuth } from '@/router/utils';
defineOptions({ name: 'WebConfiguration' });
@ -332,7 +331,7 @@ onMounted(() => {
</re-col>
<!-- 提交内容 -->
<re-col v-if="hasAuth(auth.update)" :sm="24" :value="24" :xs="24">
<re-col :sm="24" :value="24" :xs="24">
<el-form-item label-width="0">
<el-button class="w-full" plain type="primary" @click="submitForm(ruleFormRef)">
{{ $t('modifyingConfiguration') }}

View File

@ -1,3 +0,0 @@
export const auth = {
update: ['config::update'],
};

View File

@ -1,3 +1,2 @@
export * from './columns';
export * from './auth';
export * from './hooks';

View File

@ -11,7 +11,6 @@ interface Props {
type: string;
file: any;
fileType: string;
isAppend: boolean;
};
}
@ -37,7 +36,6 @@ const props = withDefaults(defineProps<Props>(), {
type: '',
file: undefined,
fileType: '',
isAppend: true,
}),
});
@ -65,10 +63,6 @@ defineExpose({ formRef });
</el-select>
</el-form-item>
<el-form-item :label="$t('isAppend')" prop="isAppend">
<el-switch v-model="form.isAppend" style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949" />
</el-form-item>
<el-form-item :label="$t('files')" prop="file">
<el-upload ref="uploadRef" v-model:file-list="form.file" :autoUpload="false" :limit="1" :on-exceed="handleExceed" class="w-full mt-2" drag>
<el-icon class="el-icon--upload">
@ -81,6 +75,6 @@ defineExpose({ formRef });
</el-form-item>
<!-- 更新提示 -->
<el-text type="danger">{{ $t('update_i18n_tip') }}</el-text>
<el-text type="danger">{{ $t('update_tip') }}</el-text>
</el-form>
</template>

View File

@ -8,7 +8,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
{ label: $t('i18n.keyName'), prop: 'keyName' },

View File

@ -23,18 +23,18 @@ export const downloadI18nSetting = (type: string) => {
i18nStore.downloadI18nFile({ type });
};
/* 上传多语言配置 */
/* 下载多语言配置 */
export const updateI18nSetting = (fileType: string) => {
const uploadFormRef = ref();
const form = { type: '', file: undefined, fileType, isAppend: true };
addDialog({
title: $t('update_multilingual'),
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
props: { form },
contentRenderer: () => h(I18NUploadDialog, { ref: uploadFormRef, form }),
props: { form: { type: undefined, file: undefined, fileType } },
contentRenderer: () => h(I18NUploadDialog, { ref: uploadFormRef, form: { type: '', file: undefined, fileType } }),
beforeSure: async (done, { options }) => {
uploadFormRef.value.formRef.validate(async (valid: any) => {
if (!valid) return;
@ -49,15 +49,14 @@ export const updateI18nSetting = (fileType: string) => {
/* 行内容添加 打开添加弹窗 */
export const onAdd = () => {
const formInline = { keyName: '', translation: '', typeName: '' };
addDialog({
title: $t('addMultilingual'),
props: { formInline },
props: { formInline: { keyName: '', translation: '', typeName: '' } },
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(I18nDialog, { ref: formRef, formInline }),
contentRenderer: () => h(I18nDialog, { ref: formRef, formInline: { keyName: '', translation: '', typeName: '' } }),
footerButtons: [
{
label: $t('cancel'),
@ -105,14 +104,21 @@ export const onAdd = () => {
/* 当表格修改时 */
export const onUpdate = (row: any) => {
const id = row.id;
const formInline = { keyName: row.keyName, translation: row.translation, typeName: row.typeName };
addDialog({
title: $t('update_multilingual'),
props: { formInline },
props: {
formInline: { keyName: row.keyName, translation: row.translation, typeName: row.typeName },
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(I18nDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(I18nDialog, {
ref: formRef,
formInline: { keyName: row.keyName, translation: row.translation, typeName: row.typeName },
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormProps;
formRef.value.ruleFormRef.validate(async (valid: any) => {

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
{ label: $t('i18n_typeName'), prop: 'typeName' },

View File

@ -17,15 +17,16 @@ export async function onSearch() {
/* 添加多语言 */
export function onAdd() {
const formInline = { typeName: '', summary: '', isDefault: false };
addDialog({
title: `添加多语言类型`,
props: { formInline },
props: {
formInline: { typeName: '', summary: '', isDefault: false },
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(AddI18nType, { ref: formRef, formInline }),
contentRenderer: () => h(AddI18nType, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -42,15 +43,20 @@ export function onAdd() {
/* 更新内容 */
export function onUpdate(row: any) {
const formInline = { typeName: row.typeName, summary: row.summary, isDefault: row.isDefault };
addDialog({
title: `修改多语言类型`,
props: { formInline },
props: {
formInline: {
typeName: row.typeName,
summary: row.summary,
isDefault: row.isDefault,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(AddI18nType, { ref: formRef, formInline }),
contentRenderer: () => h(AddI18nType, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -4,7 +4,11 @@ import 'md-editor-v3/lib/style.css';
import { uploadFile } from '@/api/v1/system/system';
import { formState } from '@/views/message-manger/message-editing/utils';
/* 上传图片 */
/**
* * 上传图片
* @param files
* @param callback
*/
const onUploadImg = async (files: any, callback: any) => {
//
const res = await Promise.all(
@ -13,9 +17,6 @@ const onUploadImg = async (files: any, callback: any) => {
const form = new FormData();
form.append('file', file);
form.append('type', 'message');
// markdown使
// md
resolve(await uploadFile(form));
});
})

View File

@ -161,7 +161,7 @@ onMounted(() => {
<!-- 消息等级简介 -->
<el-form-item :label="$t('extra')" prop="extra">
<el-input v-model="formState.extra" maxlength="4" show-word-limit type="text" />
<el-input v-model="formState.extra" maxlength="5" show-word-limit type="text" />
</el-form-item>
<!-- 提交 -->

View File

@ -15,7 +15,7 @@ const token = ref(getToken().token);
editorConfig.MENU_CONF['uploadImage'] = {
//
server: `${defaultConfig.baseURL}/files/private/file`,
server: `${defaultConfig.baseURL}/files/private/upload`,
// form-data fieldName
fieldName: 'file',
//
@ -27,8 +27,10 @@ editorConfig.MENU_CONF['uploadImage'] = {
customInsert(res: any, insertFn) {
// res.data.url
if (res.data.url) {
// insertFn
insertFn(res.data.url);
setTimeout(() => {
// insertFn
insertFn(res.data.url);
}, 2000);
}
},
};

View File

@ -2,7 +2,7 @@ import { reactive, ref } from 'vue';
import type { UploadRawFile, UploadRequestOptions } from 'element-plus';
import { SystemEnum } from '@/enums/upload';
import { message } from '@/utils/message';
import { uploadImage } from '@/api/v1/system/system';
import { uploadFile } from '@/api/v1/system/system';
import { useAdminUserStore } from '@/store/system/adminUser';
// 用户信息列表
@ -36,9 +36,9 @@ export const onSearchUserinfo = async (keyword: string) => {
/** 上传时 */
export const onUpload = async (options: UploadRequestOptions) => {
const data = { file: options.file, type: 'message' };
const result: any = await uploadImage(data);
coverUrl.value = result.data.thUrl;
formState.cover = result.data.thUrl;
const result: any = await uploadFile(data);
coverUrl.value = result.data.url;
formState.cover = result.data.filepath;
};
/** 上传之前 */

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 消息标题

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import { uploadImage } from '@/api/v1/system/system';
import { uploadFile } from '@/api/v1/system/system';
import { updateMessage } from '@/views/message-manger/message-send/utils';
/* 上传图片 */
@ -13,7 +13,7 @@ const onUploadImg = async (files: any, callback: any) => {
const form = new FormData();
form.append('file', file);
form.append('type', 'message');
resolve(await uploadImage(form));
resolve(await uploadFile(form));
});
})
);

View File

@ -14,7 +14,7 @@ const token = ref(getToken().token);
editorConfig.MENU_CONF['uploadImage'] = {
//
server: '/api/files/private/file',
server: '/api/files/private/upload',
// form-data fieldName
fieldName: 'file',
//

View File

@ -8,7 +8,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 消息标题

View File

@ -6,7 +6,7 @@ import { $t } from '@/plugins/i18n';
import { useAdminUserStore } from '@/store/system/adminUser';
import { decode, encode } from 'js-base64';
import type { UploadRequestOptions } from 'element-plus';
import { uploadImage } from '@/api/v1/system/system';
import { uploadFile } from '@/api/v1/system/system';
import { useMessageSendStore } from '@/store/message/messageSend';
export const formRef = ref();
@ -150,7 +150,7 @@ export const onDeleteBatch = async () => {
/** 上传时 */
export const onUpload = async (options: UploadRequestOptions) => {
const data = { file: options.file, type: 'message' };
const result: any = await uploadImage(data);
coverUrl.value = result.data.thUrl;
updateMessage.cover = result.data.thUrl;
const result: any = await uploadFile(data);
coverUrl.value = result.data.url;
updateMessage.cover = result.data.filepath;
};

View File

@ -8,7 +8,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 是否启用

View File

@ -20,14 +20,21 @@ export async function onSearch() {
/** 添加系统消息类型 */
export function onAdd() {
const formInline = { status: true, messageName: undefined, messageType: undefined, summary: undefined };
addDialog({
title: `${$t('addNew')}${$t('messageType')}`,
props: { formInline },
props: {
formInline: {
status: true,
messageName: undefined,
messageType: undefined,
summary: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(MessageTypeDialog, { ref: formRef, formInline }),
contentRenderer: () => h(MessageTypeDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -44,15 +51,21 @@ export function onAdd() {
/* 更新系统消息类型 */
export function onUpdate(row: any) {
const formInline = { status: row.status, messageName: row.messageName, messageType: row.messageType, summary: row.summary };
addDialog({
title: `${$t('modify')}${$t('messageType')}`,
props: { formInline },
props: {
formInline: {
status: row.status,
messageName: row.messageName,
messageType: row.messageType,
summary: row.summary,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(MessageTypeDialog, { ref: formRef, formInline }),
contentRenderer: () => h(MessageTypeDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -64,6 +64,13 @@ onMounted(() => {
@page-size-change="onPageSizeChange"
@page-current-change="onCurrentPageChange"
>
<!-- 用户性别 -->
<template #sex="{ row }">
<el-tag :type="row.sex === 0 ? 'danger' : null" effect="plain">
{{ row.sex === 1 ? $t('man') : $t('female') }}
</el-tag>
</template>
<template #username="{ row }">
<el-button v-show="row.username" link type="primary" @click="selectUserinfo(row.createUser)">
{{ row.username }}

View File

@ -11,7 +11,9 @@ export const columns: TableColumnList = [
{ label: $t('adminUser_username'), prop: 'username', slot: 'username', minWidth: 120 },
{ label: $t('adminUser_email'), prop: 'email', minWidth: 120 },
{ label: $t('adminUser_phone'), prop: 'phone', minWidth: 120 },
{ label: $t('adminUser_summary'), prop: 'summary', minWidth: 150 },
// 性别
{ label: $t('adminUser_sex'), prop: 'sex', slot: 'sex', width: 90 },
{ label: $t('adminUser_summary'), prop: 'personDescription', minWidth: 150 },
{
label: $t('table.operation'),
fixed: 'right',

View File

@ -0,0 +1,6 @@
export const auth = {
// 分页查询
search: ['scheduleExecuteLog::query', "'scheduleExecuteLog::queryPage'"],
// 删除操作
delete: ['scheduleExecuteLog::delete'],
};

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 任务名称

View File

@ -1,3 +1,4 @@
export * from './columns';
export * from './auth';
export * from './hooks';
export * from './types';

View File

@ -1,6 +1,6 @@
export const auth = {
// 分页查询
search: 'userLoginLog::query',
search: ['userLoginLog::query', 'userLoginLog::queryPage'],
// 删除操作
delete: ['userLoginLog::delete'],
};

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 用户名

View File

@ -1,3 +1,4 @@
export * from './columns';
export * from './auth';
export * from './hooks';
export * from './types';

View File

@ -7,7 +7,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 分组名称

View File

@ -20,15 +20,19 @@ export async function onSearch() {
/** 添加任务调度分组 */
export function onAdd() {
const formInline = { groupName: undefined, description: undefined };
addDialog({
title: `${$t('addNew')}${$t('schedulersGroup')}`,
props: { formInline },
props: {
formInline: {
groupName: undefined,
description: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(SchedulersGroupDialog, { ref: formRef, formInline }),
contentRenderer: () => h(SchedulersGroupDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -45,14 +49,19 @@ export function onAdd() {
/* 更新任务调度分组 */
export function onUpdate(row: any) {
const formInline = { groupName: row.groupName, description: row.description };
addDialog({
title: `${$t('modify')}${$t('schedulersGroup')}`,
props: { formInline },
props: {
formInline: {
groupName: row.groupName,
description: row.description,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(SchedulersGroupDialog, { ref: formRef, formInline }),
contentRenderer: () => h(SchedulersGroupDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -6,7 +6,7 @@ export const columns: TableColumnList = [
{
type: 'index',
index: (index: number) => index + 1,
label: $t("index"),
label: $t('index'),
width: 60,
},
// 任务名称

View File

@ -18,22 +18,22 @@ export async function onSearch() {
/** 添加Schedulers视图 */
export function onAdd() {
const formInline = {
jobName: undefined,
jobGroup: undefined,
description: undefined,
jobClassName: undefined,
cronExpression: undefined,
isUpdate: false,
};
addDialog({
title: `${$t('addNew')}${$t('schedulers')}`,
props: { formInline },
props: {
formInline: {
jobName: undefined,
jobGroup: undefined,
description: undefined,
jobClassName: undefined,
cronExpression: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(SchedulersDialog, { ref: formRef, formInline }),
contentRenderer: () => h(SchedulersDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -50,22 +50,23 @@ export function onAdd() {
/* 更新Schedulers视图 */
export function onUpdate(row: any) {
const formInline = {
jobName: row.jobName,
jobGroup: row.jobGroup,
description: row.description,
jobClassName: row.jobClassName,
cronExpression: row.cronExpression,
isUpdate: true,
};
addDialog({
title: `${$t('modify')}${$t('schedulers')}`,
props: { formInline },
props: {
formInline: {
jobName: row.jobName,
jobGroup: row.jobGroup,
description: row.description,
jobClassName: row.jobClassName,
cronExpression: row.cronExpression,
isUpdate: true,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(SchedulersDialog, { ref: formRef, formInline }),
contentRenderer: () => h(SchedulersDialog, { ref: formRef }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -50,27 +50,41 @@ export async function onSearch() {
/** 添加用户信息 */
export function onAdd() {
isAddUserinfo.value = true;
const formInline = {
username: undefined,
nickname: undefined,
email: undefined,
phone: undefined,
password: undefined,
avatar: undefined,
sex: undefined,
summary: undefined,
status: false,
deptId: undefined,
};
addDialog({
title: `${$t('addNew')}${$t('adminUser')}`,
props: { formInline },
props: {
formInline: {
username: undefined,
nickname: undefined,
email: undefined,
phone: undefined,
password: undefined,
avatar: undefined,
sex: undefined,
summary: undefined,
status: false,
deptId: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(AdminUserDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(AdminUserDialog, {
ref: formRef,
formInline: {
username: undefined,
nickname: undefined,
email: undefined,
phone: undefined,
password: undefined,
avatar: undefined,
sex: undefined,
summary: undefined,
status: false,
deptId: undefined,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -88,26 +102,41 @@ export function onAdd() {
/* 更新用户信息 */
export function onUpdate(row: any) {
isAddUserinfo.value = false;
const formInline = {
username: row.username,
nickname: row.nickname,
email: row.email,
phone: row.phone,
password: row.password,
avatar: row.avatar,
sex: row.sex,
summary: row.summary,
status: row.status,
deptId: row.deptId,
};
addDialog({
title: `${$t('modify')}${$t('adminUser')}`,
props: { formInline },
props: {
formInline: {
username: row.username,
nickname: row.nickname,
email: row.email,
phone: row.phone,
password: row.password,
avatar: row.avatar,
sex: row.sex,
summary: row.summary,
status: row.status,
deptId: row.deptId,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(AdminUserDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(AdminUserDialog, {
ref: formRef,
formInline: {
username: row.username,
nickname: row.nickname,
email: row.email,
phone: row.phone,
password: row.password,
avatar: row.avatar,
sex: row.sex,
summary: row.summary,
status: row.status,
deptId: row.deptId,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -148,6 +177,7 @@ export const onDeleteBatch = async () => {
addDialog({
title: $t('deleteBatchTip'),
props: { formInline: { confirmText: '' } },
draggable: true,
fullscreenIcon: true,

View File

@ -19,20 +19,29 @@ export async function onSearch() {
/** 添加部门 */
export function onAdd(parentId: string = '0') {
const formInline = {
parentId,
manager: undefined,
deptName: undefined,
summary: undefined,
};
addDialog({
title: `${$t('addNew')}${$t('dept')}`,
props: { formInline },
props: {
formInline: {
parentId,
manager: undefined,
deptName: undefined,
summary: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(DeptDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(DeptDialog, {
ref: formRef,
formInline: {
parentId,
manager: undefined,
deptName: undefined,
summary: undefined,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -49,19 +58,30 @@ export function onAdd(parentId: string = '0') {
/* 更新部门 */
export function onUpdate(row: any) {
const formInline = {
parentId: row.parentId,
manager: row.manager ? row.manager.split(',') : row.manager,
deptName: row.deptName,
summary: row.summary,
};
addDialog({
title: `${$t('modify')}${$t('dept')}`,
props: { formInline },
props: {
formInline: {
parentId: row.parentId,
manager: row.manager ? row.manager.split(',') : row.manager,
deptName: row.deptName,
summary: row.summary,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(DeptDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(DeptDialog, {
ref: formRef,
formInline: {
parentId: row.parentId,
manager: row.manager ? row.manager.split(',') : row.manager,
deptName: row.deptName,
summary: row.summary,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -8,8 +8,12 @@ import { UploadFilled } from '@element-plus/icons-vue';
const props = withDefaults(defineProps<FormProps>(), {
formInline: () => ({
//
filename: undefined,
//
filepath: undefined,
// MIME
fileType: undefined,
//
downloadCount: undefined,
//
@ -33,6 +37,7 @@ const handleExceed: UploadProps['onExceed'] = (files) => {
};
onMounted(() => {
filesStore.loadMediaTypeList();
filesStore.loadFilesStoragePath();
});
@ -41,6 +46,11 @@ defineExpose({ formRef });
<template>
<el-form ref="formRef" :model="form" :rules="form.isUpload ? uploadRules : addRules" label-width="auto">
<!-- 文件名称---上传显示 -->
<el-form-item v-show="form.isUpload" :label="$t('files_filename')" prop="filename">
<el-input v-model="form.filename" autocomplete="off" type="text" />
</el-form-item>
<!-- 文件路径---上传显示文本框 -->
<el-form-item :label="$t('files_filepath')" prop="filepath">
<el-input v-if="form.isUpload" v-model="form.filepath" autocomplete="off" disabled type="text" />
@ -51,6 +61,12 @@ defineExpose({ formRef });
</el-form-item>
<!-- 文件类型---上传显示 -->
<el-form-item v-show="form.isUpload" :label="$t('files_fileType')" prop="fileType">
<el-select v-model="form.fileType" :placeholder="$t('select') + $t('files_fileType')" clearable filterable>
<el-option v-for="(item, index) in filesStore.allMediaTypes" :key="index" :label="item" :navigationBar="false" :value="item" />
</el-select>
</el-form-item>
<el-form-item :label="$t('files_downloadCount')" prop="downloadCount">
<el-input v-model="form.downloadCount" :min="0" autocomplete="off" type="number" />
</el-form-item>

View File

@ -52,27 +52,15 @@ onMounted(() => {
<template>
<div class="main">
<el-form ref="formRef" :inline="true" :model="filesStore.form" class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto">
<!--文件的名称-->
<el-form-item :label="$t('files_filename')" prop="filename">
<el-input v-model="filesStore.form.filename" :placeholder="`${$t('input')}${$t('files_filename')}`" class="!w-[180px]" clearable />
</el-form-item>
<!--文件在服务器上的存储路径-->
<el-form-item :label="$t('files_filepath')" prop="filepath">
<el-input v-model="filesStore.form.filepath" :placeholder="`${$t('input')}${$t('files_filepath')}`" class="!w-[180px]" clearable />
</el-form-item>
<!--文件的MIME类型-->
<el-form-item :label="$t('files_fileType')" prop="contentType">
<el-input v-model="filesStore.form.contentType" :placeholder="`${$t('input')}${$t('files_fileType')}`" class="!w-[180px]" clearable />
<el-form-item :label="$t('files_fileType')" prop="fileType">
<el-input v-model="filesStore.form.fileType" :placeholder="`${$t('input')}${$t('files_fileType')}`" class="!w-[180px]" clearable />
</el-form-item>
<!--扩展名-->
<el-form-item :label="$t('ext')" prop="ext">
<el-input v-model="filesStore.form.ext" :placeholder="`${$t('input')}${$t('ext')}`" class="!w-[180px]" clearable />
</el-form-item>
<!--平台-->
<el-form-item :label="$t('platform')" prop="platform">
<el-input v-model="filesStore.form.platform" :placeholder="`${$t('input')}${$t('platform')}`" class="!w-[180px]" clearable />
</el-form-item>
<!--下载量-->
<el-form-item :label="$t('files_downloadCount')" prop="downloadCount">
<el-input
v-model="filesStore.form.downloadCount"
@ -83,7 +71,6 @@ onMounted(() => {
type="number"
/>
</el-form-item>
<el-form-item>
<el-button :icon="useRenderIcon('ri/search-line')" :loading="filesStore.loading" type="primary" @click="onSearch">
{{ $t('search') }}

View File

@ -10,8 +10,7 @@ export const columns: TableColumnList = [
// 文件在服务器上的存储路径
{ label: $t('files_filepath'), prop: 'filepath' },
// 文件的MIME类型
{ label: $t('files_fileType'), prop: 'contentType', width: 180 },
{ label: '缩略图类型', prop: 'thContentType', width: 180 },
{ label: $t('files_fileType'), prop: 'fileType', width: 180 },
// 下载数量
{ label: $t('files_downloadCount'), prop: 'downloadCount', width: 115 },
{ label: $t('table.updateTime'), prop: 'updateTime', sortable: true, width: 160 },

View File

@ -25,11 +25,13 @@ export async function onSearch() {
export function onAdd() {
addDialog({
title: `${$t('addNew')}${$t('files')}`,
props: {
formInline: {
filename: undefined,
fileType: undefined,
filepath: undefined,
downloadCount: 0,
// 上传为 files更新时为 file
files: [],
isUpload: false,
},
@ -41,9 +43,10 @@ export function onAdd() {
const dialog = h(FilesDialog, {
ref: formRef,
formInline: {
filename: undefined,
fileType: undefined,
filepath: undefined,
downloadCount: 0,
// 上传为 files更新时为 file
files: [],
isUpload: false,
},
@ -55,17 +58,14 @@ export function onAdd() {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
if (!valid) return;
// 添加文件
form.files = (form.files as UploadFiles).map((file) => file.raw);
const data = {
filepath: form.filepath,
downloadCount: form.downloadCount,
files: [],
files: form.files,
};
// 判断是否更新了文件
if (form.files) {
data.files = (form.files as UploadFiles).map((file) => file.raw);
}
const result = await filesStore.addFiles(data);
// 成功后关闭窗口
@ -79,22 +79,33 @@ export function onAdd() {
/* 更新系统文件表 */
export function onUpdate(row: any) {
const formInline = {
filepath: row.filepath,
downloadCount: row.downloadCount,
// 上传为 files更新时为 file
files: undefined,
isUpload: true,
};
addDialog({
title: `${$t('modify')}${$t('files')}`,
props: { formInline },
props: {
formInline: {
filename: row.filename,
fileType: row.fileType,
filepath: row.filepath,
downloadCount: row.downloadCount,
isUpload: true,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => {
const dialog = h(FilesDialog, { ref: formRef, formInline });
const dialog = h(FilesDialog, {
ref: formRef,
formInline: {
filename: row.filename,
fileType: row.fileType,
filepath: row.filepath,
downloadCount: row.downloadCount,
isUpload: true,
files: undefined,
},
});
formRef.value = dialog;
return dialog;
},
@ -103,21 +114,13 @@ export function onUpdate(row: any) {
formRef.value.formRef.validate(async (valid: any) => {
if (!valid) return;
const data = {
id: row.id,
filepath: form.filepath,
downloadCount: form.downloadCount,
file: undefined,
isUpload: true,
};
// 判断是否更新了文件
if (form.files) {
data.file = (form.files as UploadFiles).map((file) => file.raw)[0];
form.files = (form.files as UploadFiles).map((file) => file.raw);
}
// 更新文件
const result = await filesStore.editFiles(data);
const result = await filesStore.editFiles({ ...form, id: row.id });
// 更新完成
if (!result) return;

View File

@ -1,10 +1,14 @@
// 添加或者修改表单元素
export interface FormItemProps {
// 文件的名称
filename: string;
// 文件在服务器上的存储路径
filepath: string;
// 文件的MIME类型
fileType: string;
// 下载数量
downloadCount: number;
// 文件内容,添加是 files上传是file
// 文件内容
files: any;
// 是否是上传
isUpload: boolean;

View File

@ -94,7 +94,6 @@ onMounted(() => {
row-key="id"
showOverflowTooltip
table-layout="auto"
@row-dblclick="(row) => onUpdate(row)"
@selection-change="onSelectionChange"
>
<template #rank="{ row }">

View File

@ -25,39 +25,68 @@ async function onSearch() {
/** 添加菜单 */
function onAdd(parentId: any = 0) {
const formInline = {
menuType: 0,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId,
title: '',
name: '',
path: '',
component: '',
rank: 99,
icon: '',
id: '',
extraIcon: '',
enterTransition: '',
leaveTransition: '',
activePath: '',
redirect: '',
roles: [],
frameSrc: '',
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: true,
};
addDialog({
title: $t('addNew') + $t('menu'),
props: { formInline },
props: {
formInline: {
menuType: 0,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId,
title: '',
name: '',
path: '',
component: '',
rank: 99,
icon: '',
id: '',
extraIcon: '',
enterTransition: '',
leaveTransition: '',
activePath: '',
redirect: '',
roles: [],
frameSrc: '',
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: true,
},
},
width: '45%',
draggable: true,
closeOnClickModal: false,
fullscreenIcon: true,
contentRenderer: () => h(EditForm, { ref: dialogFormRef, formInline }),
contentRenderer: () =>
h(EditForm, {
ref: dialogFormRef,
formInline: {
menuType: 0,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId,
title: '',
name: '',
path: '',
component: '',
rank: 99,
icon: '',
id: '',
extraIcon: '',
enterTransition: '',
leaveTransition: '',
activePath: '',
redirect: '',
roles: [],
frameSrc: '',
frameLoading: true,
keepAlive: false,
hiddenTag: false,
fixedTag: false,
showLink: true,
showParent: true,
},
}),
beforeSure: (done, { options }) => {
const menuFormRef = dialogFormRef.value.menuFormRef;
const curData = options.props.formInline as FormItemProps;
@ -79,42 +108,71 @@ function onAdd(parentId: any = 0) {
/* 更新菜单 */
function onUpdate(row?: FormItemProps) {
const formInline = {
id: row?.id,
menuType: row?.menuType,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId: row?.parentId,
title: row?.title,
name: row?.name,
path: row?.path,
component: row?.component,
rank: row?.rank,
icon: row?.icon,
frameSrc: row?.frameSrc,
extraIcon: row?.extraIcon,
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
enterTransition: row?.enterTransition?.replace('animate__', ''),
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
leaveTransition: row?.leaveTransition?.replace('animate__', ''),
roles: row.roles,
activePath: row?.activePath,
frameLoading: row?.frameLoading,
keepAlive: row?.keepAlive,
hiddenTag: row?.hiddenTag,
fixedTag: row?.fixedTag,
showLink: row?.showLink,
showParent: row?.showParent,
redirect: row?.redirect,
};
addDialog({
title: $t('update') + $t('menu'),
props: { formInline },
props: {
formInline: {
id: row?.id,
menuType: row?.menuType,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId: row?.parentId,
title: row?.title,
name: row?.name,
path: row?.path,
component: row?.component,
rank: row?.rank,
icon: row?.icon,
frameSrc: row?.frameSrc,
extraIcon: row?.extraIcon,
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
enterTransition: row?.enterTransition?.replace('animate__', ''),
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
leaveTransition: row?.leaveTransition?.replace('animate__', ''),
activePath: row?.activePath,
frameLoading: row?.frameLoading,
keepAlive: row?.keepAlive,
hiddenTag: row?.hiddenTag,
fixedTag: row?.fixedTag,
showLink: row?.showLink,
showParent: row?.showParent,
redirect: row?.redirect,
},
},
width: '45%',
draggable: true,
fullscreen: deviceDetection(),
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(EditForm, { ref: dialogFormRef, formInline }),
contentRenderer: () =>
h(EditForm, {
ref: dialogFormRef,
formInline: {
id: row?.id,
menuType: row?.menuType,
higherMenuOptions: formatHigherMenuOptions(cloneDeep(menuStore.datalist)),
parentId: row?.parentId,
title: row?.title,
name: row?.name,
path: row?.path,
component: row?.component,
rank: row?.rank,
icon: row?.icon,
frameSrc: row?.frameSrc,
extraIcon: row?.extraIcon,
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
enterTransition: row?.enterTransition?.replace('animate__', ''),
// 因为要使用动画需要在前面加上 animate__ 修改时需要手动去除
leaveTransition: row?.leaveTransition?.replace('animate__', ''),
activePath: row?.activePath,
frameLoading: row?.frameLoading,
keepAlive: row?.keepAlive,
hiddenTag: row?.hiddenTag,
fixedTag: row?.fixedTag,
showLink: row?.showLink,
showParent: row?.showParent,
redirect: row?.redirect,
},
}),
beforeSure: (done, { options }) => {
const menuFormRef = dialogFormRef.value.menuFormRef;
const curData = options.props.formInline as FormItemProps;

View File

@ -34,6 +34,9 @@ const form = ref(props.formInline);
const powerStore = usePermissionStore();
const { allPowerList, systemApiInfoList } = storeToRefs(powerStore);
// tab
const activeName = ref('system');
//
const queryText = ref('');
@ -51,7 +54,9 @@ const onNodeClick = (node) => {
form.value.powerName = node.summary;
form.value.requestMethod = node.httpMethod;
form.value.requestUrl = node.path;
form.value.powerCode = node.powerCode;
if (node.powerCodes && node.powerCodes.length > 0) {
form.value.powerCode = node.powerCodes[0];
}
};
onMounted(() => {

View File

@ -23,24 +23,31 @@ export async function onSearch() {
/** 添加权限 */
export function onAdd(parentId = 0) {
const formInline = {
parentId,
powerCode: undefined,
powerName: undefined,
requestUrl: undefined,
requestMethod: undefined,
};
addDialog({
title: `${$t('addNew')}${$t('power')}`,
props: { formInline },
props: {
formInline: {
parentId,
powerCode: undefined,
powerName: undefined,
requestUrl: undefined,
requestMethod: undefined,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () =>
h(PermissionFromDialog, {
ref: formRef,
formInline,
formInline: {
parentId,
powerCode: undefined,
powerName: undefined,
requestUrl: undefined,
requestMethod: undefined,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
@ -58,21 +65,32 @@ export function onAdd(parentId = 0) {
/* 更新权限 */
export function onUpdate(row: any) {
const formInline = {
parentId: row.parentId,
powerCode: row.powerCode,
powerName: row.powerName,
requestUrl: row.requestUrl,
requestMethod: row.requestMethod,
};
addDialog({
title: `${$t('modify')}${$t('power')}`,
props: { formInline },
props: {
formInline: {
parentId: row.parentId,
powerCode: row.powerCode,
powerName: row.powerName,
requestUrl: row.requestUrl,
requestMethod: row.requestMethod,
},
},
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(PermissionFromDialog, { ref: formRef, formInline }),
contentRenderer: () =>
h(PermissionFromDialog, {
ref: formRef,
formInline: {
parentId: row.parentId,
powerCode: row.powerCode,
powerName: row.powerName,
requestUrl: row.requestUrl,
requestMethod: row.requestMethod,
},
}),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -5,7 +5,7 @@ import { h, ref } from 'vue';
import { messageBox } from '@/utils/message';
import type { FormItemProps } from '@/views/system/role/utils/types';
import { $t } from '@/plugins/i18n';
import { getPowerListByRoleId } from '@/api/v1/system/permission';
import { getPowerListByRoleId } from '@/api/v1/system/power';
// 表格ref
export const tableRef = ref();
@ -32,14 +32,14 @@ export async function onSearch() {
/** 添加角色 */
export function onAdd() {
const formInline = { roleCode: undefined, description: undefined };
addDialog({
title: `${$t('addNew')}${$t('role')}`,
props: { formInline },
props: { formInline: { roleCode: undefined, description: undefined } },
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(RoleDialog, { ref: formRef, formInline }),
contentRenderer: () => h(RoleDialog, { ref: formRef, formInline: { roleCode: undefined, description: undefined } }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {
@ -56,15 +56,14 @@ export function onAdd() {
/* 更新角色 */
export function onUpdate(row: any) {
const formInline = { roleCode: row.roleCode, description: row.description };
addDialog({
title: `${$t('modify')}${$t('role')}`,
props: { formInline },
props: { formInline: { roleCode: row.roleCode, description: row.description } },
draggable: true,
fullscreenIcon: true,
closeOnClickModal: false,
contentRenderer: () => h(RoleDialog, { ref: formRef, formInline }),
contentRenderer: () => h(RoleDialog, { ref: formRef, formInline: { roleCode: row.roleCode, description: row.description } }),
beforeSure: (done, { options }) => {
const form = options.props.formInline as FormItemProps;
formRef.value.formRef.validate(async (valid: any) => {

View File

@ -1,134 +1,134 @@
declare module 'vue' {
declare module "vue" {
/**
* Volar Volar
*/
export interface GlobalComponents {
IconifyIconOffline: (typeof import('../src/components/ReIcon'))['IconifyIconOffline'];
IconifyIconOnline: (typeof import('../src/components/ReIcon'))['IconifyIconOnline'];
FontIcon: (typeof import('../src/components/ReIcon'))['FontIcon'];
Auth: (typeof import('../src/components/ReAuth'))['Auth'];
Perms: (typeof import('../src/components/RePerms'))['Perms'];
IconifyIconOffline: (typeof import("../src/components/ReIcon"))["IconifyIconOffline"];
IconifyIconOnline: (typeof import("../src/components/ReIcon"))["IconifyIconOnline"];
FontIcon: (typeof import("../src/components/ReIcon"))["FontIcon"];
Auth: (typeof import("../src/components/ReAuth"))["Auth"];
Perms: (typeof import("../src/components/RePerms"))["Perms"];
}
}
/**
* https://github.com/element-plus/element-plus/blob/dev/global.d.ts#L2
* TODO https://github.com/element-plus/element-plus/blob/dev/global.d.ts#L2
* No need to install @vue/runtime-core
*/
declare module 'vue' {
declare module "vue" {
export interface GlobalComponents {
ElAffix: (typeof import('element-plus'))['ElAffix'];
ElAlert: (typeof import('element-plus'))['ElAlert'];
ElAside: (typeof import('element-plus'))['ElAside'];
ElAutocomplete: (typeof import('element-plus'))['ElAutocomplete'];
ElAvatar: (typeof import('element-plus'))['ElAvatar'];
ElAnchor: (typeof import('element-plus'))['ElAnchor'];
ElAnchorLink: (typeof import('element-plus'))['ElAnchorLink'];
ElBacktop: (typeof import('element-plus'))['ElBacktop'];
ElBadge: (typeof import('element-plus'))['ElBadge'];
ElBreadcrumb: (typeof import('element-plus'))['ElBreadcrumb'];
ElBreadcrumbItem: (typeof import('element-plus'))['ElBreadcrumbItem'];
ElButton: (typeof import('element-plus'))['ElButton'];
ElButtonGroup: (typeof import('element-plus'))['ElButtonGroup'];
ElCalendar: (typeof import('element-plus'))['ElCalendar'];
ElCard: (typeof import('element-plus'))['ElCard'];
ElCarousel: (typeof import('element-plus'))['ElCarousel'];
ElCarouselItem: (typeof import('element-plus'))['ElCarouselItem'];
ElCascader: (typeof import('element-plus'))['ElCascader'];
ElCascaderPanel: (typeof import('element-plus'))['ElCascaderPanel'];
ElCheckbox: (typeof import('element-plus'))['ElCheckbox'];
ElCheckboxButton: (typeof import('element-plus'))['ElCheckboxButton'];
ElCheckboxGroup: (typeof import('element-plus'))['ElCheckboxGroup'];
ElCol: (typeof import('element-plus'))['ElCol'];
ElCollapse: (typeof import('element-plus'))['ElCollapse'];
ElCollapseItem: (typeof import('element-plus'))['ElCollapseItem'];
ElCollapseTransition: (typeof import('element-plus'))['ElCollapseTransition'];
ElColorPicker: (typeof import('element-plus'))['ElColorPicker'];
ElContainer: (typeof import('element-plus'))['ElContainer'];
ElConfigProvider: (typeof import('element-plus'))['ElConfigProvider'];
ElDatePicker: (typeof import('element-plus'))['ElDatePicker'];
ElDialog: (typeof import('element-plus'))['ElDialog'];
ElDivider: (typeof import('element-plus'))['ElDivider'];
ElDrawer: (typeof import('element-plus'))['ElDrawer'];
ElDropdown: (typeof import('element-plus'))['ElDropdown'];
ElDropdownItem: (typeof import('element-plus'))['ElDropdownItem'];
ElDropdownMenu: (typeof import('element-plus'))['ElDropdownMenu'];
ElEmpty: (typeof import('element-plus'))['ElEmpty'];
ElFooter: (typeof import('element-plus'))['ElFooter'];
ElForm: (typeof import('element-plus'))['ElForm'];
ElFormItem: (typeof import('element-plus'))['ElFormItem'];
ElHeader: (typeof import('element-plus'))['ElHeader'];
ElIcon: (typeof import('element-plus'))['ElIcon'];
ElImage: (typeof import('element-plus'))['ElImage'];
ElImageViewer: (typeof import('element-plus'))['ElImageViewer'];
ElInput: (typeof import('element-plus'))['ElInput'];
ElInputNumber: (typeof import('element-plus'))['ElInputNumber'];
ElLink: (typeof import('element-plus'))['ElLink'];
ElMain: (typeof import('element-plus'))['ElMain'];
ElMenu: (typeof import('element-plus'))['ElMenu'];
ElMenuItem: (typeof import('element-plus'))['ElMenuItem'];
ElMenuItemGroup: (typeof import('element-plus'))['ElMenuItemGroup'];
ElOption: (typeof import('element-plus'))['ElOption'];
ElOptionGroup: (typeof import('element-plus'))['ElOptionGroup'];
ElPageHeader: (typeof import('element-plus'))['ElPageHeader'];
ElPagination: (typeof import('element-plus'))['ElPagination'];
ElPopconfirm: (typeof import('element-plus'))['ElPopconfirm'];
ElPopper: (typeof import('element-plus'))['ElPopper'];
ElPopover: (typeof import('element-plus'))['ElPopover'];
ElProgress: (typeof import('element-plus'))['ElProgress'];
ElRadio: (typeof import('element-plus'))['ElRadio'];
ElRadioButton: (typeof import('element-plus'))['ElRadioButton'];
ElRadioGroup: (typeof import('element-plus'))['ElRadioGroup'];
ElRate: (typeof import('element-plus'))['ElRate'];
ElRow: (typeof import('element-plus'))['ElRow'];
ElScrollbar: (typeof import('element-plus'))['ElScrollbar'];
ElSelect: (typeof import('element-plus'))['ElSelect'];
ElSlider: (typeof import('element-plus'))['ElSlider'];
ElStep: (typeof import('element-plus'))['ElStep'];
ElSteps: (typeof import('element-plus'))['ElSteps'];
ElSubMenu: (typeof import('element-plus'))['ElSubMenu'];
ElSwitch: (typeof import('element-plus'))['ElSwitch'];
ElTabPane: (typeof import('element-plus'))['ElTabPane'];
ElTable: (typeof import('element-plus'))['ElTable'];
ElTableColumn: (typeof import('element-plus'))['ElTableColumn'];
ElTabs: (typeof import('element-plus'))['ElTabs'];
ElTag: (typeof import('element-plus'))['ElTag'];
ElText: (typeof import('element-plus'))['ElText'];
ElTimePicker: (typeof import('element-plus'))['ElTimePicker'];
ElTimeSelect: (typeof import('element-plus'))['ElTimeSelect'];
ElTimeline: (typeof import('element-plus'))['ElTimeline'];
ElTimelineItem: (typeof import('element-plus'))['ElTimelineItem'];
ElTooltip: (typeof import('element-plus'))['ElTooltip'];
ElTransfer: (typeof import('element-plus'))['ElTransfer'];
ElTree: (typeof import('element-plus'))['ElTree'];
ElTreeV2: (typeof import('element-plus'))['ElTreeV2'];
ElTreeSelect: (typeof import('element-plus'))['ElTreeSelect'];
ElUpload: (typeof import('element-plus'))['ElUpload'];
ElSpace: (typeof import('element-plus'))['ElSpace'];
ElSkeleton: (typeof import('element-plus'))['ElSkeleton'];
ElSkeletonItem: (typeof import('element-plus'))['ElSkeletonItem'];
ElStatistic: (typeof import('element-plus'))['ElStatistic'];
ElCheckTag: (typeof import('element-plus'))['ElCheckTag'];
ElDescriptions: (typeof import('element-plus'))['ElDescriptions'];
ElDescriptionsItem: (typeof import('element-plus'))['ElDescriptionsItem'];
ElResult: (typeof import('element-plus'))['ElResult'];
ElSelectV2: (typeof import('element-plus'))['ElSelectV2'];
ElWatermark: (typeof import('element-plus'))['ElWatermark'];
ElTour: (typeof import('element-plus'))['ElTour'];
ElTourStep: (typeof import('element-plus'))['ElTourStep'];
ElSegmented: (typeof import('element-plus'))['ElSegmented'];
ElAffix: (typeof import("element-plus"))["ElAffix"];
ElAlert: (typeof import("element-plus"))["ElAlert"];
ElAside: (typeof import("element-plus"))["ElAside"];
ElAutocomplete: (typeof import("element-plus"))["ElAutocomplete"];
ElAvatar: (typeof import("element-plus"))["ElAvatar"];
ElAnchor: (typeof import("element-plus"))["ElAnchor"];
ElAnchorLink: (typeof import("element-plus"))["ElAnchorLink"];
ElBacktop: (typeof import("element-plus"))["ElBacktop"];
ElBadge: (typeof import("element-plus"))["ElBadge"];
ElBreadcrumb: (typeof import("element-plus"))["ElBreadcrumb"];
ElBreadcrumbItem: (typeof import("element-plus"))["ElBreadcrumbItem"];
ElButton: (typeof import("element-plus"))["ElButton"];
ElButtonGroup: (typeof import("element-plus"))["ElButtonGroup"];
ElCalendar: (typeof import("element-plus"))["ElCalendar"];
ElCard: (typeof import("element-plus"))["ElCard"];
ElCarousel: (typeof import("element-plus"))["ElCarousel"];
ElCarouselItem: (typeof import("element-plus"))["ElCarouselItem"];
ElCascader: (typeof import("element-plus"))["ElCascader"];
ElCascaderPanel: (typeof import("element-plus"))["ElCascaderPanel"];
ElCheckbox: (typeof import("element-plus"))["ElCheckbox"];
ElCheckboxButton: (typeof import("element-plus"))["ElCheckboxButton"];
ElCheckboxGroup: (typeof import("element-plus"))["ElCheckboxGroup"];
ElCol: (typeof import("element-plus"))["ElCol"];
ElCollapse: (typeof import("element-plus"))["ElCollapse"];
ElCollapseItem: (typeof import("element-plus"))["ElCollapseItem"];
ElCollapseTransition: (typeof import("element-plus"))["ElCollapseTransition"];
ElColorPicker: (typeof import("element-plus"))["ElColorPicker"];
ElContainer: (typeof import("element-plus"))["ElContainer"];
ElConfigProvider: (typeof import("element-plus"))["ElConfigProvider"];
ElDatePicker: (typeof import("element-plus"))["ElDatePicker"];
ElDialog: (typeof import("element-plus"))["ElDialog"];
ElDivider: (typeof import("element-plus"))["ElDivider"];
ElDrawer: (typeof import("element-plus"))["ElDrawer"];
ElDropdown: (typeof import("element-plus"))["ElDropdown"];
ElDropdownItem: (typeof import("element-plus"))["ElDropdownItem"];
ElDropdownMenu: (typeof import("element-plus"))["ElDropdownMenu"];
ElEmpty: (typeof import("element-plus"))["ElEmpty"];
ElFooter: (typeof import("element-plus"))["ElFooter"];
ElForm: (typeof import("element-plus"))["ElForm"];
ElFormItem: (typeof import("element-plus"))["ElFormItem"];
ElHeader: (typeof import("element-plus"))["ElHeader"];
ElIcon: (typeof import("element-plus"))["ElIcon"];
ElImage: (typeof import("element-plus"))["ElImage"];
ElImageViewer: (typeof import("element-plus"))["ElImageViewer"];
ElInput: (typeof import("element-plus"))["ElInput"];
ElInputNumber: (typeof import("element-plus"))["ElInputNumber"];
ElLink: (typeof import("element-plus"))["ElLink"];
ElMain: (typeof import("element-plus"))["ElMain"];
ElMenu: (typeof import("element-plus"))["ElMenu"];
ElMenuItem: (typeof import("element-plus"))["ElMenuItem"];
ElMenuItemGroup: (typeof import("element-plus"))["ElMenuItemGroup"];
ElOption: (typeof import("element-plus"))["ElOption"];
ElOptionGroup: (typeof import("element-plus"))["ElOptionGroup"];
ElPageHeader: (typeof import("element-plus"))["ElPageHeader"];
ElPagination: (typeof import("element-plus"))["ElPagination"];
ElPopconfirm: (typeof import("element-plus"))["ElPopconfirm"];
ElPopper: (typeof import("element-plus"))["ElPopper"];
ElPopover: (typeof import("element-plus"))["ElPopover"];
ElProgress: (typeof import("element-plus"))["ElProgress"];
ElRadio: (typeof import("element-plus"))["ElRadio"];
ElRadioButton: (typeof import("element-plus"))["ElRadioButton"];
ElRadioGroup: (typeof import("element-plus"))["ElRadioGroup"];
ElRate: (typeof import("element-plus"))["ElRate"];
ElRow: (typeof import("element-plus"))["ElRow"];
ElScrollbar: (typeof import("element-plus"))["ElScrollbar"];
ElSelect: (typeof import("element-plus"))["ElSelect"];
ElSlider: (typeof import("element-plus"))["ElSlider"];
ElStep: (typeof import("element-plus"))["ElStep"];
ElSteps: (typeof import("element-plus"))["ElSteps"];
ElSubMenu: (typeof import("element-plus"))["ElSubMenu"];
ElSwitch: (typeof import("element-plus"))["ElSwitch"];
ElTabPane: (typeof import("element-plus"))["ElTabPane"];
ElTable: (typeof import("element-plus"))["ElTable"];
ElTableColumn: (typeof import("element-plus"))["ElTableColumn"];
ElTabs: (typeof import("element-plus"))["ElTabs"];
ElTag: (typeof import("element-plus"))["ElTag"];
ElText: (typeof import("element-plus"))["ElText"];
ElTimePicker: (typeof import("element-plus"))["ElTimePicker"];
ElTimeSelect: (typeof import("element-plus"))["ElTimeSelect"];
ElTimeline: (typeof import("element-plus"))["ElTimeline"];
ElTimelineItem: (typeof import("element-plus"))["ElTimelineItem"];
ElTooltip: (typeof import("element-plus"))["ElTooltip"];
ElTransfer: (typeof import("element-plus"))["ElTransfer"];
ElTree: (typeof import("element-plus"))["ElTree"];
ElTreeV2: (typeof import("element-plus"))["ElTreeV2"];
ElTreeSelect: (typeof import("element-plus"))["ElTreeSelect"];
ElUpload: (typeof import("element-plus"))["ElUpload"];
ElSpace: (typeof import("element-plus"))["ElSpace"];
ElSkeleton: (typeof import("element-plus"))["ElSkeleton"];
ElSkeletonItem: (typeof import("element-plus"))["ElSkeletonItem"];
ElStatistic: (typeof import("element-plus"))["ElStatistic"];
ElCheckTag: (typeof import("element-plus"))["ElCheckTag"];
ElDescriptions: (typeof import("element-plus"))["ElDescriptions"];
ElDescriptionsItem: (typeof import("element-plus"))["ElDescriptionsItem"];
ElResult: (typeof import("element-plus"))["ElResult"];
ElSelectV2: (typeof import("element-plus"))["ElSelectV2"];
ElWatermark: (typeof import("element-plus"))["ElWatermark"];
ElTour: (typeof import("element-plus"))["ElTour"];
ElTourStep: (typeof import("element-plus"))["ElTourStep"];
ElSegmented: (typeof import("element-plus"))["ElSegmented"];
}
interface ComponentCustomProperties {
$storage: ResponsiveStorage;
$message: (typeof import('element-plus'))['ElMessage'];
$notify: (typeof import('element-plus'))['ElNotification'];
$msgbox: (typeof import('element-plus'))['ElMessageBox'];
$messageBox: (typeof import('element-plus'))['ElMessageBox'];
$alert: (typeof import('element-plus'))['ElMessageBox']['alert'];
$confirm: (typeof import('element-plus'))['ElMessageBox']['confirm'];
$prompt: (typeof import('element-plus'))['ElMessageBox']['prompt'];
$loading: (typeof import('element-plus'))['ElLoadingService'];
$message: (typeof import("element-plus"))["ElMessage"];
$notify: (typeof import("element-plus"))["ElNotification"];
$msgbox: (typeof import("element-plus"))["ElMessageBox"];
$messageBox: (typeof import("element-plus"))["ElMessageBox"];
$alert: (typeof import("element-plus"))["ElMessageBox"]["alert"];
$confirm: (typeof import("element-plus"))["ElMessageBox"]["confirm"];
$prompt: (typeof import("element-plus"))["ElMessageBox"]["prompt"];
$loading: (typeof import("element-plus"))["ElLoadingService"];
}
}