Compare commits

..

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

2023 changed files with 83577 additions and 111951 deletions

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.js linguist-language=java
*.css linguist-language=java
*.html linguist-language=java
*.btl linguist-language=java

View File

@ -1,13 +0,0 @@
### 当前使用版本、分支(必填,否则不予处理)
### 该问题是如何引起的?(确定最新版也有问题再提!!!)
### 重现步骤(如果有就写完整)
### 报错信息

56
.gitignore vendored
View File

@ -1,34 +1,48 @@
# Compiled class file
*.class
*.iml
*.idea
target/
logs/
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
target/
*.tar.gz
*.rar
# eclipse
.settings/
.classpath
.project
logs/
# idea
.idea/
*.iml
.murphy.yml
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*velocity.log*
### STS ###
.apt_generated
.factorypath
.springBeans
# Eclipse #
.classpath
.project
.settings/
.DS_Store
_dockerCerts/
.factorypath
node_modules/
dist/
package-lock.json
yarn.lock
rebel.xml
### IntelliJ IDEA ###
.idea
*.iws
*.ipr
*.log
tmp/
!DmJdbcDriver18.jar
!kingbase8-8.6.0.jar

394
README.md
View File

@ -1,19 +1,14 @@
<div align="center">
<p align="center">
<img src="./snowy-admin-web/public/img/logo.png" height="150" alt="logo"/>
<img src="./_web/public/logo.png" height="150" alt="logo"/>
</p>
</div>
## 框架介绍
### 框架介绍
SnowySnowyAdmin是国内首个国密前后端分离快速开发平台集成国密加解密插件
软件层面完全符合等保测评要求,同时实现国产化机型、中间件、数据库适配,是您的不二之选!
技术框架与密码结合,让更多的人认识密码,使用密码;更是让前后分离“密”不可分。
采用SpringBoot+MybatisPlus+AntDesignVue+Vite 等更多优秀组件及前沿技术开发,注释丰富,代码简洁,开箱即用!
Snowy谐音“小诺”恰应小诺团队名称意思为”下雪的、纯洁的“寓意框架追求简洁至上大道至简。
<div><h5>Snowy是一款基于国产密码算法后台权限管理系统其中采用了SM2、SM3、SM4及签名验签软件层面完全符合等保测评要求让更多的人认识密码使用密码。技术框架与密码结合让前后分离“密”不可分。</h5></div>
<div><h4>结合SpringBoot+AntDesignVue开发注释丰富代码简洁。适配国产数据库金仓、达梦、主流数据库Mysql、Oracle、Mssql、Postgresql小诺的产品一致追求简洁干净一套代码搞定同时支持国产中间件部署、麒麟操作系统、Windows、Linux部署使用。</h4></div>
<div align="center"><h5 align="center">Snowy谐音“小诺”恰应小诺团队名称意思为”下雪的、纯洁的“寓意框架追求简洁至上大道至简。</h5></div>
<p align="center">
<p align="center">
@ -24,171 +19,266 @@ Snowy谐音“小诺”恰应小诺团队名称意思为”下雪的、纯
<img src="https://gitee.com/xiaonuobase/snowy/badge/fork.svg?theme=dark" alt="Gitee fork">
</a>
<a href="https://www.antdv.com/docs/vue/introduce-cn/">
<img src="https://img.shields.io/badge/vue-3.2-blue.svg" alt="bootstrap">
<img src="https://img.shields.io/badge/vue-2.x-blue.svg" alt="bootstrap">
</a>
<a href="http://spring.io/projects/spring-boot">
<img src="https://img.shields.io/badge/vite-2.8-green.svg" alt="spring-boot">
</a>
<a href="https://www.antdv.com/docs/vue/introduce-cn/">
<img src="https://img.shields.io/badge/vue--ant--design-3.2-blue.svg" alt="bootstrap">
<img src="https://img.shields.io/badge/vue--ant--design-1.5.6-blue.svg" alt="bootstrap">
</a>
<a href="http://spring.io/projects/spring-boot">
<img src="https://img.shields.io/badge/spring--boot-2.5-green.svg" alt="spring-boot">
<img src="https://img.shields.io/badge/spring--boot-2.3.1-green.svg" alt="spring-boot">
</a>
<a href="http://mp.baomidou.com">
<img src="https://img.shields.io/badge/mybatis--plus-3.5-blue.svg" alt="mybatis-plus">
<img src="https://img.shields.io/badge/mybatis--plus-3.3.2-blue.svg" alt="mybatis-plus">
</a>
<a href="./LICENSE">
<img src="https://img.shields.io/badge/license-Apache%202-red" alt="license Apache 2.0">
</a>
<a href="https://old.murphysec.com/dr/mQ1xAybeOLMLOxH8pU" alt="OSCS Status">
<img src="https://www.oscs1024.com/platform/badge//xiaonuobase/snowy.git.svg?size=small"/>
</a>
</p>
</p>
## 快速链接
### 快速启动
gitee下载地址[https://gitee.com/xiaonuobase/snowy](https://gitee.com/xiaonuobase/snowy)
您的开发电脑需要安装NodeJs14.x、npm或yarn最新版建议使用yarn、Mysql5.7、Jdk1.8、Maven3.6.3最新版、开发工具推荐idea
github下载地址镜像[https://github.com/xiaonuobase/Snowy](https://github.com/xiaonuobase/Snowy)
* 启动前端打开_web文件夹进行依赖下载运行npm install或yarn命令再运行npm run serve或 yarn run serve
* 启动后端打开application-local中配置数据库信息运行SnowyApplication类即可启动
* 浏览器访问http://localhost:81 默认前端端口为81后端端口为82
演示地址:[https://snowy.xiaonuo.vip](https://snowy.xiaonuo.vip)
### 快速链接
* 演示地址(superAdmin/123456)https://snowy.xiaonuo.vip
* 在线文档https://doc.xiaonuo.vip
* layui单体版本https://gitee.com/xiaonuobase/snowy-layui
* vue前后分离版本https://gitee.com/xiaonuobase/snowy
* cloud微服务前后分离版本https://gitee.com/xiaonuobase/snowy-cloud
* 我们的其他产品线同样开源如需关注最新动态可加入QQ群聊探讨[732230670](https://wpa.qq.com/msgrd?v=3&uin=732230670&_blank)
* 如果我们的产品能满足您的需求,很期待您给我们右上角点个 star
文档地址:[https://xiaonuo.vip/doc](https://xiaonuo.vip/doc)
## 快速启动
全栈工程师推荐idea
### 前端支撑
| 插件 | 版本 | 用途 |
|--- | ----- | ----- |
| node.js | 最新版 | JavaScript运行环境 |
### 启动前端
```
npm install
```
```
npm run dev
```
### 后端支撑
| 插件 | 版本 | 用途 |
| --- | ----- | ----- |
| jdk | 11 / 1.8 |java环境 |
| lombok | idea内 |代码简化插件 |
| maven | 最新版 |包管理工具 |
| redis | 最新版 | 缓存库 |
| mysql | 8.0 / 5.7 | 数据库 |
### 启动后端
开发工具内配置好maven并在代码中配置数据库即可启动
## 代码结构
Snowy2.0框架对代码以插件化的模式进行分包使得包层级结构更加清晰合理同时降低了耦合度关于插件模块化开发的规范请查阅文档【SNOWY开源文档——前端手册or后端手册——开发规范】板块。
```
snowy
|-snowy-admin-web == 前端
|-public == 基础静态文件
|-src == 前端源代码
|-api == API接口转发
|-assets == 静态文件
|-components == VUE组件
|-config == 基础配置
|-layout == 基础布局
|-locales == 多语言配置
|-router == 基础路由配置
|-store == Pinia缓存配置
|-style == 样式风格配置
|-utils == 工具类
|-views == 所有视图界面
|-snowy-common == 基础通用模块
|-snowy-plugin == 插件包
|-snowy-plugin-auth == 登录鉴权插件
|-snowy-plugin-biz == 业务功能插件
|-snowy-plugin-client == C端功能插件
|-snowy-plugin-dev == 开发工具插件
|-snowy-plugin-gen == 代码生成插件
|-snowy-plugin-mobile == 移动端管理插件
|-snowy-plugin-sys == 系统功能插件
|-snowy-plugin-api == 插件api包
|-snowy-plugin-auth-api == 登录鉴权插件api接口
|-snowy-plugin-biz-api == 业务功能插件api接口
|-snowy-plugin-client-api == C端功能插件api接口
|-snowy-plugin-dev-api == 开发工具插件api接口
|-snowy-plugin-gen == 代码生成插件api接口
|-snowy-plugin-mobile == 移动端管理插件api接口
|-snowy-plugin-sys-api == 系统功能插件api接口
|-snowy-web-app == 主启动模块
```
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwJHENw&path=%7BshareItemLink%3A7qwJHENw%7D%2F"/>
## 分支说明
### 升级计划:fire:
- master
正式稳定版本,具体版本升级内容看更新标签
- dev
团队开发的分支(代码可能随时会推,不保证运行和使用)
- snowy1.8
1.x分支目前已停止新增功能只限于bug的维护推荐使用2x版本
## 视频教程
教程地址(免费开放):[https://space.bilibili.com/50101698/channel/collectiondetail?sid=739071](https://space.bilibili.com/50101698/channel/collectiondetail?sid=739071)
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8nOccuKg&path=%7BshareItemLink%3A8nOccuKg%7D%2F"/>
作者也在上班工作,所以在利用休息时间为大家创作,录制视频的目的也是为各位小伙伴提供文档跟技术交流群聊之外的上手学习资料
> 视频由小诺开源技术团队王同学(每天一点)进行录制
## 效果图:fire:
官方预告2.0版本目前处于收尾工作此月9月会发出来哦具体技术细节详情等发布大家可以稍作等待相信您的等待会有好的收获请看下面功能图
<table>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=a938SjhgZ5ayRmNxjyvqNeG4piLbdyB39rdXaFyKsqCVrkmwLRyBcBc&name=/%E7%99%BB%E5%BD%95%E9%A1%B5%E9%9D%A2.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=b5e9VS9CKAeez01eHUfUdGyzm9eRSvtPrw9AF90mt_vPImvieiU9BR0&name=/%E7%B3%BB%E7%BB%9F%E9%A6%96%E9%A1%B5.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHr1wiA&path=%7BshareItemLink%3A8XHr1wiA%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHyk66w&path=%7BshareItemLink%3A8XHyk66w%7D%2F"/></td>
</tr>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=d173c7qJ7dgrK3vN1ovs55qtuDGW6bFOdiYglAsDNCJbI1LDifNuu_E&name=/%E7%94%A8%E6%88%B7%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=0086BVQAINW_1mFSSz3Of4gsyreG3fX-6BZqiqLb0kWSXA-6ff6dD4Y&name=/%E6%9C%8D%E5%8A%A1%E7%9B%91%E6%8E%A7.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHy7cOw&path=%7BshareItemLink%3A8XHy7cOw%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHzPDJg&path=%7BshareItemLink%3A8XHzPDJg%7D%2F"/></td>
</tr>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=1b72cVKHNtArl1A7qTeaAMicO1Pcv99U9PrPn4ESfwgk1VqCRmEIVqc&name=/%E6%8E%88%E6%9D%83%E6%9D%83%E9%99%90.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=1cc4CdKq2y5-hjuCfBLe5QiydnJMJfHWiM25mbobRsDBD7LK2Czkl3g&name=/%E6%93%8D%E4%BD%9C%E6%97%A5%E5%BF%97.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHzm0ng&path=%7BshareItemLink%3A8XHzm0ng%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XHz1PZg&path=%7BshareItemLink%3A8XHz1PZg%7D%2F"/></td>
</tr>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=f923EqvOkfbhNtN2pXA0Z55I5fRX4-_XWTmiGA8QBM_JJyIELv7ugLM&name=/EC%E6%95%A3%E7%82%B9%E5%9B%BE.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=e43fxc4TEVvJCZNuBNcueFAh5Mi9CDwnc25v25krItJ0iKj1wKBnqfY&name=/%E8%8F%9C%E5%8D%95%E7%AE%A1%E7%90%86.png"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH0H8nw&path=%7BshareItemLink%3A8XH0H8nw%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH0c-yw&path=%7BshareItemLink%3A8XH0c-yw%7D%2F"/></td>
</tr>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH0sCdg&path=%7BshareItemLink%3A8XH0sCdg%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH05YLw&path=%7BshareItemLink%3A8XH05YLw%7D%2F"/></td>
</tr>
<tr>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH1QLzQ&path=%7BshareItemLink%3A8XH1QLzQ%7D%2F"/></td>
<td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=8XH8Uyhg&path=%7BshareItemLink%3A8XH8Uyhg%7D%2F"/></td>
</tr>
</table>
## 密码分步:fire:
### 密码分步:fire:
| 功能 | 算法类型 |
| ---------------------- | ------------- |
| 登录 | SM2前端加密后端解密 |
| 登录登出日志 | SM2对登录登出日志做签名完整性保护存储 |
| 操作日志 | SM2对操作日志做签名完整性保护存储 |
| Token | SM4cbc模式加密Token不再曝光暴露 |
| 用户密码 | SM3完整性保护存储登录时做完整性校验 |
| 用户手机号 | SM4cbc模式加解密使用字段脱敏 |
## 官方技术群
### 视频教程:fire:
| 序号 | 链接地址 |
| ---------------------- | ------------- |
| 1 | [小诺开源技术团队及框架介绍](https://www.bilibili.com/video/BV1Yf4y1N7YU?from=search&seid=16730766915542181758) |
| 2 | [小诺框架Snowy基础环境介绍](https://www.bilibili.com/video/BV1yA411c7d3) |
| 3 | [Snowy代码下载及启动](https://www.bilibili.com/video/BV1SP4y1p7M8) |
| 4 | [Snowy生成一个完整的前后端模块](https://www.bilibili.com/video/BV1Ry4y1G7er) |
QQ技术群732230670已满、685395081
新的视频即将等2.0发布后针对新版重录
微信技术群因群达到200人以上需加微信拉群
### 架构原理图
* 业务架构
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKTmEw&path=%7BshareItemLink%3A7qwKTmEw%7D%2F"/>
</p>
* 应用架构
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKfxFw&path=%7BshareItemLink%3A7qwKfxFw%7D%2F"/>
</p>
* 数据架构
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKrjRw&path=%7BshareItemLink%3A7qwKrjRw%7D%2F"/>
</p>
* 技术架构
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwK4RoA&path=%7BshareItemLink%3A7qwK4RoA%7D%2F"/>
</p>
* 部署架构
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwLD35w&path=%7BshareItemLink%3A7qwLD35w%7D%2F"/>
</p>
### 效果图
<table>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/111529_02708b11_1980003.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/111909_5957b35a_1980003.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/112254_5e8a3a0b_1980003.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/112510_90191be8_1980003.png"/></td>
</tr>
<tr>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/112640_73ea49e9_1980003.png"/></td>
<td><img src="https://images.gitee.com/uploads/images/2021/0413/112804_a21e5aef_1980003.png"/></td>
</tr>
</table>
### 框架亮点及优势
1. 模块化架构设计,层次清晰,业务层推荐写到单独模块,框架升级不影响业务。
```
模块树
├─snowy ->项目工程
│ ├─snowy-base ->框架基础模块
│ ├─snowy-core ->核心模块
│ ├─snowy-gen ->代码生成
│ ├─snowy-system ->基础业务
│ ├─snowy-main ->业务开始模块
│ ├─业务 ->您的业务
```
2、独创前端字典翻译
全部字典数据储存前端store后端接口数据统一过滤器翻译
下拉框多选框等取值只需1行代码'dictData'为过滤器名称,'sex'为字典类型code返回数组字典
```
this.$options.filters['dictData']('sex')
或直接给值
{{ code | dictData }}
```
列表数据中字典翻译:('code'为字典类型唯一code'value'为待翻译的值返回name
```
{{ code | dictType(value) }}
```
3、独创的数据权限范围机制
数据范围的分配也来自于给用户单独分配的数据范围,最终决定用户有几个公司的数据范围的是,用户拥有的角色的数据范围 + 用户直接分配的数据范围
若一个用户有多个角色,系统最终判定用户有哪些数据范围是以多个角色和用户数据范围的 并集 为准。
仅通过注解就可以获取当前用户的数据范围不强制联查sql可根据业务需求极其灵活的使用
```
@DataScope
```
param类继承baseparam使用param.getDadaScope即可获取到数据权限列表
```
@EqualsAndHashCode(callSuper = true)
@Data
public class SysUserParam extends BaseParam {
```
4、独创的文件预览系统
支持txt.doc.docx.ppt.pptx.xls.xlsx.pdf.png.jpg.jpeg.bmp.gif等
预览速度快,兼容性好,支持常见文本格式.只需在运行环境一键安装libreoffice即可运行简单操作方便。
```
#libreoffice文档在线预览配置
# CentOS 下安装 libreoffice
# 安装yum -y install libreoffice
# Linux 中文字体乱码解决:
# 1、上传 C:\Windows\Fonts 下的字体到 /usr/share/fonts/windows 目录
# 2、执行命令 chmod 644 /usr/share/fonts/windows/* && fc-cache -fv
jodconverter:
local:
#暂时关闭预览,启动时会有点慢
enabled: false
#设置libreoffice主目录 linux地址如/usr/lib64/libreoffice
office-home: C:\Program Files\LibreOffice
#开启多个libreoffice进程,每个端口对应一个进程
port-numbers: 8100
#libreoffice进程重启前的最大进程数
max-tasks-per-process: 100
```
5、其他优势
前后端分离架构,分离开发,分离部署,前后端互不影响。
前端技术采用vue + antdvPro + axios。
后端采用spring boot + mybatis-plus + hutool等开源可靠。
基于spring security(jwt) + 用户UUID双重认证。
基于AOP实现的接口粒度的鉴权最细粒度过滤权限资源。
基于hibernate validator实现的校验框架支持自定义校验注解。
提供Request-No的响应header快速定位线上异常问题。
在线用户可查,可在线踢人,同账号登录可同时在线,可单独在线(通过系统参数配置)。
支持前端 + 后端在线代码生成。
文件,短信,缓存,邮件等,利用接口封装,方便拓展。
短信默认使用阿里云sms缓存默认使用内存缓存。
### 框架说明及后续补充
* 纯手研发搭建框架脚手架,在自己用的时候,也为各位小伙伴打下坚固的接私活利器。
* 后续我们会行发多个版本,将适配多个数据库环境,国产化环境,并且根据多年经验会出相关系统中用到的案例,提供给大家使用!
* 如需了解我们更多请移步官网https://xiaonuo.vip
* 当然有问题讨论的小伙伴还可以加入我们的QQ技术群[732230670](https://wpa.qq.com/msgrd?v=3&uin=732230670&_blank),一起学习讨论。
### 详细功能
1. 主控面板、控制台页面,可进行工作台,分析页,统计等功能的展示。
2. 用户管理、对企业用户和系统管理员用户的维护,可绑定用户职务,机构,角色,数据权限等。
3. 应用管理、通过应用来控制不同维度的菜单展示。
4. 机构管理、公司组织架构维护,支持多层级结构的树形结构。
5. 职位管理、用户职务管理,职务可作为用户的一个标签,职务目前没有和权限等其他功能挂钩。
6. 菜单管理、菜单目录,菜单,和按钮的维护是权限控制的基本单位。
7. 角色管理、角色绑定菜单后,可限制相关角色的人员登录系统的功能范围。角色也可以绑定数据授权范围。
8. 字典管理、系统内各种枚举类型的维护。
9. 访问日志、用户的登录和退出日志的查看和管理。
10. 操作日志、用户的操作业务的日志的查看和管理。
11. 服务监控、服务器的运行状态Java虚拟机信息jvm等数据的查看。
12. 在线用户、当前系统在线用户的查看。
13. 数据监控、druid控制台功能可查看sql的运行信息。
14. 公告管理、系统的公告的管理。
15. 文件管理、文件的上传下载查看等操作文件可使用本地存储阿里云oss腾讯cos接入支持拓展。
16. 定时任务、定时任务的维护通过cron表达式控制任务的执行频率。
17. 系统配置、系统运行的参数的维护,参数的配置与系统运行机制息息相关。
18. 邮件发送、发送邮件功能。
19. 短信发送、短信发送功能可使用阿里云sms腾讯云sms支持拓展。
### 官方微信群
因群达到200人以上需加微信拉群
<table>
<tr>
@ -197,39 +287,35 @@ QQ技术群732230670已满、685395081
</tr>
</table>
## 代码贡献
### 参与贡献
近期有很多热心开源的小伙伴陆续为咱们Snowy框架提交PR或者提出好的建议基本合格的PR我们都接受这样您的头像就列入到咱们Snowy仓库的贡献者列表啦
- 欢迎各路英雄好汉参与Snowy全系版本代码贡献期待您的加入
- 1. Fork 本仓库
- 2. 新建 Feat_xxx 分支
- 3. 提交代码
- 4. 新建 Pull Request
如何贡献:
### 更新日志
1、fork一份代码至自己的账号下本地修改您要提的代码提交至您fork的仓库
更新日志https://doc.xiaonuo.vip/snowy_vue/#%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97
2、登录gitee后到Snowy仓库下创建Pull Requests,选择您的仓库到Snowy的dev分支提交即可
因为dev分支是团队开发分支并不是统一发版本的测试过的所以我们建议提代码至dev即可
## 团队成员
| 成员 | 技术 | 昵称 |
| :---: | :---: | :---: |
| 俞宝山 | 全栈 | 俞宝山 |
| 徐玉祥 | 全栈 | 就是那个锅 |
| 董夏雨 | 全栈 | 阿董 |
| 王鹏 | 全栈 | 每天一点 |
## 曾获荣誉
### 曾获荣誉
<p align="center">
<img src="https://pan.xiaonuo.vip/?explorer/share/file&hash=ec54DtG4v8DfcUEPF0ACAHWW-urCcymI_0fSSaqMmMXKLsTWdHpQqH0e&name=/%E8%8D%A3%E8%AA%892021%E4%B8%8E2022.jpg"/>
<img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7xtGQLOA&path=%7BshareItemLink%3A7xtGQLOA%7D%2F"/>
</p>
## 版权说明
### 版权说明
- Snowy生态技术框架全系版本采用 Apache License2.0协议
- 代码可用于个人项目等接私活或企业项目脚手架使用Snowy全系开源版完全免费
- 二次开发如于开源竞品请先联系群主沟通合作。
- 请不要删除和修改Snowy源码头部的版权与作者声明及出处。
- 二次开发如用于开源竞品请先联系群主沟通,未经审核视为侵权
### 小诺技术团队荣誉作品
- 请不要删除和修改Snowy源码头部的版权与作者声明及出处
| 成员组成 | 负责内容 |
| :---: | :---: |
| 俞宝山 | 全栈 |
| 徐玉祥 | 全栈 |
| 董夏雨 | 全栈 |

11
_images/README.md Normal file
View File

@ -0,0 +1,11 @@
## _images 相关说明
1. 【snowy_biz.png】 业务架构
2. 【snowy_application.png】 应用架构
3. 【snowy_data.png】 数据架构
4. 【snowy_tech.png】 技术架构
5. 【snowy_deploy.png】 部署架构

BIN
_images/gitee_star.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
_images/snowy_biz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
_images/snowy_data.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
_images/snowy_deploy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
_images/snowy_tech.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

11
_sql/README.md Normal file
View File

@ -0,0 +1,11 @@
### 数据库文件夹说明:
1. 【snowy-pub.sql】 为mysql版本的sql如果您的数据库为mysql可直接运行此sql
2. 【snowy-pub-oracle.sql】 为oracle版本的sql如果您的数据库为oracle可直接运行此sql
3. 【snowy-pub-mssql.sql】 为mssql版本的sql如果您的数据库为mssql可直接运行此sql
4. 【snowy-pub-postgresql.sql】 为postgresql版本的sql如果您的数据库为postgresql可直接运行此sql
5. 【snowy-pub-dm8.sql】 为达梦数据库8版本的dmp备份文件如果您的数据库为达梦数据库8可直接备份恢复即可
6. 【snowy-layui-pub-kingbase8_r6.dmp】 为人大金仓V8R6版本的dmp备份文件如果您的数据库为人大金仓V8R6可直接备份恢复即可
### 达梦与人大金仓jdbc驱动文件夹说明
1. 【DmJdbcDriver18.jar】为达梦数据库8jdbc驱动如果您的数据库为达梦数据库8可使用此驱动
2. 【kingbase8-8.6.0.jar】为人大金仓V8R6jdbc驱动如果您的数据库为人大金仓V8R6可使用此驱动

View File

@ -0,0 +1,15 @@
sys_area.sql[zip]: https://pan.xiaonuo.vip/#s/7rFnfuTA
sys_area_mssql.sql: https://pan.xiaonuo.vip/#s/65K_gwMA
sys_area_mssql.sql[zip]: https://pan.xiaonuo.vip/#s/7eINRsLA
sys_area_oracle.sql: https://pan.xiaonuo.vip/#s/65K_nkaQ
sys_area_oracle.sql[zip]: https://pan.xiaonuo.vip/#s/7eINrDlg
sys_area_postgresql.sql: https://pan.xiaonuo.vip/#s/65K_vyrg
sys_area_postgresql.sql[zip]: https://pan.xiaonuo.vip/#s/7eIX3g8w
sys_area_kingbase8_r3.dmp: https://pan.xiaonuo.vip/#s/65K_UJFQ

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3
_web/.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 10

39
_web/.editorconfig Normal file
View File

@ -0,0 +1,39 @@
[*]
charset=utf-8
end_of_line=lf
insert_final_newline=false
indent_style=space
indent_size=2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style=space
indent_size=2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style=space
indent_size=2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style=space
indent_size=2
[*.svg]
indent_style=space
indent_size=2
[*.js.map]
indent_style=space
indent_size=2
[*.less]
indent_style=space
indent_size=2
[*.vue]
indent_style=space
indent_size=2
[{.analysis_options,*.yml,*.yaml}]
indent_style=space
indent_size=2

3
_web/.env Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=production
VUE_APP_PREVIEW=true
VUE_APP_API_BASE_URL=http://localhost:82

3
_web/.env.development Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=development
VUE_APP_PREVIEW=true
VUE_APP_API_BASE_URL=http://localhost:82

3
_web/.env.preview Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_API_BASE_URL=http://localhost:82

77
_web/.eslintrc.js Normal file
View File

@ -0,0 +1,77 @@
module.exports = {
root: true,
env: {
node: true
},
'extends': [
'plugin:vue/strongly-recommended',
'@vue/standard'
],
rules: {
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'vue/max-attributes-per-line': [
2,
{
'singleline': 5,
'multiline': {
'max': 1,
'allowFirstLine': false
}
}
],
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-tabs': 0,
'quotes': [
2,
'single',
{
'avoidEscape': true,
'allowTemplateLiterals': true
}
],
'semi': [
2,
'never',
{
'beforeStatementContinuationChars': 'never'
}
],
'no-delete-var': 2,
'prefer-const': [
2,
{
'ignoreReadBeforeAssign': false
}
],
'template-curly-spacing': 'off',
'indent': 'off',
"space-before-function-paren": 0,
'no-multi-spaces': 2, //不能用多余的空格
},
parserOptions: {
parser: 'babel-eslint'
},
overrides: [
{
files: [
'**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'
],
env: {
jest: true
}
}
]
}

3
_web/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
dist/
.idea/

5
_web/.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"printWidth": 120,
"semi": false,
"singleQuote": true
}

7
_web/.travis.yml Normal file
View File

@ -0,0 +1,7 @@
language: node_js
node_js:
- 10.15.0
cache: yarn
script:
- yarn
- yarn run lint --no-fix && yarn run build

21
_web/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Anan Yang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

28
_web/babel.config.js Normal file
View File

@ -0,0 +1,28 @@
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
const plugins = []
if (IS_PROD) {
plugins.push('transform-remove-console')
}
// lazy load ant-design-vue
// if your use import on Demand, Use this code
plugins.push(['import', {
'libraryName': 'ant-design-vue',
'libraryDirectory': 'es',
'style': true // `style: true` 会加载 less 文件
}])
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': 3
}
]
],
plugins
}

View File

@ -0,0 +1,46 @@
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors/lib/generate').default
const getAntdSerials = (color) => {
// 淡化即less的tint
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#1890ff'), // 主色系列
// 改变样式选择器解决样式覆盖问题
changeSelector (selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption)
module.exports = createThemeColorReplacerPlugin

23
_web/jest.config.js Normal file
View File

@ -0,0 +1,23 @@
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/'
}

88
_web/package.json Normal file
View File

@ -0,0 +1,88 @@
{
"name": "vue-antd-pro",
"version": "2.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"build:preview": "vue-cli-service build --mode preview",
"postinstall": "opencollective-postinstall"
},
"dependencies": {
"@antv/data-set": "^0.10.2",
"ant-design-vue": "1.5.0-rc.6",
"axios": "^0.19.0",
"babel-polyfill": "^6.26.0",
"clipboard": "^2.0.6",
"compression-webpack-plugin": "5.0.1",
"core-js": "^3.1.2",
"crypto-js": "^4.0.0",
"default-passive-events": "^1.0.10",
"enquire.js": "^2.1.6",
"font-awesome": "^4.7.0",
"jquery": "^3.5.1",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"mockjs2": "1.0.8",
"moment": "^2.24.0",
"nprogress": "^0.2.0",
"print-js": "^1.0.63",
"raphael": "^2.3.0",
"screenfull": "^5.1.0",
"sm-crypto": "^0.3.6",
"viser-vue": "^2.4.6",
"vue": "2.6.10",
"vue-clipboard2": "^0.2.1",
"vue-codemirror-lite": "^1.0.4",
"vue-cropper": "0.4.9",
"vue-ls": "^3.2.1",
"vue-quill-editor": "^3.0.6",
"vue-router": "^3.1.2",
"vue-svg-component-runtime": "^1.0.1",
"vuedraggable": "^2.23.2",
"vuex": "^3.1.1",
"wangeditor": "^3.1.1"
},
"devDependencies": {
"@ant-design/colors": "^3.2.1",
"@vue/cli-plugin-babel": "^4.0.4",
"@vue/cli-plugin-eslint": "^4.0.4",
"@vue/cli-plugin-router": "^4.0.4",
"@vue/cli-plugin-unit-jest": "^4.0.4",
"@vue/cli-plugin-vuex": "^4.0.4",
"@vue/cli-service": "^4.0.4",
"@vue/eslint-config-prettier": "^5.0.0",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-eslint": "^10.0.1",
"babel-plugin-import": "^1.13.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^6.8.0",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-vue": "^5.2.3",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"opencollective": "^1.0.3",
"opencollective-postinstall": "^2.0.2",
"prettier": "^1.18.2",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "2.6.10",
"webpack-theme-color-replacer": "1.3.18"
},
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/ant-design-pro-vue"
},
"main": ".eslintrc.js",
"directories": {
"test": "tests"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}

5
_web/postcss.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}

BIN
_web/public/avatar2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

37
_web/public/index.html Normal file
View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>logo.png">
<title>Snowy快速开发平台</title>
<style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style>
<!-- <script type="text/javascript" src="https://onlyoffice.xiaonuo.vip/web-apps/apps/api/documents/api.js"></script> -->
<!-- require cdn assets css -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<div class="first-loading-wrp">
<h1>Snowy</h1>
<div class="loading-wrp">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
<div style="display: flex; justify-content: center; align-items: center;">Snowy快速开发平台</div>
</div>
</div>
<!-- require cdn assets js -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -0,0 +1 @@
#preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}}

View File

@ -0,0 +1 @@
<div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div>

View File

@ -0,0 +1,5 @@
<div class="preloading-animate">
<div class="preloading-wrapper">
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>
</div>
</div>

View File

@ -0,0 +1 @@
.preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;}

View File

@ -0,0 +1 @@
<svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
_web/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

47
_web/src/App.vue Normal file
View File

@ -0,0 +1,47 @@
<template>
<a-config-provider :locale="locale">
<div id="app" class="app app1">
<router-view class="scrollbar"/>
</div>
</a-config-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import { AppDeviceEnquire } from '@/utils/mixin'
export default {
mixins: [AppDeviceEnquire],
data () {
return {
locale: zhCN
}
},
mounted () {
}
}
</script>
<style>
.app {
overflow: auto;
border : none;
}
.scrollbar {
margin: 0 auto;
}
.app1::-webkit-scrollbar {
/*滚动条整体样式*/
width : 8px; /*高宽分别对应横竖滚动条的尺寸*/
}
.app1::-webkit-scrollbar-thumb {
/*滚动条里面小方块*/
border-radius: 6px;
background : #aaa;
}
.app1::-webkit-scrollbar-track {
/*滚动条里面轨道*/
border-radius: 8px;
background : #FFFFFF;
}
</style>

View File

@ -0,0 +1,120 @@
/**
* 代码生成
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
import { axios } from '@/utils/request'
/**
* 查询列表
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGeneratePage (parameter) {
return axios({
url: '/codeGenerate/page',
method: 'get',
params: parameter
})
}
/**
* 增加
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateAdd (parameter) {
return axios({
url: '/codeGenerate/add',
method: 'post',
data: parameter
})
}
/**
* 编辑
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateEdit (parameter) {
return axios({
url: '/codeGenerate/edit',
method: 'post',
data: parameter
})
}
/**
* 删除
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateDelete (parameter) {
return axios({
url: '/codeGenerate/delete',
method: 'post',
data: parameter
})
}
/**
* 查询当前数据库用户下的所有表
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateInformationList (parameter) {
return axios({
url: '/codeGenerate/InformationList',
method: 'get',
params: parameter
})
}
/**
* 本地生成
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateRunLocal (parameter) {
return axios({
url: '/codeGenerate/runLocal',
method: 'post',
data: parameter
})
}
/**
* 压缩包方式下载
*
* @author yubaoshan
* @date 2020/12/23 15:00
*/
export function codeGenerateRunDown (parameter) {
return axios({
url: '/codeGenerate/runDown',
method: 'get',
params: parameter,
responseType: 'blob'
})
}
/**
* 代码生产预览接口
*
* @author yubaoshan
* @date 2022/05/16
*/
export function codeGenerateRunFileContent (parameter) {
return axios({
url: '/codeGenerate/runFileContent',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,29 @@
import { axios } from '@/utils/request'
/**
* 代码生成详细配置列表
*
* @author yubaoshan
* @date 2021-02-06 20:19:49
*/
export function sysCodeGenerateConfigList (parameter) {
return axios({
url: '/sysCodeGenerateConfig/list',
method: 'get',
params: parameter
})
}
/**
* 编辑代码生成详细配置
*
* @author yubaoshan
* @date 2021-02-06 20:19:49
*/
export function sysCodeGenerateConfigEdit (parameter) {
return axios({
url: '/sysCodeGenerateConfig/edit',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1 @@
/** 您的业务接口文件全写在此文件夹下面,升级底座直接迁移代码即可 **/

View File

@ -0,0 +1 @@
/** 此文件夹下代码尽量不要动,底座升级直接覆盖替换 **/

View File

@ -0,0 +1,92 @@
/**
* 系统应用
*
* @author yubaoshan
* @date 2020年4月23日12:10:57
*/
import { axios } from '@/utils/request'
/**
* 系统应用列表
*
* @author yubaoshan
* @date 2020年7月9日15:05:01
*/
export function getAppPage (parameter) {
return axios({
url: '/sysApp/page',
method: 'get',
params: parameter
})
}
/**
* 系统应用列表
*
* @author yubaoshan
* @date 2020年7月9日15:05:01
*/
export function getAppList (parameter) {
return axios({
url: '/sysApp/list',
method: 'get',
params: parameter
})
}
/**
* 新增系统应用
*
* @author yubaoshan
* @date 2020年7月9日15:05:01
*/
export function sysAppAdd (parameter) {
return axios({
url: '/sysApp/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统应用
*
* @author yubaoshan
* @param parameter
* @returns {*}
*/
export function sysAppEdit (parameter) {
return axios({
url: '/sysApp/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统应用
*
* @author yubaoshan
* @date 2020年7月9日15:05:01
*/
export function sysAppDelete (parameter) {
return axios({
url: '/sysApp/delete',
method: 'post',
data: parameter
})
}
/**
* 设为默认应用
*
* @author yubaoshan
* @date 2020年7月9日15:05:01
*/
export function sysAppSetAsDefault (parameter) {
return axios({
url: '/sysApp/setAsDefault',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,16 @@
import { axios } from '@/utils/request'
/**
* 获取区域列表
*
* @author xuyuxiang
* @param parameter
* @returns {*}
*/
export function getAreaList (parameter) {
return axios({
url: '/sysArea/list',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,85 @@
import { axios } from '@/utils/request'
/**
* 分页查询配置列表
*
* @author yubaoshan
* @date 2020/5/25 01:57
*/
export function sysConfigPage (parameter) {
return axios({
url: '/sysConfig/page',
method: 'get',
params: parameter
})
}
/**
* 添加系统参数配置
*
* @author yubaoshan
* @date 2020/5/25 01:57
*/
export function sysConfigAdd (parameter) {
return axios({
url: '/sysConfig/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统参数配置
*
* @author yubaoshan
* @date 2020/5/25 01:57
*/
export function sysConfigEdit (parameter) {
return axios({
url: '/sysConfig/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统参数配置
*
* @author yubaoshan
* @date 2020/5/25 01:57
*/
export function sysConfigDelete (parameter) {
return axios({
url: '/sysConfig/delete',
method: 'post',
data: parameter
})
}
/**
* 获取字典类型下所有字典举例返回格式为[{code:"M",value:""},{code:"F",value:""}]
*
* @author yubaoshan
* @date 2020/5/25 02:06
*/
export function sysDictTypeDropDown (parameter) {
return axios({
url: '/sysDictType/dropDown',
method: 'get',
params: parameter
})
}
/**
* 获取系统的所有任务列表
*
* @author yubaoshan
* @date 2020/7/8 20:46
*/
export function sysTimersGetActionClasses (parameter) {
return axios({
url: '/sysTimers/getActionClasses',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,57 @@
import { axios } from '@/utils/request'
/**
* 查询系统字典值
*
* @author yubaoshan
* @date 2020/5/17 02:24
*/
export function sysDictDataPage (parameter) {
return axios({
url: '/sysDictData/page',
method: 'get',
params: parameter
})
}
/**
* 添加系统字典值
*
* @author yubaoshan
* @date 2020/5/17 02:24
*/
export function sysDictDataAdd (parameter) {
return axios({
url: '/sysDictData/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统字典值
*
* @author yubaoshan
* @date 2020/5/17 02:25
*/
export function sysDictDataEdit (parameter) {
return axios({
url: '/sysDictData/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统字典值
*
* @author yubaoshan
* @date 2020/5/17 02:25
*/
export function sysDictDataDelete (parameter) {
return axios({
url: '/sysDictData/delete',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,85 @@
import { axios } from '@/utils/request'
/**
* 分页查询系统字典类型
*
* @author yubaoshan
* @date 2020/5/17 01:46
*/
export function sysDictTypePage (parameter) {
return axios({
url: '/sysDictType/page',
method: 'get',
params: parameter
})
}
/**
* 添加系统字典类型
*
* @author yubaoshan
* @date 2020/5/17 01:46
*/
export function sysDictTypeAdd (parameter) {
return axios({
url: '/sysDictType/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统字典类型
*
* @author yubaoshan
* @date 2020/5/17 01:50
*/
export function sysDictTypeEdit (parameter) {
return axios({
url: '/sysDictType/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统字典类型
*
* @author yubaoshan
* @date 2020/5/17 01:50
*/
export function sysDictTypeDelete (parameter) {
return axios({
url: '/sysDictType/delete',
method: 'post',
data: parameter
})
}
/**
* 获取字典类型下所有字典举例返回格式为[{code:"M",value:""},{code:"F",value:""}]
*
* @author yubaoshan
* @date 2020/6/10 00:10
*/
export function sysDictTypeDropDown (parameter) {
return axios({
url: '/sysDictType/dropDown',
method: 'get',
params: parameter
})
}
/**
* 获取所有字典启动时加入缓存使用
*
* @author yubaoshan
* @date 2020/6/10 00:10
*/
export function sysDictTypeTree (parameter) {
return axios({
url: '/sysDictType/tree',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,29 @@
import { axios } from '@/utils/request'
/**
* 发送邮件
*
* @author yubaoshan
* @date 2020/7/3 23:22
*/
export function emailSendEmail (parameter) {
return axios({
url: '/email/sendEmail',
method: 'post',
data: parameter
})
}
/**
* 发送html邮件
*
* @author yubaoshan
* @date 2020/7/3 23:23
*/
export function emailSendEmailHtml (parameter) {
return axios({
url: '/email/sendEmailHtml',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,115 @@
import { axios } from '@/utils/request'
/**
* 分页查询文件信息表
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoPage (parameter) {
return axios({
url: '/sysFileInfo/page',
method: 'get',
params: parameter
})
}
/**
* 获取全部文件信息表
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoList (parameter) {
return axios({
url: '/sysFileInfo/list',
method: 'get',
params: parameter
})
}
/**
* 上传文件
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoUpload (parameter) {
return axios({
url: '/sysFileInfo/upload',
method: 'post',
data: parameter
})
}
/**
* 下载文件
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoDownload (parameter) {
return axios({
url: '/sysFileInfo/download',
method: 'get',
params: parameter,
responseType: 'blob'
})
}
/**
* 查看图片
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoPreview (parameter) {
return axios({
url: '/sysFileInfo/preview',
method: 'get',
params: parameter,
responseType: 'arraybuffer'
})
}
/**
* 查看详情文件信息表
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoDetail (parameter) {
return axios({
url: '/sysFileInfo/detail',
method: 'get',
params: parameter
})
}
/**
* 删除文件信息表
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoDelete (parameter) {
return axios({
url: '/sysFileInfo/delete',
method: 'post',
data: parameter
})
}
/**
* 获取在线文档配置
*
* @author yubaoshan
* @date 2020/6/30 00:20
*/
export function sysFileInfoGetOnlineConfig (parameter) {
return axios({
url: '/sysFileInfo/getOnlineFileConfig',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,87 @@
import { axios } from '@/utils/request'
/**
* 查询访问日志
*
* @author yubaoshan
* @date 2020/5/19 11:57
*/
export function sysVisLogPage (parameter) {
return axios({
url: '/sysVisLog/page',
method: 'get',
params: parameter
})
}
/**
* 查询操作日志
*
* @author yubaoshan
* @date 2020/5/19 11:57
*/
export function sysOpLogPage (parameter) {
return axios({
url: '/sysOpLog/page',
method: 'get',
params: parameter
})
}
/**
* 清空访问日志
*
* @author yubaoshan
* @date 2020/6/23 23:09
*/
export function sysVisLogDelete (parameter) {
return axios({
url: '/sysVisLog/delete',
method: 'post',
data: parameter
})
}
/**
* 清空登录日志
*
* @author yubaoshan
* @date 2020/6/23 23:09
*/
export function sysOpLogDelete (parameter) {
return axios({
url: '/sysOpLog/delete',
method: 'post',
data: parameter
})
}
/**
* 导出登录日志
*
* @author yubaoshan
* @date 2021/5/30 18:03
*/
export function sysVisLogExport (parameter) {
return axios({
url: '/sysVisLog/export',
method: 'get',
params: parameter,
responseType: 'blob'
})
}
/**
* 导出操作日志
*
* @author yubaoshan
* @date 2021/5/30 18:03
*/
export function sysOpLogExport (parameter) {
return axios({
url: '/sysOpLog/export',
method: 'get',
params: parameter,
responseType: 'blob'
})
}

View File

@ -0,0 +1,125 @@
/**
* 系统应用
*
* @author yubaoshan
* @date 2020/5/26 19:06
*/
import { axios } from '@/utils/request'
/**
* 登录
*
* @author yubaoshan
* @date 2020/5/26 19:06
*/
export function login (parameter) {
// 密码采用sm2加密传输密码
const sm2 = require('sm-crypto').sm2
const publicKey = '04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54'
const encryptData = sm2.doEncrypt(parameter.password, publicKey, 1)
parameter.password = encryptData
return axios({
url: '/login',
method: 'post',
data: parameter
})
}
/**
* 登出
*
* @author yubaoshan
* @date 2020/5/26 19:07
*/
export function logout (parameter) {
return axios({
url: '/logout',
method: 'get',
params: parameter
})
}
/**
* 获取登录用户信息
*
* @author yubaoshan
* @date 2020/5/26 19:08
*/
export function getLoginUser (parameter) {
return axios({
url: '/getLoginUser',
method: 'get',
params: parameter
})
}
/**
* 获取租户开关
*
* @author yubaoshan
* @date 2020/9/5 1:24
*/
export function getTenantOpen (parameter) {
return axios({
url: '/getTenantOpen',
method: 'get',
params: parameter
})
}
/**
* 获取短信验证码
*
* @author yubaoshan
* @date 2020/5/26 19:29
*/
export function getSmsCaptcha (parameter) {
return axios({
url: '/getSmsCaptcha',
method: 'get',
params: parameter
})
}
/**
* 获取验证码开关
*
* @author Jax
* @date 2021/1/22 00:00
*/
export function getCaptchaOpen (parameter) {
return axios({
url: '/getCaptchaOpen',
method: 'get',
params: parameter
})
}
/**
* 获取验证图片 以及token
*
* @author Jax
* @date 2021/1/22 00:00
*/
export function reqGet(data) {
return axios({
url: '/captcha/get',
method: 'post',
data
})
}
/**
* 滑动或者点选验证
*
* @author Jax
* @date 2021/1/22 00:00
*/
export function reqCheck(data) {
return axios({
url: '/captcha/check',
method: 'post',
data
})
}

View File

@ -0,0 +1,15 @@
import { axios } from '@/utils/request'
/**
* 系统属性监控
*
* @author yubaoshan
* @date 2020/6/8 19:47
*/
export function sysMachineQuery (parameter) {
return axios({
url: '/sysMachine/query',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,114 @@
import { axios } from '@/utils/request'
/**
* 获取菜单列表
*
* @author yubaoshan
* @param parameter
* @returns {*}
*/
export function getMenuList (parameter) {
return axios({
url: '/sysMenu/list',
method: 'get',
params: parameter
})
}
/**
* 获取系统菜单树用于新增编辑时选择上级节点
*
* @author yubaoshan
* @date 2020/4/23 12:22
*/
export function getMenuTree (parameter) {
return axios({
url: '/sysMenu/tree',
method: 'get',
params: parameter
})
}
/**
* 增加菜单
*
* @author yubaoshan
* @date 2020/4/24 23:23
*/
export function sysMenuAdd (parameter) {
return axios({
url: '/sysMenu/add',
method: 'post',
data: parameter
})
}
/**
* 增加菜单
*
* @author yubaoshan
* @date 2020/4/24 23:23
*/
export function sysMenuDelete (parameter) {
return axios({
url: '/sysMenu/delete',
method: 'post',
data: parameter
})
}
/**
* 查看菜单详情
*
* @author yubaoshan
* @date 2020/4/25 01:11
*/
export function sysMenuDetail (parameter) {
return axios({
url: '/sysMenu/detail',
method: 'post',
data: parameter
})
}
/**
* 编辑系统菜单
*
* @author yubaoshan
* @date 2020/4/25 01:11
*/
export function sysMenuEdit (parameter) {
return axios({
url: '/sysMenu/edit',
method: 'post',
data: parameter
})
}
/**
* 获取系统菜单树用于给角色授权时选择
*
* @author yubaoshan
* @date 2020/6/2 17:30
*/
export function SysMenuTreeForGrant (parameter) {
return axios({
url: '/sysMenu/treeForGrant',
method: 'get',
params: parameter
})
}
/**
* 根据系统切换菜单
*
* @author yubaoshan
* @date 2020/6/28 15:25
*/
export function sysMenuChange (parameter) {
return axios({
url: '/sysMenu/change',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,85 @@
import { axios } from '@/utils/request'
/**
* 查询系统通知公告
*
* @author yubaoshan
* @date 2020/6/30 01:56
*/
export function sysNoticePage (parameter) {
return axios({
url: '/sysNotice/page',
method: 'get',
params: parameter
})
}
/**
* 添加系统通知公告
*
* @author yubaoshan
* @date 2020/6/30 01:56
*/
export function sysNoticeAdd (parameter) {
return axios({
url: '/sysNotice/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统通知公告
*
* @author yubaoshan
* @date 2020/6/30 01:56
*/
export function sysNoticeEdit (parameter) {
return axios({
url: '/sysNotice/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统通知公告
*
* @author yubaoshan
* @date 2020/6/30 01:56
*/
export function sysNoticeDelete (parameter) {
return axios({
url: '/sysNotice/delete',
method: 'post',
data: parameter
})
}
/**
* 通知公告详情
*
* @author yubaoshan
* @date 2020/6/30 01:56
*/
export function sysNoticeDetail (parameter) {
return axios({
url: '/sysNotice/detail',
method: 'get',
params: parameter
})
}
/**
* 修改状态
*
* @author yubaoshan
* @date 2020/7/30 02:23
*/
export function sysNoticeChangeStatus (parameter) {
return axios({
url: '/sysNotice/changeStatus',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,15 @@
import { axios } from '@/utils/request'
/**
* 查询我收到的系统通知公告
*
* @author yubaoshan
* @date 2020/7/3 03:02
*/
export function sysNoticeReceived (parameter) {
return axios({
url: '/sysNotice/received',
method: 'get',
params: parameter
})
}

View File

@ -0,0 +1,29 @@
import { axios } from '@/utils/request'
/**
* 在线用户列表
*
* @author yubaoshan
* @date 2020/6/8 11:11
*/
export function sysOnlineUserList (parameter) {
return axios({
url: '/sysOnlineUser/list',
method: 'get',
params: parameter
})
}
/**
* 强制下线
*
* @author yubaoshan
* @date 2020/6/8 11:11
*/
export function sysOnlineUserForceExist (parameter) {
return axios({
url: '/sysOnlineUser/forceExist',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,100 @@
import { axios } from '@/utils/request'
/**
* 获取机构树
*
* @author yubaoshan
* @date 2020/4/26 12:08
*/
export function getOrgTree (parameter) {
return axios({
url: '/sysOrg/tree',
method: 'get',
params: parameter
})
}
/**
* 获取机构列表
*
* @author yubaoshan
* @date 2020/5/11 12:59
*/
export function getOrgList (parameter) {
return axios({
url: '/sysOrg/list',
method: 'get',
params: parameter
})
}
/**
* 获取机构列表
*
* @author yubaoshan
* @date 2020/5/11 16:17
*/
export function getOrgPage (parameter) {
return axios({
url: '/sysOrg/page',
method: 'get',
params: parameter
})
}
/**
* 新增机构
*
* @author yubaoshan
* @date 2020/5/11 13:56
*/
export function sysOrgAdd (parameter) {
return axios({
url: '/sysOrg/add',
method: 'post',
data: parameter
})
}
/**
* 编辑机构
*
* @author yubaoshan
* @date 2020/5/11 13:56
*/
export function sysOrgEdit (parameter) {
return axios({
url: '/sysOrg/edit',
method: 'post',
data: parameter
})
}
/**
* 删除机构
*
* @author yubaoshan
* @date 2020/5/11 12:59
*/
export function sysOrgDelete (parameter) {
return axios({
url: '/sysOrg/delete',
method: 'post',
data: parameter
})
}
/**
* 导出系统机构
*
* @author yubaoshan
* @date 2021/5/30 12:58
*/
export function sysOrgExport (parameter) {
return axios({
url: '/sysOrg/export',
method: 'get',
params: parameter,
responseType: 'blob'
})
}

View File

@ -0,0 +1,86 @@
import { axios } from '@/utils/request'
/**
* 查询系统职位
*
* @author yubaoshan
* @date 2020/5/25 01:31
*/
export function sysPosPage (parameter) {
return axios({
url: '/sysPos/page',
method: 'get',
params: parameter
})
}
/**
* 系统职位列表
*
* @author yubaoshan
* @date 2020/6/21 23:50
*/
export function sysPosList (parameter) {
return axios({
url: '/sysPos/list',
method: 'get',
params: parameter
})
}
/**
* 添加系统职位
*
* @author yubaoshan
* @date 2020/5/25 01:31
*/
export function sysPosAdd (parameter) {
return axios({
url: '/sysPos/add',
method: 'post',
data: parameter
})
}
/**
* 编辑系统职位
*
* @author yubaoshan
* @date 2020/5/25 01:31
*/
export function sysPosEdit (parameter) {
return axios({
url: '/sysPos/edit',
method: 'post',
data: parameter
})
}
/**
* 删除系统职位
*
* @author yubaoshan
* @date 2020/5/25 01:31
*/
export function sysPosDelete (parameter) {
return axios({
url: '/sysPos/delete',
method: 'post',
data: parameter
})
}
/**
* 导出系统职位
*
* @author yubaoshan
* @date 2021/5/29 16:19
*/
export function sysPosExport (parameter) {
return axios({
url: '/sysPos/export',
method: 'get',
params: parameter,
responseType: 'blob'
})
}

View File

@ -0,0 +1,141 @@
import { axios } from '@/utils/request'
/**
* 获取角色列表
*
* @author yubaoshan
* @date 2020/5/6 11:44
*/
export function getRolePage (parameter) {
return axios({
url: '/sysRole/page',
method: 'get',
params: parameter
})
}
/**
* 增加角色
*
* @author yubaoshan
* @date 2020/5/6 11:44
*/
export function sysRoleAdd (parameter) {
return axios({
url: '/sysRole/add',
method: 'post',
data: parameter
})
}
/**
* 编辑角色
*
* @author yubaoshan
* @date 2020/5/6 11:44
*/
export function sysRoleEdit (parameter) {
return axios({
url: '/sysRole/edit',
method: 'post',
data: parameter
})
}
/**
* 删除角色
*
* @author yubaoshan
* @date 2020/5/6 17:51
*/
export function sysRoleDelete (parameter) {
return axios({
url: '/sysRole/delete',
method: 'post',
data: parameter
})
}
/**
* 删除角色
*
* @author yubaoshan
* @date 2020/5/7 11:28
*/
export function sysRoleDeteil (parameter) {
return axios({
url: '/sysRole/detail',
method: 'get',
params: parameter
})
}
/**
* 获取授权角色列表
*
* @author yubaoshan
* @date 2020/5/26 23:59
*/
export function sysRoleDropDown (parameter) {
return axios({
url: '/sysRole/dropDown',
method: 'get',
params: parameter
})
}
/**
* 拥有菜单
*
* @author yubaoshan
* @date 2020/6/02 19:02
*/
export function sysRoleOwnMenu (parameter) {
return axios({
url: '/sysRole/ownMenu',
method: 'get',
params: parameter
})
}
/**
* 授权菜单
*
* @author yubaoshan
* @date 2020/6/2 21:10
*/
export function sysRoleGrantMenu (parameter) {
return axios({
url: '/sysRole/grantMenu',
method: 'post',
data: parameter
})
}
/**
* 拥有数据
*
* @author yubaoshan
* @date 2020/6/02 21:40
*/
export function sysRoleOwnData (parameter) {
return axios({
url: '/sysRole/ownData',
method: 'get',
params: parameter
})
}
/**
* 授权数据
*
* @author yubaoshan
* @date 2020/6/2 21:50
*/
export function sysRoleGrantData (parameter) {
return axios({
url: '/sysRole/grantData',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,43 @@
import { axios } from '@/utils/request'
/**
* 发送记录查询
*
* @author yubaoshan
* @date 2020/7/3 22:11
*/
export function smsPage (parameter) {
return axios({
url: '/sms/page',
method: 'get',
params: parameter
})
}
/**
* 验证短信验证码
*
* @author yubaoshan
* @date 2020/7/3 22:12
*/
export function sysSendLoginMessage (parameter) {
return axios({
url: '/sms/sendLoginMessage',
method: 'post',
data: parameter
})
}
/**
* 验证短信验证码
*
* @author yubaoshan
* @date 2020/7/3 22:12
*/
export function sysValidateMessage (parameter) {
return axios({
url: '/sms/validateMessage',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,127 @@
import { axios } from '@/utils/request'
/**
* 分页查询定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:13
*/
export function sysTimersPage (parameter) {
return axios({
url: '/sysTimers/page',
method: 'get',
params: parameter
})
}
/**
* 获取全部定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersList (parameter) {
return axios({
url: '/sysTimers/list',
method: 'get',
params: parameter
})
}
/**
* 查看详情定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersDetail (parameter) {
return axios({
url: '/sysTimers/detail',
method: 'get',
params: parameter
})
}
/**
* 添加定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersAdd (parameter) {
return axios({
url: '/sysTimers/add',
method: 'post',
data: parameter
})
}
/**
* 删除定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersDelete (parameter) {
return axios({
url: '/sysTimers/delete',
method: 'post',
data: parameter
})
}
/**
* 编辑定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersEdit (parameter) {
return axios({
url: '/sysTimers/edit',
method: 'post',
data: parameter
})
}
/**
* 获取系统的所有任务列表
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersGetActionClasses (parameter) {
return axios({
url: '/sysTimers/getActionClasses',
method: 'post',
data: parameter
})
}
/**
* 启动定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersStart (parameter) {
return axios({
url: '/sysTimers/start',
method: 'post',
data: parameter
})
}
/**
* 停止定时任务
*
* @author yubaoshan
* @date 2020/7/3 03:23
*/
export function sysTimersStop (parameter) {
return axios({
url: '/sysTimers/stop',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1,226 @@
import { axios } from '@/utils/request'
/**
* 获取用户列表
*
* @author yubaoshan
* @date 2020/4/26 12:08
*/
export function getUserPage (parameter) {
return axios({
url: '/sysUser/page',
method: 'get',
params: parameter
})
}
/**
* 增加用户
*
* @author yubaoshan
* @date 2020/5/5 02:08
*/
export function sysUserAdd (parameter) {
return axios({
url: '/sysUser/add',
method: 'post',
data: parameter
})
}
/**
* 编辑用户
*
* @author yubaoshan
* @date 2020/5/5 02:08
*/
export function sysUserEdit (parameter) {
return axios({
url: '/sysUser/edit',
method: 'post',
data: parameter
})
}
/**
* 获取用户详情
*
* @author yubaoshan
* @date 2020/5/5 19:55
*/
export function sysUserDetail (parameter) {
return axios({
url: '/sysUser/detail',
method: 'get',
params: parameter
})
}
/**
* 删除用户
*
* @author yubaoshan
* @date 2020/5/7 19:31
*/
export function sysUserDelete (parameter) {
return axios({
url: '/sysUser/delete',
method: 'post',
data: parameter
})
}
/**
* 导出用户
*
* @author yubaoshan
* @date 2021/5/30 00:23
*/
export function sysUserExport (parameter) {
return axios({
url: '/sysUser/export',
method: 'get',
params: parameter,
responseType: 'blob'
})
}
/**
* 拥有角色
*
* @author yubaoshan
* @date 2020/6/3 11:58
*/
export function sysUserOwnRole (parameter) {
return axios({
url: '/sysUser/ownRole',
method: 'get',
params: parameter
})
}
/**
* 授权角色
*
* @author yubaoshan
* @date 2020/5/26 23:59
*/
export function sysUserGrantRole (parameter) {
return axios({
url: '/sysUser/grantRole',
method: 'post',
data: parameter
})
}
/**
* 拥有数据
*
* @author yubaoshan
* @date 2020/6/2 23:14
*/
export function sysUserOwnData (parameter) {
return axios({
url: '/sysUser/ownData',
method: 'get',
params: parameter
})
}
/**
* 授权数据
*
* @author yubaoshan
* @date 2020/6/2 23:15
*/
export function sysUserGrantData (parameter) {
return axios({
url: '/sysUser/grantData',
method: 'post',
data: parameter
})
}
/**
* 修改状态
*
* @author yubaoshan
* @date 2020/6/23 21:36
*/
export function sysUserChangeStatus (parameter) {
return axios({
url: '/sysUser/changeStatus',
method: 'post',
data: parameter
})
}
/**
* 重置密码
*
* @author yubaoshan
* @date 2020/6/23 22:04
*/
export function sysUserResetPwd (parameter) {
return axios({
url: '/sysUser/resetPwd',
method: 'post',
data: parameter
})
}
/**
* 修改密码
*
* @author yubaoshan
* @date 2020/6/25 00:25
*/
export function sysUserUpdatePwd (parameter) {
return axios({
url: '/sysUser/updatePwd',
method: 'post',
data: parameter
})
}
/**
* 用户选择器
*
* @author yubaoshan
* @date 2020/6/25 00:25
*/
export function sysUserSelector (parameter) {
return axios({
url: '/sysUser/selector',
method: 'get',
params: parameter
})
}
/**
* 修改头像
*
* @author yubaoshan
* @date 2020/9/20 2:21
*/
export function sysUserUpdateAvatar (parameter) {
return axios({
url: '/sysUser/updateAvatar',
method: 'post',
data: parameter
})
}
/**
* 更新基本信息
*
* @author yubaoshan
* @date 2020/9/20 03:12
*/
export function sysUserUpdateInfo (parameter) {
return axios({
url: '/sysUser/updateInfo',
method: 'post',
data: parameter
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1551058675966" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7872" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M85.333333 512h85.333334a340.736 340.736 0 0 1 99.712-241.621333 337.493333 337.493333 0 0 1 108.458666-72.96 346.453333 346.453333 0 0 1 261.546667-1.749334A106.154667 106.154667 0 0 0 746.666667 298.666667C805.802667 298.666667 853.333333 251.136 853.333333 192S805.802667 85.333333 746.666667 85.333333c-29.397333 0-55.978667 11.776-75.221334 30.933334-103.722667-41.514667-222.848-40.874667-325.76 2.517333a423.594667 423.594667 0 0 0-135.68 91.264 423.253333 423.253333 0 0 0-91.306666 135.637333A426.88 426.88 0 0 0 85.333333 512z m741.248 133.205333c-17.109333 40.618667-41.685333 77.141333-72.96 108.416s-67.797333 55.850667-108.458666 72.96a346.453333 346.453333 0 0 1-261.546667 1.749334A106.154667 106.154667 0 0 0 277.333333 725.333333C218.197333 725.333333 170.666667 772.864 170.666667 832S218.197333 938.666667 277.333333 938.666667c29.397333 0 55.978667-11.776 75.221334-30.933334A425.173333 425.173333 0 0 0 512 938.666667a425.941333 425.941333 0 0 0 393.258667-260.352A426.325333 426.325333 0 0 0 938.666667 512h-85.333334a341.034667 341.034667 0 0 1-26.752 133.205333z" p-id="7873"></path><path d="M512 318.378667c-106.752 0-193.621333 86.869333-193.621333 193.621333S405.248 705.621333 512 705.621333s193.621333-86.869333 193.621333-193.621333S618.752 318.378667 512 318.378667z m0 301.909333c-59.690667 0-108.288-48.597333-108.288-108.288S452.309333 403.712 512 403.712s108.288 48.597333 108.288 108.288-48.597333 108.288-108.288 108.288z" p-id="7874"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
_web/src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

203
_web/src/assets/logo.svg Normal file
View File

@ -0,0 +1,203 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve"> <image id="image0" width="500" height="500" x="0" y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAMAAAD8CC+4AAAABGdBTUEAALGPC/xhBQAAACBjSFJN
AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAADAFBMVEX////X7v+w3//L6f/U
7v8ao/+64//i8/8lqP+v3//Z8P8HnP/S7f/v+f87sf8Amf8Onv8tq//n9f/5/f9Vu//R7f8Lnf9G
tf/z+v9txf/Q7P8Mnv9dvv/8/v+Dzf+75P/M6/8Jnf94yf+a1/+85P/I6f+T1P+04f8Bmf/F6P8E
m/8Tof8Cmv+95f/C5/8Qn//G6P/X7/8ip//A5v8Dmv8hpv/a8P/l9f8yrf83r//r9/+24v9Mt//2
+/9ev/9kwf/9/v9yx/++5f+BzP+Iz/+d2P+i2v+r3f+54//B5v+k2/8Xov/K6v8mqP/f8v/c8f8k
p/89sf/w+f/s9/+e2P9Tuv/7/f/4/P9NuP8Fm/+Z1v9pw/9iwP+Fzv97yv8Gm//L6v+W1f+Q0/+s
3v8InP/O6/+M0f8Yo/8SoP/P7P+Hz//h8//W7/8epf8Knf8+sv/x+f/m9f8vrP9XvP9Bs/9/zP9x
xv/6/f+K0P9vxf+p3f91yP+c1//T7f+y4P/J6f9lwf92yP9gv/9avf9Wu/8RoP/E5//Y7/+g2f9Q
uf80rv9OuP/b8f9jwf/e8v8oqf9Cs/99y/8Uof/d8f/3/P/1+/8jp//0+/8pqf86sP/t+P/y+v9r
xP8Vof/g8//o9v/u+P/H6f/j9P/k9P/p9v81rv8bpP9Jtv/q9/90x/+P0v9sxP9DtP9Zvf8sqv9Y
vP9mwv9qw//q9v84pv8Akv9Itv8Ck//g8v8qqv8Bkv+J0P8wrP+S0/+m2/9Puf95yf+95P+o3P+t
3v/V7v85sP+n3P+i2f9hwP9kwP98y/9PuP9Es/8/sf87sP+/5v8Alf/v+P9EtP/H6P8Mnf8Im/8E
mv+x4P8Dmf8cpP8AmP+r3v+S1P9vxv9bvv9Ouf////88sv8zqf8tpf8ppP8kov/0+v9txP8gof8b
nv8Wm/8Sm/8Omf8Kl/8Glf/d8v9ZvP8Ak/9jwP8jpP8VnP/l9P88r/84q/82qv8Bk//R7P8wp/8q
of9Suf9Js/81rP+Y1f/HFJ4WAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAHdElNRQflAwMC
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
D5OidHPCce5xV2HoNy3XZLbDvBrni+tuDT0VM90xme3KOoOrYMwY/JpTKgr9TJ/oYrfXmvkG18AY
M8Gl7jstev88e6tD+xv4z9iQ5rA3Qt/e+xTa2szd0jZ0SFoCE6yPrv6UtJllm8xW9ryB54w9abYF
Hukls9W97Czu5A+1uHHD7sn+LGlwXczG3sqkr+ObULxlVY1kNGnvgE3btystuFZQGJ6xWKDbQlqz
6JK4akUPA3cZCIPNF+i2kcZOGndmOzHVwFcGxnXT6akQ3XCtL+fd46GvvqXyluFrPP1MLzVrx1fW
18BLBkx0Sxg90UcYdWa72WxbmycHG0xe4+nbew+DJl0rOWctONf138G60Fb0G0csaGEl2hOTs911
9TpNG1mgaSKDP9QShGuaC3SLaBOajSNKikJfK1PLWr1v9h20Ba3GEelLjmq7xPhH61DN4/Tf6zwi
yibq+8PEg+kaso2k/1wjmW3UBn1vmDgxOfI1vjv9x+9G/W3xdX1PmPixNmqy76L/Nqo24dwW0zKv
2TG+2FZ0dTLb7s8LQl8aQ6JejVfc3pW1CUu49F9iM7nYSvT36L9afog/1BKdtO42ot9H/tGQQfpj
M8EYT73GK57pZHWTRXP0B2YCQn2zX6b/5Cv5X+TfCH0tjDbXpAt0Heg/kJe02cQfas2Js7KOjXPp
38tqHvRr4W0SmyF7mz7Z99G/HtO05sFm/lBrfhxo8mR/lf7xm4WNfrt+LKeiNkuuNfpmH07/tHFe
01Wu0d5cWdvwyX6R/uVDGfV/mNtldmjXGXvGv1NPy0707/bH6n+ofRXabcaJA5e1RJ9RrzPbprWh
nWZcub31phA9LbX2R4cfC+0wAyDtw+hn+oXarIlOnIqaJOwtjhJ9dk1e0/KeR0L7yqA4UD3Zr9A/
OL6x6gdcMyi5qJrsimf60SFCZHPNoGRjxi4xi/6/R4aKad+GdpHB8w8v0U1ut/7+3LdC+8f4oC3d
8nTrm5yjxjAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAM
wzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzDJyf8H/RgaDtLOxBkAAAAldEVYdGRhdGU6Y3JlYXRl
ADIwMjEtMDMtMDJUMTg6MDA6MDArMDg6MDA9+rsMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAz
LTAyVDE4OjAwOjAwKzA4OjAwTKcDsAAAACB0RVh0c29mdHdhcmUAaHR0cHM6Ly9pbWFnZW1hZ2lj
ay5vcme8zx2dAAAAGHRFWHRUaHVtYjo6RG9jdW1lbnQ6OlBhZ2VzADGn/7svAAAAGHRFWHRUaHVt
Yjo6SW1hZ2U6OkhlaWdodAA1MDB4mAPsAAAAF3RFWHRUaHVtYjo6SW1hZ2U6OldpZHRoADUwMOtp
U7EAAAAZdEVYdFRodW1iOjpNaW1ldHlwZQBpbWFnZS9wbmc/slZOAAAAF3RFWHRUaHVtYjo6TVRp
bWUAMTYxNDY3OTIwMCAf/wcAAAATdEVYdFRodW1iOjpTaXplADExMjczQkI7FGLOAAAARnRFWHRU
aHVtYjo6VVJJAGZpbGU6Ly8vYXBwL3RtcC9pbWFnZWxjL2ltZ3ZpZXcyXzlfMTYwOTkwMzUxMTcy
MzMzODZfOTJfWzBdRG9xdgAAAABJRU5ErkJggg==" ></image>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

BIN
_web/src/assets/welcome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,89 @@
<template>
<div class="antd-pro-components-article-list-content-index-listContent">
<div class="description">
<slot>
{{ description }}
</slot>
</div>
<div class="extra">
<a-avatar :src="avatar" size="small" />
<a :href="href">{{ owner }}</a> 发布在 <a :href="href">{{ href }}</a>
<em>{{ updateAt | moment }}</em>
</div>
</div>
</template>
<script>
export default {
name: 'ArticleListContent',
props: {
prefixCls: {
type: String,
default: 'antd-pro-components-article-list-content-index-listContent'
},
description: {
type: String,
default: ''
},
owner: {
type: String,
required: true
},
avatar: {
type: String,
required: true
},
href: {
type: String,
required: true
},
updateAt: {
type: String,
required: true
}
}
}
</script>
<style lang="less" scoped>
@import '../index.less';
.antd-pro-components-article-list-content-index-listContent {
.description {
max-width: 720px;
line-height: 22px;
}
.extra {
margin-top: 16px;
color: @text-color-secondary;
line-height: 22px;
& /deep/ .ant-avatar {
position: relative;
top: 1px;
width: 20px;
height: 20px;
margin-right: 8px;
vertical-align: top;
}
& > em {
margin-left: 16px;
color: @disabled-color;
font-style: normal;
}
}
}
@media screen and (max-width: @screen-xs) {
.antd-pro-components-article-list-content-index-listContent {
.extra {
& > em {
display: block;
margin-top: 8px;
margin-left: 0;
}
}
}
}
</style>

View File

@ -0,0 +1,3 @@
import ArticleListContent from './ArticleListContent'
export default ArticleListContent

View File

@ -0,0 +1,46 @@
<template>
<tooltip v-if="tips !== ''">
<template slot="title">{{ tips }}</template>
<avatar :size="avatarSize" :src="src" />
</tooltip>
<avatar v-else :size="avatarSize" :src="src" />
</template>
<script>
import Avatar from 'ant-design-vue/es/avatar'
import Tooltip from 'ant-design-vue/es/tooltip'
export default {
name: 'AvatarItem',
components: {
Avatar,
Tooltip
},
props: {
tips: {
type: String,
default: '',
required: false
},
src: {
type: String,
default: ''
}
},
data () {
return {
size: this.$parent.size
}
},
computed: {
avatarSize () {
return this.size !== 'mini' && this.size || 20
}
},
watch: {
'$parent.size' (val) {
this.size = val
}
}
}
</script>

View File

@ -0,0 +1,99 @@
<!--
<template>
<div :class="[prefixCls]">
<ul>
<slot></slot>
<template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template>
<template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength">
<avatar-item :size="size">
<avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar>
</avatar-item>
</template>
</ul>
</div>
</template>
-->
<script>
import Avatar from 'ant-design-vue/es/avatar'
import AvatarItem from './Item'
import { filterEmpty } from '@/components/_util/util'
export default {
AvatarItem,
name: 'AvatarList',
components: {
Avatar,
AvatarItem
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-avatar-list'
},
/**
* 头像大小 类型: largesmall mini, default
* 默认值: default
*/
size: {
type: [String, Number],
default: 'default'
},
/**
* 要显示的最大项目
*/
maxLength: {
type: Number,
default: 0
},
/**
* 多余的项目风格
*/
excessItemsStyle: {
type: Object,
default: () => {
return {
color: '#f56a00',
backgroundColor: '#fde3cf'
}
}
}
},
data () {
return {}
},
methods: {
getItems (items) {
const classString = {
[`${this.prefixCls}-item`]: true,
[`${this.size}`]: true
}
if (this.maxLength > 0) {
items = items.slice(0, this.maxLength)
items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>))
}
const itemList = items.map((item) => (
<li class={ classString }>{ item }</li>
))
return itemList
}
},
render () {
const { prefixCls, size } = this.$props
const classString = {
[`${prefixCls}`]: true,
[`${size}`]: true
}
const items = filterEmpty(this.$slots.default)
const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null
return (
<div class={ classString }>
{ itemsDom }
</div>
)
}
}
</script>

View File

@ -0,0 +1,4 @@
import AvatarList from './List'
import './index.less'
export default AvatarList

View File

@ -0,0 +1,60 @@
@import "../index";
@avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list";
@avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item";
.@{avatar-list-prefix-cls} {
display: inline-block;
ul {
list-style: none;
display: inline-block;
padding: 0;
margin: 0 0 0 8px;
font-size: 0;
}
}
.@{avatar-list-item-prefix-cls} {
display: inline-block;
font-size: @font-size-base;
margin-left: -8px;
width: @avatar-size-base;
height: @avatar-size-base;
:global {
.ant-avatar {
border: 1px solid #fff;
cursor: pointer;
}
}
&.large {
width: @avatar-size-lg;
height: @avatar-size-lg;
}
&.small {
width: @avatar-size-sm;
height: @avatar-size-sm;
}
&.mini {
width: 20px;
height: 20px;
:global {
.ant-avatar {
width: 20px;
height: 20px;
line-height: 20px;
.ant-avatar-string {
font-size: 12px;
line-height: 18px;
}
}
}
}
}

View File

@ -0,0 +1,64 @@
# AvatarList 用户头像列表
一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。
引用方式:
```javascript
import AvatarList from '@/components/AvatarList'
const AvatarListItem = AvatarList.AvatarItem
export default {
components: {
AvatarList,
AvatarListItem
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
<avatar-list size="mini">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```
```html
<avatar-list :max-length="3">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
```
## API
### AvatarList
| 参数 | 说明 | 类型 | 默认值 |
| ---------------- | -------- | ---------------------------------- | --------- |
| size | 头像大小 | `large``small``mini`, `default` | `default` |
| maxLength | 要显示的最大项目 | number | - |
| excessItemsStyle | 多余的项目风格 | CSSProperties | - |
### AvatarList.Item
| 参数 | 说明 | 类型 | 默认值 |
| ---- | ------ | --------- | --- |
| tips | 头像展示文案 | string | - |
| src | 头像图片连接 | string | - |

View File

@ -0,0 +1,62 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="data"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
export default {
name: 'Bar',
props: {
title: {
type: String,
default: ''
},
data: {
type: Array,
default: () => {
return []
}
},
scale: {
type: Array,
default: () => {
return [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
}
},
tooltip: {
type: Array,
default: () => {
return [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
}
}
},
data () {
return {
}
}
}
</script>

View File

@ -0,0 +1,120 @@
<template>
<a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false">
<div class="chart-card-header">
<div class="meta">
<span class="chart-card-title">
<slot name="title">
{{ title }}
</slot>
</span>
<span class="chart-card-action">
<slot name="action"></slot>
</span>
</div>
<div class="total">
<slot name="total">
<span>{{ typeof total === 'function' && total() || total }}</span>
</slot>
</div>
</div>
<div class="chart-card-content">
<div class="content-fix">
<slot></slot>
</div>
</div>
<div class="chart-card-footer">
<div class="field">
<slot name="footer"></slot>
</div>
</div>
</a-card>
</template>
<script>
export default {
name: 'ChartCard',
props: {
title: {
type: String,
default: ''
},
total: {
type: [Function, Number, String],
required: false,
default: null
},
loading: {
type: Boolean,
default: false
}
}
}
</script>
<style lang="less" scoped>
.chart-card-header {
position: relative;
overflow: hidden;
width: 100%;
.meta {
position: relative;
overflow: hidden;
width: 100%;
color: rgba(0, 0, 0, .45);
font-size: 14px;
line-height: 22px;
}
}
.chart-card-action {
cursor: pointer;
position: absolute;
top: 0;
right: 0;
}
.chart-card-footer {
border-top: 1px solid #e8e8e8;
padding-top: 9px;
margin-top: 8px;
> * {
position: relative;
}
.field {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
}
.chart-card-content {
margin-bottom: 12px;
position: relative;
height: 46px;
width: 100%;
.content-fix {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
}
}
.total {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
color: #000;
margin-top: 4px;
margin-bottom: 0;
font-size: 30px;
line-height: 38px;
height: 38px;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div>
<v-chart
:forceFit="true"
:height="height"
:width="width"
:data="data"
:scale="scale"
:padding="0">
<v-tooltip />
<v-interval
:shape="['liquid-fill-gauge']"
position="transfer*value"
color=""
:v-style="{
lineWidth: 10,
opacity: 0.75
}"
:tooltip="[
'transfer*value',
(transfer, value) => {
return {
name: transfer,
value,
};
},
]"
></v-interval>
<v-guide
v-for="(row, index) in data"
:key="index"
type="text"
:top="true"
:position="{
gender: row.transfer,
value: 45
}"
:content="row.value + '%'"
:v-style="{
fontSize: 100,
textAlign: 'center',
opacity: 0.75,
}"
/>
</v-chart>
</div>
</template>
<script>
export default {
name: 'Liquid',
props: {
height: {
type: Number,
default: 0
},
width: {
type: Number,
default: 0
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,56 @@
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]">
<v-tooltip />
<v-smooth-area position="x*y" />
</v-chart>
</div>
</div>
</template>
<script>
import moment from 'moment'
const data = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
export default {
name: 'MiniArea',
data () {
return {
data,
tooltip,
scale,
height: 100
}
}
}
</script>
<style lang="less" scoped>
@import "chart";
</style>

View File

@ -0,0 +1,57 @@
<template>
<div class="antv-chart-mini">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]">
<v-tooltip />
<v-bar position="x*y" />
</v-chart>
</div>
</div>
</template>
<script>
import moment from 'moment'
const data = []
const beginDay = new Date().getTime()
for (let i = 0; i < 10; i++) {
data.push({
x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
y: Math.round(Math.random() * 10)
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 30
}]
export default {
name: 'MiniBar',
data () {
return {
data,
tooltip,
scale,
height: 100
}
}
}
</script>
<style lang="less" scoped>
@import "chart";
</style>

View File

@ -0,0 +1,75 @@
<template>
<div class="chart-mini-progress">
<div class="target" :style="{ left: target + '%'}">
<span :style="{ backgroundColor: color }" />
<span :style="{ backgroundColor: color }"/>
</div>
<div class="progress-wrapper">
<div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div>
</div>
</div>
</template>
<script>
export default {
name: 'MiniProgress',
props: {
target: {
type: Number,
default: 0
},
height: {
type: String,
default: '10px'
},
color: {
type: String,
default: '#13C2C2'
},
percentage: {
type: Number,
default: 0
}
}
}
</script>
<style lang="less" scoped>
.chart-mini-progress {
padding: 5px 0;
position: relative;
width: 100%;
.target {
position: absolute;
top: 0;
bottom: 0;
span {
border-radius: 100px;
position: absolute;
top: 0;
left: 0;
height: 4px;
width: 2px;
&:last-child {
top: auto;
bottom: 0;
}
}
}
.progress-wrapper {
background-color: #f5f5f5;
position: relative;
.progress {
transition: all .4s cubic-bezier(.08,.82,.17,1) 0s;
border-radius: 1px 0 0 1px;
background-color: #1890ff;
width: 0;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,40 @@
<template>
<div :class="prefixCls">
<div class="chart-wrapper" :style="{ height: 46 }">
<v-chart :force-fit="true" :height="100" :data="dataSource" :scale="scale" :padding="[36, 0, 18, 0]">
<v-tooltip />
<v-smooth-line position="x*y" :size="2" />
<v-smooth-area position="x*y" />
</v-chart>
</div>
</div>
</template>
<script>
export default {
name: 'MiniSmoothArea',
props: {
prefixCls: {
type: String,
default: 'ant-pro-smooth-area'
},
scale: {
type: [Object, Array],
required: true
},
dataSource: {
type: Array,
required: true
}
},
data () {
return {
height: 100
}
}
}
</script>
<style lang="less" scoped>
@import "smooth.area.less";
</style>

View File

@ -0,0 +1,68 @@
<template>
<v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale">
<v-tooltip></v-tooltip>
<v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" />
<v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" />
<v-legend dataKey="user" marker="circle" :offset="30" />
<v-coord type="polar" radius="0.8" />
<v-line position="item*score" color="user" :size="2" />
<v-point position="item*score" color="user" :size="4" shape="circle" />
</v-chart>
</template>
<script>
const axis1Opts = {
dataKey: 'item',
line: null,
tickLine: null,
grid: {
lineStyle: {
lineDash: null
},
hideFirstLine: false
}
}
const axis2Opts = {
dataKey: 'score',
line: null,
tickLine: null,
grid: {
type: 'polygon',
lineStyle: {
lineDash: null
}
}
}
const scale = [
{
dataKey: 'score',
min: 0,
max: 80
}, {
dataKey: 'user',
alias: '类型'
}
]
export default {
name: 'Radar',
props: {
data: {
type: Array,
default: null
}
},
data () {
return {
axis1Opts,
axis2Opts,
scale
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,77 @@
<template>
<div class="rank">
<h4 class="title">{{ title }}</h4>
<ul class="list">
<li :key="index" v-for="(item, index) in list">
<span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span>
<span>{{ item.name }}</span>
<span>{{ item.total }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'RankList',
// ['title', 'list']
props: {
title: {
type: String,
default: ''
},
list: {
type: Array,
default: null
}
}
}
</script>
<style lang="less" scoped>
.rank {
padding: 0 32px 32px 72px;
.list {
margin: 25px 0 0;
padding: 0;
list-style: none;
li {
margin-top: 16px;
span {
color: rgba(0, 0, 0, .65);
font-size: 14px;
line-height: 22px;
&:first-child {
background-color: #f5f5f5;
border-radius: 20px;
display: inline-block;
font-size: 12px;
font-weight: 600;
margin-right: 24px;
height: 20px;
line-height: 20px;
width: 20px;
text-align: center;
}
&.active {
background-color: #314659;
color: #fff;
}
&:last-child {
float: right;
}
}
}
}
}
.mobile .rank {
padding: 0 32px 32px 32px;
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<v-chart :width="width" :height="height" :padding="[0]" :data="data" :scale="scale">
<v-tooltip :show-title="false" />
<v-coord type="rect" direction="TL" />
<v-point position="x*y" color="category" shape="cloud" tooltip="value*category" />
</v-chart>
</template>
<script>
import { registerShape } from 'viser-vue'
const DataSet = require('@antv/data-set')
const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png'
const scale = [
{ dataKey: 'x', nice: false },
{ dataKey: 'y', nice: false }
]
registerShape('point', 'cloud', {
draw (cfg, container) {
return container.addShape('text', {
attrs: {
fillOpacity: cfg.opacity,
fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate,
text: cfg.origin._origin.text,
textAlign: 'center',
fontFamily: cfg.origin._origin.font,
fill: cfg.color,
textBaseline: 'Alphabetic',
...cfg.style,
x: cfg.x,
y: cfg.y
}
})
}
})
export default {
name: 'TagCloud',
props: {
tagList: {
type: Array,
required: true
},
height: {
type: Number,
default: 400
},
width: {
type: Number,
default: 640
}
},
data () {
return {
data: [],
scale
}
},
watch: {
tagList: function (val) {
if (val.length > 0) {
this.initTagCloud(val)
}
}
},
mounted () {
if (this.tagList.length > 0) {
this.initTagCloud(this.tagList)
}
},
methods: {
initTagCloud (dataSource) {
const { height, width } = this
const dv = new DataSet.View().source(dataSource)
const range = dv.range('value')
const min = range[0]
const max = range[1]
const imageMask = new Image()
imageMask.crossOrigin = ''
imageMask.src = imgUrl
imageMask.onload = () => {
dv.transform({
type: 'tag-cloud',
fields: ['name', 'value'],
size: [width, height],
imageMask,
font: 'Verdana',
padding: 0,
timeInterval: 5000, // max execute time
rotate () {
let random = ~~(Math.random() * 4) % 4
if (random === 2) {
random = 0
}
return random * 90 // 0, 90, 270
},
fontSize (d) {
if (d.value) {
return ((d.value - min) / (max - min)) * (32 - 8) + 8
}
return 0
}
})
this.data = dv.rows
}
}
}
}
</script>

View File

@ -0,0 +1,64 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="data"
:scale="scale"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
title: '日期(天)',
alias: '日期(天)',
min: 2
}, {
dataKey: 'y',
title: '流量(Gb)',
alias: '流量(Gb)',
min: 1
}]
export default {
name: 'Bar',
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
data: [],
scale,
tooltip
}
},
created () {
this.getMonthBar()
},
methods: {
getMonthBar () {
this.$http.get('/analysis/month-bar')
.then(res => {
this.data = res.result
})
}
}
}
</script>

View File

@ -0,0 +1,82 @@
<template>
<div class="chart-trend">
{{ term }}
<span>{{ rate }}%</span>
<span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span>
</div>
</template>
<script>
export default {
name: 'Trend',
props: {
term: {
type: String,
default: '',
required: true
},
percentage: {
type: Number,
default: null
},
type: {
type: Boolean,
default: null
},
target: {
type: Number,
default: 0
},
value: {
type: Number,
default: 0
},
fixed: {
type: Number,
default: 2
}
},
data () {
return {
trend: this.type && 'up' || 'down',
rate: this.percentage
}
},
created () {
const type = this.type === null ? this.value >= this.target : this.type
this.trend = type ? 'up' : 'down'
this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed)
}
}
</script>
<style lang="less" scoped>
.chart-trend {
display: inline-block;
font-size: 14px;
line-height: 22px;
.trend-icon {
font-size: 12px;
&.up, &.down {
margin-left: 4px;
position: relative;
top: 1px;
i {
font-size: 12px;
transform: scale(.83);
}
}
&.up {
color: #f5222d;
}
&.down {
color: #52c41a;
top: -1px;
}
}
}
</style>

View File

@ -0,0 +1,13 @@
.antv-chart-mini {
position: relative;
width: 100%;
.chart-wrapper {
position: absolute;
bottom: -28px;
width: 100%;
/* margin: 0 -5px;
overflow: hidden;*/
}
}

View File

@ -0,0 +1,14 @@
@import "../index";
@smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area";
.@{smoothArea-prefix-cls} {
position: relative;
width: 100%;
.chart-wrapper {
position: absolute;
bottom: -28px;
width: 100%;
}
}

View File

@ -0,0 +1,102 @@
<template>
<span>
{{ lastTime | format }}
</span>
</template>
<script>
function fixedZero (val) {
return val * 1 < 10 ? `0${val}` : val
}
export default {
name: 'CountDown',
props: {
format: {
type: Function,
default: undefined
},
target: {
type: [Date, Number],
required: true
},
onEnd: {
type: Function,
default: () => ({})
}
},
data () {
return {
dateTime: '0',
originTargetTime: 0,
lastTime: 0,
timer: 0,
interval: 1000
}
},
filters: {
format (time) {
const hours = 60 * 60 * 1000
const minutes = 60 * 1000
const h = Math.floor(time / hours)
const m = Math.floor((time - h * hours) / minutes)
const s = Math.floor((time - h * hours - m * minutes) / 1000)
return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}`
}
},
created () {
this.initTime()
this.tick()
},
methods: {
initTime () {
let lastTime = 0
let targetTime = 0
this.originTargetTime = this.target
try {
if (Object.prototype.toString.call(this.target) === '[object Date]') {
targetTime = this.target
} else {
targetTime = new Date(this.target).getTime()
}
} catch (e) {
throw new Error('invalid target prop')
}
lastTime = targetTime - new Date().getTime()
this.lastTime = lastTime < 0 ? 0 : lastTime
},
tick () {
const { onEnd } = this
this.timer = setTimeout(() => {
if (this.lastTime < this.interval) {
clearTimeout(this.timer)
this.lastTime = 0
if (typeof onEnd === 'function') {
onEnd()
}
} else {
this.lastTime -= this.interval
this.tick()
}
}, this.interval)
}
},
beforeUpdate () {
if (this.originTargetTime !== this.target) {
this.initTime()
}
},
beforeDestroy () {
clearTimeout(this.timer)
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,3 @@
import CountDown from './CountDown'
export default CountDown

View File

@ -0,0 +1,34 @@
# CountDown 倒计时
倒计时组件。
引用方式:
```javascript
import CountDown from '@/components/CountDown/CountDown'
export default {
components: {
CountDown
}
}
```
## 代码演示 [demo](https://pro.loacg.com/test/home)
```html
<count-down :target="new Date().getTime() + 3000000" :on-end="onEndHandle" />
```
## API
| 参数 | 说明 | 类型 | 默认值 |
|----------|------------------------------------------|-------------|-------|
| target | 目标时间 | Date | - |
| onEnd | 倒计时结束回调 | funtion | -|

View File

@ -0,0 +1,48 @@
<template>
<a-tree-select
:dropdownStyle="{ maxHeight: '300px', overflow: 'auto' }"
allowClear
:treeData="orgTree"
:placeholder="placeholder"
treeDefaultExpandAll
@change="onchange"
>
<span slot="title" slot-scope="{ id }">{{ id }}</span>
</a-tree-select>
</template>
<script>
import { getOrgTree } from '@/api/modular/system/orgManage'
export default {
name: 'DepartSelect',
props: {
placeholder: {
type: String
},
value: {
type: String
}
},
data() {
return {
orgTree: []
}
},
created() {
this.getOrgData()
},
methods: {
getOrgData() {
getOrgTree().then((res) => {
this.orgTree = res
})
},
/**
* 选择树机构初始化机构名称于表单中
*/
onchange (value) {
this.$emit('change', value)
}
}
}
</script>

Some files were not shown because too many files have changed in this diff Show More