Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| feff838650 | |||
| 029dea0100 | |||
| 5b71369251 | |||
|
|
05ab89d03f | ||
|
|
f49503a924 | ||
|
|
39465dcaa3 | ||
|
|
99c37d7de4 | ||
|
|
2642862569 | ||
|
|
9d570a808c | ||
| c0c5ba8efd | |||
| ca25285237 | |||
|
|
c70d1adbf9 | ||
| 70510894ef | |||
| c0a5c2dffe | |||
| 3dfa97e785 | |||
|
|
f99c8f1b5a | ||
|
|
724f60eaf6 | ||
| fcceaf8f51 | |||
| b8a84a3a08 | |||
| 81dbea8793 | |||
|
|
b2a1658e37 | ||
|
|
5264cf226f | ||
|
|
64648d0c1d |
BIN
.image/screenshot/000登录页面-H5.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 147 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 146 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 375 KiB After Width: | Height: | Size: 305 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 849 KiB After Width: | Height: | Size: 808 KiB |
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 131 KiB |
28
CHANGELOG.md
@@ -1,3 +1,31 @@
|
|||||||
|
## [v3.0.1](https://github.com/Charles7c/continew-admin-ui/compare/v3.0.0...v3.0.1) (2024-05-03)
|
||||||
|
|
||||||
|
### ✨ 新特性
|
||||||
|
|
||||||
|
* 新增表格全屏、尺寸工具 ([b8a84a3](https://github.com/Charles7c/continew-admin-ui/commit/b8a84a3a0890d4f6d0e39ecbe50c4f645bd0f106))
|
||||||
|
* 新增验证码超时显示效果,超时后显示已过期请刷新 (GitHub#14) ([f99c8f1](https://github.com/Charles7c/continew-admin-ui/commit/f99c8f1b5a521f987b2822352f976fb0b1dc93b3))
|
||||||
|
* 文件管理增加资源统计,统计总存储量、各类型文件存储占用 (GitHub#15) ([c70d1ad](https://github.com/Charles7c/continew-admin-ui/commit/c70d1adbf922d28853bf4e6cf8cc4e14ad5b0ac7))
|
||||||
|
|
||||||
|
### 💎 功能优化
|
||||||
|
|
||||||
|
- 统一性别约束/统一上级部门为必填 ([5264cf2](https://github.com/Charles7c/continew-admin-ui/commit/5264cf226fa3acd1398d9309e6a97d4d45b64850))
|
||||||
|
- 一级部门不能修改上级部门 ([b2a1658](https://github.com/Charles7c/continew-admin-ui/commit/b2a1658e3730078cf2fbeb3032c23c0922544594))
|
||||||
|
- 优化根据选中部门查询用户的点击效果 ([ca25285](https://github.com/Charles7c/continew-admin-ui/commit/ca252852373840b000c1f65ab925d18335a71fcb)) ([99c37d7](https://github.com/Charles7c/continew-admin-ui/commit/99c37d7de4a245836f89c29cf4b638032efae31f))
|
||||||
|
- 登录页面,H5 端排版更换 ([05ab89d](https://github.com/Charles7c/continew-admin-ui/commit/05ab89d03fe6401994698ad9ecdeb8540ec49553))
|
||||||
|
- 优化 queryForm 的 Query 类型使用 ([5b71369](https://github.com/Charles7c/continew-admin-ui/commit/5b713692516db586f2d401a163192c62a963137a))
|
||||||
|
|
||||||
|
|
||||||
|
### 🐛 问题修复
|
||||||
|
|
||||||
|
- 修复 Markdown 样式加载错误,改为全局统一加载 (GitHub#9) ([64648d0](https://github.com/Charles7c/continew-admin-ui/commit/64648d0c1d897d6e426199e7924ede9dfb40e8b8))
|
||||||
|
- 修复由于文件组件名称错误导致的侧边栏筛选功能失效 ([81dbea8](https://github.com/Charles7c/continew-admin-ui/commit/81dbea879377054e3646c2d07b51c3352501bbce))
|
||||||
|
- 修复文件管理数据不刷新和批量操作选中问题 (GitHub#13) ([724f60e](https://github.com/Charles7c/continew-admin-ui/commit/724f60eaf6b076cfb165ca0b1028c461146495ad))
|
||||||
|
- 修复文件重命名时不能回显原值的问题 ([3dfa97e](https://github.com/Charles7c/continew-admin-ui/commit/3dfa97e785acb42edd3798117f7e8eea326b4b64))
|
||||||
|
- 修复修改公告时保存按钮点击无效的问题 ([c0a5c2d](https://github.com/Charles7c/continew-admin-ui/commit/c0a5c2dffe50905b8610fbd066b8eecd5a4cbe89))
|
||||||
|
- 修复账号管理、安全设置路由处理错误 ([c0c5ba8](https://github.com/Charles7c/continew-admin-ui/commit/c0c5ba8efdab009e7e38ad9a8f68a655aba28718))
|
||||||
|
- 修复首页卡片显示问题 ([39465dc](https://github.com/Charles7c/continew-admin-ui/commit/39465dcaa38c9d79c820583a1dd82978e5588dec))
|
||||||
|
- 修复 H5 下登录页面错位显示 ([9d570a8](https://github.com/Charles7c/continew-admin-ui/commit/9d570a808ce1a15a1513eac0e9ec355d683febef))
|
||||||
|
|
||||||
## v3.0.0 (2024-04-27)
|
## v3.0.0 (2024-04-27)
|
||||||
|
|
||||||
### ✨ 新特性
|
### ✨ 新特性
|
||||||
|
|||||||
84
README.md
@@ -4,7 +4,7 @@
|
|||||||
<img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License" />
|
<img src="https://img.shields.io/badge/License-Apache--2.0-blue.svg" alt="License" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
|
<a href="https://github.com/Charles7c/continew-admin-ui" target="_blank">
|
||||||
<img src="https://img.shields.io/badge/RELEASE-v3.0.0-%23ff3f59.svg" alt="Release" />
|
<img src="https://img.shields.io/badge/RELEASE-v3.0.1-%23ff3f59.svg" alt="Release" />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/Charles7c/continew-admin" target="_blank">
|
<a href="https://github.com/Charles7c/continew-admin" target="_blank">
|
||||||
<img src="https://img.shields.io/github/stars/Charles7c/continew-admin?style=social" alt="GitHub stars" />
|
<img src="https://img.shields.io/github/stars/Charles7c/continew-admin?style=social" alt="GitHub stars" />
|
||||||
@@ -90,7 +90,7 @@ ContiNew Admin(Continue New Admin)持续迭代优化的前后端分离中后
|
|||||||
## 系统截图
|
## 系统截图
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> 受篇幅长度及功能更新频率影响,下方仅为系统 **部分** 功能于 **2024年4月27日** 进行的截图,更多新增功能及细节请登录演示环境或 clone 代码到本地启动查看。
|
> 受篇幅长度及功能更新频率影响,下方仅为系统 **部分** 功能于 **2024年5月3日** 进行的截图,更多新增功能及细节请登录演示环境或 clone 代码到本地启动查看。
|
||||||
|
|
||||||
<table border="1" cellpadding="1" cellspacing="1" style="width: 500px">
|
<table border="1" cellpadding="1" cellspacing="1" style="width: 500px">
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -180,6 +180,86 @@ pnpm i
|
|||||||
pnpm dev
|
pnpm dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
continew-admin-ui # 前端项目
|
||||||
|
├─ config # Vite 插件配置
|
||||||
|
├─ public # 公共静态资源(favicon.ico、logo.svg)
|
||||||
|
├─ src
|
||||||
|
│ ├─ apis # 请求接口
|
||||||
|
│ │ ├─ auth # 认证模块
|
||||||
|
│ │ ├─ common # 公共模块
|
||||||
|
│ │ ├─ monitor # 系统监控模块
|
||||||
|
│ │ ├─ system # 系统管理模块
|
||||||
|
│ │ └─ tool # 系统工具模块
|
||||||
|
│ ├─ assets # 静态资源
|
||||||
|
│ │ ├─ icons # 图标资源
|
||||||
|
│ │ ├─ images # 图片资源
|
||||||
|
│ │ └─ fonts # 字体资源
|
||||||
|
│ ├─ components # 通用业务组件
|
||||||
|
│ ├─ config # 全局配置(包含 echarts 主题)
|
||||||
|
│ │ └─ settings.json # 配置文件
|
||||||
|
│ ├─ directives # 指令集(如需,可自行补充)
|
||||||
|
│ ├─ hooks # 全局 hooks
|
||||||
|
│ ├─ layout # 布局
|
||||||
|
│ ├─ mock # 模拟数据
|
||||||
|
│ ├─ router # 路由配置
|
||||||
|
│ ├─ stores # 状态管理中心
|
||||||
|
│ ├─ types # TypeScript 类型
|
||||||
|
│ ├─ utils # 工具库(mock 全局开启/关闭)
|
||||||
|
│ ├─ views # 页面
|
||||||
|
│ │ ├─ default # 默认页面
|
||||||
|
│ │ ├─ home # 首页模块
|
||||||
|
│ │ ├─ login # 登录模块
|
||||||
|
│ │ ├─ monitor # 系统监控
|
||||||
|
│ │ │ ├─ log # 系统日志
|
||||||
|
│ │ │ │ ├─ login # 登录日志
|
||||||
|
│ │ │ │ ├─ operation # 操作日志
|
||||||
|
│ │ │ │ └─ index
|
||||||
|
│ │ │ └─ online # 在线用户
|
||||||
|
│ │ ├─ setting # 设置
|
||||||
|
│ │ │ ├─ profile # 账号管理
|
||||||
|
│ │ │ └─ security # 安全设置
|
||||||
|
│ │ ├─ tool # 系统工具
|
||||||
|
│ │ │ └─ generator # 代码生成
|
||||||
|
│ │ └─ system # 系统管理
|
||||||
|
│ │ ├─ config # 系统配置
|
||||||
|
│ │ ├─ dept # 部门管理
|
||||||
|
│ │ ├─ dict # 字典管理
|
||||||
|
│ │ ├─ file # 文件管理
|
||||||
|
│ │ ├─ menu # 菜单管理
|
||||||
|
│ │ ├─ notice # 通知公告
|
||||||
|
│ │ ├─ role # 角色管理
|
||||||
|
│ │ ├─ storage # 存储管理
|
||||||
|
│ │ └─ user # 用户管理
|
||||||
|
│ ├─ App.vue
|
||||||
|
│ └─ main.ts
|
||||||
|
├─ .env.development
|
||||||
|
├─ .env.production
|
||||||
|
├─ .env.test
|
||||||
|
├─ .eslintignore
|
||||||
|
├─ .eslintrc.cjs
|
||||||
|
├─ .prettierignore
|
||||||
|
├─ .prettierrc.js
|
||||||
|
├─ index.html
|
||||||
|
├─ package.json
|
||||||
|
├─ package-lock.json
|
||||||
|
├─ pnpm-lock.yaml
|
||||||
|
├─ tsconfig.json
|
||||||
|
├─ vite.config.ts
|
||||||
|
├─ .gitignore(Git 忽略文件相关配置文件)
|
||||||
|
├─ .gitee(Gitee 相关配置目录,实际开发时直接删除)
|
||||||
|
├─ .github(GitHub 相关配置目录,实际开发时直接删除)
|
||||||
|
├─ .idea
|
||||||
|
│ └─ icon.png(IDEA 项目图标,实际开发时直接删除)
|
||||||
|
├─ .image(截图目录,实际开发时直接删除)
|
||||||
|
├─ .vscode(VSCode 配置目录)
|
||||||
|
├─ LICENSE(开源协议文件)
|
||||||
|
├─ CHANGELOG.md(更新日志文件,实际开发时直接删除)
|
||||||
|
└─ README.md(项目 README 文件,实际开发时替换为真实内容)
|
||||||
|
```
|
||||||
|
|
||||||
## 贡献指南
|
## 贡献指南
|
||||||
|
|
||||||
ContiNew Admin 致力于提供开箱即用,持续舒适的开发体验。作为一个开源项目,Creator 的初心是希望 ContiNew Admin 依托开源协作模式,提升技术透明度、放大集体智慧、共创优秀实践,源源不断地为企业级项目开发提供助力。
|
ContiNew Admin 致力于提供开箱即用,持续舒适的开发体验。作为一个开源项目,Creator 的初心是希望 ContiNew Admin 依托开源协作模式,提升技术透明度、放大集体智慧、共创优秀实践,源源不断地为企业级项目开发提供助力。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "continew-admin-ui",
|
"name": "continew-admin-ui",
|
||||||
"version": "3.0.0",
|
"version": "3.0.1",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const BASE_URL = '/captcha'
|
|||||||
|
|
||||||
/** @desc 获取图片验证码 */
|
/** @desc 获取图片验证码 */
|
||||||
export function getImageCaptcha() {
|
export function getImageCaptcha() {
|
||||||
return http.get<Common.ImageCaptchaResp>(`${BASE_URL}/img`)
|
return http.get<Common.ImageCaptchaResp>(`${BASE_URL}/image`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 获取短信验证码 */
|
/** @desc 获取短信验证码 */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
export interface ImageCaptchaResp {
|
export interface ImageCaptchaResp {
|
||||||
uuid: string
|
uuid: string
|
||||||
img: string
|
img: string
|
||||||
|
expireTime: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仪表盘访问趋势类型 */
|
/** 仪表盘访问趋势类型 */
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as Monitor from './type'
|
|||||||
const BASE_URL = '/monitor/online'
|
const BASE_URL = '/monitor/online'
|
||||||
|
|
||||||
/** @desc 查询在线用户列表 */
|
/** @desc 查询在线用户列表 */
|
||||||
export function listOnlineUser(query: Monitor.OnlineUserQuery) {
|
export function listOnlineUser(query: Monitor.OnlineUserPageQuery) {
|
||||||
return http.get<PageRes<Monitor.OnlineUserResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<Monitor.OnlineUserResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ export interface OnlineUserResp {
|
|||||||
createUserString: string
|
createUserString: string
|
||||||
createTime: string
|
createTime: string
|
||||||
}
|
}
|
||||||
export interface OnlineUserQuery extends PageQuery {
|
export interface OnlineUserQuery {
|
||||||
nickname?: string
|
nickname?: string
|
||||||
loginTime?: string
|
loginTime?: string
|
||||||
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
export interface OnlineUserPageQuery extends OnlineUserQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统日志类型 */
|
/** 系统日志类型 */
|
||||||
export interface LogResp {
|
export interface LogResp {
|
||||||
@@ -52,4 +54,4 @@ export interface LogQuery {
|
|||||||
status?: number
|
status?: number
|
||||||
sort: Array<string>
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
export interface LogPageQuery extends PageQuery, LogQuery {}
|
export interface LogPageQuery extends LogQuery, PageQuery {}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as System from './type'
|
|||||||
const BASE_URL = '/system/dict'
|
const BASE_URL = '/system/dict'
|
||||||
|
|
||||||
/** @desc 查询字典列表 */
|
/** @desc 查询字典列表 */
|
||||||
export function listDict(query: System.DictQuery) {
|
export function listDict(query: System.DictPageQuery) {
|
||||||
return http.get<PageRes<System.DictResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<System.DictResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export function deleteDict(id: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @desc 查询字典项列表 */
|
/** @desc 查询字典项列表 */
|
||||||
export function listDictItem(query: System.DictItemQuery) {
|
export function listDictItem(query: System.DictItemPageQuery) {
|
||||||
return http.get<PageRes<System.DictItemResp[]>>(`${BASE_URL}/item`, query)
|
return http.get<PageRes<System.DictItemResp[]>>(`${BASE_URL}/item`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as System from './type'
|
|||||||
const BASE_URL = '/system/file'
|
const BASE_URL = '/system/file'
|
||||||
|
|
||||||
/** @desc 查询文件列表 */
|
/** @desc 查询文件列表 */
|
||||||
export function listFile(query: System.FileQuery) {
|
export function listFile(query: System.FilePageQuery) {
|
||||||
return http.get<PageRes<System.FileItem[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<System.FileItem[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,3 +17,8 @@ export function updateFile(data: any, id: string) {
|
|||||||
export function deleteFile(ids: string | Array<string>) {
|
export function deleteFile(ids: string | Array<string>) {
|
||||||
return http.del(`${BASE_URL}/${ids}`)
|
return http.del(`${BASE_URL}/${ids}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @desc 查询文件资源统计统计 */
|
||||||
|
export function getFileStatistics() {
|
||||||
|
return http.get<System.FileStatisticsResp>(`${BASE_URL}/statistics`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as System from './type'
|
|||||||
const BASE_URL = '/system/notice'
|
const BASE_URL = '/system/notice'
|
||||||
|
|
||||||
/** @desc 查询公告列表 */
|
/** @desc 查询公告列表 */
|
||||||
export function listNotice(query: System.NoticeQuery) {
|
export function listNotice(query: System.NoticePageQuery) {
|
||||||
return http.get<PageRes<System.NoticeResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<System.NoticeResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as System from './type'
|
|||||||
const BASE_URL = '/system/role'
|
const BASE_URL = '/system/role'
|
||||||
|
|
||||||
/** @desc 查询角色列表 */
|
/** @desc 查询角色列表 */
|
||||||
export function listRole(query: System.RoleQuery) {
|
export function listRole(query: System.RolePageQuery) {
|
||||||
return http.get<PageRes<System.RoleResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<System.RoleResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type * as System from './type'
|
|||||||
const BASE_URL = '/system/storage'
|
const BASE_URL = '/system/storage'
|
||||||
|
|
||||||
/** @desc 查询存储列表 */
|
/** @desc 查询存储列表 */
|
||||||
export function listStorage(query: System.StorageQuery) {
|
export function listStorage(query: System.StoragePageQuery) {
|
||||||
return http.get<PageRes<System.StorageResp[]>>(`${BASE_URL}`, query)
|
return http.get<PageRes<System.StorageResp[]>>(`${BASE_URL}`, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export interface UserQuery {
|
|||||||
deptId?: string
|
deptId?: string
|
||||||
sort: Array<string>
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
export interface UserPageQuery extends PageQuery, UserQuery {}
|
export interface UserPageQuery extends UserQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统角色类型 */
|
/** 系统角色类型 */
|
||||||
export interface RoleResp {
|
export interface RoleResp {
|
||||||
@@ -62,9 +62,11 @@ export interface RoleDetailResp {
|
|||||||
updateTime: string
|
updateTime: string
|
||||||
disabled: boolean
|
disabled: boolean
|
||||||
}
|
}
|
||||||
export interface RoleQuery extends PageQuery {
|
export interface RoleQuery {
|
||||||
description?: string
|
description?: string
|
||||||
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
export interface RolePageQuery extends RoleQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统菜单类型 */
|
/** 系统菜单类型 */
|
||||||
export interface MenuResp {
|
export interface MenuResp {
|
||||||
@@ -116,25 +118,6 @@ export interface DeptQuery {
|
|||||||
sort: Array<string>
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 系统公告类型 */
|
|
||||||
export interface NoticeResp {
|
|
||||||
id: string
|
|
||||||
title: string
|
|
||||||
content: string
|
|
||||||
status: number
|
|
||||||
type: string
|
|
||||||
effectiveTime: string
|
|
||||||
terminateTime: string
|
|
||||||
createUserString: string
|
|
||||||
createTime: string
|
|
||||||
updateUserString: string
|
|
||||||
updateTime: string
|
|
||||||
}
|
|
||||||
export interface NoticeQuery extends PageQuery {
|
|
||||||
title?: string
|
|
||||||
type?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 系统字典类型 */
|
/** 系统字典类型 */
|
||||||
export interface DictResp {
|
export interface DictResp {
|
||||||
id: string
|
id: string
|
||||||
@@ -147,9 +130,11 @@ export interface DictResp {
|
|||||||
updateUserString: string
|
updateUserString: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
}
|
}
|
||||||
export interface DictQuery extends PageQuery {
|
export interface DictQuery {
|
||||||
description?: string
|
description?: string
|
||||||
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
export interface DictPageQuery extends DictQuery, PageQuery {}
|
||||||
export type DictItemResp = {
|
export type DictItemResp = {
|
||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
@@ -164,11 +149,34 @@ export type DictItemResp = {
|
|||||||
updateUserString: string
|
updateUserString: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
}
|
}
|
||||||
export interface DictItemQuery extends PageQuery {
|
export interface DictItemQuery {
|
||||||
description?: string
|
description?: string
|
||||||
status?: number
|
status?: number
|
||||||
|
sort: Array<string>
|
||||||
dictId: string
|
dictId: string
|
||||||
}
|
}
|
||||||
|
export interface DictItemPageQuery extends DictItemQuery, PageQuery {}
|
||||||
|
|
||||||
|
/** 系统公告类型 */
|
||||||
|
export interface NoticeResp {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
status: number
|
||||||
|
type: string
|
||||||
|
effectiveTime: string
|
||||||
|
terminateTime: string
|
||||||
|
createUserString: string
|
||||||
|
createTime: string
|
||||||
|
updateUserString: string
|
||||||
|
updateTime: string
|
||||||
|
}
|
||||||
|
export interface NoticeQuery {
|
||||||
|
title?: string
|
||||||
|
type?: string
|
||||||
|
sort: Array<string>
|
||||||
|
}
|
||||||
|
export interface NoticePageQuery extends NoticeQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统文件类型 */
|
/** 系统文件类型 */
|
||||||
export type FileItem = {
|
export type FileItem = {
|
||||||
@@ -184,10 +192,19 @@ export type FileItem = {
|
|||||||
updateUserString: string
|
updateUserString: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
}
|
}
|
||||||
export interface FileQuery extends PageQuery {
|
/** 文件资源统计信息 */
|
||||||
|
export interface FileStatisticsResp {
|
||||||
|
type: string
|
||||||
|
size: number
|
||||||
|
number: number
|
||||||
|
data: Array<FileStatisticsResp>
|
||||||
|
}
|
||||||
|
export interface FileQuery {
|
||||||
name?: string
|
name?: string
|
||||||
type?: string
|
type?: string
|
||||||
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
export interface FilePageQuery extends FileQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统存储类型 */
|
/** 系统存储类型 */
|
||||||
export type StorageResp = {
|
export type StorageResp = {
|
||||||
@@ -209,10 +226,12 @@ export type StorageResp = {
|
|||||||
updateUserString: string
|
updateUserString: string
|
||||||
updateTime: string
|
updateTime: string
|
||||||
}
|
}
|
||||||
export interface StorageQuery extends PageQuery {
|
export interface StorageQuery {
|
||||||
description?: string
|
description?: string
|
||||||
status?: number
|
status?: number
|
||||||
|
sort: Array<string>
|
||||||
}
|
}
|
||||||
|
export interface StoragePageQuery extends StorageQuery, PageQuery {}
|
||||||
|
|
||||||
/** 系统参数类型 */
|
/** 系统参数类型 */
|
||||||
export interface OptionResp {
|
export interface OptionResp {
|
||||||
@@ -224,7 +243,6 @@ export interface OptionResp {
|
|||||||
export interface OptionQuery {
|
export interface OptionQuery {
|
||||||
code: Array<string>
|
code: Array<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 基础配置类型 */
|
/** 基础配置类型 */
|
||||||
export interface BasicConfigResp {
|
export interface BasicConfigResp {
|
||||||
site_favicon: string
|
site_favicon: string
|
||||||
|
|||||||
BIN
src/assets/images/login_h5.jpg
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
@@ -7,10 +7,22 @@
|
|||||||
<a-space wrap class="gi-table__toolbar-right" :size="[8, 8]">
|
<a-space wrap class="gi-table__toolbar-right" :size="[8, 8]">
|
||||||
<slot name="custom-right"></slot>
|
<slot name="custom-right"></slot>
|
||||||
<a-tooltip content="刷新">
|
<a-tooltip content="刷新">
|
||||||
<a-button v-if="showRefreshBtn" @click="refresh">
|
<a-button v-if="showRefreshBtn" class="gi_hover_btn-border" @click="refresh">
|
||||||
<template #icon><icon-refresh /></template>
|
<template #icon><icon-refresh /></template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<a-dropdown v-if="showSizeBtn" @select="handleSelect">
|
||||||
|
<a-tooltip content="尺寸">
|
||||||
|
<a-button class="gi_hover_btn-border">
|
||||||
|
<template #icon><icon-table-size style="width: 14px; height: 14px" /></template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<template #content>
|
||||||
|
<a-doption v-for="item in sizeList" :key="item.value" :value="item.value" :active="item.value === size">{{
|
||||||
|
item.label
|
||||||
|
}}</a-doption>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
<a-popover
|
<a-popover
|
||||||
v-if="showSettingColumnBtn"
|
v-if="showSettingColumnBtn"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
@@ -18,7 +30,7 @@
|
|||||||
:content-style="{ minWidth: '120px', padding: '6px 8px 10px' }"
|
:content-style="{ minWidth: '120px', padding: '6px 8px 10px' }"
|
||||||
>
|
>
|
||||||
<a-tooltip content="列设置">
|
<a-tooltip content="列设置">
|
||||||
<a-button>
|
<a-button class="gi_hover_btn-border">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-settings />
|
<icon-settings />
|
||||||
</template>
|
</template>
|
||||||
@@ -42,6 +54,14 @@
|
|||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
<a-tooltip content="全屏">
|
||||||
|
<a-button v-if="showFullscreenBtn" class="gi_hover_btn-border" @click="isFullscreen = !isFullscreen">
|
||||||
|
<template #icon>
|
||||||
|
<icon-fullscreen v-if="!isFullscreen" />
|
||||||
|
<icon-fullscreen-exit v-else />
|
||||||
|
</template>
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-row>
|
</a-row>
|
||||||
<div class="gi-table__container">
|
<div class="gi-table__container">
|
||||||
@@ -60,7 +80,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TableInstance, TableColumnData } from '@arco-design/web-vue'
|
import type { TableInstance, TableColumnData, DropdownInstance } from '@arco-design/web-vue'
|
||||||
import { VueDraggable } from 'vue-draggable-plus'
|
import { VueDraggable } from 'vue-draggable-plus'
|
||||||
|
|
||||||
defineOptions({ name: 'GiTable', inheritAttrs: false })
|
defineOptions({ name: 'GiTable', inheritAttrs: false })
|
||||||
@@ -87,11 +107,23 @@ const size = ref<TableInstance['size']>('medium')
|
|||||||
const isBordered = ref(false)
|
const isBordered = ref(false)
|
||||||
const isFullscreen = ref(false)
|
const isFullscreen = ref(false)
|
||||||
|
|
||||||
|
type SizeItem = { label: string; value: TableInstance['size'] }
|
||||||
|
const sizeList: SizeItem[] = [
|
||||||
|
{ label: '紧凑', value: 'small' },
|
||||||
|
{ label: '默认', value: 'medium' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleSelect: DropdownInstance['onSelect'] = (value) => {
|
||||||
|
size.value = value as TableInstance['size']
|
||||||
|
}
|
||||||
|
|
||||||
const refresh = () => {
|
const refresh = () => {
|
||||||
emit('refresh')
|
emit('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
const showRefreshBtn = computed(() => !props.disabledTools.includes('refresh'))
|
const showRefreshBtn = computed(() => !props.disabledTools.includes('refresh'))
|
||||||
|
const showSizeBtn = computed(() => !props.disabledTools.includes('size'))
|
||||||
|
const showFullscreenBtn = computed(() => !props.disabledTools.includes('fullscreen'))
|
||||||
const showSettingColumnBtn = computed(
|
const showSettingColumnBtn = computed(
|
||||||
() => !props.disabledTools.includes('setting') && attrs?.columns && (attrs?.columns as TableColumnData[])?.length
|
() => !props.disabledTools.includes('setting') && attrs?.columns && (attrs?.columns as TableColumnData[])?.length
|
||||||
)
|
)
|
||||||
@@ -177,7 +209,6 @@ defineExpose({ tableRef })
|
|||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.arco-form-layout-inline .arco-form-item) {
|
:deep(.arco-form-layout-inline .arco-form-item) {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import router from './router'
|
|||||||
// 引入 Arco Design 组件库以及自定义主题
|
// 引入 Arco Design 组件库以及自定义主题
|
||||||
import ArcoVue from '@arco-design/web-vue'
|
import ArcoVue from '@arco-design/web-vue'
|
||||||
import '@/styles/arco-ui/index.less'
|
import '@/styles/arco-ui/index.less'
|
||||||
|
import 'md-editor-v3/lib/style.css'
|
||||||
// import '@arco-themes/vue-gi-demo/index.less'
|
// import '@arco-themes/vue-gi-demo/index.less'
|
||||||
// import '@arco-design/web-vue/dist/arco.css'
|
// import '@arco-design/web-vue/dist/arco.css'
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export function resetRouter() {
|
|||||||
router.getRoutes().forEach((route) => {
|
router.getRoutes().forEach((route) => {
|
||||||
const { name } = route
|
const { name } = route
|
||||||
// console.log('name', name, path)
|
// console.log('name', name, path)
|
||||||
if (name && name !== 'Home') {
|
if (name && !['Home', 'Setting', 'SettingProfile', 'SettingSecurity'].includes(name.toString())) {
|
||||||
router.hasRoute(name) && router.removeRoute(name)
|
router.hasRoute(name) && router.removeRoute(name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ body[arco-theme='dark'] {
|
|||||||
#app {
|
#app {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
min-height: 800px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -156,6 +156,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gi_hover_btn-border {
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-secondary-hover) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: var(--color-secondary-active) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.gi_card {
|
.gi_card {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -317,7 +327,6 @@
|
|||||||
-webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, .15);
|
-webkit-box-shadow: 0 2px 3px rgba(0, 0, 0, .15);
|
||||||
box-shadow: 0 2px 3px rgba(0, 0, 0, .15);
|
box-shadow: 0 2px 3px rgba(0, 0, 0, .15);
|
||||||
border-color: rgb(var(--primary-5));
|
border-color: rgb(var(--primary-5));
|
||||||
color: var(--color-text-2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/types/api.d.ts
vendored
@@ -17,5 +17,4 @@ interface PageRes<T> {
|
|||||||
interface PageQuery {
|
interface PageQuery {
|
||||||
page: number
|
page: number
|
||||||
size: number
|
size: number
|
||||||
sort: Array<string>
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/types/global.d.ts
vendored
@@ -23,5 +23,5 @@ export interface DictState {
|
|||||||
/** 状态(1:启用;2:禁用) */
|
/** 状态(1:启用;2:禁用) */
|
||||||
type Status = 1 | 2
|
type Status = 1 | 2
|
||||||
|
|
||||||
/** 性别(1:男;2:女;3:未知) */
|
/** 性别(1:男;2:女;0:未知) */
|
||||||
type Gender = 1 | 2 | 3
|
type Gender = 1 | 2 | 0
|
||||||
|
|||||||
@@ -243,6 +243,8 @@ export const formatFileSize = (fileSize: number) => {
|
|||||||
const size = srcSize / 1024 ** index
|
const size = srcSize / 1024 ** index
|
||||||
return `${size.toFixed(2)} ${unitArr[index]}`
|
return `${size.toFixed(2)} ${unitArr[index]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @desc 复制文本 */
|
||||||
export const copyText = (text: any) => {
|
export const copyText = (text: any) => {
|
||||||
const textarea = document.createElement('textarea')
|
const textarea = document.createElement('textarea')
|
||||||
textarea.value = text
|
textarea.value = text
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
>
|
>
|
||||||
<a-carousel-item v-for="(image, idx) in imageList" :key="idx">
|
<a-carousel-item v-for="(image, idx) in imageList" :key="idx">
|
||||||
<a :href="image.link" target="_blank" :title="image.title">
|
<a :href="image.link" target="_blank" :title="image.title">
|
||||||
<img :src="image.src" style="width: 100%" :alt="image.title" />
|
<img :src="image.src" style="width: 100%; height: 100%" :alt="image.title" />
|
||||||
</a>
|
</a>
|
||||||
</a-carousel-item>
|
</a-carousel-item>
|
||||||
</a-carousel>
|
</a-carousel>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :md="10" :lg="8" :xl="8" :xxl="6" style="margin: -8px -7px">
|
<a-col :xs="24" :sm="24" :md="10" :lg="8" :xl="8" :xxl="6" style="margin: -8px -7px">
|
||||||
<a-row justify="end">
|
<a-row justify="end">
|
||||||
<SupportCard />
|
<SupportCard v-if="isDesktop" />
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -26,10 +26,12 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import NowTime from './NowTime/index.vue'
|
import NowTime from './NowTime/index.vue'
|
||||||
|
import { useDevice } from '@/hooks'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { goodTimeText } from '@/utils'
|
import { goodTimeText } from '@/utils'
|
||||||
import SupportCard from './SupportCard.vue'
|
import SupportCard from './SupportCard.vue'
|
||||||
|
|
||||||
|
const { isDesktop } = useDevice()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
<WorkCard />
|
<WorkCard />
|
||||||
|
|
||||||
<a-alert>
|
<a-alert>
|
||||||
全新版本 v3.0.0 发布预告,采用全新前端模板,提供更可靠、更舒适的前端开发体验,点击查看
|
全新版本 v3.0.0 已发布,采用全新前端模板,提供更可靠、更舒适的前端开发体验,点击查看
|
||||||
<span class="link" @click="open('https://gitee.com/continew/continew-admin-ui/commits/dev')">项目进展</span>。
|
<span class="link" @click="open('https://continew.top/admin/other/changelog.html')">更新日志</span>。
|
||||||
</a-alert>
|
</a-alert>
|
||||||
|
|
||||||
<a-row class="home__content">
|
<a-row class="home__content">
|
||||||
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="18" :xxl="20">
|
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="18" :xxl="18">
|
||||||
<div class="home__item"><ProjectCard /></div>
|
<div class="home__item"><ProjectCard /></div>
|
||||||
<div class="home__item"><AccessTrendCard /></div>
|
<div class="home__item"><AccessTrendCard /></div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="6" :xxl="4">
|
<a-col :xs="24" :sm="24" :md="24" :lg="12" :xl="6" :xxl="6">
|
||||||
<div class="home__item"><FastCard /></div>
|
<div class="home__item"><FastCard /></div>
|
||||||
<div class="home__item"><SponsorCard /></div>
|
<div class="home__item"><SponsorCard /></div>
|
||||||
<div class="home__item"><NoticeCard /></div>
|
<div class="home__item"><NoticeCard /></div>
|
||||||
|
|||||||
@@ -16,7 +16,12 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="captcha" hide-label>
|
<a-form-item field="captcha" hide-label>
|
||||||
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="4" allow-clear style="flex: 1 1" />
|
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="4" allow-clear style="flex: 1 1" />
|
||||||
<img :src="captchaImgBase64" alt="验证码" class="captcha" @click="getCaptcha" />
|
<div class="captcha-container" @click="getCaptcha">
|
||||||
|
<img :src="captchaImgBase64" alt="验证码" class="captcha" />
|
||||||
|
<div v-if="form.expired" class="overlay">
|
||||||
|
<p>已过期,请刷新</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-row justify="space-between" align="center" class="w-full">
|
<a-row justify="space-between" align="center" class="w-full">
|
||||||
@@ -52,7 +57,8 @@ const form = reactive({
|
|||||||
username: loginConfig.value.username,
|
username: loginConfig.value.username,
|
||||||
password: loginConfig.value.password,
|
password: loginConfig.value.password,
|
||||||
captcha: '',
|
captcha: '',
|
||||||
uuid: ''
|
uuid: '',
|
||||||
|
expired: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules: FormInstance['rules'] = {
|
const rules: FormInstance['rules'] = {
|
||||||
@@ -98,11 +104,36 @@ const captchaImgBase64 = ref()
|
|||||||
// 获取验证码
|
// 获取验证码
|
||||||
const getCaptcha = () => {
|
const getCaptcha = () => {
|
||||||
getImageCaptcha().then((res) => {
|
getImageCaptcha().then((res) => {
|
||||||
form.uuid = res.data.uuid
|
const { uuid, img, expireTime } = res.data
|
||||||
captchaImgBase64.value = res.data.img
|
form.uuid = uuid
|
||||||
|
captchaImgBase64.value = img
|
||||||
|
form.expired = false
|
||||||
|
startTimer(expireTime)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证码过期定时器
|
||||||
|
let timer
|
||||||
|
const startTimer = (expireTime: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
const remainingTime = expireTime - Date.now()
|
||||||
|
if (remainingTime <= 0) {
|
||||||
|
form.expired = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
form.expired = true
|
||||||
|
}, remainingTime)
|
||||||
|
}
|
||||||
|
// 组件销毁时清理定时器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getCaptcha()
|
getCaptcha()
|
||||||
})
|
})
|
||||||
@@ -137,10 +168,37 @@ onMounted(() => {
|
|||||||
width: 111px;
|
width: 111px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.captcha-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(51, 51, 51, 0.8);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay p {
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
// import { getEmailCaptcha } from '@/apis'
|
// import { getEmailCaptcha } from '@/apis'
|
||||||
import { Message, type FormInstance } from '@arco-design/web-vue'
|
import { Message, type FormInstance } from '@arco-design/web-vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -45,7 +46,10 @@ const form = reactive({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules: FormInstance['rules'] = {
|
const rules: FormInstance['rules'] = {
|
||||||
email: [{ required: true, message: '请输入邮箱' }],
|
email: [
|
||||||
|
{ required: true, message: '请输入邮箱' },
|
||||||
|
{ match: Regexp.Email, message: '请输入正确的邮箱' }
|
||||||
|
],
|
||||||
captcha: [{ required: true, message: '请输入验证码' }]
|
captcha: [{ required: true, message: '请输入验证码' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
// import { getSmsCaptcha } from '@/apis'
|
// import { getSmsCaptcha } from '@/apis'
|
||||||
import { Message, type FormInstance } from '@arco-design/web-vue'
|
import { Message, type FormInstance } from '@arco-design/web-vue'
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@@ -47,7 +48,7 @@ const form = reactive({
|
|||||||
const rules: FormInstance['rules'] = {
|
const rules: FormInstance['rules'] = {
|
||||||
phone: [
|
phone: [
|
||||||
{ required: true, message: '请输入手机号' },
|
{ required: true, message: '请输入手机号' },
|
||||||
{ match: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
|
{ match: Regexp.Phone, message: '请输入正确的手机号' }
|
||||||
],
|
],
|
||||||
captcha: [{ required: true, message: '请输入验证码' }]
|
captcha: [{ required: true, message: '请输入验证码' }]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login pc">
|
||||||
<h3 class="login-logo">
|
<h3 class="login-logo">
|
||||||
<img v-if="logo" :src="logo" alt="logo" />
|
<img v-if="logo" :src="logo" alt="logo" />
|
||||||
<img v-else src="/logo.svg" alt="logo" />
|
<img v-else src="/logo.svg" alt="logo" />
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
<a-col :xs="24" :sm="12" :md="11">
|
<a-col :xs="24" :sm="12" :md="11">
|
||||||
<div class="login-right">
|
<div class="login-right">
|
||||||
<h3 class="login-right__title" v-if="isEmailLogin">邮箱登录</h3>
|
<h3 class="login-right__title" v-if="isEmailLogin">邮箱登录</h3>
|
||||||
<EmailLogin v-if="isEmailLogin" />
|
<EmailLogin v-show="isEmailLogin" />
|
||||||
<a-tabs v-else class="login-right__form">
|
<a-tabs v-show="!isEmailLogin" class="login-right__form">
|
||||||
<a-tab-pane title="账号登录" key="1">
|
<a-tab-pane title="账号登录" key="1">
|
||||||
<AccountLogin />
|
<AccountLogin />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
@@ -50,6 +50,42 @@
|
|||||||
<GiThemeBtn class="theme-btn" />
|
<GiThemeBtn class="theme-btn" />
|
||||||
<Background />
|
<Background />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="login h5">
|
||||||
|
<div class="login-logo">
|
||||||
|
<img v-if="logo" :src="logo" alt="logo" />
|
||||||
|
<img v-else src="/logo.svg" alt="logo" />
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
<a-row align="stretch" class="login-box">
|
||||||
|
<a-col :xs="24" :sm="12" :md="11">
|
||||||
|
<div class="login-right">
|
||||||
|
<h3 class="login-right__title" v-if="isEmailLogin">邮箱登录</h3>
|
||||||
|
<EmailLogin v-show="isEmailLogin" />
|
||||||
|
<a-tabs v-show="!isEmailLogin" class="login-right__form">
|
||||||
|
<a-tab-pane title="账号登录" key="1">
|
||||||
|
<AccountLogin />
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane title="手机号登录" key="2">
|
||||||
|
<PhoneLogin />
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<div class="login-right__oauth">
|
||||||
|
<a-divider orientation="center">其他登录方式</a-divider>
|
||||||
|
<div class="list">
|
||||||
|
<div v-if="isEmailLogin" class="mode item" @click="toggleLoginMode"><icon-user /> 账号/手机号登录</div>
|
||||||
|
<div v-else class="mode item" @click="toggleLoginMode"><icon-email /> 邮箱登录</div>
|
||||||
|
<a class="item" title="使用 Gitee 账号登录" @click="onOauth('gitee')">
|
||||||
|
<GiSvgIcon name="gitee" :size="24" />
|
||||||
|
</a>
|
||||||
|
<a class="item" title="使用 GitHub 账号登录" @click="onOauth('github')">
|
||||||
|
<GiSvgIcon name="github" :size="24" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -82,181 +118,350 @@ const onOauth = async (source: string) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.login {
|
@media screen and (max-width: 570px) {
|
||||||
height: 100%;
|
.pc {
|
||||||
display: flex;
|
display: none !important;
|
||||||
flex-direction: column;
|
background-color: white !important;
|
||||||
justify-content: center;
|
}
|
||||||
align-items: center;
|
.login {
|
||||||
background-color: var(--color-bg-5);
|
height: 100%;
|
||||||
&-logo {
|
|
||||||
position: fixed;
|
|
||||||
top: 20px;
|
|
||||||
left: 30px;
|
|
||||||
z-index: 9999;
|
|
||||||
color: var(--color-text-1);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 32px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
flex-direction: column;
|
||||||
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
img {
|
background-color: var(--color-bg-5);
|
||||||
width: 34px;
|
color: #121314;
|
||||||
height: 34px;
|
&-logo {
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&-box {
|
|
||||||
width: 86%;
|
|
||||||
max-width: 850px;
|
|
||||||
height: 490px;
|
|
||||||
display: flex;
|
|
||||||
z-index: 999;
|
|
||||||
box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-left {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
background: linear-gradient(60deg, rgb(var(--primary-6)), rgb(var(--primary-3)));
|
|
||||||
&__img {
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(-50%);
|
|
||||||
transition: all 0.3s;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-right {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: var(--color-bg-1);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 30px 30px 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
&__title {
|
|
||||||
color: var(--color-text-1);
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 32px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
&__form {
|
|
||||||
:deep(.arco-tabs-nav-tab) {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-tab) {
|
|
||||||
color: var(--color-text-2);
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-tab-title) {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-content) {
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-tab-active),
|
|
||||||
:deep(.arco-tabs-tab-title:hover) {
|
|
||||||
color: rgb(var(--arcoblue-6));
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-nav::before) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
:deep(.arco-tabs-tab-title:before) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__oauth {
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
:deep(.arco-divider-text) {
|
|
||||||
color: var(--color-text-4);
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
.list {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
.item {
|
height: 104px;
|
||||||
margin-right: 15px;
|
font-weight: 700;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 32px;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 20px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
background-image: url('/src/assets/images/login_h5.jpg');
|
||||||
|
background-size: 100% 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
img {
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
.mode {
|
}
|
||||||
|
&-box {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-right {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 30px 30px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&__title {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
&__form {
|
||||||
|
:deep(.arco-tabs-nav-tab) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: start;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab) {
|
||||||
color: var(--color-text-2);
|
color: var(--color-text-2);
|
||||||
|
margin: 0 20px 0 0;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-title) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-content) {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-active),
|
||||||
|
:deep(.arco-tabs-tab-title:hover) {
|
||||||
|
color: rgb(var(--arcoblue-6));
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-nav::before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-title:before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__oauth {
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
// margin-top: auto;
|
||||||
|
// margin-bottom: 20px;
|
||||||
|
:deep(.arco-divider-text) {
|
||||||
|
color: var(--color-text-4);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
padding: 6px 10px;
|
}
|
||||||
|
.list {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid var(--color-border-3);
|
|
||||||
border-radius: 32px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 32px;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
width: 100%;
|
||||||
.icon {
|
.item {
|
||||||
width: 21px;
|
margin-right: 15px;
|
||||||
height: 20px;
|
}
|
||||||
|
.mode {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--color-border-3);
|
||||||
|
border-radius: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
.icon {
|
||||||
|
width: 21px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mode svg {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.mode:hover,
|
||||||
|
.mode svg:hover {
|
||||||
|
background: rgba(var(--primary-6), 0.05);
|
||||||
|
border: 1px solid rgb(var(--primary-3));
|
||||||
|
color: rgb(var(--arcoblue-6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.mode svg {
|
}
|
||||||
font-size: 16px;
|
}
|
||||||
margin-right: 10px;
|
|
||||||
|
.theme-btn {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 999;
|
||||||
|
.beian {
|
||||||
|
.text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.mode:hover,
|
.below {
|
||||||
.mode svg:hover {
|
align-items: center;
|
||||||
background: rgba(var(--primary-6), 0.05);
|
display: flex;
|
||||||
border: 1px solid rgb(var(--primary-3));
|
|
||||||
color: rgb(var(--arcoblue-6));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media screen and (min-width: 571px) {
|
||||||
.theme-btn {
|
.h5 {
|
||||||
position: fixed;
|
display: none !important;
|
||||||
top: 20px;
|
}
|
||||||
right: 30px;
|
.login {
|
||||||
z-index: 9999;
|
height: 100%;
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
.footer {
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
box-sizing: border-box;
|
background-color: var(--color-bg-5);
|
||||||
position: absolute;
|
&-logo {
|
||||||
bottom: 10px;
|
position: fixed;
|
||||||
z-index: 999;
|
top: 20px;
|
||||||
.beian {
|
left: 30px;
|
||||||
.text {
|
z-index: 9999;
|
||||||
font-size: 12px;
|
color: var(--color-text-1);
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
letter-spacing: 0.2px;
|
font-size: 20px;
|
||||||
line-height: 20px;
|
line-height: 32px;
|
||||||
text-align: center;
|
margin-bottom: 20px;
|
||||||
}
|
|
||||||
.below {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
img {
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-box {
|
||||||
|
width: 86%;
|
||||||
|
max-width: 850px;
|
||||||
|
height: 490px;
|
||||||
|
display: flex;
|
||||||
|
z-index: 999;
|
||||||
|
box-shadow: 0 2px 4px 2px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-left {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(60deg, rgb(var(--primary-6)), rgb(var(--primary-3)));
|
||||||
|
&__img {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(-50%);
|
||||||
|
transition: all 0.3s;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-right {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: var(--color-bg-1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 30px 30px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
&__title {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
&__form {
|
||||||
|
:deep(.arco-tabs-nav-tab) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab) {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-title) {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-content) {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-active),
|
||||||
|
:deep(.arco-tabs-tab-title:hover) {
|
||||||
|
color: rgb(var(--arcoblue-6));
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-nav::before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:deep(.arco-tabs-tab-title:before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&__oauth {
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
:deep(.arco-divider-text) {
|
||||||
|
color: var(--color-text-4);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.list {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
.item {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.mode {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--color-border-3);
|
||||||
|
border-radius: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
height: 32px;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
.icon {
|
||||||
|
width: 21px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.mode svg {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.mode:hover,
|
||||||
|
.mode svg:hover {
|
||||||
|
background: rgba(var(--primary-6), 0.05);
|
||||||
|
border: 1px solid rgb(var(--primary-3));
|
||||||
|
color: rgb(var(--arcoblue-6));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-btn {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
align-items: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
z-index: 999;
|
||||||
|
.beian {
|
||||||
|
.text {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.2px;
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.below {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['size', 'setting']"
|
||||||
@filterChange="filterChange"
|
@filterChange="filterChange"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #custom-right>
|
<template #custom-right>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button v-permission="['monitor:log:export']" @click="onExport">
|
<a-button v-permission="['monitor:log:export']" class="gi_hover_btn-border" @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { exportLoginLog, listLog } from '@/apis'
|
import { exportLoginLog, listLog, type LogQuery } from '@/apis'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
||||||
import { useTable, useDownload } from '@/hooks'
|
import { useTable, useDownload } from '@/hooks'
|
||||||
@@ -90,15 +90,12 @@ const columns: TableInstanceColumns[] = [
|
|||||||
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<LogQuery>({
|
||||||
module: '登录',
|
module: '登录',
|
||||||
ip: undefined,
|
|
||||||
createUserString: undefined,
|
|
||||||
createTime: [
|
createTime: [
|
||||||
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||||
],
|
],
|
||||||
status: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
column-resizable
|
column-resizable
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['size', 'setting']"
|
||||||
@filterChange="filterChange"
|
@filterChange="filterChange"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #custom-right>
|
<template #custom-right>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button v-permission="['monitor:log:export']" @click="onExport">
|
<a-button v-permission="['monitor:log:export']" class="gi_hover_btn-border" @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listLog, exportOperationLog, type LogResp } from '@/apis'
|
import { listLog, exportOperationLog, type LogResp, type LogQuery } from '@/apis'
|
||||||
import OperationLogDetailDrawer from './OperationLogDetailDrawer.vue'
|
import OperationLogDetailDrawer from './OperationLogDetailDrawer.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
||||||
@@ -102,15 +102,11 @@ const columns: TableInstanceColumns[] = [
|
|||||||
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
{ title: '终端系统', dataIndex: 'os', ellipsis: true, tooltip: true }
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<LogQuery>({
|
||||||
description: undefined,
|
|
||||||
ip: undefined,
|
|
||||||
createUserString: undefined,
|
|
||||||
createTime: [
|
createTime: [
|
||||||
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
dayjs().subtract(6, 'day').startOf('day').format('YYYY-MM-DD HH:mm:ss'),
|
||||||
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
dayjs().endOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||||
],
|
],
|
||||||
status: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['size', 'setting']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
<template #custom-left>
|
<template #custom-left>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listOnlineUser, kickout } from '@/apis'
|
import { listOnlineUser, kickout, type OnlineUserQuery } from '@/apis'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
import DateRangePicker from '@/components/DateRangePicker/index.vue'
|
||||||
@@ -79,9 +79,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<OnlineUserQuery>({
|
||||||
nickname: undefined,
|
|
||||||
loginTime: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
// import { getSmsCaptcha, getEmailCaptcha, updateUserEmail, updateUserPhone } from '@/apis'
|
// import { getSmsCaptcha, getEmailCaptcha, updateUserEmail, updateUserPhone } from '@/apis'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
// import { encryptByRsa } from '@/utils/encrypt'
|
// import { encryptByRsa } from '@/utils/encrypt'
|
||||||
import * as Regexp from '@/utils/regexp'
|
|
||||||
import { useUserStore } from '@/stores'
|
import { useUserStore } from '@/stores'
|
||||||
import { type Columns, GiForm } from '@/components/GiForm'
|
import { type Columns, GiForm } from '@/components/GiForm'
|
||||||
import { useForm } from '@/hooks'
|
import { useForm } from '@/hooks'
|
||||||
|
import * as Regexp from '@/utils/regexp'
|
||||||
|
|
||||||
const verifyType = ref()
|
const verifyType = ref()
|
||||||
const title = computed(() => (verifyType.value === 'phone' ? '修改手机号' : '修改邮箱'))
|
const title = computed(() => (verifyType.value === 'phone' ? '修改手机号' : '修改邮箱'))
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer>
|
<footer>
|
||||||
<a-descriptions column="4" size="large">
|
<a-descriptions :column="4" size="large">
|
||||||
<a-descriptions-item :span="4">
|
<a-descriptions-item :span="4">
|
||||||
<template #label> <icon-user /><span style="margin-left: 5px">用户名</span></template>
|
<template #label> <icon-user /><span style="margin-left: 5px">用户名</span></template>
|
||||||
{{ userInfo.username }}
|
{{ userInfo.username }}
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ const columns: Columns = [
|
|||||||
field: 'parentId',
|
field: 'parentId',
|
||||||
type: 'tree-select',
|
type: 'tree-select',
|
||||||
data: deptList,
|
data: deptList,
|
||||||
|
hide: (form) => {
|
||||||
|
return form.parentId === 0
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
allowSearch: true,
|
allowSearch: true,
|
||||||
@@ -50,7 +53,8 @@ const columns: Columns = [
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
rules: [{ required: true, message: '请选择上级部门' }]
|
||||||
},
|
},
|
||||||
{ label: '名称', field: 'name', type: 'input', rules: [{ required: true, message: '请输入名称' }] },
|
{ label: '名称', field: 'name', type: 'input', rules: [{ required: true, message: '请输入名称' }] },
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['name']"
|
:disabledColumnKeys="['name']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
<span>新增</span>
|
<span>新增</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button v-permission="['system:dept:export']" @click="onExport">
|
<a-button v-permission="['system:dept:export']" class="gi_hover_btn-border" @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
@@ -111,9 +112,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<DeptQuery>({
|
||||||
description: undefined,
|
|
||||||
status: undefined,
|
|
||||||
sort: ['parentId,asc', 'sort,asc', 'createTime,desc']
|
sort: ['parentId,asc', 'sort,asc', 'createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['name']"
|
:disabledColumnKeys="['name']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -51,7 +52,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listDict, deleteDict, type DictResp } from '@/apis'
|
import { listDict, deleteDict, type DictResp, type DictQuery } from '@/apis'
|
||||||
import DictAddModal from './DictAddModal.vue'
|
import DictAddModal from './DictAddModal.vue'
|
||||||
import DictItemModal from '@/views/system/dict/item/index.vue'
|
import DictItemModal from '@/views/system/dict/item/index.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
@@ -86,8 +87,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<DictQuery>({
|
||||||
description: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 800 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 800 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['label']"
|
:disabledColumnKeys="['label']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -62,7 +63,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { listDictItem, deleteDictItem, type DictItemResp } from '@/apis'
|
import { listDictItem, deleteDictItem, type DictItemResp, type DictItemQuery } from '@/apis'
|
||||||
import DictItemAddModal from './DictItemAddModal.vue'
|
import DictItemAddModal from './DictItemAddModal.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import { useTable } from '@/hooks'
|
import { useTable } from '@/hooks'
|
||||||
@@ -96,9 +97,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
{ title: '操作', slotName: 'action', width: 130, align: 'center', fixed: !isMobile() ? 'right' : undefined }
|
{ title: '操作', slotName: 'action', width: 130, align: 'center', fixed: !isMobile() ? 'right' : undefined }
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<DictItemQuery>({
|
||||||
description: undefined,
|
|
||||||
status: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import type { FileItem } from '@/apis'
|
||||||
import type { FormInstance } from '@arco-design/web-vue'
|
import type { FormInstance } from '@arco-design/web-vue'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: FileItem
|
||||||
|
}
|
||||||
|
const props = withDefaults(defineProps<Props>(), {})
|
||||||
|
|
||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: ''
|
name: props.data?.name || ''
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({ formRef })
|
defineExpose({ formRef })
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ref, h } from 'vue'
|
|||||||
import { Modal, Message } from '@arco-design/web-vue'
|
import { Modal, Message } from '@arco-design/web-vue'
|
||||||
import ModalContent from './ModalContent.vue'
|
import ModalContent from './ModalContent.vue'
|
||||||
|
|
||||||
export function openFileRenameModal(data: FileItem) {
|
export function openFileRenameModal(data: FileItem, callback?: () => void) {
|
||||||
const ModalContentRef = ref<InstanceType<typeof ModalContent>>()
|
const ModalContentRef = ref<InstanceType<typeof ModalContent>>()
|
||||||
return Modal.open({
|
return Modal.open({
|
||||||
title: '重命名',
|
title: '重命名',
|
||||||
@@ -12,6 +12,7 @@ export function openFileRenameModal(data: FileItem) {
|
|||||||
width: '90%',
|
width: '90%',
|
||||||
content: () =>
|
content: () =>
|
||||||
h(ModalContent, {
|
h(ModalContent, {
|
||||||
|
data,
|
||||||
ref: (e) => {
|
ref: (e) => {
|
||||||
ModalContentRef.value = e as any
|
ModalContentRef.value = e as any
|
||||||
}
|
}
|
||||||
@@ -22,6 +23,9 @@ export function openFileRenameModal(data: FileItem) {
|
|||||||
if (isInvalid) return false
|
if (isInvalid) return false
|
||||||
await updateFile({ name: modelParams?.name }, data.id)
|
await updateFile({ name: modelParams?.name }, data.id)
|
||||||
Message.success('重命名成功')
|
Message.success('重命名成功')
|
||||||
|
if (callback) {
|
||||||
|
callback()
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,11 +16,13 @@
|
|||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
<FileAsideStatistics />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { FileTypeList, type FileTypeListItem } from '@/constant/file'
|
import { FileTypeList, type FileTypeListItem } from '@/constant/file'
|
||||||
|
import FileAsideStatistics from '@/views/system/file/main/FileAsideStatistics.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -42,7 +44,7 @@ watch(
|
|||||||
|
|
||||||
// 点击事件
|
// 点击事件
|
||||||
const onClickItem = (item: FileTypeListItem) => {
|
const onClickItem = (item: FileTypeListItem) => {
|
||||||
router.push({ name: 'File', query: { type: item.value } })
|
router.push({ name: 'SystemFile', query: { type: item.value } })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -55,19 +57,4 @@ const onClickItem = (item: FileTypeListItem) => {
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.arco-progress) {
|
|
||||||
.arco-progress-line,
|
|
||||||
.arco-progress-line-bar-buffer,
|
|
||||||
.arco-progress-line-bar {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.percent {
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 14px 12px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: var(--color-bg-1);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
124
src/views/system/file/main/FileAsideStatistics.vue
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<section class="percent">
|
||||||
|
<a-space class="statistic-space" align="center" size="medium" fill>
|
||||||
|
<template #split>
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
</template>
|
||||||
|
<a-statistic class="statistic-item" title="存储量" :value="totalData.size" :value-style="statisticValueStyle">
|
||||||
|
<template #suffix> {{ totalData.unit }}</template>
|
||||||
|
</a-statistic>
|
||||||
|
<a-statistic class="statistic-item" title="数量" :value="totalData.number" :value-style="statisticValueStyle" />
|
||||||
|
</a-space>
|
||||||
|
<a-divider />
|
||||||
|
<VCharts :option="option" autoresize :style="{ height: '120px', width: '150px' }" />
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getFileStatistics, type FileStatisticsResp } from '@/apis'
|
||||||
|
import { useChart } from '@/hooks'
|
||||||
|
import { FileTypeList } from '@/constant/file'
|
||||||
|
import VCharts from 'vue-echarts'
|
||||||
|
import { use } from 'echarts/core'
|
||||||
|
import { PieChart } from 'echarts/charts'
|
||||||
|
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
|
import { formatFileSize } from '@/utils'
|
||||||
|
|
||||||
|
use([TitleComponent, TooltipComponent, LegendComponent, PieChart, CanvasRenderer])
|
||||||
|
|
||||||
|
const totalData = ref<FileStatisticsResp>({})
|
||||||
|
const chartData = ref<Array<FileStatisticsResp>>([])
|
||||||
|
const statisticValueStyle = { color: '#5856D6', 'font-size': '18px' }
|
||||||
|
const { option } = useChart(() => {
|
||||||
|
return {
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
bottom: -5,
|
||||||
|
icon: 'circle',
|
||||||
|
itemWidth: 6,
|
||||||
|
itemHeight: 6,
|
||||||
|
textStyle: {
|
||||||
|
color: '#4E5969'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
show: true,
|
||||||
|
formatter: function (params) {
|
||||||
|
return `总计:${params.value}<br>${params.data.size}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['40%', '70%'],
|
||||||
|
avoidLabelOverlap: true,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
data: chartData.value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const getStatisticsData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
chartData.value = []
|
||||||
|
totalData.value = {}
|
||||||
|
const { data: resData } = await getFileStatistics()
|
||||||
|
const formatSize = formatFileSize(resData.size).split(' ')
|
||||||
|
totalData.value = {
|
||||||
|
size: parseFloat(formatSize[0]),
|
||||||
|
number: resData.number,
|
||||||
|
unit: formatSize[1]
|
||||||
|
}
|
||||||
|
resData.data.forEach((fs: FileStatisticsResp) => {
|
||||||
|
const matchedItem = FileTypeList.find((item) => item.value == fs.type)
|
||||||
|
chartData.value.unshift({
|
||||||
|
name: matchedItem ? matchedItem.name : '',
|
||||||
|
value: fs.number,
|
||||||
|
size: formatFileSize(fs.size)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getStatisticsData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.statistic-space {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statistic-item {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.percent {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background-color: var(--color-bg-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-divider-horizontal) {
|
||||||
|
margin: 20px 0 0 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -27,11 +27,7 @@
|
|||||||
:class="{ checked: props.selectedFileIds.includes(item.id) }"
|
:class="{ checked: props.selectedFileIds.includes(item.id) }"
|
||||||
@click.stop="handleCheckFile(item)"
|
@click.stop="handleCheckFile(item)"
|
||||||
>
|
>
|
||||||
<a-checkbox
|
<a-checkbox class="checkbox" :model-value="props.selectedFileIds.includes(item.id)"></a-checkbox>
|
||||||
class="checkbox"
|
|
||||||
:model-value="props.selectedFileIds.includes(item.id)"
|
|
||||||
@change="handleCheckFile(item)"
|
|
||||||
></a-checkbox>
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</a-grid-item>
|
</a-grid-item>
|
||||||
|
|||||||
@@ -17,7 +17,13 @@
|
|||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
|
|
||||||
<a-input-group>
|
<a-input-group>
|
||||||
<a-input v-model="queryForm.name" placeholder="请输入文件名" allow-clear @change="search" />
|
<a-input
|
||||||
|
v-model="queryForm.name"
|
||||||
|
placeholder="请输入文件名"
|
||||||
|
allow-clear
|
||||||
|
style="width: 200px"
|
||||||
|
@change="search"
|
||||||
|
/>
|
||||||
<a-button type="primary" @click="search">
|
<a-button type="primary" @click="search">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-search />
|
<icon-search />
|
||||||
@@ -47,11 +53,11 @@
|
|||||||
<template #default>{{ isBatchMode ? '取消批量' : '批量操作' }}</template>
|
<template #default>{{ isBatchMode ? '取消批量' : '批量操作' }}</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button-group>
|
<a-button-group>
|
||||||
<a-tooltip content="视图" position="bottom">
|
<a-tooltip content="视图">
|
||||||
<a-button @click="toggleMode">
|
<a-button class="gi_hover_btn-border" @click="toggleMode">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-apps v-if="mode === 'grid'" />
|
<icon-list v-if="mode === 'grid'" />
|
||||||
<icon-list v-else />
|
<icon-apps v-else />
|
||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -88,7 +94,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listFile, uploadFile, deleteFile, type FileItem, type FileQuery } from '@/apis'
|
import { listFile, uploadFile, deleteFile, type FileItem, type FileQuery, type FilePageQuery } from '@/apis'
|
||||||
import { Message, Modal, type RequestOption } from '@arco-design/web-vue'
|
import { Message, Modal, type RequestOption } from '@arco-design/web-vue'
|
||||||
import FileGrid from './FileGrid.vue'
|
import FileGrid from './FileGrid.vue'
|
||||||
import {
|
import {
|
||||||
@@ -102,6 +108,7 @@ import { ImageTypes } from '@/constant/file'
|
|||||||
import { api as viewerApi } from 'v-viewer'
|
import { api as viewerApi } from 'v-viewer'
|
||||||
import 'viewerjs/dist/viewer.css'
|
import 'viewerjs/dist/viewer.css'
|
||||||
import { downloadByUrl } from '@/utils/downloadFile'
|
import { downloadByUrl } from '@/utils/downloadFile'
|
||||||
|
|
||||||
const FileList = defineAsyncComponent(() => import('./FileList.vue'))
|
const FileList = defineAsyncComponent(() => import('./FileList.vue'))
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const fileMainDom = document.getElementById('fileMain')
|
const fileMainDom = document.getElementById('fileMain')
|
||||||
@@ -132,7 +139,7 @@ const handleScroll = (event) => {
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { mode, selectedFileIds, toggleMode, addSelectedFileItem } = useFileManage()
|
const { mode, selectedFileIds, toggleMode, addSelectedFileItem } = useFileManage()
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<FileQuery>({
|
||||||
name: undefined,
|
name: undefined,
|
||||||
type: route.query.type?.toString() || undefined,
|
type: route.query.type?.toString() || undefined,
|
||||||
sort: ['updateTime,desc']
|
sort: ['updateTime,desc']
|
||||||
@@ -145,7 +152,7 @@ const fileList = ref<FileItem[]>([])
|
|||||||
const isBatchMode = ref(false)
|
const isBatchMode = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
// 查询文件列表
|
// 查询文件列表
|
||||||
const getFileList = async (query: FileQuery = { ...queryForm, page: pagination.page, size: pagination.size }) => {
|
const getFileList = async (query: FilePageQuery = { ...queryForm, page: pagination.page, size: pagination.size }) => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
isBatchMode.value = false
|
isBatchMode.value = false
|
||||||
@@ -211,7 +218,7 @@ const handleRightMenuClick = async (mode: string, fileInfo: FileItem) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if (mode === 'rename') {
|
} else if (mode === 'rename') {
|
||||||
openFileRenameModal(fileInfo)
|
openFileRenameModal(fileInfo, search)
|
||||||
} else if (mode === 'detail') {
|
} else if (mode === 'detail') {
|
||||||
openFileDetailModal(fileInfo)
|
openFileDetailModal(fileInfo)
|
||||||
} else if (mode === 'download') {
|
} else if (mode === 'download') {
|
||||||
@@ -237,8 +244,8 @@ const handleMulDelete = () => {
|
|||||||
title: '提示',
|
title: '提示',
|
||||||
content: `是否确定删除所选的${selectedFileIds.value.length}个文件?`,
|
content: `是否确定删除所选的${selectedFileIds.value.length}个文件?`,
|
||||||
hideCancel: false,
|
hideCancel: false,
|
||||||
onOk: () => {
|
onOk: async () => {
|
||||||
deleteFile(selectedFileIds.value)
|
await deleteFile(selectedFileIds.value)
|
||||||
Message.success('删除成功')
|
Message.success('删除成功')
|
||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
<span>新增</span>
|
<span>新增</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-tooltip content="展开/折叠">
|
<a-tooltip content="展开/折叠">
|
||||||
<a-button @click="onExpanded">
|
<a-button class="gi_hover_btn-border" @click="onExpanded">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-list v-if="!isExpanded" />
|
<icon-list v-if="!isExpanded" />
|
||||||
<icon-mind-mapping v-else />
|
<icon-mind-mapping v-else />
|
||||||
@@ -129,9 +129,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<MenuQuery>({
|
||||||
title: undefined,
|
|
||||||
status: undefined,
|
|
||||||
sort: ['parentId,asc', 'sort,asc', 'createTime,desc']
|
sort: ['parentId,asc', 'sort,asc', 'createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ import { Message, type FormInstance } from '@arco-design/web-vue'
|
|||||||
import { useForm } from '@/hooks'
|
import { useForm } from '@/hooks'
|
||||||
import { useDict } from '@/hooks/app'
|
import { useDict } from '@/hooks/app'
|
||||||
import { MdEditor } from 'md-editor-v3'
|
import { MdEditor } from 'md-editor-v3'
|
||||||
import 'md-editor-v3/lib/style.css'
|
|
||||||
|
|
||||||
const { notice_type } = useDict('notice_type')
|
const { notice_type } = useDict('notice_type')
|
||||||
|
|
||||||
@@ -155,7 +154,7 @@ const save = async () => {
|
|||||||
if (isInvalid) return false
|
if (isInvalid) return false
|
||||||
try {
|
try {
|
||||||
if (isUpdate.value) {
|
if (isUpdate.value) {
|
||||||
await updateNotice(form, announcementId.value)
|
await updateNotice(form, dataId.value)
|
||||||
Message.success('修改成功')
|
Message.success('修改成功')
|
||||||
} else {
|
} else {
|
||||||
await addNotice(form)
|
await addNotice(form)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['title']"
|
:disabledColumnKeys="['title']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { listNotice, deleteNotice, type NoticeResp } from '@/apis'
|
import { listNotice, deleteNotice, type NoticeResp, type NoticeQuery } from '@/apis'
|
||||||
import NoticeAddModal from './NoticeAddModal.vue'
|
import NoticeAddModal from './NoticeAddModal.vue'
|
||||||
import NoticeDetailModal from './NoticeDetailModal.vue'
|
import NoticeDetailModal from './NoticeDetailModal.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
@@ -76,9 +77,9 @@ const columns: TableInstanceColumns[] = [
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize)
|
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize)
|
||||||
},
|
},
|
||||||
{ title: '标题', dataIndex: 'title', slotName: 'title', ellipsis: true, tooltip: true },
|
{ title: '标题', dataIndex: 'title', slotName: 'title', width: 200, ellipsis: true, tooltip: true },
|
||||||
{ title: '类型', slotName: 'type', align: 'center', width: 130 },
|
{ title: '类型', slotName: 'type', align: 'center' },
|
||||||
{ title: '状态', slotName: 'status', align: 'center', width: 130 },
|
{ title: '状态', slotName: 'status', align: 'center' },
|
||||||
{ title: '生效时间', dataIndex: 'effectiveTime', width: 180 },
|
{ title: '生效时间', dataIndex: 'effectiveTime', width: 180 },
|
||||||
{ title: '终止时间', dataIndex: 'terminateTime', width: 180 },
|
{ title: '终止时间', dataIndex: 'terminateTime', width: 180 },
|
||||||
{ title: '创建人', dataIndex: 'createUserString', show: false, ellipsis: true, tooltip: true },
|
{ title: '创建人', dataIndex: 'createUserString', show: false, ellipsis: true, tooltip: true },
|
||||||
@@ -93,9 +94,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<NoticeQuery>({
|
||||||
title: undefined,
|
|
||||||
type: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['name']"
|
:disabledColumnKeys="['name']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -56,7 +57,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listRole, deleteRole, type RoleResp } from '@/apis'
|
import { listRole, deleteRole, type RoleResp, type RoleQuery } from '@/apis'
|
||||||
import RoleAddModal from './RoleAddModal.vue'
|
import RoleAddModal from './RoleAddModal.vue'
|
||||||
import RoleDetailDrawer from './RoleDetailDrawer.vue'
|
import RoleDetailDrawer from './RoleDetailDrawer.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
@@ -96,8 +97,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<RoleQuery>({
|
||||||
description: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1300 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1300 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['name']"
|
:disabledColumnKeys="['name']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listStorage, deleteStorage, type StorageResp } from '@/apis'
|
import { listStorage, deleteStorage, type StorageResp, type StorageQuery } from '@/apis'
|
||||||
import StorageAddModal from './StorageAddModal.vue'
|
import StorageAddModal from './StorageAddModal.vue'
|
||||||
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
import type { TableInstanceColumns } from '@/components/GiTable/type'
|
||||||
import { useTable } from '@/hooks'
|
import { useTable } from '@/hooks'
|
||||||
@@ -110,9 +111,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<StorageQuery>({
|
||||||
description: undefined,
|
|
||||||
status: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,15 @@
|
|||||||
<a-input v-model="deptName" placeholder="请输入部门名称" allow-clear style="margin-bottom: 10px">
|
<a-input v-model="deptName" placeholder="请输入部门名称" allow-clear style="margin-bottom: 10px">
|
||||||
<template #prefix><icon-search /></template>
|
<template #prefix><icon-search /></template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-tree ref="treeRef" :data="deptList" default-expand-all show-line block-node @select="handleSelectDept">
|
<a-tree
|
||||||
|
ref="treeRef"
|
||||||
|
:data="deptList"
|
||||||
|
:selected-keys="selectedKeys"
|
||||||
|
default-expand-all
|
||||||
|
show-line
|
||||||
|
block-node
|
||||||
|
@select="handleSelectDept"
|
||||||
|
>
|
||||||
</a-tree>
|
</a-tree>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :md="20" :lg="20" :xl="20" :xxl="20">
|
<a-col :xs="24" :md="20" :lg="20" :xl="20" :xxl="20">
|
||||||
@@ -18,6 +26,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1500 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
|
:disabledTools="['size']"
|
||||||
:disabledColumnKeys="['nickname']"
|
:disabledColumnKeys="['nickname']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
@@ -41,17 +50,17 @@
|
|||||||
<span>新增</span>
|
<span>新增</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-tooltip content="导出">
|
<a-tooltip content="导出">
|
||||||
<a-button v-permission="['system:user:export']" @click="onExport">
|
<a-button v-permission="['system:user:export']" class="gi_hover_btn-border" @click="onExport">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<icon-download />
|
<icon-download />
|
||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<template #nickname="{ record }">
|
<template #username="{ record }">
|
||||||
<GiCellAvatar
|
<GiCellAvatar
|
||||||
:avatar="getAvatar(record.avatar, record.gender)"
|
:avatar="getAvatar(record.avatar, record.gender)"
|
||||||
:name="record.nickname"
|
:name="record.username"
|
||||||
is-link
|
is-link
|
||||||
@click="onDetail(record)"
|
@click="onDetail(record)"
|
||||||
/>
|
/>
|
||||||
@@ -79,7 +88,7 @@
|
|||||||
删除
|
删除
|
||||||
</a-link>
|
</a-link>
|
||||||
<a-dropdown>
|
<a-dropdown>
|
||||||
<a-button v-if="has.hasPermOr(['system:user:resetPwd'])" type="text">更多</a-button>
|
<a-link v-if="has.hasPermOr(['system:user:resetPwd'])" type="text">更多</a-link>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-doption v-permission="['system:user:resetPwd']" @click="onResetPwd(record)">重置密码</a-doption>
|
<a-doption v-permission="['system:user:resetPwd']" @click="onResetPwd(record)">重置密码</a-doption>
|
||||||
</template>
|
</template>
|
||||||
@@ -98,7 +107,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { listUser, deleteUser, exportUser, type UserResp } from '@/apis'
|
import { listUser, deleteUser, exportUser, type UserResp, type UserQuery } from '@/apis'
|
||||||
import UserAddModal from './UserAddModal.vue'
|
import UserAddModal from './UserAddModal.vue'
|
||||||
import UserDetailDrawer from './UserDetailDrawer.vue'
|
import UserDetailDrawer from './UserDetailDrawer.vue'
|
||||||
import UserResetPwdModal from './UserResetPwdModal.vue'
|
import UserResetPwdModal from './UserResetPwdModal.vue'
|
||||||
@@ -122,19 +131,19 @@ const columns: TableInstanceColumns[] = [
|
|||||||
fixed: !isMobile() ? 'left' : undefined
|
fixed: !isMobile() ? 'left' : undefined
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '昵称',
|
title: '用户名',
|
||||||
slotName: 'nickname',
|
slotName: 'username',
|
||||||
width: 170,
|
width: 140,
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
tooltip: true,
|
tooltip: true,
|
||||||
fixed: !isMobile() ? 'left' : undefined
|
fixed: !isMobile() ? 'left' : undefined
|
||||||
},
|
},
|
||||||
{ title: '用户名', dataIndex: 'username', width: 120, ellipsis: true, tooltip: true },
|
{ title: '昵称', dataIndex: 'nickname', width: 120, ellipsis: true, tooltip: true },
|
||||||
{ title: '状态', slotName: 'status', align: 'center' },
|
{ title: '状态', slotName: 'status', align: 'center' },
|
||||||
{ title: '性别', slotName: 'gender', align: 'center' },
|
{ title: '性别', slotName: 'gender', align: 'center' },
|
||||||
|
{ title: '所属部门', dataIndex: 'deptName', ellipsis: true, tooltip: true },
|
||||||
{ title: '手机号', dataIndex: 'phone', width: 170, ellipsis: true, tooltip: true },
|
{ title: '手机号', dataIndex: 'phone', width: 170, ellipsis: true, tooltip: true },
|
||||||
{ title: '邮箱', dataIndex: 'email', width: 170, ellipsis: true, tooltip: true },
|
{ title: '邮箱', dataIndex: 'email', width: 170, ellipsis: true, tooltip: true },
|
||||||
{ title: '所属部门', dataIndex: 'deptName', ellipsis: true, tooltip: true },
|
|
||||||
{ title: '系统内置', slotName: 'isSystem', width: 100, align: 'center', show: false },
|
{ title: '系统内置', slotName: 'isSystem', width: 100, align: 'center', show: false },
|
||||||
{ title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true },
|
{ title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true },
|
||||||
{ title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false },
|
{ title: '创建人', dataIndex: 'createUserString', ellipsis: true, tooltip: true, show: false },
|
||||||
@@ -151,10 +160,7 @@ const columns: TableInstanceColumns[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive<UserQuery>({
|
||||||
description: undefined,
|
|
||||||
status: undefined,
|
|
||||||
deptId: undefined,
|
|
||||||
sort: ['createTime,desc']
|
sort: ['createTime,desc']
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -164,7 +170,7 @@ const {
|
|||||||
pagination,
|
pagination,
|
||||||
search,
|
search,
|
||||||
handleDelete
|
handleDelete
|
||||||
} = useTable((p) => listUser({ ...queryForm, page: p.page, size: p.size }), { immediate: true })
|
} = useTable((p) => listUser({ ...queryForm, page: p.page, size: p.size }), { immediate: false })
|
||||||
|
|
||||||
// 重置
|
// 重置
|
||||||
const reset = () => {
|
const reset = () => {
|
||||||
@@ -193,22 +199,21 @@ const { deptList, getDeptList } = useDept({
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
treeRef.value?.expandAll(true)
|
treeRef.value?.expandAll(true)
|
||||||
|
queryForm.deptId = deptList.value[0]?.key as string
|
||||||
|
search()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
const selectedKeys = computed(() => {
|
||||||
|
return [queryForm.deptId ? queryForm.deptId : '']
|
||||||
|
})
|
||||||
watch(deptName, (val) => {
|
watch(deptName, (val) => {
|
||||||
getDeptList(val)
|
getDeptList(val)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 根据选中部门查询
|
// 根据选中部门查询
|
||||||
const handleSelectDept = (keys: Array<any>) => {
|
const handleSelectDept = (keys: Array<any>) => {
|
||||||
if (queryForm.deptId === keys[0]) {
|
queryForm.deptId = keys.length === 1 ? keys[0] : undefined
|
||||||
queryForm.deptId = undefined
|
|
||||||
// 如已选中,再次点击则取消选中
|
|
||||||
treeRef.value?.selectNode(keys, false)
|
|
||||||
} else {
|
|
||||||
queryForm.deptId = keys.length === 1 ? keys[0] : undefined
|
|
||||||
}
|
|
||||||
search()
|
search()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:disabledTools="['setting']"
|
:disabledTools="['size', 'setting']"
|
||||||
:disabledColumnKeys="['tableName']"
|
:disabledColumnKeys="['tableName']"
|
||||||
@refresh="search"
|
@refresh="search"
|
||||||
>
|
>
|
||||||
|
|||||||