mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc65439e4 | ||
|
|
445cf3716b | ||
|
|
f4b59b8503 | ||
|
|
4f08975df2 | ||
|
|
570db453d7 | ||
|
|
b93984bf6f | ||
|
|
e483db1b97 | ||
|
|
17d96acceb | ||
|
|
9900b236ef | ||
|
|
4fa52412c1 | ||
|
|
0076869deb | ||
|
|
af55193591 | ||
|
|
1d858118d5 | ||
|
|
8e64ba67fa | ||
|
|
58fb11b78f | ||
|
|
f6e9076a40 | ||
|
|
2fefa43aea | ||
|
|
f43b0467ba | ||
|
|
14da4d77e0 | ||
|
|
cefad86b85 | ||
|
|
738ff86344 | ||
|
|
110abc4ac7 | ||
|
|
5f1aaa40d8 | ||
|
|
0695ad9a85 | ||
|
|
7c086bbec8 | ||
|
|
c75fe7135a | ||
|
|
edf29976dd | ||
|
|
7b5f963ec4 | ||
|
|
f22badb861 | ||
|
|
8c501c90cd | ||
|
|
11eebdfcf0 | ||
|
|
46e331783f | ||
|
|
7e33e64659 | ||
|
|
7e4152ad6d | ||
|
|
d189ee7c22 | ||
|
|
641c2abb24 | ||
|
|
3ab4ac891b | ||
|
|
70b586e45a | ||
|
|
77aa724003 | ||
|
|
db7b50d96a | ||
|
|
00fee24a85 | ||
|
|
fa0cb73ec9 | ||
|
|
6ee815b71d | ||
|
|
311a048af5 | ||
|
|
3da9ecfaa3 | ||
|
|
4c2c6f613e | ||
|
|
ba63736871 | ||
|
|
ed3dbafc35 | ||
|
|
fdeffbd495 | ||
|
|
9870812e6b | ||
|
|
46f35e14c4 | ||
|
|
e89cf96ff4 | ||
|
|
5cd19bf38d | ||
|
|
a6f69f2b62 | ||
|
|
e34b9adada | ||
|
|
9f43f731b5 | ||
|
|
594ca43505 | ||
|
|
2a91cdb67a | ||
|
|
11148e720b | ||
|
|
c5835d6d8c | ||
|
|
4a26bb3ba5 | ||
|
|
4fec38724d | ||
|
|
85349df8a1 | ||
|
|
ffe250f8a9 | ||
|
|
eeb310a1d2 | ||
|
|
a42c606d20 | ||
|
|
4e64d46cd2 | ||
|
|
9886ee6828 | ||
|
|
0e1bde09c3 | ||
|
|
dda600709b | ||
|
|
15f38491b2 | ||
|
|
4b140732f7 | ||
|
|
4cb9ff3f14 | ||
|
|
e4f3e2c4c1 | ||
|
|
a80adb7dd8 | ||
|
|
195127a9d4 | ||
|
|
f2119b2c52 | ||
|
|
f15c45793b | ||
|
|
24f543e667 | ||
|
|
772995705f | ||
|
|
3475c39fe6 | ||
|
|
e6e393379f | ||
|
|
03cc91c3e5 | ||
|
|
f82f7bec6a | ||
|
|
4afd5bbd5e | ||
|
|
86aac2bf08 | ||
|
|
70c8b25a67 | ||
|
|
231af72444 | ||
|
|
480e930385 | ||
|
|
debc34f0fb | ||
|
|
99ce3bd099 | ||
|
|
99431cf9a2 | ||
|
|
83711c69f9 | ||
|
|
9e67032280 | ||
|
|
fa37937410 | ||
|
|
1be2cad78e | ||
|
|
2b1e687ed4 | ||
|
|
881009321b | ||
|
|
aed99b63b8 | ||
|
|
dfa34ba371 | ||
|
|
20beb30dd8 | ||
|
|
4475972af3 | ||
|
|
a843c65783 | ||
|
|
c2de1d3fa2 | ||
|
|
c8d091da06 | ||
|
|
553208ba57 | ||
|
|
072028699a | ||
|
|
9cdcf145a5 | ||
|
|
4df1c19e81 | ||
|
|
ac26a214bc | ||
|
|
ad616496d1 | ||
|
|
9870582779 | ||
|
|
20cc696b33 | ||
|
|
d7263f2b3c | ||
|
|
74e5ee41fb | ||
|
|
f936331dff | ||
|
|
ba311c3504 | ||
|
|
03291594b1 | ||
|
|
a6d9a4b5ae | ||
|
|
875de022c1 | ||
|
|
2c863a2774 | ||
|
|
f2f086a82c | ||
|
|
936ca61f94 | ||
|
|
87ae2f81fa | ||
|
|
ecf67db2b1 | ||
|
|
2e5589e112 | ||
|
|
2598a60898 | ||
|
|
0de226bbf3 | ||
|
|
422f0d8491 | ||
|
|
b028708b94 | ||
|
|
812c0d0f6a | ||
|
|
46df5293dd | ||
|
|
ab42b3e90b | ||
|
|
1378259cc7 | ||
|
|
c8f0b0a83f | ||
|
|
acec760ec1 | ||
|
|
2fe70d49f6 | ||
|
|
9013fff804 | ||
|
|
e925a808c4 | ||
|
|
6c197edddd | ||
|
|
c35e91b7b6 | ||
|
|
575947795a | ||
|
|
51f116c7d2 | ||
|
|
c28254855c | ||
|
|
e8f3671ffb | ||
|
|
ac62767a18 | ||
|
|
2db4c20dd3 | ||
|
|
cfb7fd5b29 | ||
|
|
22c401f9d8 | ||
|
|
be00b90c1d | ||
|
|
fb3f89c594 | ||
|
|
e7a66378ea | ||
|
|
2f88b48973 | ||
|
|
7761fe0288 | ||
|
|
09e6bdcf7e | ||
|
|
61a4d87f59 | ||
|
|
c219ec33b0 | ||
|
|
fd86f36218 | ||
|
|
efac41f392 | ||
|
|
52df61ae0d | ||
|
|
cf2bc6785c | ||
|
|
98a4c92576 | ||
|
|
b1ee9b65ff | ||
|
|
99cc4c5e5e | ||
|
|
226bb8f089 | ||
|
|
37ed5134e8 | ||
|
|
0f54d4a472 | ||
|
|
64805360d6 | ||
|
|
7f69fe2ad9 | ||
|
|
f913510d3c | ||
|
|
f2d9e7786d | ||
|
|
e1afb1ed54 | ||
|
|
12f8cf0111 | ||
|
|
daa2ef5203 | ||
|
|
1e3e183930 | ||
|
|
366563a0fe | ||
|
|
577802e5ad | ||
|
|
76d6fc3ba5 | ||
|
|
f0540559bb | ||
|
|
802e379f60 | ||
|
|
8c9253da80 | ||
|
|
5271bd21e8 | ||
|
|
db554ebdc9 | ||
|
|
1c18a01bf6 | ||
|
|
729a3d7028 | ||
|
|
b88923a128 | ||
|
|
fe8cd93c78 | ||
|
|
64b49dae2e | ||
|
|
edbbbca5f9 | ||
|
|
5ad0d90038 | ||
|
|
9b9173dea7 | ||
|
|
f58331c1c1 | ||
|
|
b2dc9dff0b | ||
|
|
51d06ab206 | ||
|
|
799e0ac9fc |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,4 +15,6 @@
|
||||
*.sum
|
||||
|
||||
*/node_modules/
|
||||
**/vendor/
|
||||
**/vendor/
|
||||
.idea
|
||||
out
|
||||
|
||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -1,18 +0,0 @@
|
||||
{
|
||||
// 使用 IntelliSense 了解相关属性。
|
||||
// 悬停以查看现有属性的描述。
|
||||
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"name": "mayfly-go",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}/main.go",
|
||||
"env": {},
|
||||
"args": []
|
||||
},
|
||||
]
|
||||
}
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
|
||||
36
README.en.md
36
README.en.md
@@ -1,36 +0,0 @@
|
||||
# mayfly-go
|
||||
|
||||
#### Description
|
||||
golang实现linux运维等
|
||||
|
||||
#### Software Architecture
|
||||
Software architecture description
|
||||
|
||||
#### Installation
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Instructions
|
||||
|
||||
1. xxxx
|
||||
2. xxxx
|
||||
3. xxxx
|
||||
|
||||
#### Contribution
|
||||
|
||||
1. Fork the repository
|
||||
2. Create Feat_xxx branch
|
||||
3. Commit your code
|
||||
4. Create Pull Request
|
||||
|
||||
|
||||
#### Gitee Feature
|
||||
|
||||
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
|
||||
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
|
||||
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
|
||||
4. The most valuable open source project [GVP](https://gitee.com/gvp)
|
||||
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
|
||||
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
|
||||
74
README.md
74
README.md
@@ -1,7 +1,29 @@
|
||||
# mayfly-go
|
||||
# 🌈mayfly-go
|
||||
|
||||
<p align="center">
|
||||
<a href="https://gitee.com/objs/mayfly-go" target="_blank">
|
||||
<img src="https://gitee.com/objs/mayfly-go/badge/star.svg?theme=white" alt="star"/>
|
||||
<img src="https://gitee.com/objs/mayfly-go/badge/fork.svg" alt="fork"/>
|
||||
</a>
|
||||
<a href="https://github.com/may-fly/mayfly-go" target="_blank">
|
||||
<img src="https://img.shields.io/github/stars/may-fly/mayfly-go.svg?style=social" alt="github star"/>
|
||||
<img src="https://img.shields.io/github/forks/may-fly/mayfly-go.svg?style=social" alt="github fork"/>
|
||||
</a>
|
||||
<a href="https://hub.docker.com/r/mayflygo/mayfly-go/tags" target="_blank">
|
||||
<img src="https://img.shields.io/docker/pulls/mayflygo/mayfly-go.svg?label=docker%20pulls&color=fac858" alt="docker pulls"/>
|
||||
</a>
|
||||
<a href="https://github.com/golang/go" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Golang-1.18%2B-yellow.svg" alt="golang"/>
|
||||
</a>
|
||||
<a href="https://cn.vuejs.org" target="_blank">
|
||||
<img src="https://img.shields.io/badge/Vue-3.x-green.svg" alt="vue">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
### 介绍
|
||||
web版 **linux(终端[终端回放] 文件 脚本 进程)、数据库(mysql postgres)、redis(单机 哨兵 集群)、mongo统一管理操作平台**
|
||||
|
||||
#### 介绍
|
||||
简单基于DDD(领域驱动设计)分层架构实现web版mysql,redis,linux统一操作管理平台
|
||||
|
||||
### 开发语言与主要框架
|
||||
- 前端:typescript、vue3、element-plus
|
||||
@@ -9,25 +31,57 @@
|
||||
|
||||
|
||||
### 交流及问题反馈加 QQ 群
|
||||
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?jump_from=webapi">119699946</a>
|
||||
<a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=IdJSHW0jTMhmWFHBUS9a83wxtrxDDhFj&jump_from=webapi">119699946</a>
|
||||
|
||||
|
||||
### 系统相关资料
|
||||
- 项目文档: https://objs.gitee.io/mayfly-go-docs
|
||||
- 系统操作视频: https://space.bilibili.com/484091081/channel/collectiondetail?sid=392854
|
||||
- 部署文档:https://objs.gitee.io/mayfly-go-docs/download
|
||||
|
||||
### 系统功能
|
||||
|
||||
记录操作记录
|
||||
### 系统核心功能截图
|
||||
|
||||
##### 记录操作记录
|
||||

|
||||
|
||||
#### 系统管理
|
||||
账号管理
|
||||
#### 机器操作
|
||||
##### 状态查看
|
||||

|
||||
|
||||
##### ssh终端
|
||||

|
||||
|
||||
##### 文件操作
|
||||

|
||||

|
||||
|
||||
|
||||
#### 数据库操作
|
||||
##### sql编辑器
|
||||

|
||||
|
||||
##### 在线增删改查数据
|
||||

|
||||
|
||||
|
||||
#### Redis操作
|
||||

|
||||
|
||||
|
||||
#### Mongo操作
|
||||

|
||||
|
||||
|
||||
##### 系统管理
|
||||
##### 账号管理
|
||||

|
||||
|
||||
角色管理
|
||||
##### 角色管理
|
||||

|
||||
|
||||
资源管理
|
||||
##### 资源管理
|
||||

|
||||
|
||||
|
||||
**其他更多功能&操作指南可查看在线文档**: https://objs.gitee.io/mayfly-go-docs
|
||||
@@ -1,34 +0,0 @@
|
||||
package biz
|
||||
|
||||
// 业务错误
|
||||
type BizError struct {
|
||||
code int16
|
||||
err string
|
||||
}
|
||||
|
||||
var (
|
||||
Success *BizError = NewBizErrCode(200, "success")
|
||||
BizErr *BizError = NewBizErrCode(400, "biz error")
|
||||
ServerError *BizError = NewBizErrCode(500, "server error")
|
||||
PermissionErr *BizError = NewBizErrCode(501, "token error")
|
||||
)
|
||||
|
||||
// 错误消息
|
||||
func (e *BizError) Error() string {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// 错误码
|
||||
func (e *BizError) Code() int16 {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,默认为业务逻辑错误
|
||||
func NewBizErr(msg string) *BizError {
|
||||
return &BizError{code: BizErr.code, err: msg}
|
||||
}
|
||||
|
||||
// 创建业务逻辑错误结构体,可设置指定错误code
|
||||
func NewBizErrCode(code int16, msg string) *BizError {
|
||||
return &BizError{code: code, err: msg}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"mayfly-go/base/biz"
|
||||
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
||||
var store = base64Captcha.DefaultMemStore
|
||||
var driver base64Captcha.Driver = base64Captcha.DefaultDriverDigit
|
||||
|
||||
// 生成验证码
|
||||
func Generate() (string, string) {
|
||||
c := base64Captcha.NewCaptcha(driver, store)
|
||||
// 获取
|
||||
id, b64s, err := c.Generate()
|
||||
biz.ErrIsNilAppendErr(err, "获取验证码错误: %s")
|
||||
return id, b64s
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
func Verify(id string, val string) bool {
|
||||
if id == "" || val == "" {
|
||||
return false
|
||||
}
|
||||
// 同时清理掉这个图片
|
||||
return store.Verify(id, val, true)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
type App struct {
|
||||
Name string `yaml:"name"`
|
||||
Version string `yaml:"version"`
|
||||
}
|
||||
|
||||
func (a *App) GetAppInfo() string {
|
||||
return fmt.Sprintf("[%s:%s]", a.Name, a.Version)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Server struct {
|
||||
Port int `yaml:"port"`
|
||||
Model string `yaml:"model"`
|
||||
Cors bool `yaml:"cors"`
|
||||
Tls *Tls `yaml:"tls"`
|
||||
Static *[]*Static `yaml:"static"`
|
||||
StaticFile *[]*StaticFile `yaml:"static-file"`
|
||||
}
|
||||
|
||||
func (s *Server) GetPort() string {
|
||||
return fmt.Sprintf(":%d", s.Port)
|
||||
}
|
||||
|
||||
type Static struct {
|
||||
RelativePath string `yaml:"relative-path"`
|
||||
Root string `yaml:"root"`
|
||||
}
|
||||
|
||||
type StaticFile struct {
|
||||
RelativePath string `yaml:"relative-path"`
|
||||
Filepath string `yaml:"filepath"`
|
||||
}
|
||||
|
||||
type Tls struct {
|
||||
Enable bool `yaml:"enable"` // 是否启用tls
|
||||
KeyFile string `yaml:"key-file"` // 私钥文件路径
|
||||
CertFile string `yaml:"cert-file"` // 证书文件路径
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package ctx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/logger"
|
||||
"mayfly-go/base/utils"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LogInfo struct {
|
||||
LogResp bool // 是否记录返回结果
|
||||
Description string // 请求描述
|
||||
}
|
||||
|
||||
func NewLogInfo(description string) *LogInfo {
|
||||
return &LogInfo{Description: description, LogResp: false}
|
||||
}
|
||||
|
||||
func (i *LogInfo) WithLogResp(logResp bool) *LogInfo {
|
||||
i.LogResp = logResp
|
||||
return i
|
||||
}
|
||||
|
||||
func LogHandler(rc *ReqCtx) error {
|
||||
li := rc.LogInfo
|
||||
if li == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
lfs := logrus.Fields{}
|
||||
if la := rc.LoginAccount; la != nil {
|
||||
lfs["uid"] = la.Id
|
||||
lfs["uname"] = la.Username
|
||||
}
|
||||
|
||||
req := rc.GinCtx.Request
|
||||
lfs[req.Method] = req.URL.Path
|
||||
|
||||
if err := rc.Err; err != nil {
|
||||
logger.Log.WithFields(lfs).Error(getErrMsg(rc, err))
|
||||
return nil
|
||||
}
|
||||
logger.Log.WithFields(lfs).Info(getLogMsg(rc))
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogMsg(rc *ReqCtx) string {
|
||||
msg := rc.LogInfo.Description + fmt.Sprintf(" ->%dms", rc.timed)
|
||||
if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
|
||||
rb, _ := json.Marshal(rc.ReqParam)
|
||||
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
|
||||
}
|
||||
|
||||
// 返回结果不为空,则记录返回结果
|
||||
if rc.LogInfo.LogResp && !utils.IsBlank(reflect.ValueOf(rc.ResData)) {
|
||||
respB, _ := json.Marshal(rc.ResData)
|
||||
msg = msg + fmt.Sprintf("\n<-- %s", string(respB))
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
func getErrMsg(rc *ReqCtx, err interface{}) string {
|
||||
msg := rc.LogInfo.Description
|
||||
if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
|
||||
rb, _ := json.Marshal(rc.ReqParam)
|
||||
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
|
||||
}
|
||||
|
||||
var errMsg string
|
||||
switch t := err.(type) {
|
||||
case *biz.BizError:
|
||||
errMsg = fmt.Sprintf("\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())
|
||||
case error:
|
||||
errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t.Error(), string(debug.Stack()))
|
||||
case string:
|
||||
errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t, string(debug.Stack()))
|
||||
}
|
||||
return (msg + errMsg)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package global
|
||||
|
||||
import (
|
||||
"github.com/go-redis/redis"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
Log *logrus.Logger // 日志
|
||||
Db *gorm.DB // gorm
|
||||
RedisCli *redis.Client // redis
|
||||
)
|
||||
@@ -1,15 +0,0 @@
|
||||
package model
|
||||
|
||||
type AppContext struct {
|
||||
}
|
||||
|
||||
type LoginAccount struct {
|
||||
Id uint64
|
||||
Username string
|
||||
}
|
||||
|
||||
type Permission struct {
|
||||
CheckToken bool // 是否检查token
|
||||
Code string // 权限码
|
||||
Name string // 描述
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package model
|
||||
|
||||
// 分页参数
|
||||
type PageParam struct {
|
||||
PageNum int `json:"pageNum"`
|
||||
PageSize int `json:"pageSize"`
|
||||
}
|
||||
|
||||
// 分页结果
|
||||
type PageResult struct {
|
||||
Total int64 `json:"total"`
|
||||
List interface{} `json:"list"`
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package rediscli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
var cli *redis.Client
|
||||
|
||||
func SetCli(client *redis.Client) {
|
||||
cli = client
|
||||
}
|
||||
|
||||
func GetCli() *redis.Client {
|
||||
return cli
|
||||
}
|
||||
|
||||
// get key value
|
||||
func Get(key string) string {
|
||||
val, err := cli.Get(key).Result()
|
||||
switch {
|
||||
case err == redis.Nil:
|
||||
fmt.Println("key does not exist")
|
||||
case err != nil:
|
||||
fmt.Println("Get failed", err)
|
||||
case val == "":
|
||||
fmt.Println("value is empty")
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// set key value
|
||||
func Set(key string, val string, expiration time.Duration) {
|
||||
cli.Set(key, val, expiration)
|
||||
}
|
||||
|
||||
func HSet(key string, field string, val interface{}) {
|
||||
cli.HSet(key, field, val)
|
||||
}
|
||||
|
||||
// hget
|
||||
func HGet(key string, field string) string {
|
||||
val, _ := cli.HGet(key, field).Result()
|
||||
return val
|
||||
}
|
||||
|
||||
// hget
|
||||
func HExist(key string, field string) bool {
|
||||
val, _ := cli.HExists(key, field).Result()
|
||||
return val
|
||||
}
|
||||
|
||||
// hgetall
|
||||
func HGetAll(key string) map[string]string {
|
||||
vals, _ := cli.HGetAll(key).Result()
|
||||
return vals
|
||||
}
|
||||
|
||||
// hdel
|
||||
func HDel(key string, fields ...string) int {
|
||||
return int(cli.HDel(key, fields...).Val())
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"mayfly-go/base/global"
|
||||
)
|
||||
|
||||
func PrintBanner() {
|
||||
global.Log.Print(`
|
||||
__ _
|
||||
_ __ ___ __ _ _ _ / _| |_ _ __ _ ___
|
||||
| '_ ' _ \ / _' | | | | |_| | | | |_____ / _' |/ _ \
|
||||
| | | | | | (_| | |_| | _| | |_| |_____| (_| | (_) |
|
||||
|_| |_| |_|\__,_|\__, |_| |_|\__, | \__, |\___/
|
||||
|___/ |___/ |___/
|
||||
`)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"mayfly-go/base/biz"
|
||||
"mayfly-go/base/config"
|
||||
"mayfly-go/base/ctx"
|
||||
"mayfly-go/base/global"
|
||||
"mayfly-go/server/initialize"
|
||||
)
|
||||
|
||||
func RunWebServer() {
|
||||
// 权限处理器
|
||||
ctx.UseBeforeHandlerInterceptor(ctx.PermissionHandler)
|
||||
// 日志处理器
|
||||
ctx.UseAfterHandlerInterceptor(ctx.LogHandler)
|
||||
// 注册路由
|
||||
web := initialize.InitRouter()
|
||||
|
||||
server := config.Conf.Server
|
||||
port := server.GetPort()
|
||||
if app := config.Conf.App; app != nil {
|
||||
global.Log.Infof("%s- Listening and serving HTTP on %s", app.GetAppInfo(), port)
|
||||
} else {
|
||||
global.Log.Infof("Listening and serving HTTP on %s", port)
|
||||
}
|
||||
|
||||
var err error
|
||||
if server.Tls != nil && server.Tls.Enable {
|
||||
err = web.RunTLS(port, server.Tls.CertFile, server.Tls.KeyFile)
|
||||
} else {
|
||||
err = web.Run(port)
|
||||
}
|
||||
biz.ErrIsNilAppendErr(err, "服务启动失败: %s")
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package utils
|
||||
|
||||
// 数组比较
|
||||
// 依次返回,新增值,删除值,以及不变值
|
||||
func ArrayCompare(newArr []interface{}, oldArr []interface{}, compareFun func(interface{}, interface{}) bool) ([]interface{}, []interface{}, []interface{}) {
|
||||
var unmodifierValue []interface{}
|
||||
ni, oi := 0, 0
|
||||
for {
|
||||
if ni >= len(newArr) {
|
||||
break
|
||||
}
|
||||
nv := newArr[ni]
|
||||
for {
|
||||
if oi >= len(oldArr) {
|
||||
oi = 0
|
||||
break
|
||||
}
|
||||
ov := oldArr[oi]
|
||||
if compareFun(nv, ov) {
|
||||
unmodifierValue = append(unmodifierValue, nv)
|
||||
// 新数组移除该位置值
|
||||
newArr = append(newArr[:ni], newArr[ni+1:]...)
|
||||
oldArr = append(oldArr[:oi], oldArr[oi+1:]...)
|
||||
ni = ni - 1
|
||||
oi = oi - 1
|
||||
}
|
||||
oi = oi + 1
|
||||
}
|
||||
ni = ni + 1
|
||||
}
|
||||
|
||||
return newArr, oldArr, unmodifierValue
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// md5
|
||||
func Md5(str string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(str))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func Json2Map(jsonStr string) map[string]interface{} {
|
||||
var res map[string]interface{}
|
||||
if jsonStr == "" {
|
||||
return res
|
||||
}
|
||||
_ = json.Unmarshal([]byte(jsonStr), &res)
|
||||
return res
|
||||
}
|
||||
200
build_release.sh
Executable file
200
build_release.sh
Executable file
@@ -0,0 +1,200 @@
|
||||
#bin/bash
|
||||
|
||||
#----------------------------------------------
|
||||
# 前后端打包编译至指定目录,即快速制作发行版
|
||||
#----------------------------------------------
|
||||
|
||||
project_path=`pwd`
|
||||
# 构建后的二进制执行文件名
|
||||
exec_file_name="mayfly-go"
|
||||
# web项目目录
|
||||
web_folder="${project_path}/mayfly_go_web"
|
||||
# server目录
|
||||
server_folder="${project_path}/server"
|
||||
|
||||
function echo_red() {
|
||||
echo -e "\033[1;31m$1\033[0m"
|
||||
}
|
||||
|
||||
function echo_green() {
|
||||
echo -e "\033[1;32m$1\033[0m"
|
||||
}
|
||||
|
||||
function echo_yellow() {
|
||||
echo -e "\033[1;33m$1\033[0m"
|
||||
}
|
||||
|
||||
function buildWeb() {
|
||||
cd ${web_folder}
|
||||
copy2Server=$1
|
||||
|
||||
echo_yellow "-------------------打包前端开始-------------------"
|
||||
yarn run build
|
||||
if [ "${copy2Server}" == "2" ] ; then
|
||||
echo_green '将打包后的静态文件拷贝至server/static/static'
|
||||
rm -rf ${server_folder}/static/static && mkdir -p ${server_folder}/static/static && cp -r ${web_folder}/dist/* ${server_folder}/static/static
|
||||
fi
|
||||
echo_yellow ">>>>>>>>>>>>>>>>>>>打包前端结束<<<<<<<<<<<<<<<<<<<<\n"
|
||||
}
|
||||
|
||||
function build() {
|
||||
cd ${project_path}
|
||||
|
||||
# 打包产物的输出目录
|
||||
toFolder=$1
|
||||
os=$2
|
||||
arch=$3
|
||||
copyDocScript=$4
|
||||
|
||||
echo_yellow "-------------------${os}-${arch}打包构建开始-------------------"
|
||||
|
||||
cd ${server_folder}
|
||||
echo_green "打包构建可执行文件..."
|
||||
|
||||
execFileName=${exec_file_name}
|
||||
# 如果是windows系统,可执行文件需要添加.exe结尾
|
||||
if [ "${os}" == "windows" ];then
|
||||
execFileName="${execFileName}.exe"
|
||||
fi
|
||||
CGO_ENABLE=0 GOOS=${os} GOARCH=${arch} go build -o ${execFileName} main.go
|
||||
|
||||
if [ -d ${toFolder} ] ; then
|
||||
echo_green "目标文件夹已存在,清空文件夹"
|
||||
sudo rm -rf ${toFolder}
|
||||
fi
|
||||
echo_green "创建'${toFolder}'目录"
|
||||
mkdir ${toFolder}
|
||||
|
||||
echo_green "移动二进制文件至'${toFolder}'"
|
||||
mv ${server_folder}/${execFileName} ${toFolder}
|
||||
|
||||
# if [ "${copy2Server}" == "1" ] ; then
|
||||
# echo_green "拷贝前端静态页面至'${toFolder}/static'"
|
||||
# mkdir -p ${toFolder}/static && cp -r ${web_folder}/dist/* ${toFolder}/static
|
||||
# fi
|
||||
|
||||
if [ "${copyDocScript}" == "1" ] ; then
|
||||
echo_green "拷贝脚本等资源文件[config.yml、mayfly-go.sql、readme.txt、startup.sh、shutdown.sh]"
|
||||
cp ${server_folder}/config.yml ${toFolder}
|
||||
cp ${server_folder}/mayfly-go.sql ${toFolder}
|
||||
cp ${server_folder}/readme.txt ${toFolder}
|
||||
cp ${server_folder}/startup.sh ${toFolder}
|
||||
cp ${server_folder}/shutdown.sh ${toFolder}
|
||||
fi
|
||||
|
||||
echo_yellow ">>>>>>>>>>>>>>>>>>>${os}-${arch}打包构建完成<<<<<<<<<<<<<<<<<<<<\n"
|
||||
}
|
||||
|
||||
function buildLinuxAmd64() {
|
||||
build "$1/mayfly-go-linux-amd64" "linux" "amd64" $2
|
||||
}
|
||||
|
||||
function buildLinuxArm64() {
|
||||
build "$1/mayfly-go-linux-arm64" "linux" "arm64" $2
|
||||
}
|
||||
|
||||
function buildWindows() {
|
||||
build "$1/mayfly-go-windows" "windows" "amd64" $2
|
||||
}
|
||||
|
||||
function buildMac() {
|
||||
build "$1/mayfly-go-mac" "darwin" "amd64" $2
|
||||
}
|
||||
|
||||
function buildDocker() {
|
||||
echo_yellow "-------------------构建docker镜像开始-------------------"
|
||||
imageVersion=$1
|
||||
cd ${server_folder}
|
||||
imageName="mayflygo/mayfly-go:${imageVersion}"
|
||||
docker build -t "${imageName}" .
|
||||
echo_green "docker镜像构建完成->[${imageName}]"
|
||||
echo_yellow "-------------------构建docker镜像结束-------------------"
|
||||
}
|
||||
|
||||
function buildxDocker() {
|
||||
echo_yellow "-------------------docker buildx构建镜像开始-------------------"
|
||||
imageVersion=$1
|
||||
cd ${server_folder}
|
||||
imageName="ccr.ccs.tencentyun.com/mayfly/mayfly-go:${imageVersion}"
|
||||
docker buildx build --push --platform linux/amd64,linux/arm64 -t "${imageName}" .
|
||||
echo_green "docker多版本镜像构建完成->[${imageName}]"
|
||||
echo_yellow "-------------------docker buildx构建镜像结束-------------------"
|
||||
}
|
||||
|
||||
function runBuild() {
|
||||
read -p "请选择构建版本[0|其他->除docker镜像外其他 1->linux-amd64 2->linux-arm64 3->windows 4->mac 5->docker 6->docker buildx]: " buildType
|
||||
|
||||
toPath="."
|
||||
imageVersion="latest"
|
||||
copyDocScript="1"
|
||||
|
||||
if [[ "${buildType}" != "5" ]] && [[ "${buildType}" != "6" ]] ; then
|
||||
# 构建结果的目的路径
|
||||
read -p "请输入构建产物输出目录[默认当前路径]: " toPath
|
||||
if [ ! -d ${toPath} ] ; then
|
||||
echo_red "构建产物输出目录不存在!"
|
||||
exit;
|
||||
fi
|
||||
if [ "${toPath}" == "" ] ; then
|
||||
toPath="."
|
||||
fi
|
||||
|
||||
read -p "是否拷贝文档&脚本[0->否 1->是][默认是]: " copyDocScript
|
||||
if [ "${copyDocScript}" == "" ] ; then
|
||||
copyDocScript="1"
|
||||
fi
|
||||
|
||||
# 进入目标路径,并赋值全路径
|
||||
cd ${toPath}
|
||||
toPath=`pwd`
|
||||
fi
|
||||
|
||||
if [[ "${buildType}" == "5" ]] || [[ "${buildType}" == "6" ]] ; then
|
||||
read -p "请输入docker镜像版本号[默认latest]: " imageVersion
|
||||
|
||||
if [ "${imageVersion}" == "" ] ; then
|
||||
imageVersion="latest"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# read -p "是否构建前端[0|其他->否 1->是 2->构建并拷贝至server/static/static]: " runBuildWeb
|
||||
runBuildWeb="2"
|
||||
# 编译web前端
|
||||
buildWeb ${runBuildWeb}
|
||||
|
||||
case ${buildType} in
|
||||
"1")
|
||||
buildLinuxAmd64 ${toPath} ${copyDocScript}
|
||||
;;
|
||||
"2")
|
||||
buildLinuxArm64 ${toPath} ${copyDocScript}
|
||||
;;
|
||||
"3")
|
||||
buildWindows ${toPath} ${copyDocScript}
|
||||
;;
|
||||
"4")
|
||||
buildMac ${toPath} ${copyDocScript}
|
||||
;;
|
||||
"5")
|
||||
buildDocker ${imageVersion}
|
||||
;;
|
||||
"6")
|
||||
buildxDocker ${imageVersion}
|
||||
;;
|
||||
*)
|
||||
buildLinuxAmd64 ${toPath} ${copyDocScript}
|
||||
buildLinuxArm64 ${toPath} ${copyDocScript}
|
||||
buildWindows ${toPath} ${copyDocScript}
|
||||
buildMac ${toPath} ${copyDocScript}
|
||||
;;
|
||||
esac
|
||||
|
||||
echo_green "删除['${server_folder}/static/static']下静态资源文件."
|
||||
# 删除静态资源文件,保留一个favicon.ico,否则后端启动会报错
|
||||
rm -rf ${server_folder}/static/static/assets
|
||||
rm -rf ${server_folder}/static/static/config.js
|
||||
rm -rf ${server_folder}/static/static/index.html
|
||||
}
|
||||
|
||||
runBuild
|
||||
55
go.mod
55
go.mod
@@ -1,55 +0,0 @@
|
||||
module mayfly-go
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // jwt
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/mojocn/base64Captcha v1.3.5 // 验证码
|
||||
github.com/pkg/sftp v1.13.4
|
||||
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
go.mongodb.org/mongo-driver v1.9.1 // mongo
|
||||
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58 // ssh
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
// gorm
|
||||
gorm.io/driver/mysql v1.3.2
|
||||
gorm.io/gorm v1.23.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.10.1 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/go-stack/stack v1.8.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.13.6 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.18.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.0.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.2 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
220
go.sum
220
go.sum
@@ -1,220 +0,0 @@
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
|
||||
github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
|
||||
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg=
|
||||
github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58 h1:L8CkJyVoa0/NslN3RUMLgasK5+KatNvyRGQ9QyCYAfc=
|
||||
golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 h1:TcHcE0vrmgzNH1v3ppjcMGbhG5+9fMuvOmUYwNEF4q4=
|
||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
|
||||
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.2 h1:xmq9QRMWL8HTJyhAUBXy8FqIIQCYESeKfJL4DoGKiWQ=
|
||||
gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
@@ -2,4 +2,4 @@
|
||||
ENV = 'production'
|
||||
|
||||
# 线上环境接口地址
|
||||
VITE_API_URL = 'http://api.mayflygo.1yue.net/api'
|
||||
VITE_API_URL = '/api'
|
||||
@@ -11,21 +11,33 @@ module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: ['plugin:vue/essential'],
|
||||
// plugins: ['vue', '@typescript-eslint'],
|
||||
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
|
||||
plugins: ['vue', '@typescript-eslint'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.vue'],
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
// http://eslint.cn/docs/rules/
|
||||
// https://eslint.vuejs.org/rules/
|
||||
'@type-eslint/ban-ts-ignore': 'off',
|
||||
'@type-eslint/explicit-function-return-type': 'off',
|
||||
'@type-eslint/no-explicit-any': 'off',
|
||||
'@type-eslint/no-var-requires': 'off',
|
||||
'@type-eslint/no-empty-function': 'off',
|
||||
'@type-eslint/no-use-before-define': 'off',
|
||||
'@type-eslint/ban-ts-comment': 'off',
|
||||
'@type-eslint/ban-types': 'off',
|
||||
'@type-eslint/no-non-null-assertion': 'off',
|
||||
'@type-eslint/explicit-module-boundary-types': 'off',
|
||||
// https://typescript-eslint.io/rules/no-unused-vars/
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [2],
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
@@ -43,6 +55,12 @@ module.exports = {
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/comment-directive': 'off',
|
||||
'vue/no-parsing-error': 'off',
|
||||
'vue/no-deprecated-v-on-native-modifier': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'no-useless-escape': 'off',
|
||||
'no-sparse-arrays': 'off',
|
||||
'no-prototype-builtins': 'off',
|
||||
'no-constant-condition': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'no-restricted-globals': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
@@ -51,5 +69,8 @@ module.exports = {
|
||||
'no-multiple-template-root': 'off',
|
||||
'no-unused-vars': 'error',
|
||||
'no-v-model-argument': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
'no-console': 'error',
|
||||
'no-redeclare': 'off',
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="zh_CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
@@ -18,8 +18,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="text/javascript" src="./config.js"></script>
|
||||
<script type="application/javascript" src="./config.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<!-- <script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=wsijQt8sLXrCW71YesmispvYHitfG9gv&s=1"></script> -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
5164
mayfly_go_web/package-lock.json
generated
Normal file
5164
mayfly_go_web/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,29 +4,34 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"build-preview": "npm run build && npm run preview",
|
||||
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.26.1",
|
||||
"codemirror": "^5.65.2",
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"asciinema-player": "^3.3.0",
|
||||
"axios": "^1.4.0",
|
||||
"countup.js": "^2.0.7",
|
||||
"cropperjs": "^1.5.11",
|
||||
"echarts": "^5.3.2",
|
||||
"element-plus": "^2.1.11",
|
||||
"@element-plus/icons-vue": "^1.1.3",
|
||||
"jsonlint": "^1.6.3",
|
||||
"echarts": "^5.4.0",
|
||||
"element-plus": "^2.3.5",
|
||||
"jsencrypt": "^3.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"monaco-editor": "^0.38.0",
|
||||
"monaco-sql-languages": "^0.11.0",
|
||||
"monaco-themes": "^0.4.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"screenfull": "^5.1.0",
|
||||
"pinia": "^2.1.3",
|
||||
"screenfull": "^6.0.2",
|
||||
"sortablejs": "^1.13.0",
|
||||
"sql-formatter": "^12.1.2",
|
||||
"vue": "^3.3.4",
|
||||
"vue-clipboard3": "^1.0.1",
|
||||
"sql-formatter": "^4.0.2",
|
||||
"vue": "^3.2.30",
|
||||
"vue-router": "^4.0.12",
|
||||
"vuex": "^4.0.2",
|
||||
"xterm": "^4.18.0",
|
||||
"xterm-addon-fit": "^0.5.0"
|
||||
"vue-router": "^4.2.2",
|
||||
"xterm": "^5.2.1",
|
||||
"xterm-addon-fit": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.178",
|
||||
@@ -35,17 +40,17 @@
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||
"@typescript-eslint/parser": "^4.23.0",
|
||||
"@vitejs/plugin-vue": "^1.2.2",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/compiler-sfc": "^3.0.11",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^8.5.0",
|
||||
"eslint": "^8.35.0",
|
||||
"eslint-plugin-vue": "^8.2.0",
|
||||
"prettier": "^2.3.0",
|
||||
"sass": "^1.45.1",
|
||||
"sass-loader": "^12.4.0",
|
||||
"typescript": "^4.2.4",
|
||||
"vite": "^2.8.6",
|
||||
"vue-eslint-parser": "^8.0.1"
|
||||
"sass": "^1.62.0",
|
||||
"sass-loader": "^13.2.0",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^4.3.9",
|
||||
"vue-eslint-parser": "^9.1.1"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
||||
1
mayfly_go_web/plugins.d.ts
vendored
1
mayfly_go_web/plugins.d.ts
vendored
@@ -1 +0,0 @@
|
||||
declare module 'vue-grid-layout';
|
||||
@@ -1,4 +1,24 @@
|
||||
window.globalConfig = {
|
||||
"BaseApiUrl": "http://localhost:8888",
|
||||
"BaseWsUrl": "ws://localhost:8888"
|
||||
}
|
||||
// 默认为空,以访问根目录为api请求地址。若前后端分离部署可单独配置该后端api请求地址
|
||||
"BaseApiUrl": "",
|
||||
"BaseWsUrl": ""
|
||||
}
|
||||
|
||||
// index.html添加百秒级时间戳,防止被浏览器缓存
|
||||
!function () {
|
||||
let t = "t=" + new Date().getTime().toString().substring(0, 8)
|
||||
let search = location.search;
|
||||
let m = search && search.match(/t=\d*/g)
|
||||
|
||||
if (m[0]) {
|
||||
if (m[0] !== t) {
|
||||
location.search = search.replace(m[0], t)
|
||||
}
|
||||
} else {
|
||||
if (search.indexOf('?') > -1) {
|
||||
location.search = search + '&' + t
|
||||
} else {
|
||||
location.search = t
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -1,70 +1,69 @@
|
||||
<template>
|
||||
<router-view v-show="getThemeConfig.lockScreenTime !== 0" />
|
||||
<LockScreen v-if="getThemeConfig.isLockScreen" />
|
||||
<Setings ref="setingsRef" v-show="getThemeConfig.lockScreenTime !== 0" />
|
||||
<router-view v-show="themeConfig.lockScreenTime !== 0" />
|
||||
<LockScreen v-if="themeConfig.isLockScreen" />
|
||||
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime !== 0" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ref, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, nextTick, defineComponent, watch } from 'vue';
|
||||
<script setup lang="ts" name="app">
|
||||
import { ref, onBeforeMount, onMounted, onUnmounted, nextTick, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import { getLocal } from '@/common/utils/storage.ts';
|
||||
// import { useTagsViewRoutes } from '@/store/tagsViewRoutes';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { getLocal } from '@/common/utils/storage';
|
||||
import LockScreen from '@/views/layout/lockScreen/index.vue';
|
||||
import Setings from '@/views/layout/navBars/breadcrumb/setings.vue';
|
||||
export default defineComponent({
|
||||
name: 'app',
|
||||
components: { LockScreen, Setings },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const setingsRef = ref();
|
||||
const route = useRoute();
|
||||
const store = useStore();
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 布局配置弹窗打开
|
||||
const openSetingsDrawer = () => {
|
||||
setingsRef.value.openDrawer();
|
||||
};
|
||||
// 设置初始化,防止刷新时恢复默认
|
||||
onBeforeMount(() => {
|
||||
// 设置批量第三方 icon 图标
|
||||
// setIntroduction.cssCdn();
|
||||
// // 设置批量第三方 js
|
||||
// setIntroduction.jsCdn();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 监听布局配置弹窗点击打开
|
||||
proxy.mittBus.on('openSetingsDrawer', () => {
|
||||
openSetingsDrawer();
|
||||
});
|
||||
// 获取缓存中的布局配置
|
||||
if (getLocal('themeConfig')) {
|
||||
store.dispatch('themeConfig/setThemeConfig', getLocal('themeConfig'));
|
||||
document.documentElement.style.cssText = getLocal('themeConfigStyle');
|
||||
}
|
||||
});
|
||||
});
|
||||
// 页面销毁时,关闭监听布局配置
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('openSetingsDrawer', () => {});
|
||||
});
|
||||
// 监听路由的变化,设置网站标题
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
document.title = `${route.meta.title} - ${getThemeConfig.value.globalTitle}` || getThemeConfig.value.globalTitle;
|
||||
});
|
||||
}
|
||||
);
|
||||
return {
|
||||
setingsRef,
|
||||
getThemeConfig,
|
||||
};
|
||||
},
|
||||
import Watermark from '@/common/utils/wartermark';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const setingsRef = ref();
|
||||
const route = useRoute();
|
||||
|
||||
const themeConfigStores = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(themeConfigStores);
|
||||
|
||||
// 布局配置弹窗打开
|
||||
const openSetingsDrawer = () => {
|
||||
setingsRef.value.openDrawer();
|
||||
};
|
||||
|
||||
// 设置初始化,防止刷新时恢复默认
|
||||
onBeforeMount(() => {
|
||||
// 设置批量第三方 icon 图标
|
||||
// setIntroduction.cssCdn();
|
||||
// // 设置批量第三方 js
|
||||
// setIntroduction.jsCdn();
|
||||
});
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
// 监听布局配置弹窗点击打开
|
||||
mittBus.on('openSetingsDrawer', () => {
|
||||
openSetingsDrawer();
|
||||
});
|
||||
// 获取缓存中的布局配置
|
||||
if (getLocal('themeConfig')) {
|
||||
themeConfigStores.setThemeConfig({ themeConfig: getLocal('themeConfig') })
|
||||
document.documentElement.style.cssText = getLocal('themeConfigStyle');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面销毁时,关闭监听布局配置
|
||||
onUnmounted(() => {
|
||||
mittBus.off('openSetingsDrawer', () => { });
|
||||
});
|
||||
|
||||
// 监听路由的变化,设置网站标题
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
// 路由变化更新水印
|
||||
Watermark.use();
|
||||
document.title = `${route.meta.title} - ${themeConfig.value.globalTitle}` || themeConfig.value.globalTitle;
|
||||
});
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
1
mayfly_go_web/src/assets/iconfont/iconfont.js
Normal file
1
mayfly_go_web/src/assets/iconfont/iconfont.js
Normal file
File diff suppressed because one or more lines are too long
51
mayfly_go_web/src/assets/iconfont/iconfont.json
Normal file
51
mayfly_go_web/src/assets/iconfont/iconfont.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"id": "3953964",
|
||||
"name": "mayfly-go",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "23957582",
|
||||
"name": "MongoDB",
|
||||
"font_class": "mongo",
|
||||
"unicode": "e646",
|
||||
"unicode_decimal": 58950
|
||||
},
|
||||
{
|
||||
"icon_id": "4969649",
|
||||
"name": "Redis",
|
||||
"font_class": "op-redis",
|
||||
"unicode": "e728",
|
||||
"unicode_decimal": 59176
|
||||
},
|
||||
{
|
||||
"icon_id": "22442993",
|
||||
"name": "PostgreSQL",
|
||||
"font_class": "op-postgres",
|
||||
"unicode": "e8b7",
|
||||
"unicode_decimal": 59575
|
||||
},
|
||||
{
|
||||
"icon_id": "10055634",
|
||||
"name": "云数据库MongoDB",
|
||||
"font_class": "op-mongo",
|
||||
"unicode": "e7d7",
|
||||
"unicode_decimal": 59351
|
||||
},
|
||||
{
|
||||
"icon_id": "10055642",
|
||||
"name": "云数据库 RDS MySQL",
|
||||
"font_class": "op-mysql",
|
||||
"unicode": "e7d8",
|
||||
"unicode_decimal": 59352
|
||||
},
|
||||
{
|
||||
"icon_id": "3876165",
|
||||
"name": "redis",
|
||||
"font_class": "redis",
|
||||
"unicode": "e619",
|
||||
"unicode_decimal": 58905
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,24 +19,6 @@ class Api {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置rl
|
||||
* @param {String} uri 请求url
|
||||
*/
|
||||
setUrl(url: string) {
|
||||
this.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* url的请求方法
|
||||
* @param {String} method 请求方法
|
||||
*/
|
||||
setMethod(method: string) {
|
||||
this.method = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取权限的完整url
|
||||
*/
|
||||
@@ -46,7 +28,7 @@ class Api {
|
||||
|
||||
/**
|
||||
* 操作该权限,即请求对应的url
|
||||
* @param {Object} param 请求该权限的参数
|
||||
* @param {Object} param 请求该api的参数
|
||||
*/
|
||||
request(param: any = null, options: any = null): Promise<any> {
|
||||
return request.send(this, param, options);
|
||||
@@ -54,7 +36,8 @@ class Api {
|
||||
|
||||
/**
|
||||
* 操作该权限,即请求对应的url
|
||||
* @param {Object} param 请求该权限的参数
|
||||
* @param {Object} param 请求该api的参数
|
||||
* @param headers headers
|
||||
*/
|
||||
requestWithHeaders(param: any, headers: any): Promise<any> {
|
||||
return request.sendWithHeaders(this, param, headers);
|
||||
@@ -68,9 +51,41 @@ class Api {
|
||||
* @param url url
|
||||
* @param method 请求方法(get,post,put,delete...)
|
||||
*/
|
||||
static create(url: string, method: string) {
|
||||
static create(url: string, method: string) :Api {
|
||||
return new Api(url, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建get api
|
||||
* @param url url
|
||||
*/
|
||||
static newGet(url: string): Api {
|
||||
return Api.create(url, 'get');
|
||||
}
|
||||
|
||||
/**
|
||||
* new post api
|
||||
* @param url url
|
||||
*/
|
||||
static newPost(url: string): Api {
|
||||
return Api.create(url, 'post');
|
||||
}
|
||||
|
||||
/**
|
||||
* new put api
|
||||
* @param url url
|
||||
*/
|
||||
static newPut(url: string): Api {
|
||||
return Api.create(url, 'put');
|
||||
}
|
||||
|
||||
/**
|
||||
* new delete api
|
||||
* @param url url
|
||||
*/
|
||||
static newDelete(url: string): Api {
|
||||
return Api.create(url, 'delete');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
function getBaseApiUrl() {
|
||||
let path = window.location.pathname;
|
||||
if (path == '/') {
|
||||
return window.location.host;
|
||||
}
|
||||
return window.location.host + path;
|
||||
}
|
||||
|
||||
const config = {
|
||||
baseApiUrl: `${(window as any).globalConfig.BaseApiUrl}/api`,
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl}/api`
|
||||
baseApiUrl: `${(window as any).globalConfig.BaseApiUrl || location.protocol + '//' + getBaseApiUrl()}/api`,
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||
|
||||
// 系统版本
|
||||
version: 'v1.4.3'
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -1,4 +1,42 @@
|
||||
import * as echarts from 'echarts'
|
||||
// import * as echarts from 'echarts'
|
||||
|
||||
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
|
||||
import * as echarts from "echarts/core";
|
||||
|
||||
/** 图表后缀都为 Chart */
|
||||
import { PieChart } from "echarts/charts";
|
||||
|
||||
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
LegendComponent,
|
||||
} from "echarts/components";
|
||||
|
||||
// 标签自动布局,全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from "echarts/features";
|
||||
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
|
||||
// 注册必须的组件
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
LegendComponent,
|
||||
// BarChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer,
|
||||
// LineChart,
|
||||
PieChart,
|
||||
]);
|
||||
|
||||
export default function(dom: any, theme: any = null, option: any) {
|
||||
let chart = echarts.init(dom, theme);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import request from './request'
|
||||
import Api from './Api'
|
||||
|
||||
export default {
|
||||
login: (param: any) => request.request('POST', '/sys/accounts/login', param, null),
|
||||
captcha: () => request.request('GET', '/sys/captcha', null, null),
|
||||
logout: (param: any) => request.request('POST', '/sys/accounts/logout/{token}', param, null),
|
||||
getMenuRoute: (param: any) => request.request('Get', '/sys/resources/account', param, null)
|
||||
login: Api.newPost("/sys/accounts/login"),
|
||||
changePwd: Api.newPost("/sys/accounts/change-pwd"),
|
||||
getPublicKey: Api.newGet("/common/public-key"),
|
||||
getConfigValue: Api.newGet("/sys/configs/value"),
|
||||
captcha: Api.newGet("/sys/captcha"),
|
||||
logout: Api.newPost("/sys/accounts/logout/{token}"),
|
||||
getPermissions: Api.newGet("/sys/accounts/permissions")
|
||||
}
|
||||
@@ -22,7 +22,8 @@ export interface Result {
|
||||
data?: any;
|
||||
}
|
||||
|
||||
const baseUrl: string = config.baseApiUrl as string
|
||||
const baseUrl: string = config.baseApiUrl
|
||||
const baseWsUrl: string = config.baseWsUrl
|
||||
|
||||
/**
|
||||
* 通知错误消息
|
||||
@@ -115,9 +116,8 @@ function request(method: string, url: string, params: any = null, headers: any =
|
||||
query.headers = headers
|
||||
}
|
||||
|
||||
const lowMethod = method.toLowerCase();
|
||||
// post和put使用json格式传参
|
||||
if (lowMethod === 'post' || lowMethod === 'put') {
|
||||
if (method === 'post' || method === 'put') {
|
||||
query.data = params;
|
||||
} else {
|
||||
query.params = params;
|
||||
@@ -155,6 +155,7 @@ function getApiUrl(url: string) {
|
||||
return baseUrl + url + '?token=' + getSession('token');
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
request,
|
||||
send,
|
||||
|
||||
36
mayfly_go_web/src/common/rsa.ts
Normal file
36
mayfly_go_web/src/common/rsa.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import openApi from './openApi';
|
||||
import JSEncrypt from 'jsencrypt'
|
||||
import { notBlank } from './assert';
|
||||
|
||||
var encryptor: any = null
|
||||
|
||||
export async function getRsaPublicKey() {
|
||||
let publicKey = sessionStorage.getItem('RsaPublicKey')
|
||||
if (publicKey) {
|
||||
return publicKey
|
||||
}
|
||||
publicKey = await openApi.getPublicKey.request() as string
|
||||
sessionStorage.setItem('RsaPublicKey', publicKey)
|
||||
return publicKey
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密指定值
|
||||
*
|
||||
* @param value value
|
||||
* @returns 加密后的值
|
||||
*/
|
||||
export async function RsaEncrypt(value: any) {
|
||||
// 不存在则返回空值
|
||||
if (!value) {
|
||||
return ""
|
||||
}
|
||||
if (encryptor != null) {
|
||||
return encryptor.encrypt(value)
|
||||
}
|
||||
encryptor = new JSEncrypt()
|
||||
const publicKey = await getRsaPublicKey() as string;
|
||||
notBlank(publicKey, "获取公钥失败")
|
||||
encryptor.setPublicKey(publicKey)//设置公钥
|
||||
return encryptor.encrypt(value)
|
||||
}
|
||||
48
mayfly_go_web/src/common/sysconfig.ts
Normal file
48
mayfly_go_web/src/common/sysconfig.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import openApi from './openApi';
|
||||
|
||||
// 登录是否使用验证码配置key
|
||||
const UseLoginCaptchaConfigKey = "UseLoginCaptcha"
|
||||
const UseWartermarkConfigKey = "UseWartermark"
|
||||
|
||||
/**
|
||||
* 获取系统配置值
|
||||
*
|
||||
* @param key 配置key
|
||||
* @returns 配置值
|
||||
*/
|
||||
export async function getConfigValue(key: string) : Promise<string> {
|
||||
return await openApi.getConfigValue.request({key}) as string
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取bool类型系统配置值
|
||||
*
|
||||
* @param key 配置key
|
||||
* @param defaultValue 默认值
|
||||
* @returns 是否为ture,1: true;其他: false
|
||||
*/
|
||||
export async function getBoolConfigValue(key :string, defaultValue :boolean) : Promise<boolean> {
|
||||
const value = await getConfigValue(key)
|
||||
if (!value) {
|
||||
return defaultValue;
|
||||
}
|
||||
return value == "1";
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否使用登录验证码
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export async function useLoginCaptcha() : Promise<boolean> {
|
||||
return await getBoolConfigValue(UseLoginCaptchaConfigKey, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用水印
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
export async function useWartermark() : Promise<boolean> {
|
||||
return await getBoolConfigValue(UseWartermarkConfigKey, true)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { store } from '@/store/index.ts';
|
||||
import { judementSameArr } from '@/common/utils/arrayOperation.ts';
|
||||
|
||||
// 单个权限验证
|
||||
export function auth(value: string) {
|
||||
return store.state.userInfos.userInfos.permissions.some((v: any) => v === value);
|
||||
}
|
||||
|
||||
// 多个权限验证,满足一个则为 true
|
||||
export function auths(value: Array<string>) {
|
||||
let flag = false;
|
||||
store.state.userInfos.userInfos.permissions.map((val: any) => {
|
||||
value.map((v: any) => {
|
||||
if (val === v) flag = true;
|
||||
});
|
||||
});
|
||||
return flag;
|
||||
}
|
||||
|
||||
// 多个权限验证,全部满足则为 true
|
||||
export function authAll(value: Array<string>) {
|
||||
return judementSameArr(value, store.state.userInfos.userInfos.permissions);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
export function dateFormat(fmt: string, date: Date) {
|
||||
export function dateFormat2(fmt: string, date: Date) {
|
||||
let ret;
|
||||
const opt = {
|
||||
"y+": date.getFullYear().toString(), // 年
|
||||
@@ -19,5 +19,9 @@ export function dateFormat(fmt: string, date: Date) {
|
||||
}
|
||||
|
||||
export function dateStrFormat(fmt: string, dateStr: string) {
|
||||
return dateFormat(fmt, new Date(dateStr))
|
||||
return dateFormat2(fmt, new Date(dateStr))
|
||||
}
|
||||
|
||||
export function dateFormat(dateStr: string) {
|
||||
return dateFormat2('yyyy-MM-dd HH:mm:ss',new Date(dateStr))
|
||||
}
|
||||
39
mayfly_go_web/src/common/utils/export.ts
Normal file
39
mayfly_go_web/src/common/utils/export.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export function exportCsv(filename: string, columns: string[], datas: []) {
|
||||
// 二维数组
|
||||
const cvsData = [columns];
|
||||
for (let data of datas) {
|
||||
// 数据值组成的一维数组
|
||||
let dataValueArr: any = [];
|
||||
for (let column of columns) {
|
||||
let val: any = data[column];
|
||||
if (typeof val == 'string' && val) {
|
||||
// csv格式如果有逗号,整体用双引号括起来;如果里面还有双引号就替换成两个双引号,这样导出来的格式就不会有问题了
|
||||
if (val.indexOf(',') != -1) {
|
||||
// 如果还有双引号,先将双引号转义,避免两边加了双引号后转义错误
|
||||
if (val.indexOf('"') != -1) {
|
||||
val = val.replace(/\"/g, "\"\"");
|
||||
}
|
||||
// 再将逗号转义
|
||||
val = `"${val}"`;
|
||||
}
|
||||
dataValueArr.push(val);
|
||||
} else {
|
||||
dataValueArr.push(val);
|
||||
}
|
||||
|
||||
}
|
||||
cvsData.push(dataValueArr);
|
||||
}
|
||||
const csvString = cvsData.map((e) => e.join(',')).join('\n');
|
||||
// 导出
|
||||
let link = document.createElement('a');
|
||||
let exportContent = '\uFEFF';
|
||||
let blob = new Blob([exportContent + csvString], {
|
||||
type: 'text/plain;charset=utrf-8',
|
||||
});
|
||||
link.id = 'download-csv';
|
||||
link.setAttribute('href', URL.createObjectURL(blob));
|
||||
link.setAttribute('download', `${filename}.csv`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
}
|
||||
@@ -72,4 +72,124 @@ export function formatJsonString(txt: string, compress: boolean) {
|
||||
indent = 0;
|
||||
notify('', data, isLast, indent, false);
|
||||
return draw.join('');
|
||||
}
|
||||
|
||||
/*
|
||||
* 年(Y) 可用1-4个占位符
|
||||
* 月(m)、日(d)、小时(H)、分(M)、秒(S) 可用1-2个占位符
|
||||
* 星期(W) 可用1-3个占位符
|
||||
* 季度(q为阿拉伯数字,Q为中文数字)可用1或4个占位符
|
||||
*
|
||||
* let date = new Date()
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS") // 2020-02-09 14:04:23
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS Q") // 2020-02-09 14:09:03 一
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS WWW") // 2020-02-09 14:45:12 星期日
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS QQQQ") // 2020-02-09 14:09:36 第一季度
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-02-09 14:46:12 星期日 第一季度
|
||||
*/
|
||||
export function formatDate(date: Date, format: string) {
|
||||
let we = date.getDay(); // 星期
|
||||
let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
|
||||
const opt: any = {
|
||||
'Y+': date.getFullYear().toString(), // 年
|
||||
'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
|
||||
'd+': date.getDate().toString(), // 日
|
||||
'H+': date.getHours().toString(), // 时
|
||||
'M+': date.getMinutes().toString(), // 分
|
||||
'S+': date.getSeconds().toString(), // 秒
|
||||
'q+': qut, // 季度
|
||||
};
|
||||
// 中文数字 (星期)
|
||||
const week: any = {
|
||||
'0': '日',
|
||||
'1': '一',
|
||||
'2': '二',
|
||||
'3': '三',
|
||||
'4': '四',
|
||||
'5': '五',
|
||||
'6': '六',
|
||||
};
|
||||
// 中文数字(季度)
|
||||
const quarter: any = {
|
||||
'1': '一',
|
||||
'2': '二',
|
||||
'3': '三',
|
||||
'4': '四',
|
||||
};
|
||||
if (/(W+)/.test(format))
|
||||
format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
|
||||
if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
|
||||
for (let k in opt) {
|
||||
let r = new RegExp('(' + k + ')').exec(format);
|
||||
// 若输入的长度不为1,则前面补零
|
||||
if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* 10秒: 10 * 1000
|
||||
* 1分: 60 * 1000
|
||||
* 1小时: 60 * 60 * 1000
|
||||
* 24小时:60 * 60 * 24 * 1000
|
||||
* 3天: 60 * 60* 24 * 1000 * 3
|
||||
*
|
||||
* let data = new Date()
|
||||
* formatPast(data) // 刚刚
|
||||
* formatPast(data - 11 * 1000) // 11秒前
|
||||
* formatPast(data - 2 * 60 * 1000) // 2分钟前
|
||||
* formatPast(data - 60 * 60 * 2 * 1000) // 2小时前
|
||||
* formatPast(data - 60 * 60 * 2 * 1000) // 2小时前
|
||||
* formatPast(data - 60 * 60 * 71 * 1000) // 2天前
|
||||
* formatPast("2020-06-01") // 2020-06-01
|
||||
* formatPast("2020-06-01", "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-06-01 08:00:00 星期一 第二季度
|
||||
*/
|
||||
export function formatPast(param: any, format: string = 'YYYY-mm-dd') {
|
||||
// 传入格式处理、存储转换值
|
||||
let t: any, s: any;
|
||||
// 获取js 时间戳
|
||||
let time: any = new Date().getTime();
|
||||
// 是否是对象
|
||||
typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
|
||||
// 当前时间戳 - 传入时间戳
|
||||
time = Number.parseInt(`${time - t}`);
|
||||
if (time < 10000) {
|
||||
// 10秒内
|
||||
return '刚刚';
|
||||
} else if (time < 60000 && time >= 10000) {
|
||||
// 超过10秒少于1分钟内
|
||||
s = Math.floor(time / 1000);
|
||||
return `${s}秒前`;
|
||||
} else if (time < 3600000 && time >= 60000) {
|
||||
// 超过1分钟少于1小时
|
||||
s = Math.floor(time / 60000);
|
||||
return `${s}分钟前`;
|
||||
} else if (time < 86400000 && time >= 3600000) {
|
||||
// 超过1小时少于24小时
|
||||
s = Math.floor(time / 3600000);
|
||||
return `${s}小时前`;
|
||||
} else if (time < 259200000 && time >= 86400000) {
|
||||
// 超过1天少于3天内
|
||||
s = Math.floor(time / 86400000);
|
||||
return `${s}天前`;
|
||||
} else {
|
||||
// 超过3天
|
||||
let date = typeof param === 'string' || 'object' ? new Date(param) : param;
|
||||
return formatDate(date, format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* formatAxis(new Date()) // 上午好
|
||||
*/
|
||||
export function formatAxis(param: any) {
|
||||
let hour: number = new Date(param).getHours();
|
||||
if (hour < 6) return '凌晨好';
|
||||
else if (hour < 9) return '早上好';
|
||||
else if (hour < 12) return '上午好';
|
||||
else if (hour < 14) return '中午好';
|
||||
else if (hour < 17) return '下午好';
|
||||
else if (hour < 19) return '傍晚好';
|
||||
else if (hour < 22) return '晚上好';
|
||||
else return '夜里好';
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* 年(Y) 可用1-4个占位符
|
||||
* 月(m)、日(d)、小时(H)、分(M)、秒(S) 可用1-2个占位符
|
||||
* 星期(W) 可用1-3个占位符
|
||||
* 季度(q为阿拉伯数字,Q为中文数字)可用1或4个占位符
|
||||
*
|
||||
* let date = new Date()
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS") // 2020-02-09 14:04:23
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS Q") // 2020-02-09 14:09:03 一
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS WWW") // 2020-02-09 14:45:12 星期日
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS QQQQ") // 2020-02-09 14:09:36 第一季度
|
||||
* formatDate(date, "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-02-09 14:46:12 星期日 第一季度
|
||||
*/
|
||||
export function formatDate(date: Date, format: string) {
|
||||
let we = date.getDay(); // 星期
|
||||
let qut = Math.floor((date.getMonth() + 3) / 3).toString(); // 季度
|
||||
const opt: any = {
|
||||
'Y+': date.getFullYear().toString(), // 年
|
||||
'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
|
||||
'd+': date.getDate().toString(), // 日
|
||||
'H+': date.getHours().toString(), // 时
|
||||
'M+': date.getMinutes().toString(), // 分
|
||||
'S+': date.getSeconds().toString(), // 秒
|
||||
'q+': qut, // 季度
|
||||
};
|
||||
// 中文数字 (星期)
|
||||
const week: any = {
|
||||
'0': '日',
|
||||
'1': '一',
|
||||
'2': '二',
|
||||
'3': '三',
|
||||
'4': '四',
|
||||
'5': '五',
|
||||
'6': '六',
|
||||
};
|
||||
// 中文数字(季度)
|
||||
const quarter: any = {
|
||||
'1': '一',
|
||||
'2': '二',
|
||||
'3': '三',
|
||||
'4': '四',
|
||||
};
|
||||
if (/(W+)/.test(format))
|
||||
format = format.replace(RegExp.$1, RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]);
|
||||
if (/(Q+)/.test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]);
|
||||
for (let k in opt) {
|
||||
let r = new RegExp('(' + k + ')').exec(format);
|
||||
// 若输入的长度不为1,则前面补零
|
||||
if (r) format = format.replace(r[1], RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0'));
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* 10秒: 10 * 1000
|
||||
* 1分: 60 * 1000
|
||||
* 1小时: 60 * 60 * 1000
|
||||
* 24小时:60 * 60 * 24 * 1000
|
||||
* 3天: 60 * 60* 24 * 1000 * 3
|
||||
*
|
||||
* let data = new Date()
|
||||
* formatPast(data) // 刚刚
|
||||
* formatPast(data - 11 * 1000) // 11秒前
|
||||
* formatPast(data - 2 * 60 * 1000) // 2分钟前
|
||||
* formatPast(data - 60 * 60 * 2 * 1000) // 2小时前
|
||||
* formatPast(data - 60 * 60 * 2 * 1000) // 2小时前
|
||||
* formatPast(data - 60 * 60 * 71 * 1000) // 2天前
|
||||
* formatPast("2020-06-01") // 2020-06-01
|
||||
* formatPast("2020-06-01", "YYYY-mm-dd HH:MM:SS WWW QQQQ") // 2020-06-01 08:00:00 星期一 第二季度
|
||||
*/
|
||||
export function formatPast(param: any, format: string = 'YYYY-mm-dd') {
|
||||
// 传入格式处理、存储转换值
|
||||
let t: any, s: any;
|
||||
// 获取js 时间戳
|
||||
let time: any = new Date().getTime();
|
||||
// 是否是对象
|
||||
typeof param === 'string' || 'object' ? (t = new Date(param).getTime()) : (t = param);
|
||||
// 当前时间戳 - 传入时间戳
|
||||
time = Number.parseInt(`${time - t}`);
|
||||
if (time < 10000) {
|
||||
// 10秒内
|
||||
return '刚刚';
|
||||
} else if (time < 60000 && time >= 10000) {
|
||||
// 超过10秒少于1分钟内
|
||||
s = Math.floor(time / 1000);
|
||||
return `${s}秒前`;
|
||||
} else if (time < 3600000 && time >= 60000) {
|
||||
// 超过1分钟少于1小时
|
||||
s = Math.floor(time / 60000);
|
||||
return `${s}分钟前`;
|
||||
} else if (time < 86400000 && time >= 3600000) {
|
||||
// 超过1小时少于24小时
|
||||
s = Math.floor(time / 3600000);
|
||||
return `${s}小时前`;
|
||||
} else if (time < 259200000 && time >= 86400000) {
|
||||
// 超过1天少于3天内
|
||||
s = Math.floor(time / 86400000);
|
||||
return `${s}天前`;
|
||||
} else {
|
||||
// 超过3天
|
||||
let date = typeof param === 'string' || 'object' ? new Date(param) : param;
|
||||
return formatDate(date, format);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* formatAxis(new Date()) // 上午好
|
||||
*/
|
||||
export function formatAxis(param: any) {
|
||||
let hour: number = new Date(param).getHours();
|
||||
if (hour < 6) return '凌晨好';
|
||||
else if (hour < 9) return '早上好';
|
||||
else if (hour < 12) return '上午好';
|
||||
else if (hour < 14) return '中午好';
|
||||
else if (hour < 17) return '下午好';
|
||||
else if (hour < 19) return '傍晚好';
|
||||
else if (hour < 22) return '晚上好';
|
||||
else return '夜里好';
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { nextTick } from 'vue';
|
||||
import loadingCss from '@/theme/loading.scss';
|
||||
import loadingCss from "@/theme/loading.scss?inline"
|
||||
|
||||
// 定义方法
|
||||
export const NextLoading = {
|
||||
|
||||
8
mayfly_go_web/src/common/utils/mitt.ts
Normal file
8
mayfly_go_web/src/common/utils/mitt.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// https://www.npmjs.com/package/mitt
|
||||
import mitt, { Emitter } from 'mitt';
|
||||
|
||||
// 类型
|
||||
const emitter: Emitter<any> = mitt<any>();
|
||||
|
||||
// 导出
|
||||
export default emitter;
|
||||
@@ -1,42 +1,47 @@
|
||||
// 字体图标 url
|
||||
const cssCdnUrlList: Array<string> = [
|
||||
'//at.alicdn.com/t/font_2298093_ysc3z187xhh.css',
|
||||
'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
|
||||
|
||||
];
|
||||
// 第三方 js url
|
||||
const jsCdnUrlList: Array<string> = [];
|
||||
|
||||
// 动态设置字体图标
|
||||
// 动态批量设置字体图标
|
||||
export function setCssCdn() {
|
||||
if (cssCdnUrlList.length <= 0) return false;
|
||||
cssCdnUrlList.map((v) => {
|
||||
let link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = v;
|
||||
link.crossOrigin = 'anonymous';
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
});
|
||||
if (cssCdnUrlList.length <= 0) return false;
|
||||
cssCdnUrlList.map((v) => {
|
||||
let link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = v;
|
||||
link.crossOrigin = 'anonymous';
|
||||
document.getElementsByTagName('head')[0].appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
// 批量设置第三方js
|
||||
// 动态批量设置第三方js
|
||||
export function setJsCdn() {
|
||||
if (jsCdnUrlList.length <= 0) return false;
|
||||
jsCdnUrlList.map((v) => {
|
||||
let link = document.createElement('script');
|
||||
link.src = v;
|
||||
document.body.appendChild(link);
|
||||
});
|
||||
if (jsCdnUrlList.length <= 0) return false;
|
||||
jsCdnUrlList.map((v) => {
|
||||
let link = document.createElement('script');
|
||||
link.src = v;
|
||||
document.body.appendChild(link);
|
||||
});
|
||||
}
|
||||
|
||||
// 设置执行函数
|
||||
/**
|
||||
* 批量设置字体图标、动态js
|
||||
* @method cssCdn 动态批量设置字体图标
|
||||
* @method jsCdn 动态批量设置第三方js
|
||||
*/
|
||||
const setIntroduction = {
|
||||
cssCdn: () => {
|
||||
setCssCdn();
|
||||
},
|
||||
jsCdn: () => {
|
||||
setJsCdn();
|
||||
},
|
||||
// 设置css
|
||||
cssCdn: () => {
|
||||
setCssCdn();
|
||||
},
|
||||
// 设置js
|
||||
jsCdn: () => {
|
||||
setJsCdn();
|
||||
},
|
||||
};
|
||||
|
||||
// 导出函数方法
|
||||
export default setIntroduction;
|
||||
export default setIntroduction;
|
||||
@@ -35,3 +35,22 @@ export function removeSession(key: string) {
|
||||
export function clearSession() {
|
||||
window.sessionStorage.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getUserInfo4Session() {
|
||||
return getSession("userInfo")
|
||||
}
|
||||
|
||||
export function setUserInfo2Session(userinfo: any) {
|
||||
setSession("userInfo", userinfo)
|
||||
}
|
||||
|
||||
// 获取是否开启水印
|
||||
export function getUseWatermark4Session() {
|
||||
return getSession("useWatermark")
|
||||
}
|
||||
|
||||
export function setUseWatermark2Session(useWatermark: boolean) {
|
||||
setSession("useWatermark", useWatermark)
|
||||
}
|
||||
@@ -1,5 +1,20 @@
|
||||
import { nextTick } from 'vue';
|
||||
import * as svg from '@element-plus/icons-vue';
|
||||
import iconfontJson from '@/assets/iconfont/iconfont.json'
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
|
||||
/**
|
||||
* 导出全局注册 element plus svg 图标
|
||||
* @param app vue 实例
|
||||
* @description 使用:https://element-plus.gitee.io/zh-CN/component/icon.html
|
||||
*/
|
||||
export function registElSvgIcon(app: any) {
|
||||
const icons = svg as any;
|
||||
for (const i in icons) {
|
||||
app.component(`${icons[i].name}`, icons[i]);
|
||||
}
|
||||
app.component('SvgIcon', SvgIcon);
|
||||
}
|
||||
|
||||
// 获取阿里字体图标
|
||||
const getAlicdnIconfont = () => {
|
||||
@@ -9,7 +24,8 @@ const getAlicdnIconfont = () => {
|
||||
let sheetsList = [];
|
||||
let sheetsIconList = [];
|
||||
for (let i = 0; i < styles.length; i++) {
|
||||
if (styles[i].href && styles[i].href.indexOf('at.alicdn.com') > -1) {
|
||||
console.log(styles[i]);
|
||||
if (styles[i].href && styles[i].href.indexOf('iconfont') > -1) {
|
||||
sheetsList.push(styles[i]);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +44,16 @@ const getAlicdnIconfont = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// 获取本地阿里icons
|
||||
const getLocalAliIconfont = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
nextTick(() => {
|
||||
const prefix = iconfontJson.css_prefix_text;
|
||||
resolve(iconfontJson.glyphs.map((x: any) => prefix + x.font_class));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化获取 css 样式,获取 element plus 自带图标
|
||||
const elementPlusIconfont = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -76,9 +102,9 @@ const awesomeIconfont = () => {
|
||||
|
||||
// 定义导出方法集合
|
||||
const initIconfont = {
|
||||
// ali: () => {
|
||||
// return getAlicdnIconfont();
|
||||
// },
|
||||
ali: () => {
|
||||
return getLocalAliIconfont();
|
||||
},
|
||||
ele: () => {
|
||||
return elementPlusIconfont();
|
||||
},
|
||||
@@ -1,21 +1,26 @@
|
||||
import { getUseWatermark4Session, getUserInfo4Session } from '@/common/utils/storage.ts';
|
||||
import { dateFormat2 } from '@/common/utils/date.ts'
|
||||
|
||||
// 页面添加水印效果
|
||||
const setWatermark = (str: any) => {
|
||||
const id = '1.23452384164.123412416';
|
||||
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
|
||||
const can = document.createElement('canvas');
|
||||
can.width = 250;
|
||||
can.height = 180;
|
||||
can.width = 400;
|
||||
can.height = 250;
|
||||
const cans: any = can.getContext('2d');
|
||||
cans.rotate((-20 * Math.PI) / 180);
|
||||
cans.font = '12px Vedana';
|
||||
cans.fillStyle = 'rgba(200, 200, 200, 0.30)';
|
||||
cans.textAlign = 'center';
|
||||
cans.font = '14px Vedana';
|
||||
cans.fillStyle = 'rgba(200, 200, 200, 0.35)';
|
||||
cans.textAlign = 'left';
|
||||
cans.textBaseline = 'Middle';
|
||||
cans.fillText(str, can.width / 10, can.height / 2);
|
||||
// cans.fillText('mayfly go', can.width / 4, can.height )
|
||||
cans.fillText(str, can.width / 8, can.height / 2);
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.id = id;
|
||||
div.style.pointerEvents = 'none';
|
||||
div.style.top = '35px';
|
||||
div.style.top = '30px';
|
||||
div.style.left = '0px';
|
||||
div.style.position = 'fixed';
|
||||
div.style.zIndex = '10000000';
|
||||
@@ -26,16 +31,34 @@ const setWatermark = (str: any) => {
|
||||
return id;
|
||||
};
|
||||
|
||||
function set(str: any) {
|
||||
let id = setWatermark(str);
|
||||
if (document.getElementById(id) === null) id = setWatermark(str);
|
||||
}
|
||||
|
||||
function del() {
|
||||
let id = '1.23452384164.123412416';
|
||||
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
|
||||
}
|
||||
|
||||
const watermark = {
|
||||
use: () => {
|
||||
setTimeout(() => {
|
||||
const userinfo = getUserInfo4Session()
|
||||
if (userinfo && getUseWatermark4Session()) {
|
||||
set(`${userinfo.username} ${dateFormat2('yyyy-MM-dd HH:mm:ss', new Date())}`)
|
||||
} else {
|
||||
del();
|
||||
}
|
||||
}, 1500)
|
||||
},
|
||||
// 设置水印
|
||||
set: (str: any) => {
|
||||
let id = setWatermark(str);
|
||||
if (document.getElementById(id) === null) id = setWatermark(str);
|
||||
set(str)
|
||||
},
|
||||
// 删除水印
|
||||
del: () => {
|
||||
let id = '1.23452384164.123412416';
|
||||
if (document.getElementById(id) !== null) document.body.removeChild(document.getElementById(id) as any);
|
||||
del();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '/@/store/index.ts';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
export default {
|
||||
name: 'auth',
|
||||
props: {
|
||||
@@ -16,10 +16,9 @@ export default {
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
return store.state.userInfos.userInfos.authBtnList.some((v: any) => v === props.value);
|
||||
return useUserInfo().userInfo.authBtnList.some((v: any) => v === props.value);
|
||||
});
|
||||
return {
|
||||
getUserAuthBtnList,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '/@/store/index.ts';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import { judementSameArr } from '/@/utils/arrayOperation.ts';
|
||||
export default {
|
||||
name: 'authAll',
|
||||
@@ -17,10 +17,9 @@ export default {
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
return judementSameArr(props.value, store.state.userInfos.userInfos.authBtnList);
|
||||
return judementSameArr(props.value, useUserInfo().userInfo.authBtnList);
|
||||
});
|
||||
return {
|
||||
getUserAuthBtnList,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '/@/store/index.ts';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
export default {
|
||||
name: 'auths',
|
||||
props: {
|
||||
@@ -16,11 +16,10 @@ export default {
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const store = useStore();
|
||||
// 获取 vuex 中的用户权限
|
||||
const getUserAuthBtnList = computed(() => {
|
||||
let flag = false;
|
||||
store.state.userInfos.userInfos.authBtnList.map((val: any) => {
|
||||
useUserInfo().userInfo.authBtnList.map((val: any) => {
|
||||
props.value.map((v) => {
|
||||
if (val === v) flag = true;
|
||||
});
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
<template>
|
||||
<div class="in-coder-panel">
|
||||
<textarea ref="textarea"></textarea>
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="mode" @change="changeMode">
|
||||
<el-option v-for="mode in modes" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, nextTick, toRefs, reactive, watch, onMounted, defineComponent } from 'vue';
|
||||
// 引入全局实例
|
||||
import _CodeMirror from 'codemirror';
|
||||
|
||||
// 核心样式
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
// 引入主题后还需要在 options 中指定主题才会生效
|
||||
import 'codemirror/theme/cobalt.css';
|
||||
import 'codemirror/addon/selection/active-line.js';
|
||||
// 匹配括号
|
||||
import 'codemirror/addon/edit/matchbrackets.js';
|
||||
import 'codemirror/addon/selection/active-line';
|
||||
import 'codemirror/addon/comment/comment';
|
||||
|
||||
// 需要引入具体的语法高亮库才会有对应的语法高亮效果
|
||||
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
|
||||
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
|
||||
import 'codemirror/mode/yaml/yaml.js';
|
||||
import 'codemirror/mode/dockerfile/dockerfile.js';
|
||||
import 'codemirror/mode/nginx/nginx.js';
|
||||
import 'codemirror/mode/javascript/javascript.js';
|
||||
import 'codemirror/mode/css/css.js';
|
||||
import 'codemirror/mode/xml/xml.js';
|
||||
import 'codemirror/mode/markdown/markdown.js';
|
||||
import 'codemirror/mode/python/python.js';
|
||||
import 'codemirror/mode/shell/shell.js';
|
||||
import 'codemirror/mode/sql/sql.js';
|
||||
import 'codemirror/mode/vue/vue.js';
|
||||
import 'codemirror/mode/textile/textile.js';
|
||||
import 'codemirror/addon/hint/show-hint.css';
|
||||
import 'codemirror/addon/hint/show-hint.js';
|
||||
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
// 尝试获取全局实例
|
||||
const CodeMirror = (window as any).CodeMirror || _CodeMirror;
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CodeMirror',
|
||||
components: {
|
||||
ElOption,
|
||||
ElSelect,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: "500px",
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: "auto",
|
||||
},
|
||||
canChangeMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
setup(props: any, { emit }) {
|
||||
let { modelValue, language } = toRefs(props);
|
||||
const textarea: any = ref(null);
|
||||
// 编辑器实例
|
||||
let coder = null as any;
|
||||
|
||||
const state = reactive({
|
||||
coder: null as any,
|
||||
content: '',
|
||||
// 默认的语法类型
|
||||
mode: 'x-sh',
|
||||
// 默认配置
|
||||
options: {
|
||||
// 缩进格式
|
||||
tabSize: 2,
|
||||
// 主题,对应主题库 JS 需要提前引入
|
||||
theme: 'cobalt',
|
||||
// 显示行号
|
||||
lineNumbers: true,
|
||||
line: true,
|
||||
indentWithTabs: true,
|
||||
smartIndent: true,
|
||||
matchBrackets: true,
|
||||
autofocus: true,
|
||||
styleSelectedText: true,
|
||||
styleActiveLine: true, // 高亮选中行
|
||||
foldGutter: true, // 块槽
|
||||
// extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
|
||||
hintOptions: {
|
||||
// 当匹配只有一项的时候是否自动补全
|
||||
completeSingle: false,
|
||||
},
|
||||
},
|
||||
// 支持切换的语法高亮类型,对应 JS 已经提前引入
|
||||
// 使用的是 MIME-TYPE ,不过作为前缀的 text/ 在后面指定时写死了
|
||||
modes: [
|
||||
{
|
||||
value: 'x-sh',
|
||||
label: 'Shell',
|
||||
},
|
||||
{
|
||||
value: 'x-yaml',
|
||||
label: 'Yaml',
|
||||
},
|
||||
{
|
||||
value: 'x-dockerfile',
|
||||
label: 'Dockerfile',
|
||||
},
|
||||
{
|
||||
value: 'x-nginx-conf',
|
||||
label: 'Nginx',
|
||||
},
|
||||
{
|
||||
value: 'html',
|
||||
label: 'XML/HTML',
|
||||
},
|
||||
{
|
||||
value: 'x-python',
|
||||
label: 'Python',
|
||||
},
|
||||
{
|
||||
value: 'x-sql',
|
||||
label: 'SQL',
|
||||
},
|
||||
{
|
||||
value: 'css',
|
||||
label: 'CSS',
|
||||
},
|
||||
{
|
||||
value: 'javascript',
|
||||
label: 'Javascript',
|
||||
},
|
||||
{
|
||||
value: 'x-java',
|
||||
label: 'Java',
|
||||
},
|
||||
{
|
||||
value: 'x-vue',
|
||||
label: 'Vue',
|
||||
},
|
||||
{
|
||||
value: 'markdown',
|
||||
label: 'Markdown',
|
||||
},
|
||||
{
|
||||
value: 'text/x-textile',
|
||||
label: 'text',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
handerCodeChange(newValue);
|
||||
}
|
||||
);
|
||||
|
||||
// watch(
|
||||
// () => props.options,
|
||||
// (newValue, oldValue) => {
|
||||
// for (const key in newValue) {
|
||||
// coder.setOption(key, newValue[key]);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
const init = () => {
|
||||
if (props.options) {
|
||||
state.options = props.options;
|
||||
}
|
||||
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
|
||||
coder = CodeMirror.fromTextArea(textarea.value, state.options);
|
||||
coder.setValue(modelValue.value || state.content);
|
||||
|
||||
// 支持双向绑定
|
||||
coder.on('change', (coder: any) => {
|
||||
state.content = coder.getDoc().getValue();
|
||||
emit('update:modelValue', state.content);
|
||||
});
|
||||
|
||||
coder.on('inputRead', (instance: any, changeObj: any) => {
|
||||
if (/^[a-zA-Z]/.test(changeObj.text[0])) {
|
||||
instance.showHint();
|
||||
}
|
||||
});
|
||||
|
||||
coder.setSize(props.width, props.height);
|
||||
// editor.setSize('width','height');
|
||||
|
||||
// 修改编辑器的语法配置
|
||||
setMode(language.value);
|
||||
|
||||
[
|
||||
'scroll',
|
||||
'changes',
|
||||
'beforeChange',
|
||||
'cursorActivity',
|
||||
'keyHandled',
|
||||
'inputRead',
|
||||
'electricInput',
|
||||
'beforeSelectionChange',
|
||||
'viewportChange',
|
||||
'swapDoc',
|
||||
'gutterClick',
|
||||
'gutterContextMenu',
|
||||
'focus',
|
||||
'blur',
|
||||
'refresh',
|
||||
'optionChange',
|
||||
'scrollCursorIntoView',
|
||||
'update',
|
||||
].forEach((event) => {
|
||||
// 循环事件,并兼容 run-time 事件命名
|
||||
coder.on(event, (...args: any) => {
|
||||
// console.log('当有事件触发了', event, args);
|
||||
emit(event, ...args);
|
||||
const lowerCaseEvent = event.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
if (lowerCaseEvent !== event) {
|
||||
emit(lowerCaseEvent, ...args);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
state.coder = coder;
|
||||
// 不加无法显示内容,需点击后才可显示
|
||||
refresh();
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
nextTick(() => {
|
||||
coder.refresh();
|
||||
});
|
||||
};
|
||||
|
||||
// 设置模式
|
||||
const setMode = (val: string) => {
|
||||
if (val) {
|
||||
// 获取具体的语法类型对象
|
||||
let modeObj = getLanguage(val);
|
||||
// 判断父容器传入的语法是否被支持
|
||||
if (modeObj) {
|
||||
state.mode = modeObj.value;
|
||||
}
|
||||
}
|
||||
// 修改编辑器的语法配置
|
||||
coder.setOption('mode', `text/${state.mode}`);
|
||||
};
|
||||
|
||||
// 获取当前语法类型
|
||||
const getLanguage = (language: string) => {
|
||||
// 在支持的语法类型列表中寻找传入的语法类型
|
||||
return state.modes.find((mode: any) => {
|
||||
// 所有的值都忽略大小写,方便比较
|
||||
let currentLanguage = language.toLowerCase();
|
||||
let currentLabel = mode.label.toLowerCase();
|
||||
let currentValue = mode.value.toLowerCase();
|
||||
|
||||
// 由于真实值可能不规范,例如 java 的真实值是 x-java ,所以讲 value 和 label 同时和传入语法进行比较
|
||||
return currentLabel === currentLanguage || currentValue === currentLanguage;
|
||||
});
|
||||
};
|
||||
|
||||
// 更改模式
|
||||
const changeMode = (val: string) => {
|
||||
setMode(val);
|
||||
// 获取修改后的语法
|
||||
let label = (getLanguage(val) as any).label.toLowerCase();
|
||||
|
||||
// 允许父容器通过以下函数监听当前的语法值
|
||||
emit('language-change', label);
|
||||
};
|
||||
|
||||
const handerCodeChange = (newVal: string) => {
|
||||
const cm_value = coder.getValue();
|
||||
if (newVal !== cm_value) {
|
||||
const scrollInfo = coder.getScrollInfo();
|
||||
coder.setValue(newVal);
|
||||
state.content = newVal;
|
||||
coder.scrollTo(scrollInfo.left, scrollInfo.top);
|
||||
refresh()
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
textarea,
|
||||
changeMode,
|
||||
refresh,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.in-coder-panel {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
.CodeMirror {
|
||||
flex-grow: 1;
|
||||
z-index: 1;
|
||||
.CodeMirror-code {
|
||||
line-height: 19px;
|
||||
}
|
||||
font-family: 'JetBrainsMono';
|
||||
}
|
||||
|
||||
.code-mode-select {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
max-width: 130px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +0,0 @@
|
||||
import _CodeMirror from 'codemirror'
|
||||
import codemirror from './codemirror.vue'
|
||||
|
||||
const CodeMirror = window.CodeMirror || _CodeMirror
|
||||
const install = (Vue, config) => {
|
||||
if (config) {
|
||||
if (config.options) {
|
||||
codemirror.props.globalOptions.default = () => config.options
|
||||
}
|
||||
if (config.events) {
|
||||
codemirror.props.globalEvents.default = () => config.events
|
||||
}
|
||||
}
|
||||
Vue.component(codemirror.name, codemirror)
|
||||
}
|
||||
|
||||
const VueCodemirror = { CodeMirror, codemirror, install }
|
||||
|
||||
export default VueCodemirror
|
||||
export { CodeMirror, codemirror, install }
|
||||
132
mayfly_go_web/src/components/contextmenu/index.vue
Normal file
132
mayfly_go_web/src/components/contextmenu/index.vue
Normal file
@@ -0,0 +1,132 @@
|
||||
<template>
|
||||
<transition name="el-zoom-in-center">
|
||||
<div aria-hidden="true" class="el-dropdown__popper el-popper is-light is-pure custom-contextmenu" role="tooltip"
|
||||
data-popper-placement="bottom" :style="`top: ${dropdowns.y + 5}px;left: ${dropdowns.x}px;`" :key="Math.random()"
|
||||
v-show="state.isShow">
|
||||
<ul class="el-dropdown-menu">
|
||||
<template v-for="(v, k) in state.dropdownList">
|
||||
<li class="el-dropdown-menu__item" aria-disabled="false" tabindex="-1" :key="k" v-if="!v.affix"
|
||||
@click="onCurrentContextmenuClick(v.contextMenuClickId)">
|
||||
<SvgIcon :name="v.icon" />
|
||||
<span>{{ v.txt }}</span>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
<div class="el-popper__arrow" :style="{ left: `${state.arrowLeft}px` }"></div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutTagsViewContextmenu">
|
||||
import { computed, reactive, onMounted, onUnmounted, watch } from 'vue';
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
dropdown: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
},
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
});
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['currentContextmenuClick']);
|
||||
|
||||
// 定义变量内容
|
||||
const state = reactive({
|
||||
isShow: false,
|
||||
dropdownList: [
|
||||
{ contextMenuClickId: 0, txt: '刷新', affix: false, icon: 'RefreshRight' },
|
||||
],
|
||||
item: {} as any,
|
||||
arrowLeft: 10,
|
||||
});
|
||||
|
||||
// 父级传过来的坐标 x,y 值
|
||||
const dropdowns = computed(() => {
|
||||
// 117 为 `Dropdown 下拉菜单` 的宽度
|
||||
if (props.dropdown.x + 117 > document.documentElement.clientWidth) {
|
||||
return {
|
||||
x: document.documentElement.clientWidth - 117 - 5,
|
||||
y: props.dropdown.y,
|
||||
};
|
||||
} else {
|
||||
return props.dropdown;
|
||||
}
|
||||
});
|
||||
// 当前项菜单点击
|
||||
const onCurrentContextmenuClick = (contextMenuClickId: number) => {
|
||||
emit('currentContextmenuClick', { id: contextMenuClickId, item: state.item });
|
||||
};
|
||||
// 打开右键菜单:判断是否固定,固定则不显示关闭按钮
|
||||
const openContextmenu = (item: any) => {
|
||||
state.item = item;
|
||||
closeContextmenu();
|
||||
setTimeout(() => {
|
||||
state.isShow = true;
|
||||
}, 10);
|
||||
};
|
||||
// 关闭右键菜单
|
||||
const closeContextmenu = () => {
|
||||
state.isShow = false;
|
||||
};
|
||||
// 监听页面监听进行右键菜单的关闭
|
||||
onMounted(() => {
|
||||
document.body.addEventListener('click', closeContextmenu);
|
||||
});
|
||||
// 页面卸载时,移除右键菜单监听事件
|
||||
onUnmounted(() => {
|
||||
document.body.removeEventListener('click', closeContextmenu);
|
||||
});
|
||||
// 监听下拉菜单位置
|
||||
watch(
|
||||
() => props.dropdown,
|
||||
({ x }) => {
|
||||
if (x + 117 > document.documentElement.clientWidth) state.arrowLeft = 117 - (document.documentElement.clientWidth - x);
|
||||
else state.arrowLeft = 10;
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.items,
|
||||
(x: any) => {
|
||||
state.dropdownList = x
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openContextmenu,
|
||||
closeContextmenu,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-contextmenu {
|
||||
transform-origin: center top;
|
||||
z-index: 2190;
|
||||
position: fixed;
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
font-size: 12px !important;
|
||||
white-space: nowrap;
|
||||
|
||||
i {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,261 +1,242 @@
|
||||
<template>
|
||||
<div class="icon-selector">
|
||||
<el-popover placement="bottom" :width="450" v-model:visible="fontIconVisible" popper-class="icon-selector-popper">
|
||||
<template #reference>
|
||||
<el-input
|
||||
v-model="fontIconSearch"
|
||||
:placeholder="fontIconPlaceholder"
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
ref="inputWidthRef"
|
||||
@clear="onClearFontIcon"
|
||||
@focus="onIconFocus"
|
||||
@blur="onIconBlur"
|
||||
>
|
||||
<template #prepend>
|
||||
<SvgIcon :name="prepend" class="font14" />
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
<transition name="el-zoom-in-top">
|
||||
<div class="icon-selector-warp" v-show="fontIconVisible">
|
||||
<div class="icon-selector-warp-title flex">
|
||||
<div class="flex-auto">{{ title }}</div>
|
||||
<div class="icon-selector-warp-title-tab" v-if="type === 'all'">
|
||||
<span :class="{ 'span-active': fontIconType === 'ali' }" @click="onIconChange('ali')" class="ml10" title="iconfont 图标"
|
||||
>ali</span
|
||||
>
|
||||
<span
|
||||
:class="{ 'span-active': fontIconType === 'ele' }"
|
||||
@click="onIconChange('ele')"
|
||||
class="ml10"
|
||||
title="elementPlus 图标"
|
||||
>ele</span
|
||||
>
|
||||
<span
|
||||
:class="{ 'span-active': fontIconType === 'awe' }"
|
||||
@click="onIconChange('awe')"
|
||||
class="ml10"
|
||||
title="fontawesome 图标"
|
||||
>awe</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon-selector-warp-row">
|
||||
<el-scrollbar ref="selectorScrollbarRef">
|
||||
<el-row :gutter="10" v-if="fontIconSheetsFilterList.length > 0">
|
||||
<el-col
|
||||
:xs="6"
|
||||
:sm="4"
|
||||
:md="4"
|
||||
:lg="4"
|
||||
:xl="4"
|
||||
@click="onColClick(v)"
|
||||
v-for="(v, k) in fontIconSheetsFilterList"
|
||||
:key="k"
|
||||
>
|
||||
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': fontIconPrefix === v }">
|
||||
<div class="flex-margin">
|
||||
<div class="icon-selector-warp-item-value">
|
||||
<SvgIcon :name="v" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty :image-size="100" v-if="fontIconSheetsFilterList.length <= 0" :description="emptyDescription"></el-empty>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="icon-selector w100 h100">
|
||||
<el-input
|
||||
v-model="state.fontIconSearch"
|
||||
:placeholder="state.fontIconPlaceholder"
|
||||
:clearable="clearable"
|
||||
:disabled="disabled"
|
||||
:size="size"
|
||||
ref="inputWidthRef"
|
||||
@clear="onClearFontIcon"
|
||||
@focus="onIconFocus"
|
||||
@blur="onIconBlur"
|
||||
>
|
||||
<template #prepend>
|
||||
<SvgIcon
|
||||
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
|
||||
class="font14"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-popover
|
||||
placement="bottom"
|
||||
:width="state.fontIconWidth"
|
||||
transition="el-zoom-in-top"
|
||||
popper-class="icon-selector-popper"
|
||||
trigger="click"
|
||||
:virtual-ref="inputWidthRef"
|
||||
virtual-triggering
|
||||
>
|
||||
<template #default>
|
||||
<div class="icon-selector-warp">
|
||||
<div class="icon-selector-warp-title">{{ title }}</div>
|
||||
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
|
||||
<el-tab-pane lazy label="ele" name="ele">
|
||||
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane lazy label="ali" name="ali">
|
||||
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane lazy label="awe" name="awe">
|
||||
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, nextTick, computed, watch } from 'vue';
|
||||
import initIconfont from '@/common/utils/getStyleSheets';
|
||||
export default {
|
||||
name: 'iconSelector',
|
||||
emits: ['update:modelValue', 'get', 'clear'],
|
||||
props: {
|
||||
// 输入框前置内容
|
||||
prepend: {
|
||||
type: String,
|
||||
default: () => 'Pointer',
|
||||
},
|
||||
// 输入框占位文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容搜索图标或者选择图标',
|
||||
},
|
||||
// 输入框占位文本
|
||||
size: {
|
||||
type: String,
|
||||
default: () => 'default',
|
||||
},
|
||||
// 弹窗标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => '请选择图标',
|
||||
},
|
||||
// icon 图标类型
|
||||
type: {
|
||||
type: String,
|
||||
default: () => 'ele',
|
||||
},
|
||||
// 禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
// 自定义空状态描述文字
|
||||
emptyDescription: {
|
||||
type: String,
|
||||
default: () => '无相关图标',
|
||||
},
|
||||
// 双向绑定值,字段名为固定,改了之后将不生效
|
||||
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
|
||||
modelValue: String,
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const inputWidthRef = ref();
|
||||
const selectorScrollbarRef = ref();
|
||||
const state: any = reactive({
|
||||
fontIconPrefix: '',
|
||||
fontIconVisible: false,
|
||||
fontIconWidth: 0,
|
||||
fontIconSearch: '',
|
||||
fontIconTabsIndex: 0,
|
||||
fontIconSheetsList: [],
|
||||
fontIconPlaceholder: '',
|
||||
fontIconType: 'ali',
|
||||
fontIconShow: true,
|
||||
});
|
||||
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
|
||||
const onIconFocus = () => {
|
||||
state.fontIconVisible = true;
|
||||
if (!props.modelValue) return false;
|
||||
state.fontIconSearch = '';
|
||||
state.fontIconPlaceholder = props.modelValue;
|
||||
};
|
||||
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
|
||||
const onIconBlur = () => {
|
||||
state.fontIconVisible = false;
|
||||
setTimeout(() => {
|
||||
const icon = state.fontIconSheetsList.filter((icon: string) => icon === state.fontIconSearch);
|
||||
if (icon.length <= 0) state.fontIconSearch = '';
|
||||
}, 300);
|
||||
};
|
||||
// 处理 icon 双向绑定数值回显
|
||||
const initModeValueEcho = () => {
|
||||
if (props.modelValue === '') return false;
|
||||
state.fontIconPlaceholder = props.modelValue;
|
||||
state.fontIconPrefix = props.modelValue;
|
||||
};
|
||||
// 图标搜索及图标数据显示
|
||||
const fontIconSheetsFilterList = computed(() => {
|
||||
if (!state.fontIconSearch) return state.fontIconSheetsList;
|
||||
let search = state.fontIconSearch.trim().toLowerCase();
|
||||
return state.fontIconSheetsList.filter((item: any) => {
|
||||
if (item.toLowerCase().indexOf(search) !== -1) return item;
|
||||
});
|
||||
});
|
||||
// 获取 input 的宽度
|
||||
const getInputWidth = () => {
|
||||
nextTick(() => {
|
||||
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
|
||||
});
|
||||
};
|
||||
// 监听页面宽度改变
|
||||
const initResize = () => {
|
||||
window.addEventListener('resize', () => {
|
||||
getInputWidth();
|
||||
});
|
||||
};
|
||||
// 初始化数据
|
||||
const initFontIconData = async (type: string) => {
|
||||
state.fontIconSheetsList = [];
|
||||
if (type === 'ali') {
|
||||
// await initIconfont.ali().then((res: any) => {
|
||||
// // 阿里字体图标使用 `iconfont xxx`
|
||||
// state.fontIconSheetsList = res.map((i) => `iconfont ${i}`);
|
||||
// });
|
||||
} else if (type === 'ele') {
|
||||
await initIconfont.ele().then((res: any) => {
|
||||
state.fontIconSheetsList = res;
|
||||
});
|
||||
} else if (type === 'awe') {
|
||||
// await initIconfont.awe().then((res: any) => {
|
||||
// // fontawesome字体图标使用 `fa xxx`
|
||||
// state.fontIconSheetsList = res.map((i) => `fa ${i}`);
|
||||
// });
|
||||
}
|
||||
// 初始化 input 的 placeholder
|
||||
// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
|
||||
state.fontIconPlaceholder = props.placeholder;
|
||||
// 初始化双向绑定回显
|
||||
initModeValueEcho();
|
||||
// 切换时,滚动条置顶。感兴趣可以使用 keep-alive <component :is="xxx"/> 进行缓存
|
||||
selectorScrollbarRef.value.wrap$.scrollTop = 0;
|
||||
};
|
||||
// 图标点击切换
|
||||
const onIconChange = (type: string) => {
|
||||
state.fontIconType = type;
|
||||
initFontIconData(type);
|
||||
};
|
||||
// 获取当前点击的 icon 图标
|
||||
const onColClick = (v: any) => {
|
||||
state.fontIconPlaceholder = v;
|
||||
state.fontIconVisible = false;
|
||||
state.fontIconPrefix = v;
|
||||
emit('get', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
};
|
||||
// 清空当前点击的 icon 图标
|
||||
const onClearFontIcon = () => {
|
||||
state.fontIconPrefix = '';
|
||||
emit('clear', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
// 判断默认进来是什么类型图标,进行 tab 回显
|
||||
if (props.type === 'all') {
|
||||
// if (props.modelValue?.indexOf('iconfont') > -1) onIconChange('ali');
|
||||
// else if (props.modelValue?.indexOf('element') > -1) onIconChange('ele');
|
||||
// else if (props.modelValue?.indexOf('fa') > -1) onIconChange('awe');
|
||||
// else onIconChange('ali');
|
||||
} else {
|
||||
onIconChange(props.type);
|
||||
}
|
||||
initResize();
|
||||
getInputWidth();
|
||||
});
|
||||
// 监听双向绑定 modelValue 的变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
initModeValueEcho();
|
||||
}
|
||||
);
|
||||
return {
|
||||
inputWidthRef,
|
||||
selectorScrollbarRef,
|
||||
fontIconSheetsFilterList,
|
||||
onColClick,
|
||||
onIconChange,
|
||||
onClearFontIcon,
|
||||
onIconFocus,
|
||||
onIconBlur,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
<script setup lang="ts" name="iconSelector">
|
||||
import { defineAsyncComponent, ref, reactive, onMounted, nextTick, computed, watch } from 'vue';
|
||||
import type { TabsPaneContext } from 'element-plus';
|
||||
import initIconfont from '@/common/utils/svgIcons';
|
||||
import '@/theme/iconSelector.scss';
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
// 输入框前置内容
|
||||
prepend: {
|
||||
type: String,
|
||||
default: () => 'Pointer',
|
||||
},
|
||||
// 输入框占位文本
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => '请输入内容搜索图标或者选择图标',
|
||||
},
|
||||
// 输入框占位文本
|
||||
size: {
|
||||
type: String,
|
||||
default: () => 'default',
|
||||
},
|
||||
// 弹窗标题
|
||||
title: {
|
||||
type: String,
|
||||
default: () => '请选择图标',
|
||||
},
|
||||
// 禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
// 是否可清空
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: () => true,
|
||||
},
|
||||
// 自定义空状态描述文字
|
||||
emptyDescription: {
|
||||
type: String,
|
||||
default: () => '无相关图标',
|
||||
},
|
||||
// 双向绑定值,默认为 modelValue,
|
||||
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
|
||||
// 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
|
||||
modelValue: String,
|
||||
});
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['update:modelValue', 'get', 'clear']);
|
||||
|
||||
// 引入组件
|
||||
const IconList = defineAsyncComponent(() => import('@/components/iconSelector/list.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const inputWidthRef = ref();
|
||||
const state = reactive({
|
||||
fontIconPrefix: '',
|
||||
fontIconWidth: 0,
|
||||
fontIconSearch: '',
|
||||
fontIconPlaceholder: '',
|
||||
fontIconTabActive: 'ele',
|
||||
fontIconList: {
|
||||
ali: [],
|
||||
ele: [],
|
||||
awe: [],
|
||||
},
|
||||
});
|
||||
|
||||
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
|
||||
const onIconFocus = () => {
|
||||
if (!props.modelValue) return false;
|
||||
state.fontIconSearch = '';
|
||||
state.fontIconPlaceholder = props.modelValue;
|
||||
};
|
||||
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
|
||||
const onIconBlur = () => {
|
||||
const list = fontIconTabNameList();
|
||||
setTimeout(() => {
|
||||
const icon = list.filter((icon: string) => icon === state.fontIconSearch);
|
||||
if (icon.length <= 0) state.fontIconSearch = '';
|
||||
}, 300);
|
||||
};
|
||||
// 图标搜索及图标数据显示
|
||||
const fontIconSheetsFilterList = computed(() => {
|
||||
const list = fontIconTabNameList();
|
||||
if (!state.fontIconSearch) return list;
|
||||
let search = state.fontIconSearch.trim().toLowerCase();
|
||||
return list.filter((item: string) => {
|
||||
if (item.toLowerCase().indexOf(search) !== -1) return item;
|
||||
});
|
||||
});
|
||||
// 根据 tab name 类型设置图标
|
||||
const fontIconTabNameList = () => {
|
||||
let iconList: any = [];
|
||||
if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali;
|
||||
else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele;
|
||||
else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe;
|
||||
return iconList;
|
||||
};
|
||||
// 处理 icon 双向绑定数值回显
|
||||
const initModeValueEcho = () => {
|
||||
if (props.modelValue === '') return ((<string | undefined>state.fontIconPlaceholder) = props.placeholder);
|
||||
(<string | undefined>state.fontIconPlaceholder) = props.modelValue;
|
||||
(<string | undefined>state.fontIconPrefix) = props.modelValue;
|
||||
};
|
||||
// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
|
||||
const initFontIconName = () => {
|
||||
let name = 'ele';
|
||||
if (props.modelValue!.indexOf('iconfont') > -1) {
|
||||
name = 'ali';
|
||||
} else {
|
||||
name = 'ele';
|
||||
}
|
||||
// else if (props.modelValue!.indexOf('ele-') > -1) name = 'ele';
|
||||
// else if (props.modelValue!.indexOf('fa') > -1) name = 'awe';
|
||||
// 初始化 tab 高亮回显
|
||||
state.fontIconTabActive = name;
|
||||
return name;
|
||||
};
|
||||
// 初始化数据
|
||||
const initFontIconData = async (name: string) => {
|
||||
if (name === 'ali') {
|
||||
// 阿里字体图标使用 `iconfont xxx`
|
||||
if (state.fontIconList.ali.length > 0) return;
|
||||
const res: any = await initIconfont.ali();
|
||||
state.fontIconList.ali = res.map((i: string) => `iconfont ${i}`);
|
||||
} else if (name === 'ele') {
|
||||
// element plus 图标
|
||||
if (state.fontIconList.ele.length > 0) return;
|
||||
await initIconfont.ele().then((res: any) => {
|
||||
state.fontIconList.ele = res;
|
||||
});
|
||||
} else if (name === 'awe') {
|
||||
// fontawesome字体图标使用 `fa xxx`
|
||||
// if (state.fontIconList.awe.length > 0) return;
|
||||
// await initIconfont.awe().then((res: any) => {
|
||||
// state.fontIconList.awe = res.map((i: string) => `fa ${i}`);
|
||||
// });
|
||||
}
|
||||
// 初始化 input 的 placeholder
|
||||
// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
|
||||
state.fontIconPlaceholder = props.placeholder;
|
||||
// 初始化双向绑定回显
|
||||
initModeValueEcho();
|
||||
};
|
||||
// 图标点击切换
|
||||
const onIconClick = (pane: TabsPaneContext) => {
|
||||
initFontIconData(pane.paneName as string);
|
||||
inputWidthRef.value.focus();
|
||||
};
|
||||
// 获取当前点击的 icon 图标
|
||||
const onColClick = (v: string) => {
|
||||
state.fontIconPlaceholder = v;
|
||||
state.fontIconPrefix = v;
|
||||
emit('get', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
inputWidthRef.value.focus();
|
||||
};
|
||||
// 清空当前点击的 icon 图标
|
||||
const onClearFontIcon = () => {
|
||||
state.fontIconPrefix = '';
|
||||
emit('clear', state.fontIconPrefix);
|
||||
emit('update:modelValue', state.fontIconPrefix);
|
||||
};
|
||||
// 获取 input 的宽度
|
||||
const getInputWidth = () => {
|
||||
nextTick(() => {
|
||||
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
|
||||
});
|
||||
};
|
||||
// 监听页面宽度改变
|
||||
const initResize = () => {
|
||||
window.addEventListener('resize', () => {
|
||||
getInputWidth();
|
||||
});
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initFontIconData(initFontIconName());
|
||||
initResize();
|
||||
getInputWidth();
|
||||
});
|
||||
// 监听双向绑定 modelValue 的变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => {
|
||||
initModeValueEcho();
|
||||
initFontIconName();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
84
mayfly_go_web/src/components/iconSelector/list.vue
Normal file
84
mayfly_go_web/src/components/iconSelector/list.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div class="icon-selector-warp-row">
|
||||
<el-scrollbar ref="selectorScrollbarRef">
|
||||
<el-row :gutter="10" v-if="props.list.length > 0">
|
||||
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
|
||||
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
|
||||
<SvgIcon :name="v" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="iconSelectorList">
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
// 图标列表数据
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 自定义空状态描述文字
|
||||
empty: {
|
||||
type: String,
|
||||
default: () => '无相关图标',
|
||||
},
|
||||
// 高亮当前选中图标
|
||||
prefix: {
|
||||
type: String,
|
||||
default: () => '',
|
||||
},
|
||||
});
|
||||
|
||||
// 定义子组件向父组件传值/事件
|
||||
const emit = defineEmits(['get-icon']);
|
||||
|
||||
// 当前 icon 图标点击时
|
||||
const onColClick = (v: unknown | string) => {
|
||||
emit('get-icon', v);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.icon-selector-warp-row {
|
||||
height: 230px;
|
||||
overflow: hidden;
|
||||
.el-row {
|
||||
padding: 15px;
|
||||
}
|
||||
.el-scrollbar__bar.is-horizontal {
|
||||
display: none;
|
||||
}
|
||||
.icon-selector-warp-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
height: 30px;
|
||||
i {
|
||||
font-size: 20px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 1px solid var(--el-color-primary-light-5);
|
||||
i {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-selector-active {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
border: 1px solid var(--el-color-primary-light-5);
|
||||
i {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
280
mayfly_go_web/src/components/monaco/MonacoEditor.vue
Normal file
280
mayfly_go_web/src/components/monaco/MonacoEditor.vue
Normal file
@@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<div class="monaco-editor" style="border: 1px solid #ccc;">
|
||||
<div class="monaco-editor-content" ref="monacoTextarea" :style="{ height: height }"></div>
|
||||
<el-select v-if="canChangeMode" class="code-mode-select" v-model="languageMode" @change="changeLanguage">
|
||||
<el-option v-for="mode in languageArr" :key="mode.value" :label="mode.label" :value="mode.value"> </el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, toRefs, reactive, onMounted, onBeforeUnmount } from 'vue';
|
||||
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker.js?worker';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import { editor, languages } from 'monaco-editor';
|
||||
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
|
||||
// 主题仓库 https://github.com/brijeshb42/monaco-themes
|
||||
// 主题例子 https://editor.bitwiser.in/
|
||||
// import Monokai from 'monaco-themes/themes/Monokai.json'
|
||||
// import Active4D from 'monaco-themes/themes/Active4D.json'
|
||||
// import ahe from 'monaco-themes/themes/All Hallows Eve.json'
|
||||
// import bop from 'monaco-themes/themes/Birds of Paradise.json'
|
||||
// import krTheme from 'monaco-themes/themes/krTheme.json'
|
||||
// import Dracula from 'monaco-themes/themes/Dracula.json'
|
||||
import SolarizedLight from 'monaco-themes/themes/Solarized-light.json'
|
||||
import { language as shellLan } from 'monaco-editor/esm/vs/basic-languages/shell/shell.js';
|
||||
import { ElOption, ElSelect } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px',
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: 'auto',
|
||||
},
|
||||
canChangeMode: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
})
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const languageArr = [
|
||||
{
|
||||
value: 'shell',
|
||||
label: 'Shell',
|
||||
},
|
||||
{
|
||||
value: 'json',
|
||||
label: 'JSON',
|
||||
},
|
||||
{
|
||||
value: 'yaml',
|
||||
label: 'Yaml',
|
||||
},
|
||||
{
|
||||
value: 'dockerfile',
|
||||
label: 'Dockerfile',
|
||||
},
|
||||
{
|
||||
value: 'html',
|
||||
label: 'XML/HTML',
|
||||
},
|
||||
{
|
||||
value: 'python',
|
||||
label: 'Python',
|
||||
},
|
||||
{
|
||||
value: 'sql',
|
||||
label: 'SQL',
|
||||
},
|
||||
{
|
||||
value: 'css',
|
||||
label: 'CSS',
|
||||
},
|
||||
{
|
||||
value: 'javascript',
|
||||
label: 'Javascript',
|
||||
},
|
||||
{
|
||||
value: 'java',
|
||||
label: 'Java',
|
||||
},
|
||||
{
|
||||
value: 'markdown',
|
||||
label: 'Markdown',
|
||||
},
|
||||
{
|
||||
value: 'text',
|
||||
label: 'text',
|
||||
},
|
||||
];
|
||||
|
||||
const options = {
|
||||
language: 'shell',
|
||||
theme: 'SolarizedLight',
|
||||
automaticLayout: true, //自适应宽高布局
|
||||
foldingStrategy: 'indentation',//代码可分小段折叠
|
||||
roundedSelection: false, // 禁用选择文本背景的圆角
|
||||
matchBrackets: 'near',
|
||||
linkedEditing: true,
|
||||
cursorBlinking: 'smooth',// 光标闪烁样式
|
||||
mouseWheelZoom: true, // 在按住Ctrl键的同时使用鼠标滚轮时,在编辑器中缩放字体
|
||||
overviewRulerBorder: false, // 不要滚动条的边框
|
||||
tabSize: 4, // tab 缩进长度
|
||||
// fontFamily: 'JetBrainsMono', // 字体 暂时不要设置,否则光标容易错位
|
||||
fontWeight: 'bold',
|
||||
// fontSize: 12,
|
||||
// letterSpacing: 1, 字符间距
|
||||
// quickSuggestions:false, // 禁用代码提示
|
||||
minimap: {
|
||||
enabled: false, // 不要小地图
|
||||
},
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
languageMode: 'shell',
|
||||
})
|
||||
|
||||
const {
|
||||
languageMode,
|
||||
} = toRefs(state)
|
||||
|
||||
onMounted(() => {
|
||||
state.languageMode = props.language;
|
||||
initMonacoEditorIns();
|
||||
setEditorValue(props.modelValue);
|
||||
registerCompletionItemProvider();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (monacoEditorIns) {
|
||||
monacoEditorIns.dispose();
|
||||
}
|
||||
if (completionItemProvider) {
|
||||
completionItemProvider.dispose();
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (newValue: any) => {
|
||||
if (!monacoEditorIns.hasTextFocus()) {
|
||||
state.languageMode = props.language;
|
||||
monacoEditorIns?.setValue(newValue);
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.language, (newValue: any) => {
|
||||
changeLanguage(newValue);
|
||||
})
|
||||
|
||||
|
||||
const monacoTextarea: any = ref(null);
|
||||
|
||||
let monacoEditorIns: editor.IStandaloneCodeEditor = null as any;
|
||||
let completionItemProvider: any = null;
|
||||
|
||||
self.MonacoEnvironment = {
|
||||
getWorker(_: any, label: string) {
|
||||
if (label === 'json') {
|
||||
return new JsonWorker()
|
||||
}
|
||||
return new EditorWorker();
|
||||
}
|
||||
};
|
||||
|
||||
const initMonacoEditorIns = () => {
|
||||
console.log('初始化monaco编辑器')
|
||||
// options参数参考 https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html#language
|
||||
// 初始化一些主题
|
||||
monaco.editor.defineTheme('SolarizedLight', SolarizedLight);
|
||||
options.language = state.languageMode;
|
||||
// 从localStorage中获取,通过store可能存在父子组件都使用store报错
|
||||
options.theme = JSON.parse(localStorage.getItem('themeConfig') as string).editorTheme || 'vs';
|
||||
monacoEditorIns = monaco.editor.create(monacoTextarea.value, Object.assign(options, props.options as any));
|
||||
|
||||
// 监听内容改变,双向绑定
|
||||
monacoEditorIns.onDidChangeModelContent(() => {
|
||||
emit('update:modelValue', monacoEditorIns.getModel()?.getValue());
|
||||
})
|
||||
|
||||
// 动态设置主题
|
||||
// monaco.editor.setTheme('hc-black');
|
||||
};
|
||||
|
||||
const changeLanguage = (value: any) => {
|
||||
console.log('change lan');
|
||||
// 获取当前的文档模型
|
||||
let oldModel = monacoEditorIns.getModel()
|
||||
if (!oldModel) {
|
||||
return;
|
||||
}
|
||||
// 创建一个新的文档模型
|
||||
let newModel = monaco.editor.createModel(oldModel.getValue(), value)
|
||||
// 设置成新的
|
||||
monacoEditorIns.setModel(newModel)
|
||||
// 销毁旧的模型
|
||||
if (oldModel) {
|
||||
oldModel.dispose()
|
||||
}
|
||||
|
||||
registerCompletionItemProvider();
|
||||
}
|
||||
|
||||
const setEditorValue = (value: any) => {
|
||||
monacoEditorIns.getModel()?.setValue(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册联想补全提示
|
||||
*/
|
||||
const registerCompletionItemProvider = () => {
|
||||
if (completionItemProvider) {
|
||||
completionItemProvider.dispose();
|
||||
}
|
||||
if (state.languageMode == 'shell') {
|
||||
registeShell()
|
||||
}
|
||||
}
|
||||
|
||||
const registeShell = () => {
|
||||
completionItemProvider = monaco.languages.registerCompletionItemProvider('shell', {
|
||||
provideCompletionItems: async () => {
|
||||
let suggestions: languages.CompletionItem[] = []
|
||||
shellLan.keywords.forEach((item: any) => {
|
||||
suggestions.push({
|
||||
label: item,
|
||||
kind: monaco.languages.CompletionItemKind.Keyword,
|
||||
insertText: item,
|
||||
} as any);
|
||||
})
|
||||
shellLan.builtins.forEach((item: any) => {
|
||||
suggestions.push({
|
||||
label: item,
|
||||
kind: monaco.languages.CompletionItemKind.Property,
|
||||
insertText: item,
|
||||
} as any);
|
||||
})
|
||||
return {
|
||||
suggestions: suggestions
|
||||
};
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
const format = () => {
|
||||
/*
|
||||
触发自动格式化;
|
||||
*/
|
||||
monacoEditorIns.trigger('', 'editor.action.formatDocument', '')
|
||||
}
|
||||
|
||||
defineExpose({ format })
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.monaco-editor {
|
||||
.code-mode-select {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
max-width: 130px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,24 +1,105 @@
|
||||
<script lang="ts">
|
||||
// 渲染函数:https://v3.cn.vuejs.org/guide/render-function.html
|
||||
import { h, resolveComponent, defineComponent } from 'vue';
|
||||
export default defineComponent({
|
||||
name: 'svgIcon',
|
||||
props: {
|
||||
// svg 图标组件名字
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
// svg 大小
|
||||
size: {
|
||||
type: Number,
|
||||
},
|
||||
// svg 颜色
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup(props: any) {
|
||||
return () => h('i', { class: 'el-icon', style: `--font-size: ${props.size};--color: ${props.color}` }, [h(resolveComponent(`${props.name}`))]);
|
||||
},
|
||||
<template>
|
||||
<i v-if="isShowIconSvg" class="el-icon icon-middle" :style="setIconSvgStyle">
|
||||
<component :is="getIconName" :style="setIconSvgStyle" />
|
||||
</i>
|
||||
|
||||
<svg v-else-if="isIconfont()" class="el-icon iconfont-icon icon-middle" aria-hidden="true" :style="setIconSvgStyle">
|
||||
<use :xlink:href="'#' + getIconfontName()"></use>
|
||||
</svg>
|
||||
|
||||
<div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
|
||||
<img :src="getIconName" :style="setIconSvgInsStyle" />
|
||||
</div>
|
||||
|
||||
<i v-else :class="getIconName" :style="setIconSvgStyle" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="svgIcon">
|
||||
import { computed } from 'vue';
|
||||
|
||||
// 定义父组件传过来的值
|
||||
const props = defineProps({
|
||||
// svg 图标组件名字
|
||||
name: {
|
||||
type: String,
|
||||
},
|
||||
// svg 大小
|
||||
size: {
|
||||
type: Number,
|
||||
default: () => 14,
|
||||
},
|
||||
// svg 颜色
|
||||
color: {
|
||||
type: String,
|
||||
},
|
||||
isEle: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
// 在线链接、本地引入地址前缀
|
||||
const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH];
|
||||
|
||||
// 获取 icon 图标名称
|
||||
const getIconName = computed(() => {
|
||||
return props?.name as any;
|
||||
});
|
||||
|
||||
// 用于判断 element plus 自带 svg 图标的显示、隐藏。不存在 空格分隔的icon name即为element plus自带icon
|
||||
const isShowIconSvg = computed(() => {
|
||||
const ss = props?.name?.split(" ")
|
||||
if (!ss) {
|
||||
return true;
|
||||
}
|
||||
return ss.length == 1;
|
||||
});
|
||||
|
||||
const isIconfont = () => {
|
||||
return props?.name?.startsWith("iconfont")
|
||||
}
|
||||
|
||||
const getIconfontName = () => {
|
||||
// iconfont icon-xxxx 获取icon-xxx即可
|
||||
return props?.name?.split(" ")[1]
|
||||
}
|
||||
|
||||
// 用于判断在线链接、本地引入等图标显示、隐藏
|
||||
const isShowIconImg = computed(() => {
|
||||
return linesString.find((str) => props.name?.startsWith(str));
|
||||
});
|
||||
|
||||
// 设置图标样式
|
||||
const setIconSvgStyle = computed(() => {
|
||||
return `font-size: ${props.size}px;color: ${props.color};width: ${props.size}px;height: ${props.size}px;`;
|
||||
});
|
||||
|
||||
// 设置图片样式
|
||||
const setIconImgOutStyle = computed(() => {
|
||||
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
|
||||
});
|
||||
|
||||
// 设置图片样式
|
||||
const setIconSvgInsStyle = computed(() => {
|
||||
const filterStyle: string[] = [];
|
||||
const compatibles: string[] = ['-webkit', '-ms', '-o', '-moz'];
|
||||
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
|
||||
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
.iconfont-icon {
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon-middle {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { App } from 'vue';
|
||||
import { auth, auths, authAll } from './authFunction'
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import { judementSameArr } from '@/common/utils/arrayOperation';
|
||||
|
||||
// 用户权限指令
|
||||
export function authDirective(app: App) {
|
||||
// 单个权限验证(v-auth="xxx")
|
||||
app.directive('auth', {
|
||||
mounted(el, binding) {
|
||||
if (!auth(binding.value)) {
|
||||
if (!useUserInfo().userInfo.permissions.some((v: any) => v === binding.value)) {
|
||||
parseNoAuth(el, binding);
|
||||
};
|
||||
},
|
||||
@@ -14,7 +15,14 @@ export function authDirective(app: App) {
|
||||
// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
|
||||
app.directive('auths', {
|
||||
mounted(el, binding) {
|
||||
if (!auths(binding.value)) {
|
||||
const value = binding.value
|
||||
let flag = false;
|
||||
useUserInfo().userInfo.permissions.map((val: any) => {
|
||||
value.map((v: any) => {
|
||||
if (val === v) flag = true;
|
||||
});
|
||||
});
|
||||
if (!flag) {
|
||||
parseNoAuth(el, binding);
|
||||
}
|
||||
},
|
||||
@@ -22,7 +30,7 @@ export function authDirective(app: App) {
|
||||
// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
|
||||
app.directive('auth-all', {
|
||||
mounted(el, binding) {
|
||||
if (!authAll(binding.value)) {
|
||||
if (!judementSameArr(binding.value, useUserInfo().userInfo.permissions)) {
|
||||
parseNoAuth(el, binding);
|
||||
};
|
||||
},
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { App } from 'vue';
|
||||
import { authDirective } from '@/common/utils/authDirective.ts';
|
||||
import { wavesDirective } from '@/common/utils/customDirective.ts';
|
||||
import { authDirective } from './auth';
|
||||
import { wavesDirective } from './waves';
|
||||
|
||||
// 导出指令方法
|
||||
export function directive(app: App) {
|
||||
@@ -1,56 +1,33 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import App from '@/App.vue';
|
||||
|
||||
import router from './router';
|
||||
import { store, key } from './store';
|
||||
import { directive } from '@/common/utils/directive.ts';
|
||||
import { globalComponentSize } from '@/common/utils/componentSize.ts';
|
||||
import { dateStrFormat } from '@/common/utils/date.ts'
|
||||
import pinia from '@/store/index';
|
||||
import { directive } from '@/directive/index';
|
||||
import { globalComponentSize } from '@/common/utils/componentSize';
|
||||
import { registElSvgIcon } from '@/common/utils/svgIcons';
|
||||
|
||||
import ElementPlus from 'element-plus';
|
||||
import 'element-plus/dist/index.css';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn'
|
||||
import '@/theme/index.scss';
|
||||
import mitt from 'mitt';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
import * as svg from '@element-plus/icons-vue';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import '@/theme/index.scss';
|
||||
import '@/assets/font/font.css'
|
||||
import '@/assets/iconfont/iconfont.js'
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
/**
|
||||
* 导出全局注册 element plus svg 图标
|
||||
* @param app vue 实例
|
||||
* @description 使用:https://element-plus.gitee.io/zh-CN/component/icon.html
|
||||
*/
|
||||
function elSvg(app: any) {
|
||||
const icons = svg as any;
|
||||
for (const i in icons) {
|
||||
app.component(`${icons[i].name}`, icons[i]);
|
||||
}
|
||||
app.component('SvgIcon', SvgIcon);
|
||||
}
|
||||
|
||||
elSvg(app);
|
||||
registElSvgIcon(app);
|
||||
directive(app);
|
||||
|
||||
app.use(router)
|
||||
.use(store, key)
|
||||
.use(ElementPlus, { size: globalComponentSize, locale: zhCn})
|
||||
app.use(pinia)
|
||||
.use(router)
|
||||
.use(ElementPlus, { size: globalComponentSize, locale: zhCn })
|
||||
.mount('#app');
|
||||
|
||||
|
||||
// 自定义全局过滤器
|
||||
app.config.globalProperties.$filters = {
|
||||
dateFormat(value: any) {
|
||||
if (!value) {
|
||||
return ""
|
||||
}
|
||||
return dateStrFormat('yyyy-MM-dd HH:mm:ss', value)
|
||||
}
|
||||
}
|
||||
|
||||
// 屏蔽警告信息
|
||||
app.config.warnHandler = () => null;
|
||||
// 全局error处理
|
||||
app.config.errorHandler = function (err: any, vm, info) {
|
||||
// 如果是断言错误,则进行提示即可
|
||||
@@ -60,5 +37,3 @@ app.config.errorHandler = function (err: any, vm, info) {
|
||||
console.error(err, info)
|
||||
}
|
||||
}
|
||||
|
||||
app.config.globalProperties.mittBus = mitt();
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
|
||||
export const imports = {
|
||||
'RouterParent': RouterParent,
|
||||
"Home": () => import('@/views/home/index.vue'),
|
||||
'Personal': () => import('@/views/personal/index.vue'),
|
||||
// machine
|
||||
"MachineList": () => import('@/views/ops/machine'),
|
||||
// sys
|
||||
"ResourceList": () => import('@/views/system/resource'),
|
||||
"RoleList": () => import('@/views/system/role'),
|
||||
"AccountList": () => import('@/views/system/account'),
|
||||
// project
|
||||
"ProjectList": () => import('@/views/ops/project/ProjectList.vue'),
|
||||
// db
|
||||
"DbList": () => import('@/views/ops/db/DbList.vue'),
|
||||
"SqlExec": () => import('@/views/ops/db'),
|
||||
// redis
|
||||
"RedisList": () => import('@/views/ops/redis'),
|
||||
"DataOperation": () => import('@/views/ops/redis/DataOperation.vue'),
|
||||
// mongo
|
||||
"MongoDataOp": () => import('@/views/ops/mongo/MongoDataOp.vue'),
|
||||
// redis
|
||||
"MongoList": () => import('@/views/ops/mongo/MongoList.vue'),
|
||||
}
|
||||
@@ -1,14 +1,25 @@
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import { store } from '@/store/index.ts';
|
||||
import { getSession, clearSession } from '@/common/utils/storage.ts';
|
||||
import { templateResolve } from '@/common/utils/string.ts'
|
||||
import { NextLoading } from '@/common/utils/loading.ts';
|
||||
import { dynamicRoutes, staticRoutes, pathMatch } from './route.ts'
|
||||
import { imports } from './imports';
|
||||
import { getSession, clearSession } from '@/common/utils/storage';
|
||||
import { templateResolve } from '@/common/utils/string'
|
||||
import { NextLoading } from '@/common/utils/loading';
|
||||
import { dynamicRoutes, staticRoutes, pathMatch } from './route'
|
||||
import openApi from '@/common/openApi';
|
||||
import sockets from '@/common/sockets';
|
||||
import pinia from '@/store/index';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
import { useKeepALiveNames } from '@/store/keepAliveNames';
|
||||
|
||||
/**
|
||||
* 获取目录下的 .vue、.tsx 全部文件
|
||||
* @method import.meta.glob
|
||||
* @link 参考:https://cn.vitejs.dev/guide/features.html#json
|
||||
*/
|
||||
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
|
||||
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...viewsModules });
|
||||
|
||||
// 添加静态路由
|
||||
const router = createRouter({
|
||||
@@ -24,7 +35,7 @@ export function initAllFun() {
|
||||
// 无 token 停止执行下一步
|
||||
return false
|
||||
}
|
||||
store.dispatch('userInfos/setUserInfos'); // 触发初始化用户信息
|
||||
useUserInfo().setUserInfo({});
|
||||
router.addRoute(pathMatch); // 添加404界面
|
||||
resetRoute(); // 删除/重置路由
|
||||
// 添加动态路由
|
||||
@@ -32,23 +43,20 @@ export function initAllFun() {
|
||||
router.addRoute((route as unknown) as RouteRecordRaw);
|
||||
});
|
||||
// 过滤权限菜单
|
||||
store.dispatch('routesList/setRoutesList', setFilterMenuFun(dynamicRoutes[0].children, store.state.userInfos.userInfos.menus));
|
||||
useRoutesList().setRoutesList(setFilterMenuFun(dynamicRoutes[0].children, useUserInfo().userInfo.menus))
|
||||
}
|
||||
|
||||
// 后端控制路由:模拟执行路由数据初始化
|
||||
export function initBackEndControlRoutesFun() {
|
||||
// 后端控制路由:执行路由数据初始化
|
||||
export async function initBackEndControlRoutesFun() {
|
||||
NextLoading.start(); // 界面 loading 动画开始执行
|
||||
const token = getSession('token'); // 获取浏览器缓存 token 值
|
||||
if (!token) {
|
||||
// 无 token 停止执行下一步
|
||||
return false
|
||||
}
|
||||
store.dispatch('userInfos/setUserInfos'); // 触发初始化用户信息
|
||||
let menuRoute = getSession('menus')
|
||||
if (!menuRoute) {
|
||||
menuRoute = getBackEndControlRoutes(); // 获取路由
|
||||
// const oldRoutes = res; // 获取接口原始路由(未处理component)
|
||||
}
|
||||
useUserInfo().setUserInfo({});
|
||||
// 获取路由
|
||||
let menuRoute = await getBackEndControlRoutes();
|
||||
dynamicRoutes[0].children = backEndRouterConverter(menuRoute); // 处理路由(component)
|
||||
// 添加404界面
|
||||
router.addRoute(pathMatch);
|
||||
@@ -57,12 +65,20 @@ export function initBackEndControlRoutesFun() {
|
||||
formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes)).forEach((route: any) => {
|
||||
router.addRoute((route as unknown) as RouteRecordRaw);
|
||||
});
|
||||
store.dispatch('routesList/setRoutesList', dynamicRoutes[0].children);
|
||||
useRoutesList().setRoutesList(dynamicRoutes[0].children)
|
||||
}
|
||||
|
||||
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
export function getBackEndControlRoutes() {
|
||||
return openApi.getMenuRoute({});
|
||||
export async function getBackEndControlRoutes() {
|
||||
try {
|
||||
const menuAndPermission = await openApi.getPermissions.request();
|
||||
// 赋值权限码,用于控制按钮等
|
||||
useUserInfo().userInfo.permissions = menuAndPermission.permissions;
|
||||
return menuAndPermission.menus;
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 后端控制路由,后端返回路由 转换为vue route
|
||||
@@ -76,7 +92,7 @@ export function backEndRouterConverter(routes: any, parentPath: string = "/") {
|
||||
item.meta = JSON.parse(item.meta)
|
||||
// 将meta.comoponet 解析为route.component
|
||||
if (item.meta.component) {
|
||||
item.component = imports[item.meta.component as string]
|
||||
item.component = dynamicImport(dynamicViewsModules, item.meta.component)
|
||||
delete item.meta['component']
|
||||
}
|
||||
// route.path == resource.code
|
||||
@@ -106,6 +122,27 @@ export function backEndRouterConverter(routes: any, parentPath: string = "/") {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端路由 component 转换函数
|
||||
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
|
||||
* @param component 当前要处理项 component
|
||||
* @returns 返回处理成函数后的 component
|
||||
*/
|
||||
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
|
||||
const keys = Object.keys(dynamicViewsModules);
|
||||
const matchKeys = keys.filter((key) => {
|
||||
const k = key.replace(/..\/views|../, '');
|
||||
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
|
||||
});
|
||||
if (matchKeys?.length === 1) {
|
||||
const matchKey = matchKeys[0];
|
||||
return dynamicViewsModules[matchKey];
|
||||
}
|
||||
if (matchKeys?.length > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 多级嵌套数组处理成一维数组
|
||||
export function formatFlatteningRoutes(arr: any) {
|
||||
if (arr.length <= 0) return false;
|
||||
@@ -134,7 +171,7 @@ export function formatTwoStageRoutes(arr: any) {
|
||||
}
|
||||
}
|
||||
});
|
||||
store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);
|
||||
useKeepALiveNames().setCacheKeepAlive(cacheList);
|
||||
return newArr;
|
||||
}
|
||||
|
||||
@@ -167,7 +204,7 @@ export function setFilterRoute(chil: any) {
|
||||
chil.forEach((route: any) => {
|
||||
// 如果路由需要拥有指定code才可访问,则校验该用户菜单是否存在该code
|
||||
if (route.meta.code) {
|
||||
store.state.userInfos.userInfos.menus.forEach((m: any) => {
|
||||
useUserInfo().userInfo.menus.forEach((m: any) => {
|
||||
if (route.meta.code == m) {
|
||||
filterRoute.push({ ...route })
|
||||
}
|
||||
@@ -188,33 +225,35 @@ export function setFilterRouteEnd() {
|
||||
|
||||
// 删除/重置路由
|
||||
export function resetRoute() {
|
||||
store.state.routesList.routesList.forEach((route: any) => {
|
||||
useRoutesList().routesList.forEach((route: any) => {
|
||||
const { name } = route;
|
||||
router.hasRoute(name) && router.removeRoute(name);
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化方法执行
|
||||
const { isRequestRoutes } = store.state.themeConfig.themeConfig;
|
||||
if (!isRequestRoutes) {
|
||||
// 未开启后端控制路由
|
||||
initAllFun();
|
||||
} else if (isRequestRoutes) {
|
||||
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
initBackEndControlRoutesFun();
|
||||
export async function initRouter() {
|
||||
// 初始化方法执行
|
||||
const { isRequestRoutes } = useThemeConfig(pinia).themeConfig;
|
||||
if (!isRequestRoutes) {
|
||||
// 未开启后端控制路由
|
||||
initAllFun();
|
||||
} else if (isRequestRoutes) {
|
||||
// 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
|
||||
await initBackEndControlRoutesFun();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let SysWs: any;
|
||||
let loadRouter = false;
|
||||
|
||||
// 路由加载前
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.configure({ showSpinner: false });
|
||||
if (to.meta.title) NProgress.start();
|
||||
|
||||
// 如果有标题参数,则再原标题后加上参数来区别
|
||||
if (to.meta.titleRename) {
|
||||
to.meta.title = templateResolve(to.meta.title, to.query)
|
||||
to.meta.title = templateResolve(to.meta.title as string, to.query)
|
||||
}
|
||||
|
||||
const token = getSession('token');
|
||||
@@ -245,7 +284,12 @@ router.beforeEach((to, from, next) => {
|
||||
if (!SysWs && to.path != '/machine/terminal') {
|
||||
SysWs = sockets.sysMsgSocket();
|
||||
}
|
||||
if (store.state.routesList.routesList.length > 0) {
|
||||
// 不存在路由(避免刷新页面找不到路由)并且未加载过(避免token过期,导致获取权限接口报权限不足,无限获取),则重新初始化路由
|
||||
if (useRoutesList().routesList.length == 0 && !loadRouter) {
|
||||
await initRouter();
|
||||
loadRouter = true;
|
||||
next({ path: to.path, query: to.query });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import Layout from '@/views/layout/index.vue'
|
||||
import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
|
||||
// 定义动态路由
|
||||
export const dynamicRoutes = [
|
||||
@@ -12,119 +11,109 @@ export const dynamicRoutes = [
|
||||
meta: {
|
||||
isKeepAlive: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
// iframe链接
|
||||
link: '',
|
||||
// 是否在菜单栏显示,默认显示
|
||||
isHide: false,
|
||||
isKeepAlive: true,
|
||||
// tag标签是否不可删除
|
||||
isAffix: true,
|
||||
// 是否为iframe
|
||||
isIframe: false,
|
||||
icon: 'el-icon-s-home',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/sys',
|
||||
name: 'Resource',
|
||||
redirect: '/sys/resources',
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'sys',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'sys/resources',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/resource'),
|
||||
meta: {
|
||||
title: '资源管理',
|
||||
code: 'resource:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/roles',
|
||||
name: 'RoleList',
|
||||
component: () => import('@/views/system/role'),
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
code: 'role:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/accounts',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/account'),
|
||||
meta: {
|
||||
title: '账号管理',
|
||||
code: 'account:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/machine',
|
||||
name: 'Machine',
|
||||
redirect: '/machine/list',
|
||||
meta: {
|
||||
title: '机器管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'machine',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/list',
|
||||
name: 'MachineList',
|
||||
component: () => import('@/views/ops/machine'),
|
||||
meta: {
|
||||
title: '机器列表',
|
||||
code: 'machine:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/personal',
|
||||
name: 'personal',
|
||||
component: () => import('@/views/personal/index.vue'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-user',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/iframes',
|
||||
name: 'layoutIfameView',
|
||||
component: RouterParent,
|
||||
meta: {
|
||||
title: 'iframe',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin',
|
||||
isIframe: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
children: []
|
||||
// children: [
|
||||
// {
|
||||
// path: '/home',
|
||||
// name: 'home',
|
||||
// component: () => import('@/views/home/index.vue'),
|
||||
// meta: {
|
||||
// title: '首页',
|
||||
// // iframe链接
|
||||
// link: '',
|
||||
// // 是否在菜单栏显示,默认显示
|
||||
// isHide: false,
|
||||
// isKeepAlive: true,
|
||||
// // tag标签是否不可删除
|
||||
// isAffix: true,
|
||||
// // 是否为iframe
|
||||
// isIframe: false,
|
||||
// icon: 'el-icon-s-home',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: '/sys',
|
||||
// name: 'Resource',
|
||||
// redirect: '/sys/resources',
|
||||
// meta: {
|
||||
// title: '系统管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'sys',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'sys/resources',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/resource'),
|
||||
// meta: {
|
||||
// title: '资源管理',
|
||||
// code: 'resource:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/roles',
|
||||
// name: 'RoleList',
|
||||
// component: () => import('@/views/system/role'),
|
||||
// meta: {
|
||||
// title: '角色管理',
|
||||
// code: 'role:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/accounts',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/account'),
|
||||
// meta: {
|
||||
// title: '账号管理',
|
||||
// code: 'account:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/machine',
|
||||
// name: 'Machine',
|
||||
// redirect: '/machine/list',
|
||||
// meta: {
|
||||
// title: '机器管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'machine',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/list',
|
||||
// name: 'MachineList',
|
||||
// component: () => import('@/views/ops/machine'),
|
||||
// meta: {
|
||||
// title: '机器列表',
|
||||
// code: 'machine:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/personal',
|
||||
// name: 'personal',
|
||||
// component: () => import('@/views/personal/index.vue'),
|
||||
// meta: {
|
||||
// title: '个人中心',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-user',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -135,7 +124,7 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
name: 'login',
|
||||
component: () => import('@/views/login/index.vue'),
|
||||
meta: {
|
||||
title: '登陆',
|
||||
title: '登录',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -163,7 +152,6 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
title: '终端 | {name}',
|
||||
// 是否根据query对标题名进行参数替换,即最终显示为‘终端_机器名’
|
||||
titleRename: true,
|
||||
icon: 'iconfont icon-caidan',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
import { InjectionKey } from 'vue';
|
||||
import { createStore, useStore as baseUseStore, Store } from 'vuex';
|
||||
import { RootStateTypes } from '@/store/interface/index';
|
||||
import themeConfig from '@/store/modules/themeConfig.ts';
|
||||
import routesList from '@/store/modules/routesList.ts';
|
||||
import keepAliveNames from '@/store/modules/keepAliveNames.ts';
|
||||
import userInfos from '@/store/modules/userInfos.ts';
|
||||
// https://pinia.vuejs.org/
|
||||
import { createPinia } from 'pinia';
|
||||
|
||||
export const key: InjectionKey<Store<RootStateTypes>> = Symbol();
|
||||
// 创建
|
||||
const pinia = createPinia();
|
||||
|
||||
export const store = createStore<RootStateTypes>({
|
||||
modules: {
|
||||
themeConfig,
|
||||
routesList,
|
||||
keepAliveNames,
|
||||
userInfos,
|
||||
},
|
||||
});
|
||||
|
||||
export function useStore() {
|
||||
return baseUseStore(key);
|
||||
}
|
||||
// 导出
|
||||
export default pinia;
|
||||
|
||||
35
mayfly_go_web/src/store/keepAliveNames.ts
Normal file
35
mayfly_go_web/src/store/keepAliveNames.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
/**
|
||||
* 路由缓存列表
|
||||
* @methods setCacheKeepAlive 设置要缓存的路由 names(开启 Tagsview)
|
||||
* @methods addCachedView 添加要缓存的路由 names(关闭 Tagsview)
|
||||
* @methods delCachedView 删除要缓存的路由 names(关闭 Tagsview)
|
||||
* @methods delOthersCachedViews 右键菜单`关闭其它`,删除要缓存的路由 names(关闭 Tagsview)
|
||||
* @methods delAllCachedViews 右键菜单`全部关闭`,删除要缓存的路由 names(关闭 Tagsview)
|
||||
*/
|
||||
export const useKeepALiveNames = defineStore('keepALiveNames', {
|
||||
state: (): KeepAliveNamesState => ({
|
||||
keepAliveNames: [],
|
||||
cachedViews: [],
|
||||
}),
|
||||
actions: {
|
||||
async setCacheKeepAlive(data: Array<string>) {
|
||||
this.keepAliveNames = data;
|
||||
},
|
||||
async addCachedView(view: any) {
|
||||
if (view.meta.isKeepAlive) this.cachedViews?.push(view.name);
|
||||
},
|
||||
async delCachedView(view: any) {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
index > -1 && this.cachedViews.splice(index, 1);
|
||||
},
|
||||
async delOthersCachedViews(view: any) {
|
||||
if (view.meta.isKeepAlive) this.cachedViews = [view.name];
|
||||
else this.cachedViews = [];
|
||||
},
|
||||
async delAllCachedViews() {
|
||||
this.cachedViews = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Module } from 'vuex';
|
||||
// 此处加上 `.ts` 后缀报错,具体原因不详
|
||||
import { KeepAliveNamesState, RootStateTypes } from '@/store/interface/index';
|
||||
|
||||
const keepAliveNamesModule: Module<KeepAliveNamesState, RootStateTypes> = {
|
||||
namespaced: true,
|
||||
state: {
|
||||
keepAliveNames: [],
|
||||
},
|
||||
mutations: {
|
||||
// 设置路由缓存(name字段)
|
||||
getCacheKeepAlive(state: any, data: Array<string>) {
|
||||
state.keepAliveNames = data;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 设置路由缓存(name字段)
|
||||
async setCacheKeepAlive({ commit }, data: Array<string>) {
|
||||
commit('getCacheKeepAlive', data);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default keepAliveNamesModule;
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Module } from 'vuex';
|
||||
// 此处加上 `.ts` 后缀报错,具体原因不详
|
||||
import { RoutesListState, RootStateTypes } from '@/store/interface/index';
|
||||
|
||||
const routesListModule: Module<RoutesListState, RootStateTypes> = {
|
||||
namespaced: true,
|
||||
state: {
|
||||
routesList: [],
|
||||
},
|
||||
mutations: {
|
||||
// 设置路由,菜单中使用到
|
||||
getRoutesList(state: any, data: Array<object>) {
|
||||
state.routesList = data;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 设置路由,菜单中使用到
|
||||
async setRoutesList({ commit }, data: any) {
|
||||
commit('getRoutesList', data);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default routesListModule;
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Module } from 'vuex';
|
||||
import { getSession } from '@/common/utils/storage.ts';
|
||||
// 此处加上 `.ts` 后缀报错,具体原因不详
|
||||
import { UserInfosState, RootStateTypes } from '@/store/interface/index';
|
||||
|
||||
const userInfosModule: Module<UserInfosState, RootStateTypes> = {
|
||||
namespaced: true,
|
||||
state: {
|
||||
userInfos: {},
|
||||
},
|
||||
mutations: {
|
||||
// 设置用户信息
|
||||
getUserInfos(state: any, data: object) {
|
||||
state.userInfos = data;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 设置用户信息
|
||||
async setUserInfos({ commit }, data: object) {
|
||||
if (data) {
|
||||
commit('getUserInfos', data);
|
||||
} else {
|
||||
if (getSession('userInfo')) commit('getUserInfos', getSession('userInfo'));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default userInfosModule;
|
||||
18
mayfly_go_web/src/store/routesList.ts
Normal file
18
mayfly_go_web/src/store/routesList.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
/**
|
||||
* 路由列表
|
||||
* @methods setRoutesList 设置路由数据
|
||||
* @methods setColumnsMenuHover 设置分栏布局菜单鼠标移入 boolean
|
||||
* @methods setColumnsNavHover 设置分栏布局最左侧导航鼠标移入 boolean
|
||||
*/
|
||||
export const useRoutesList = defineStore('routesList', {
|
||||
state: (): RoutesListState => ({
|
||||
routesList: [],
|
||||
}),
|
||||
actions: {
|
||||
async setRoutesList(data: Array<string>) {
|
||||
this.routesList = data;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,10 +1,7 @@
|
||||
import { Module } from 'vuex';
|
||||
// 此处加上 `.ts` 后缀报错,具体原因不详
|
||||
import { ThemeConfigState, RootStateTypes } from '@/store/interface/index';
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
|
||||
namespaced: true,
|
||||
state: {
|
||||
export const useThemeConfig = defineStore('themeConfig', {
|
||||
state: (): ThemeConfigState => ({
|
||||
themeConfig: {
|
||||
// 是否开启布局配置抽屉
|
||||
isDrawer: false,
|
||||
@@ -73,12 +70,13 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
|
||||
isBreadcrumb: true,
|
||||
// 是否开启 Tagsview
|
||||
isTagsview: true,
|
||||
isShareTagsView: false,
|
||||
// 是否开启 Breadcrumb 图标
|
||||
isBreadcrumbIcon: true,
|
||||
// 是否开启 Tagsview 图标
|
||||
isTagsviewIcon: true,
|
||||
// 是否开启 TagsView 缓存
|
||||
isCacheTagsView: false,
|
||||
isCacheTagsView: true,
|
||||
// 是否开启 TagsView 拖拽
|
||||
isSortableTagsView: true,
|
||||
// 是否开启 Footer 底部版权信息
|
||||
@@ -94,8 +92,8 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
|
||||
|
||||
/* 其它设置
|
||||
------------------------------- */
|
||||
// 默认 Tagsview 风格,可选 1、 tags-style-one 2、 tags-style-two 3、 tags-style-three 4、 tags-style-four
|
||||
tagsStyle: 'tags-style-one',
|
||||
// 默认 Tagsview 风格,可选 1、 tags-style-one 2、 tags-style-two 3、 tags-style-three
|
||||
tagsStyle: 'tags-style-three',
|
||||
// 默认主页面切换动画,可选 1、 slide-right 2、 slide-left 3、 opacitys
|
||||
animation: 'slide-right',
|
||||
// 默认分栏高亮风格,可选 1、 圆角 columns-round 2、 卡片 columns-card
|
||||
@@ -107,12 +105,16 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
|
||||
layout: 'classic',
|
||||
|
||||
// ssh终端字体颜色
|
||||
terminalForeground: '#7e9192',
|
||||
terminalForeground: '#C5C8C6',
|
||||
// ssh终端背景色
|
||||
terminalBackground: '#002833',
|
||||
terminalBackground: '#121212',
|
||||
// ssh终端cursor色
|
||||
terminalCursor: '#268F81',
|
||||
terminalFontSize: 15,
|
||||
terminalCursor: '#F0CC09',
|
||||
terminalFontSize: 14,
|
||||
terminalFontWeight: 'bold',
|
||||
|
||||
// 编辑器主题
|
||||
editorTheme: 'vs',
|
||||
|
||||
|
||||
/* 后端控制路由
|
||||
@@ -131,19 +133,11 @@ const themeConfigModule: Module<ThemeConfigState, RootStateTypes> = {
|
||||
// 默认全局组件大小,可选值"<|large|default|small>",默认 ''
|
||||
globalComponentSize: '',
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
// 设置布局配置
|
||||
getThemeConfig(state: any, data: object) {
|
||||
state.themeConfig = data;
|
||||
},
|
||||
},
|
||||
}),
|
||||
actions: {
|
||||
// 设置布局配置
|
||||
setThemeConfig({ commit }, data: object) {
|
||||
commit('getThemeConfig', data);
|
||||
setThemeConfig(data: ThemeConfigState) {
|
||||
this.themeConfig = data.themeConfig;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default themeConfigModule;
|
||||
})
|
||||
19
mayfly_go_web/src/store/userInfo.ts
Normal file
19
mayfly_go_web/src/store/userInfo.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
|
||||
export const useUserInfo = defineStore('userInfo', {
|
||||
state: (): UserInfoState => ({
|
||||
userInfo: {},
|
||||
}),
|
||||
actions: {
|
||||
// 设置用户信息
|
||||
async setUserInfo(data: object) {
|
||||
const ui = getSession('userInfo')
|
||||
if (ui) {
|
||||
this.userInfo = ui;
|
||||
} else {
|
||||
this.userInfo = data;
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,225 +1,261 @@
|
||||
/* 初始化样式
|
||||
------------------------------- */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
outline: none !important;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: Microsoft YaHei, Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, SimSun, sans-serif;
|
||||
font-weight: 450;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: #f8f8f8;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
font-weight: 450;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
background-color: #f8f8f8;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 主布局样式
|
||||
------------------------------- */
|
||||
.layout-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.layout-aside {
|
||||
background: var(--bg-menuBar);
|
||||
box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
|
||||
height: inherit;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden !important;
|
||||
.el-scrollbar__view {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
.layout-header {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.layout-main {
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.el-scrollbar {
|
||||
width: 100%;
|
||||
}
|
||||
.layout-view-bg-white {
|
||||
background: white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
.layout-el-aside-br-color {
|
||||
border-right: 1px solid rgb(238, 238, 238);
|
||||
}
|
||||
.layout-aside-width-default {
|
||||
width: 220px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.layout-aside-width64 {
|
||||
width: 64px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.layout-aside-width1 {
|
||||
width: 1px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
.layout-scrollbar {
|
||||
@extend .el-scrollbar;
|
||||
padding: 10px;
|
||||
}
|
||||
.layout-mian-height-50 {
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
.layout-columns-warp {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.layout-hide {
|
||||
display: none;
|
||||
}
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.layout-aside {
|
||||
background: var(--bg-menuBar);
|
||||
box-shadow: 2px 0 6px rgb(0 21 41 / 1%);
|
||||
height: inherit;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden !important;
|
||||
|
||||
.el-scrollbar__view {
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-header {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.layout-main {
|
||||
padding: 0 !important;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.layout-view-bg-white {
|
||||
background: white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.layout-el-aside-br-color {
|
||||
border-right: 1px solid rgb(238, 238, 238);
|
||||
}
|
||||
|
||||
.layout-aside-width-default {
|
||||
width: 220px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-aside-width64 {
|
||||
width: 64px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-aside-width1 {
|
||||
width: 1px !important;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.layout-scrollbar {
|
||||
@extend .el-scrollbar;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.layout-mian-height-50 {
|
||||
height: calc(100vh - 50px);
|
||||
}
|
||||
|
||||
.layout-columns-warp {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.layout-hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* element plus 全局样式
|
||||
------------------------------- */
|
||||
.layout-breadcrumb-seting {
|
||||
.el-drawer__header {
|
||||
padding: 0 15px !important;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom: 1px solid rgb(230, 230, 230);
|
||||
}
|
||||
.el-divider {
|
||||
background-color: rgb(230, 230, 230);
|
||||
}
|
||||
.el-drawer__header {
|
||||
padding: 0 15px !important;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0 !important;
|
||||
border-bottom: 1px solid rgb(230, 230, 230);
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
background-color: rgb(230, 230, 230);
|
||||
}
|
||||
}
|
||||
|
||||
/* nprogress 进度条跟随主题颜色
|
||||
------------------------------- */
|
||||
#nprogress {
|
||||
.bar {
|
||||
background: var(--color-primary) !important;
|
||||
z-index: 9999999 !important;
|
||||
}
|
||||
.bar {
|
||||
background: var(--color-primary) !important;
|
||||
z-index: 9999999 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* flex 弹性布局
|
||||
------------------------------- */
|
||||
.flex {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-center {
|
||||
@extend .flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@extend .flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.flex-margin {
|
||||
margin: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.flex-warp {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin: 0 -5px;
|
||||
|
||||
.flex-warp-item {
|
||||
padding: 5px;
|
||||
|
||||
.flex-warp-item-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 宽高 100%
|
||||
------------------------------- */
|
||||
.w100 {
|
||||
width: 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.h100 {
|
||||
height: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.vh100 {
|
||||
height: 100vh !important;
|
||||
height: 100vh !important;
|
||||
}
|
||||
|
||||
.max100vh {
|
||||
max-height: 100vh !important;
|
||||
max-height: 100vh !important;
|
||||
}
|
||||
|
||||
.min100vh {
|
||||
min-height: 100vh !important;
|
||||
min-height: 100vh !important;
|
||||
}
|
||||
|
||||
/* 颜色值
|
||||
------------------------------- */
|
||||
.color-primary {
|
||||
color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.color-success {
|
||||
color: var(--color-success);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.color-warning {
|
||||
color: var(--color-warning);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.color-danger {
|
||||
color: var(--color-danger);
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.color-info {
|
||||
color: var(--color-info);
|
||||
color: var(--color-info);
|
||||
}
|
||||
|
||||
/* 字体大小全局样式
|
||||
------------------------------- */
|
||||
@for $i from 10 through 32 {
|
||||
.font#{$i} {
|
||||
font-size: #{$i}px !important;
|
||||
}
|
||||
.font#{$i} {
|
||||
font-size: #{$i}px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 外边距、内边距全局样式
|
||||
------------------------------- */
|
||||
@for $i from 1 through 35 {
|
||||
.mt#{$i} {
|
||||
margin-top: #{$i}px !important;
|
||||
}
|
||||
.mr#{$i} {
|
||||
margin-right: #{$i}px !important;
|
||||
}
|
||||
.mb#{$i} {
|
||||
margin-bottom: #{$i}px !important;
|
||||
}
|
||||
.ml#{$i} {
|
||||
margin-left: #{$i}px !important;
|
||||
}
|
||||
.pt#{$i} {
|
||||
padding-top: #{$i}px !important;
|
||||
}
|
||||
.pr#{$i} {
|
||||
padding-right: #{$i}px !important;
|
||||
}
|
||||
.pb#{$i} {
|
||||
padding-bottom: #{$i}px !important;
|
||||
}
|
||||
.pl#{$i} {
|
||||
padding-left: #{$i}px !important;
|
||||
}
|
||||
.mt#{$i} {
|
||||
margin-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mr#{$i} {
|
||||
margin-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.mb#{$i} {
|
||||
margin-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.ml#{$i} {
|
||||
margin-left: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pt#{$i} {
|
||||
padding-top: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pr#{$i} {
|
||||
padding-right: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pb#{$i} {
|
||||
padding-bottom: #{$i}px !important;
|
||||
}
|
||||
|
||||
.pl#{$i} {
|
||||
padding-left: #{$i}px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -249,15 +285,22 @@ body,
|
||||
.el-menu .fa:not(.is-children) {
|
||||
font-size: 14px;
|
||||
}
|
||||
.gray-mode{
|
||||
|
||||
.gray-mode {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity .2s ease-in-out;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to
|
||||
|
||||
/* .fade-leave-active below version 2.1.8 */
|
||||
{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@@ -270,7 +313,7 @@ body,
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
width: 100%;
|
||||
@@ -285,8 +328,20 @@ body,
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
.el-form-item {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table-z-index-inherit .el-table .el-table__cell {
|
||||
z-index: inherit !important;
|
||||
}
|
||||
|
||||
.f12 {
|
||||
font-size: 12px
|
||||
}
|
||||
@@ -239,16 +239,6 @@
|
||||
color: set-color(primary);
|
||||
}
|
||||
|
||||
/* Switch 开关
|
||||
------------------------------- */
|
||||
.el-switch.is-checked .el-switch__core {
|
||||
border-color: set-color(primary);
|
||||
background-color: set-color(primary);
|
||||
}
|
||||
.el-switch__label.is-active {
|
||||
color: set-color(primary);
|
||||
}
|
||||
|
||||
/* Slider 滑块
|
||||
------------------------------- */
|
||||
.el-slider__bar {
|
||||
@@ -957,12 +947,6 @@
|
||||
.el-select-dropdown .el-scrollbar__wrap {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
.el-select-dropdown__wrap {
|
||||
max-height: 274px !important; /*修复Select 选择器高度问题*/
|
||||
}
|
||||
.el-cascader-menu__wrap.el-scrollbar__wrap {
|
||||
height: 204px !important; /*修复Cascader 级联选择器高度问题*/
|
||||
}
|
||||
|
||||
/* Drawer 抽屉
|
||||
------------------------------- */
|
||||
@@ -993,4 +977,15 @@
|
||||
}
|
||||
.el-drawer-fade-leave-active .el-drawer.ltr {
|
||||
animation: ltr-drawer-animation 0.3s ease !important;
|
||||
}
|
||||
}
|
||||
|
||||
// el-tooltip使用自定义主题时的样式
|
||||
.el-popper.is-customized {
|
||||
/* Set padding to ensure the height is 32px */
|
||||
// padding: 6px 12px;
|
||||
background: linear-gradient(90deg, rgb(159, 229, 151), rgb(204, 229, 129));
|
||||
}
|
||||
.el-popper.is-customized .el-popper__arrow::before {
|
||||
background: linear-gradient(45deg, #b2e68d, #bce689);
|
||||
right: 0;
|
||||
}
|
||||
@@ -3,85 +3,29 @@
|
||||
.icon-selector-popper {
|
||||
padding: 0 !important;
|
||||
.icon-selector-warp {
|
||||
height: 260px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.icon-selector-warp-title {
|
||||
position: absolute;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
left: 15px;
|
||||
}
|
||||
.el-tabs__header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.icon-selector-warp-row {
|
||||
max-height: 260px;
|
||||
overflow-y: auto;
|
||||
padding: 15px 15px 5px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
.ele-col:nth-last-child(1),
|
||||
.ele-col:nth-last-child(2) {
|
||||
display: none;
|
||||
}
|
||||
.awe-col:nth-child(-n + 24) {
|
||||
display: none;
|
||||
}
|
||||
.icon-selector-warp-item {
|
||||
display: flex;
|
||||
border: 1px solid #ebeef5;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
transition: all 0.3s ease;
|
||||
.icon-selector-warp-item-value {
|
||||
transition: all 0.3s ease;
|
||||
i {
|
||||
font-size: 20px;
|
||||
color: #606266;
|
||||
}
|
||||
border-bottom: 1px solid var(--el-border-color-light);
|
||||
margin: 0 !important;
|
||||
.el-tabs__nav-wrap {
|
||||
&::after {
|
||||
height: 0 !important;
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid var(--color-primary);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
.icon-selector-warp-item-value {
|
||||
i {
|
||||
color: var(--color-primary);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-selector-active {
|
||||
border: 1px solid var(--color-primary);
|
||||
.icon-selector-warp-item-value {
|
||||
i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-selector-all {
|
||||
.el-input {
|
||||
padding: 0 15px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
&-tabs {
|
||||
display: flex;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 15px;
|
||||
margin-bottom: 5px;
|
||||
&-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
&-active {
|
||||
background: var(--color-primary);
|
||||
border-radius: 5px;
|
||||
.label {
|
||||
color: #ffffff;
|
||||
}
|
||||
.el-tabs__item {
|
||||
padding: 0 5px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,6 @@
|
||||
@import './base.scss';
|
||||
@import './other.scss';
|
||||
@import './element.scss';
|
||||
@import './iconSelector.scss';
|
||||
@import './media/media.scss';
|
||||
@import './waves.scss';
|
||||
@import './iconSelector.scss';
|
||||
@@ -1,8 +1,5 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue';
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
declare module 'codemirror';
|
||||
declare module 'sql-formatter';
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
// 接口类型声明
|
||||
declare interface UserInfoState<T = any> {
|
||||
userInfo: any
|
||||
}
|
||||
|
||||
// 布局配置
|
||||
export interface ThemeConfigState {
|
||||
declare interface ThemeConfigState {
|
||||
themeConfig: {
|
||||
isDrawer: boolean;
|
||||
primary: string;
|
||||
@@ -30,6 +31,7 @@ export interface ThemeConfigState {
|
||||
isShowLogoChange: boolean;
|
||||
isBreadcrumb: boolean;
|
||||
isTagsview: boolean;
|
||||
isShareTagsView: boolean;
|
||||
isBreadcrumbIcon: boolean;
|
||||
isTagsviewIcon: boolean;
|
||||
isCacheTagsView: boolean;
|
||||
@@ -52,34 +54,24 @@ export interface ThemeConfigState {
|
||||
terminalBackground: string;
|
||||
terminalCursor: string;
|
||||
terminalFontSize: number;
|
||||
terminalFontWeight: string | any;
|
||||
editorTheme: string;
|
||||
};
|
||||
}
|
||||
|
||||
// TagsView 路由列表
|
||||
declare interface TagsViewRoutesState<T = any> {
|
||||
tagsViewRoutes: T[];
|
||||
isTagsViewCurrenFull: Boolean;
|
||||
}
|
||||
|
||||
// 路由列表
|
||||
export interface RoutesListState {
|
||||
routesList: Array<object>;
|
||||
declare interface RoutesListState {
|
||||
routesList: T[];
|
||||
}
|
||||
|
||||
// 路由缓存列表
|
||||
export interface KeepAliveNamesState {
|
||||
keepAliveNames: Array<string>;
|
||||
}
|
||||
|
||||
// 用户信息
|
||||
export interface UserInfosState {
|
||||
userInfos: object;
|
||||
}
|
||||
|
||||
// 后端返回原始路由(未处理时)
|
||||
// export interface RequestOldRoutesState {
|
||||
// requestOldRoutes: Array<object>;
|
||||
// }
|
||||
|
||||
// 主接口(顶级类型声明)
|
||||
export interface RootStateTypes {
|
||||
themeConfig: ThemeConfigState;
|
||||
routesList: RoutesListState;
|
||||
keepAliveNames: KeepAliveNamesState;
|
||||
userInfos: UserInfosState;
|
||||
// requestOldRoutes: RequestOldRoutesState;
|
||||
}
|
||||
declare interface KeepAliveNamesState {
|
||||
keepAliveNames: string[];
|
||||
cachedViews: string[];
|
||||
}
|
||||
14
mayfly_go_web/src/types/shim.d.ts
vendored
Normal file
14
mayfly_go_web/src/types/shim.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable */
|
||||
import {IDisposable} from 'monaco-editor';
|
||||
declare global {
|
||||
interface Window {
|
||||
completionItemProvider?: IDisposable | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// 申明外部 npm 插件模块
|
||||
declare module 'sql-formatter';
|
||||
declare module 'jsoneditor';
|
||||
declare module 'asciinema-player';
|
||||
declare module 'monaco-editor';
|
||||
declare module 'vue-grid-layout';
|
||||
@@ -1,6 +1,7 @@
|
||||
// 声明一个模块,防止引入文件时报错
|
||||
declare module '*.json';
|
||||
declare module '*.png';
|
||||
declare module '*.jpg';
|
||||
declare module '*.scss';
|
||||
declare module '*.ts';
|
||||
declare module '*.js';
|
||||
declare module '*.js';
|
||||
@@ -4,16 +4,17 @@
|
||||
<el-col :sm="6" class="mb15">
|
||||
<div @click="toPage({ id: 'personal' })" class="home-card-item home-card-first">
|
||||
<div class="flex-margin flex">
|
||||
<img :src="getUserInfos.photo" />
|
||||
<img :src="userInfo.photo" />
|
||||
<div class="home-card-first-right ml15">
|
||||
<div class="flex-margin">
|
||||
<div class="home-card-first-right-title">{{ `${currentTime}, ${getUserInfos.username}` }}</div>
|
||||
<div class="home-card-first-right-title">{{ `${currentTime}, ${userInfo.username}`
|
||||
}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList" :key="k">
|
||||
<el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList as any" :key="k">
|
||||
<div @click="toPage(v)" class="home-card-item home-card-item-box" :style="{ background: v.color }">
|
||||
<div class="home-card-item-flex">
|
||||
<div class="home-card-item-title pb3">{{ v.title }}</div>
|
||||
@@ -26,111 +27,101 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, onMounted, nextTick, computed } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
// import * as echarts from 'echarts';
|
||||
import { CountUp } from 'countup.js';
|
||||
import { formatAxis } from '@/common/utils/formatTime.ts';
|
||||
import { formatAxis } from '@/common/utils/format.ts';
|
||||
import { indexApi } from './api';
|
||||
import { useRouter } from 'vue-router';
|
||||
export default {
|
||||
name: 'HomePage',
|
||||
setup() {
|
||||
// const { proxy } = getCurrentInstance() as any;
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const state = reactive({
|
||||
topCardItemList: [
|
||||
{
|
||||
title: '项目数',
|
||||
id: 'projectNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
{
|
||||
title: 'Linux机器数',
|
||||
id: 'machineNum',
|
||||
color: '#F95959',
|
||||
},
|
||||
{
|
||||
title: '数据库总数',
|
||||
id: 'dbNum',
|
||||
color: '#8595F4',
|
||||
},
|
||||
{
|
||||
title: 'redis总数',
|
||||
id: 'redisNum',
|
||||
color: '#1abc9c',
|
||||
},
|
||||
],
|
||||
});
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useUserInfo } from '@/store/userInfo';
|
||||
|
||||
// 当前时间提示语
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
const router = useRouter();
|
||||
const { userInfo } = storeToRefs(useUserInfo());
|
||||
|
||||
// 初始化数字滚动
|
||||
const initNumCountUp = async () => {
|
||||
const res: any = await indexApi.getIndexCount.request();
|
||||
nextTick(() => {
|
||||
new CountUp('projectNum', res.projectNum).start();
|
||||
new CountUp('machineNum', res.machineNum).start();
|
||||
new CountUp('dbNum', res.dbNum).start();
|
||||
new CountUp('redisNum', res.redisNum).start();
|
||||
});
|
||||
};
|
||||
const state = reactive({
|
||||
topCardItemList: [
|
||||
{
|
||||
title: 'Linux机器',
|
||||
id: 'machineNum',
|
||||
color: '#F95959',
|
||||
},
|
||||
{
|
||||
title: '数据库',
|
||||
id: 'dbNum',
|
||||
color: '#8595F4',
|
||||
},
|
||||
{
|
||||
title: 'redis',
|
||||
id: 'redisNum',
|
||||
color: '#1abc9c',
|
||||
},
|
||||
{
|
||||
title: 'Mongo',
|
||||
id: 'mongoNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const toPage = (item: any) => {
|
||||
switch (item.id) {
|
||||
case 'personal': {
|
||||
router.push('/personal');
|
||||
break;
|
||||
}
|
||||
case 'projectNum': {
|
||||
router.push('/ops/projects');
|
||||
break;
|
||||
}
|
||||
case 'machineNum': {
|
||||
router.push('/ops/machines');
|
||||
break;
|
||||
}
|
||||
case 'dbNum': {
|
||||
router.push('/ops/dbms/dbs');
|
||||
break;
|
||||
}
|
||||
case 'redisNum': {
|
||||
router.push('/ops/redis/manage');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
const {
|
||||
topCardItemList,
|
||||
} = toRefs(state)
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initNumCountUp();
|
||||
// initHomeLaboratory();
|
||||
// initHomeOvertime();
|
||||
});
|
||||
// 当前时间提示语
|
||||
const currentTime = computed(() => {
|
||||
return formatAxis(new Date());
|
||||
});
|
||||
|
||||
// 获取用户信息 vuex
|
||||
const getUserInfos = computed(() => {
|
||||
return store.state.userInfos.userInfos;
|
||||
});
|
||||
|
||||
return {
|
||||
getUserInfos,
|
||||
currentTime,
|
||||
toPage,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
// 初始化数字滚动
|
||||
const initNumCountUp = async () => {
|
||||
const res: any = await indexApi.getIndexCount.request();
|
||||
nextTick(() => {
|
||||
new CountUp('mongoNum', res.mongoNum).start();
|
||||
new CountUp('machineNum', res.machineNum).start();
|
||||
new CountUp('dbNum', res.dbNum).start();
|
||||
new CountUp('redisNum', res.redisNum).start();
|
||||
});
|
||||
};
|
||||
|
||||
const toPage = (item: any) => {
|
||||
switch (item.id) {
|
||||
case 'personal': {
|
||||
router.push('/personal');
|
||||
break;
|
||||
}
|
||||
case 'mongoNum': {
|
||||
router.push('/mongo/mongo-data-operation');
|
||||
break;
|
||||
}
|
||||
case 'machineNum': {
|
||||
router.push('/machine/machines');
|
||||
break;
|
||||
}
|
||||
case 'dbNum': {
|
||||
router.push('/dbms/sql-exec');
|
||||
break;
|
||||
}
|
||||
case 'redisNum': {
|
||||
router.push('/redis/data-operation');
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initNumCountUp();
|
||||
// initHomeLaboratory();
|
||||
// initHomeOvertime();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.home-container {
|
||||
overflow-x: hidden;
|
||||
|
||||
.home-card-item {
|
||||
width: 100%;
|
||||
height: 103px;
|
||||
@@ -138,16 +129,19 @@ export default {
|
||||
border-radius: 4px;
|
||||
transition: all ease 0.3s;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.home-card-item-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
i {
|
||||
right: 0px !important;
|
||||
@@ -155,6 +149,7 @@ export default {
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
@@ -163,48 +158,59 @@ export default {
|
||||
transform: rotate(-30deg);
|
||||
transition: all ease 0.3s;
|
||||
}
|
||||
|
||||
.home-card-item-flex {
|
||||
padding: 0 20px;
|
||||
color: white;
|
||||
|
||||
.home-card-item-title,
|
||||
.home-card-item-tip {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.home-card-item-title-num {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.home-card-item-tip-num {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-card-first {
|
||||
background: white;
|
||||
border: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 100%;
|
||||
border: 2px solid var(--color-primary-light-5);
|
||||
}
|
||||
|
||||
.home-card-first-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.home-card-first-right-msg {
|
||||
font-size: 13px;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-monitor {
|
||||
height: 200px;
|
||||
|
||||
.flex-warp-item {
|
||||
width: 50%;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
|
||||
.flex-warp-item-box {
|
||||
margin: auto;
|
||||
height: auto;
|
||||
@@ -212,19 +218,24 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-warning-card {
|
||||
height: 292px;
|
||||
|
||||
::v-deep(.el-card) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.home-dynamic {
|
||||
height: 200px;
|
||||
|
||||
.home-dynamic-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
|
||||
&:first-of-type {
|
||||
.home-dynamic-item-line {
|
||||
i {
|
||||
@@ -232,20 +243,24 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-dynamic-item-left {
|
||||
text-align: right;
|
||||
.home-dynamic-item-left-time1 {
|
||||
}
|
||||
|
||||
.home-dynamic-item-left-time1 {}
|
||||
|
||||
.home-dynamic-item-left-time2 {
|
||||
font-size: 13px;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.home-dynamic-item-line {
|
||||
height: 60px;
|
||||
border-right: 2px dashed #dfdfdf;
|
||||
margin: 0 20px;
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
color: var(--color-primary);
|
||||
font-size: 12px;
|
||||
@@ -256,8 +271,10 @@ export default {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
.home-dynamic-item-right {
|
||||
flex: 1;
|
||||
|
||||
.home-dynamic-item-right-title {
|
||||
i {
|
||||
margin-right: 5px;
|
||||
@@ -270,6 +287,7 @@ export default {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.home-dynamic-item-right-label {
|
||||
font-size: 13px;
|
||||
color: gray;
|
||||
@@ -1,6 +1,6 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const indexApi = {
|
||||
getIndexCount: Api.create("/common/index/count", 'get'),
|
||||
getIndexCount: Api.newGet("/common/index/count"),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,132 +1,131 @@
|
||||
<template>
|
||||
<el-aside class="layout-aside" :class="setCollapseWidth" v-if="clientWidth > 1000">
|
||||
<el-aside class="layout-aside" :class="setCollapseWidth" v-if="state.clientWidth > 1000">
|
||||
<Logo v-if="setShowLogo" />
|
||||
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef">
|
||||
<Vertical :menuList="menuList" :class="setCollapseWidth" />
|
||||
<Vertical :menuList="state.menuList" :class="setCollapseWidth" />
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-drawer v-model="getThemeConfig.isCollapse" :with-header="false" direction="ltr" size="220px" v-else>
|
||||
<el-drawer v-model="themeConfig.isCollapse" :with-header="false" direction="ltr" size="220px" v-else>
|
||||
<el-aside class="layout-aside w100 h100">
|
||||
<Logo v-if="setShowLogo" />
|
||||
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef">
|
||||
<Vertical :menuList="menuList" />
|
||||
<Vertical :menuList="state.menuList" />
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, watch, getCurrentInstance, onBeforeMount, onUnmounted } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
<script lang="ts" setup name="layoutAside">
|
||||
import { reactive, computed, watch, getCurrentInstance, onBeforeMount, onUnmounted } from 'vue';
|
||||
import pinia from '@/store/index';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
import Logo from '@/views/layout/logo/index.vue';
|
||||
import Vertical from '@/views/layout/navMenu/vertical.vue';
|
||||
export default {
|
||||
name: 'layoutAside',
|
||||
components: { Logo, Vertical },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const state: any = reactive({
|
||||
menuList: [],
|
||||
clientWidth: '',
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 设置菜单展开/收起时的宽度
|
||||
const setCollapseWidth = computed(() => {
|
||||
let { layout, isCollapse, menuBar } = store.state.themeConfig.themeConfig;
|
||||
let asideBrColor =
|
||||
menuBar === '#FFFFFF' || menuBar === '#FFF' || menuBar === '#fff' || menuBar === '#ffffff' ? 'layout-el-aside-br-color' : '';
|
||||
if (layout === 'columns') {
|
||||
// 分栏布局,菜单收起时宽度给 1px
|
||||
if (isCollapse) {
|
||||
return ['layout-aside-width1', asideBrColor];
|
||||
} else {
|
||||
return ['layout-aside-width-default', asideBrColor];
|
||||
}
|
||||
} else {
|
||||
// 其它布局给 64px
|
||||
if (isCollapse) {
|
||||
return ['layout-aside-width64', asideBrColor];
|
||||
} else {
|
||||
return ['layout-aside-width-default', asideBrColor];
|
||||
}
|
||||
}
|
||||
});
|
||||
// 设置显示/隐藏 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let { layout, isShowLogo } = store.state.themeConfig.themeConfig;
|
||||
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
|
||||
});
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
if (store.state.themeConfig.themeConfig.layout === 'columns') return false;
|
||||
state.menuList = filterRoutesFun(store.state.routesList.routesList);
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 设置菜单导航是否固定(移动端)
|
||||
const initMenuFixed = (clientWidth: number) => {
|
||||
state.clientWidth = clientWidth;
|
||||
};
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(store.state.themeConfig.themeConfig, (val) => {
|
||||
if (val.isShowLogoChange !== val.isShowLogo) {
|
||||
if (!proxy.$refs.layoutAsideScrollbarRef) return false;
|
||||
proxy.$refs.layoutAsideScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(store.state, (val) => {
|
||||
if (val.routesList.routesList.length === state.menuList.length) return false;
|
||||
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initMenuFixed(document.body.clientWidth);
|
||||
setFilterRoutes();
|
||||
proxy.mittBus.on('setSendColumnsChildren', (res: any) => {
|
||||
state.menuList = res.children;
|
||||
});
|
||||
proxy.mittBus.on('setSendClassicChildren', (res: any) => {
|
||||
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = [];
|
||||
state.menuList = res.children;
|
||||
}
|
||||
});
|
||||
proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
proxy.mittBus.on('layoutMobileResize', (res: any) => {
|
||||
initMenuFixed(res.clientWidth);
|
||||
});
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('setSendColumnsChildren');
|
||||
proxy.mittBus.off('setSendClassicChildren');
|
||||
proxy.mittBus.off('getBreadcrumbIndexSetFilterRoutes');
|
||||
proxy.mittBus.off('layoutMobileResize');
|
||||
});
|
||||
return {
|
||||
setCollapseWidth,
|
||||
setShowLogo,
|
||||
getThemeConfig,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
const { routesList } = storeToRefs(useRoutesList());
|
||||
|
||||
const state: any = reactive({
|
||||
menuList: [],
|
||||
clientWidth: '',
|
||||
});
|
||||
|
||||
// 设置菜单展开/收起时的宽度
|
||||
const setCollapseWidth = computed(() => {
|
||||
let { layout, isCollapse, menuBar } = themeConfig.value;
|
||||
let asideBrColor =
|
||||
menuBar === '#FFFFFF' || menuBar === '#FFF' || menuBar === '#fff' || menuBar === '#ffffff' ? 'layout-el-aside-br-color' : '';
|
||||
if (layout === 'columns') {
|
||||
// 分栏布局,菜单收起时宽度给 1px
|
||||
if (isCollapse) {
|
||||
return ['layout-aside-width1', asideBrColor];
|
||||
} else {
|
||||
return ['layout-aside-width-default', asideBrColor];
|
||||
}
|
||||
} else {
|
||||
// 其它布局给 64px
|
||||
if (isCollapse) {
|
||||
return ['layout-aside-width64', asideBrColor];
|
||||
} else {
|
||||
return ['layout-aside-width-default', asideBrColor];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 设置显示/隐藏 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let { layout, isShowLogo } = themeConfig.value;
|
||||
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
|
||||
});
|
||||
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
if (themeConfig.value.layout === 'columns') return false;
|
||||
state.menuList = filterRoutesFun(routesList.value);
|
||||
};
|
||||
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 设置菜单导航是否固定(移动端)
|
||||
const initMenuFixed = (clientWidth: number) => {
|
||||
state.clientWidth = clientWidth;
|
||||
};
|
||||
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(themeConfig.value, (val) => {
|
||||
if (val.isShowLogoChange !== val.isShowLogo) {
|
||||
if (!proxy.$refs.layoutAsideScrollbarRef) return false;
|
||||
proxy.$refs.layoutAsideScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(pinia.state, (val) => {
|
||||
if (val.routesList.routesList.length === state.menuList.length) return false;
|
||||
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initMenuFixed(document.body.clientWidth);
|
||||
setFilterRoutes();
|
||||
mittBus.on('setSendColumnsChildren', (res: any) => {
|
||||
state.menuList = res.children;
|
||||
});
|
||||
mittBus.on('setSendClassicChildren', (res: any) => {
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = [];
|
||||
state.menuList = res.children;
|
||||
}
|
||||
});
|
||||
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
mittBus.on('layoutMobileResize', (res: any) => {
|
||||
initMenuFixed(res.clientWidth);
|
||||
});
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
mittBus.off('setSendColumnsChildren');
|
||||
mittBus.off('setSendClassicChildren');
|
||||
mittBus.off('getBreadcrumbIndexSetFilterRoutes');
|
||||
mittBus.off('layoutMobileResize');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,19 +2,12 @@
|
||||
<div class="layout-columns-aside">
|
||||
<el-scrollbar>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(v, k) in columnsAsideList"
|
||||
:key="k"
|
||||
@click="onColumnsAsideMenuClick(v, k)"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) columnsAsideOffsetTopRefs[k] = el;
|
||||
}
|
||||
"
|
||||
:class="{ 'layout-columns-active': liIndex === k }"
|
||||
:title="v.meta.title"
|
||||
>
|
||||
<div class="layout-columns-aside-li-box" v-if="!v.meta.link || (v.meta.link && v.meta.isIframe)">
|
||||
<li v-for="(v, k) in state.columnsAsideList" :key="k" @click="onColumnsAsideMenuClick(v, k)" :ref="
|
||||
(el) => {
|
||||
if (el) columnsAsideOffsetTopRefs[k] = el;
|
||||
}
|
||||
" :class="{ 'layout-columns-active': state.liIndex === k }" :title="v.meta.title">
|
||||
<div class="layout-columns-aside-li-box" v-if="!v.meta.link || (v.meta.link && v.meta.linkType == 1)">
|
||||
<i :class="v.meta.icon"></i>
|
||||
<div class="layout-columns-aside-li-box-title font12">
|
||||
{{ v.meta.title && v.meta.title.length >= 4 ? v.meta.title.substr(0, 4) : v.meta.title }}
|
||||
@@ -35,114 +28,103 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, ref, computed, onMounted, nextTick, getCurrentInstance, watch } from 'vue';
|
||||
<script lang="ts" setup name="layoutColumnsAside">
|
||||
import { reactive, ref, computed, onMounted, nextTick, getCurrentInstance, watch } from 'vue';
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
export default {
|
||||
name: 'layoutColumnsAside',
|
||||
setup() {
|
||||
const columnsAsideOffsetTopRefs: any = ref([]);
|
||||
const columnsAsideActiveRef = ref();
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
columnsAsideList: [],
|
||||
liIndex: 0,
|
||||
difference: 0,
|
||||
routeSplit: [],
|
||||
});
|
||||
// 设置高亮样式
|
||||
const setColumnsAsideStyle = computed(() => {
|
||||
return store.state.themeConfig.themeConfig.columnsAsideStyle;
|
||||
});
|
||||
// 设置菜单高亮位置移动
|
||||
const setColumnsAsideMove = (k: number) => {
|
||||
state.liIndex = k;
|
||||
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
|
||||
};
|
||||
// 菜单高亮点击事件
|
||||
const onColumnsAsideMenuClick = (v: Object, k: number) => {
|
||||
setColumnsAsideMove(k);
|
||||
let { path, redirect } = v as any;
|
||||
if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
};
|
||||
// 设置高亮动态位置
|
||||
const onColumnsAsideDown = (k: number) => {
|
||||
nextTick(() => {
|
||||
setColumnsAsideMove(k);
|
||||
});
|
||||
};
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
state.columnsAsideList = filterRoutesFun(store.state.routesList.routesList);
|
||||
const resData: any = setSendChildren(route.path);
|
||||
onColumnsAsideDown(resData.item[0].k);
|
||||
proxy.mittBus.emit('setSendColumnsChildren', resData);
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendChildren = (path: string) => {
|
||||
const currentPathSplit = path.split('/');
|
||||
let currentData: any = {};
|
||||
state.columnsAsideList.map((v: any, k: number) => {
|
||||
if (v.path === `/${currentPathSplit[1]}`) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = [{ ...v }];
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
|
||||
const setColumnsMenuHighlight = (path: string) => {
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
const routeFirst = `/${state.routeSplit[0]}`;
|
||||
const currentSplitRoute = state.columnsAsideList.find((v: any) => v.path === routeFirst);
|
||||
// 延迟拿值,防止取不到
|
||||
setTimeout(() => {
|
||||
onColumnsAsideDown(currentSplitRoute.k);
|
||||
}, 0);
|
||||
};
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(store.state, (val) => {
|
||||
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
|
||||
if (val.routesList.routesList.length === state.columnsAsideList.length) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setColumnsMenuHighlight(to.path);
|
||||
proxy.mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
|
||||
});
|
||||
return {
|
||||
columnsAsideOffsetTopRefs,
|
||||
columnsAsideActiveRef,
|
||||
onColumnsAsideDown,
|
||||
setColumnsAsideStyle,
|
||||
onColumnsAsideMenuClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
import pinia from '@/store/index';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const columnsAsideOffsetTopRefs: any = ref([]);
|
||||
const columnsAsideActiveRef = ref();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
columnsAsideList: [],
|
||||
liIndex: 0,
|
||||
difference: 0,
|
||||
routeSplit: [],
|
||||
});
|
||||
// 设置高亮样式
|
||||
const setColumnsAsideStyle = computed(() => {
|
||||
return useThemeConfig().themeConfig.columnsAsideStyle;
|
||||
});
|
||||
// 设置菜单高亮位置移动
|
||||
const setColumnsAsideMove = (k: number) => {
|
||||
state.liIndex = k;
|
||||
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
|
||||
};
|
||||
// 菜单高亮点击事件
|
||||
const onColumnsAsideMenuClick = (v: Object, k: number) => {
|
||||
setColumnsAsideMove(k);
|
||||
let { path, redirect } = v as any;
|
||||
if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
};
|
||||
// 设置高亮动态位置
|
||||
const onColumnsAsideDown = (k: number) => {
|
||||
nextTick(() => {
|
||||
setColumnsAsideMove(k);
|
||||
});
|
||||
};
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
state.columnsAsideList = filterRoutesFun(useRoutesList().routesList);
|
||||
const resData: any = setSendChildren(route.path);
|
||||
onColumnsAsideDown(resData.item[0].k);
|
||||
mittBus.emit('setSendColumnsChildren', resData);
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendChildren = (path: string) => {
|
||||
const currentPathSplit = path.split('/');
|
||||
let currentData: any = {};
|
||||
state.columnsAsideList.map((v: any, k: number) => {
|
||||
if (v.path === `/${currentPathSplit[1]}`) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = [{ ...v }];
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
|
||||
const setColumnsMenuHighlight = (path: string) => {
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
const routeFirst = `/${state.routeSplit[0]}`;
|
||||
const currentSplitRoute = state.columnsAsideList.find((v: any) => v.path === routeFirst);
|
||||
// 延迟拿值,防止取不到
|
||||
setTimeout(() => {
|
||||
onColumnsAsideDown(currentSplitRoute.k);
|
||||
}, 0);
|
||||
};
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(pinia.state, (val) => {
|
||||
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
|
||||
if (val.routesList.routesList.length === state.columnsAsideList.length) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
setColumnsMenuHighlight(to.path);
|
||||
mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -150,8 +132,10 @@ export default {
|
||||
width: 64px;
|
||||
height: 100%;
|
||||
background: var(--bg-columnsMenuBar);
|
||||
|
||||
ul {
|
||||
position: relative;
|
||||
|
||||
li {
|
||||
color: var(--bg-columnsMenuBarColor);
|
||||
width: 100%;
|
||||
@@ -161,21 +145,26 @@ export default {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.layout-columns-aside-li-box {
|
||||
margin: auto;
|
||||
|
||||
.layout-columns-aside-li-box-title {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--bg-columnsMenuBarColor);
|
||||
}
|
||||
}
|
||||
|
||||
.layout-columns-active {
|
||||
color: #ffffff;
|
||||
transition: 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.columns-round {
|
||||
background: var(--color-primary);
|
||||
color: #ffffff;
|
||||
@@ -189,6 +178,7 @@ export default {
|
||||
transition: 0.3s ease-in-out;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.columns-card {
|
||||
@extend .columns-round;
|
||||
top: 0;
|
||||
|
||||
@@ -6,16 +6,15 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import NavBarsIndex from '@/views/layout/navBars/index.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
export default {
|
||||
name: 'layoutHeader',
|
||||
components: { NavBarsIndex },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
// 设置 header 的高度
|
||||
const setHeaderHeight = computed(() => {
|
||||
let { isTagsview, layout } = store.state.themeConfig.themeConfig;
|
||||
let { isTagsview, layout } = useThemeConfig().themeConfig;
|
||||
if (isTagsview && layout !== 'classic') return '84px';
|
||||
else return '50px';
|
||||
});
|
||||
|
||||
@@ -1,97 +1,75 @@
|
||||
<template>
|
||||
<el-main class="layout-main">
|
||||
<el-scrollbar
|
||||
class="layout-scrollbar"
|
||||
ref="layoutScrollbarRef"
|
||||
v-show="!currentRouteMeta.link && !currentRouteMeta.isIframe"
|
||||
:style="{ minHeight: `calc(100vh - ${headerHeight}` }"
|
||||
>
|
||||
<el-scrollbar class="layout-scrollbar" ref="layoutScrollbarRef"
|
||||
v-show="!state.currentRouteMeta.link && state.currentRouteMeta.linkType != 1"
|
||||
:style="{ minHeight: `calc(100vh - ${state.headerHeight}` }">
|
||||
<LayoutParentView />
|
||||
<Footer v-if="getThemeConfig.isFooter" />
|
||||
<Footer v-if="themeConfig.isFooter" />
|
||||
</el-scrollbar>
|
||||
<Link
|
||||
:style="{ height: `calc(100vh - ${headerHeight}` }"
|
||||
:meta="currentRouteMeta"
|
||||
v-if="currentRouteMeta.link && !currentRouteMeta.isIframe"
|
||||
/>
|
||||
<Iframes
|
||||
:style="{ height: `calc(100vh - ${headerHeight}` }"
|
||||
:meta="currentRouteMeta"
|
||||
v-if="currentRouteMeta.link && currentRouteMeta.isIframe && isShowLink"
|
||||
@getCurrentRouteMeta="onGetCurrentRouteMeta"
|
||||
/>
|
||||
<Link :style="{ height: `calc(100vh - ${state.headerHeight}` }" :meta="state.currentRouteMeta"
|
||||
v-if="state.currentRouteMeta.link && state.currentRouteMeta.linkType == 2" />
|
||||
<Iframes :style="{ height: `calc(100vh - ${state.headerHeight}` }" :meta="state.currentRouteMeta"
|
||||
v-if="state.currentRouteMeta.link && state.currentRouteMeta.linkType == 1 && state.isShowLink"
|
||||
@getCurrentRouteMeta="onGetCurrentRouteMeta" />
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, toRefs, reactive, getCurrentInstance, watch, onBeforeMount } from 'vue';
|
||||
<script setup lang="ts" name="layoutMain">
|
||||
import { reactive, getCurrentInstance, watch, onBeforeMount } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import LayoutParentView from '@/views/layout/routerView/parent.vue';
|
||||
import Footer from '@/views/layout/footer/index.vue';
|
||||
import Link from '@/views/layout/routerView/link.vue';
|
||||
import Iframes from '@/views/layout/routerView/iframes.vue';
|
||||
export default defineComponent({
|
||||
name: 'layoutMain',
|
||||
components: { LayoutParentView, Footer, Link, Iframes },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const state = reactive({
|
||||
headerHeight: '',
|
||||
currentRouteMeta: {},
|
||||
isShowLink: false,
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 子组件触发更新
|
||||
const onGetCurrentRouteMeta = () => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
};
|
||||
// 初始化当前路由 meta 信息
|
||||
const initCurrentRouteMeta = (meta: object) => {
|
||||
state.isShowLink = false;
|
||||
state.currentRouteMeta = meta;
|
||||
setTimeout(() => {
|
||||
state.isShowLink = true;
|
||||
}, 100);
|
||||
};
|
||||
// 设置 main 的高度
|
||||
const initHeaderHeight = () => {
|
||||
let { isTagsview } = store.state.themeConfig.themeConfig;
|
||||
if (isTagsview) return (state.headerHeight = `84px`);
|
||||
else return (state.headerHeight = `50px`);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
initHeaderHeight();
|
||||
});
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(store.state.themeConfig.themeConfig, (val) => {
|
||||
state.headerHeight = val.isTagsview ? '84px' : '50px';
|
||||
if (val.isFixedHeaderChange !== val.isFixedHeader) {
|
||||
if (!proxy.$refs.layoutScrollbarRef) return false;
|
||||
proxy.$refs.layoutScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
// 监听路由的变化
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
proxy.$refs.layoutScrollbarRef.wrap$.scrollTop = 0;
|
||||
}
|
||||
);
|
||||
return {
|
||||
getThemeConfig,
|
||||
initCurrentRouteMeta,
|
||||
onGetCurrentRouteMeta,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
const route = useRoute();
|
||||
const state = reactive({
|
||||
headerHeight: '',
|
||||
currentRouteMeta: {} as any,
|
||||
isShowLink: false,
|
||||
});
|
||||
|
||||
// 子组件触发更新
|
||||
const onGetCurrentRouteMeta = () => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
};
|
||||
// 初始化当前路由 meta 信息
|
||||
const initCurrentRouteMeta = (meta: object) => {
|
||||
state.isShowLink = false;
|
||||
state.currentRouteMeta = meta;
|
||||
setTimeout(() => {
|
||||
state.isShowLink = true;
|
||||
}, 100);
|
||||
};
|
||||
// 设置 main 的高度
|
||||
const initHeaderHeight = () => {
|
||||
let { isTagsview } = themeConfig.value;
|
||||
if (isTagsview) return (state.headerHeight = `84px`);
|
||||
else return (state.headerHeight = `50px`);
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
initHeaderHeight();
|
||||
});
|
||||
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
|
||||
watch(themeConfig.value, (val) => {
|
||||
state.headerHeight = val.isTagsview ? '84px' : '50px';
|
||||
if (val.isFixedHeaderChange !== val.isFixedHeader) {
|
||||
if (!proxy.$refs.layoutScrollbarRef) return false;
|
||||
proxy.$refs.layoutScrollbarRef.update();
|
||||
}
|
||||
});
|
||||
// 监听路由的变化
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
initCurrentRouteMeta(route.meta);
|
||||
proxy.$refs.layoutScrollbarRef.wrapRef.scrollTop = 0;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="layout-footer mt15" v-show="isDelayFooter">
|
||||
<div class="layout-footer-warp">
|
||||
<div>vue-next-admin,Made by lyt with ❤️</div>
|
||||
<div class="mt5">mayfly</div>
|
||||
<div>Made by mayfly with ❤️</div>
|
||||
<div class="mt5">mayfly-go</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,57 +1,47 @@
|
||||
<template>
|
||||
<Defaults v-if="getThemeConfig.layout === 'defaults'" />
|
||||
<Classic v-else-if="getThemeConfig.layout === 'classic'" />
|
||||
<Transverse v-else-if="getThemeConfig.layout === 'transverse'" />
|
||||
<Columns v-else-if="getThemeConfig.layout === 'columns'" />
|
||||
<Defaults v-if="themeConfig.layout === 'defaults'" />
|
||||
<Classic v-else-if="themeConfig.layout === 'classic'" />
|
||||
<Transverse v-else-if="themeConfig.layout === 'transverse'" />
|
||||
<Columns v-else-if="themeConfig.layout === 'columns'" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, onBeforeMount, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import { getLocal, setLocal } from '@/common/utils/storage.ts';
|
||||
<script setup lang="ts" name="layout">
|
||||
import { onBeforeMount, onUnmounted } from 'vue';
|
||||
import { getLocal, setLocal } from '@/common/utils/storage';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import Defaults from '@/views/layout/main/defaults.vue';
|
||||
import Classic from '@/views/layout/main/classic.vue';
|
||||
import Transverse from '@/views/layout/main/transverse.vue';
|
||||
import Columns from '@/views/layout/main/columns.vue';
|
||||
export default {
|
||||
name: 'layout',
|
||||
components: { Defaults, Classic, Transverse, Columns },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
|
||||
// 窗口大小改变时(适配移动端)
|
||||
const onLayoutResize = () => {
|
||||
if (!getLocal('oldLayout')) setLocal('oldLayout', themeConfig.value.layout);
|
||||
const clientWidth = document.body.clientWidth;
|
||||
if (clientWidth < 1000) {
|
||||
themeConfig.value.isCollapse = false;
|
||||
mittBus.emit('layoutMobileResize', {
|
||||
layout: 'defaults',
|
||||
clientWidth,
|
||||
});
|
||||
// 窗口大小改变时(适配移动端)
|
||||
const onLayoutResize = () => {
|
||||
if (!getLocal('oldLayout')) setLocal('oldLayout', getThemeConfig.value.layout);
|
||||
const clientWidth = document.body.clientWidth;
|
||||
if (clientWidth < 1000) {
|
||||
getThemeConfig.value.isCollapse = false;
|
||||
proxy.mittBus.emit('layoutMobileResize', {
|
||||
layout: 'defaults',
|
||||
clientWidth,
|
||||
});
|
||||
} else {
|
||||
proxy.mittBus.emit('layoutMobileResize', {
|
||||
layout: getLocal('oldLayout') ? getLocal('oldLayout') : 'defaults',
|
||||
clientWidth,
|
||||
});
|
||||
}
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
onLayoutResize();
|
||||
window.addEventListener('resize', onLayoutResize);
|
||||
} else {
|
||||
mittBus.emit('layoutMobileResize', {
|
||||
layout: getLocal('oldLayout') ? getLocal('oldLayout') : 'defaults',
|
||||
clientWidth,
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', onLayoutResize);
|
||||
});
|
||||
return {
|
||||
getThemeConfig,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
// 页面加载前
|
||||
onBeforeMount(() => {
|
||||
onLayoutResize();
|
||||
window.addEventListener('resize', onLayoutResize);
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', onLayoutResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,294 +1,352 @@
|
||||
<template>
|
||||
<div v-show="isShowLockScreen">
|
||||
<div class="layout-lock-screen-mask"></div>
|
||||
<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': isShowLoockLogin }"></div>
|
||||
<div class="layout-lock-screen">
|
||||
<div
|
||||
class="layout-lock-screen-date"
|
||||
ref="layoutLockScreenDateRef"
|
||||
@mousedown="onDown"
|
||||
@mousemove="onMove"
|
||||
@mouseup="onEnd"
|
||||
@touchstart.stop="onDown"
|
||||
@touchmove.stop="onMove"
|
||||
@touchend.stop="onEnd"
|
||||
>
|
||||
<div class="layout-lock-screen-date-box">
|
||||
<div class="layout-lock-screen-date-box-time">
|
||||
{{ time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ time.s }}</span>
|
||||
</div>
|
||||
<div class="layout-lock-screen-date-box-info">{{ time.mdq }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="el-zoom-in-center">
|
||||
<div v-show="isShowLoockLogin" class="layout-lock-screen-login">
|
||||
<div class="layout-lock-screen-login-box">
|
||||
<div class="layout-lock-screen-login-box-img">
|
||||
<img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1813762643,1914315241&fm=26&gp=0.jpg" />
|
||||
</div>
|
||||
<div class="layout-lock-screen-login-box-name">Administrator</div>
|
||||
<div class="layout-lock-screen-login-box-value">
|
||||
<el-input
|
||||
placeholder="请输入密码"
|
||||
ref="layoutLockScreenInputRef"
|
||||
v-model="lockScreenPassword"
|
||||
@keyup.enter.stop="onLockScreenSubmit()"
|
||||
>
|
||||
<template #append>
|
||||
<el-button icon="el-icon-right" @click="onLockScreenSubmit"></el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-lock-screen-login-icon">
|
||||
<i class="el-icon-microphone"></i>
|
||||
<i class="el-icon-alarm-clock"></i>
|
||||
<i class="el-icon-switch-button"></i>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="state.isShowLockScreen">
|
||||
<div class="layout-lock-screen-mask"></div>
|
||||
<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': state.isShowLoockLogin }"></div>
|
||||
<div class="layout-lock-screen">
|
||||
<div
|
||||
class="layout-lock-screen-date"
|
||||
ref="layoutLockScreenDateRef"
|
||||
@mousedown="onDownPc"
|
||||
@mousemove="onMovePc"
|
||||
@mouseup="onEnd"
|
||||
@touchstart.stop="onDownApp"
|
||||
@touchmove.stop="onMoveApp"
|
||||
@touchend.stop="onEnd"
|
||||
>
|
||||
<div class="layout-lock-screen-date-box">
|
||||
<div class="layout-lock-screen-date-box-time">
|
||||
{{ state.time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ state.time.s }}</span>
|
||||
</div>
|
||||
<div class="layout-lock-screen-date-box-info">{{ state.time.mdq }}</div>
|
||||
</div>
|
||||
<div class="layout-lock-screen-date-top">
|
||||
<SvgIcon name="ele-Top" />
|
||||
<div class="layout-lock-screen-date-top-text">上滑解锁</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="el-zoom-in-center">
|
||||
<div v-show="state.isShowLoockLogin" class="layout-lock-screen-login">
|
||||
<div class="layout-lock-screen-login-box">
|
||||
<div class="layout-lock-screen-login-box-img">
|
||||
<img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" />
|
||||
</div>
|
||||
<div class="layout-lock-screen-login-box-name">Administrator</div>
|
||||
<div class="layout-lock-screen-login-box-value">
|
||||
<el-input
|
||||
placeholder="请输入密码"
|
||||
ref="layoutLockScreenInputRef"
|
||||
v-model="state.lockScreenPassword"
|
||||
@keyup.enter.native.stop="onLockScreenSubmit()"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="onLockScreenSubmit">
|
||||
<el-icon class="el-input__icon">
|
||||
<ele-Right />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-lock-screen-login-icon">
|
||||
<SvgIcon name="ele-Microphone" :size="20" />
|
||||
<SvgIcon name="ele-AlarmClock" :size="20" />
|
||||
<SvgIcon name="ele-SwitchButton" :size="20" />
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { nextTick, onMounted, reactive, toRefs, ref, onUnmounted, getCurrentInstance } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import { formatDate } from '@/common/utils/formatTime.ts';
|
||||
import { setLocal } from '@/common/utils/storage.ts';
|
||||
export default {
|
||||
name: 'layoutLockScreen',
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const layoutLockScreenInputRef = ref();
|
||||
const store = useStore();
|
||||
const state: any = reactive({
|
||||
transparency: 1,
|
||||
downClientY: 0,
|
||||
moveDifference: 0,
|
||||
isShowLoockLogin: false,
|
||||
isFlags: false,
|
||||
querySelectorEl: '',
|
||||
time: {
|
||||
hm: '',
|
||||
s: '',
|
||||
mdq: '',
|
||||
},
|
||||
setIntervalTime: 0,
|
||||
isShowLockScreen: false,
|
||||
isShowLockScreenIntervalTime: 0,
|
||||
lockScreenPassword: '',
|
||||
});
|
||||
// 鼠标按下
|
||||
const onDown = (down: any) => {
|
||||
state.isFlags = true;
|
||||
state.downClientY = down.touches ? down.touches[0].clientY : down.clientY;
|
||||
};
|
||||
// 鼠标移动
|
||||
const onMove = (move: any) => {
|
||||
if (state.isFlags) {
|
||||
const el = state.querySelectorEl;
|
||||
const opacitys = (state.transparency -= 1 / 200);
|
||||
if (move.touches) {
|
||||
state.moveDifference = move.touches[0].clientY - state.downClientY;
|
||||
} else {
|
||||
state.moveDifference = move.clientY - state.downClientY;
|
||||
}
|
||||
if (state.moveDifference >= 0) return false;
|
||||
el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`);
|
||||
if (state.moveDifference < -400) {
|
||||
el.setAttribute('style', `top:${-el.clientHeight}px;cursor:pointer;transition:all 0.3s ease;`);
|
||||
state.moveDifference = -el.clientHeight;
|
||||
setTimeout(() => {
|
||||
el && el.parentNode?.removeChild(el);
|
||||
}, 300);
|
||||
}
|
||||
if (state.moveDifference === -el.clientHeight) {
|
||||
state.isShowLoockLogin = true;
|
||||
layoutLockScreenInputRef.value.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
// 鼠标松开
|
||||
const onEnd = () => {
|
||||
state.isFlags = false;
|
||||
state.transparency = 1;
|
||||
if (state.moveDifference >= -400) {
|
||||
state.querySelectorEl.setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
|
||||
}
|
||||
};
|
||||
// 获取要拖拽的初始元素
|
||||
const initGetElement = () => {
|
||||
nextTick(() => {
|
||||
state.querySelectorEl = proxy.$refs.layoutLockScreenDateRef;
|
||||
});
|
||||
};
|
||||
// 时间初始化
|
||||
const initTime = () => {
|
||||
state.time.hm = formatDate(new Date(), 'HH:MM');
|
||||
state.time.s = formatDate(new Date(), 'SS');
|
||||
state.time.mdq = formatDate(new Date(), 'mm月dd日,WWW');
|
||||
};
|
||||
// 时间初始化定时器
|
||||
const initSetTime = () => {
|
||||
initTime();
|
||||
state.setIntervalTime = window.setInterval(() => {
|
||||
initTime();
|
||||
}, 1000);
|
||||
};
|
||||
// 锁屏时间定时器
|
||||
const initLockScreen = () => {
|
||||
if (store.state.themeConfig.themeConfig.isLockScreen) {
|
||||
state.isShowLockScreenIntervalTime = window.setInterval(() => {
|
||||
if (store.state.themeConfig.themeConfig.lockScreenTime <= 0) {
|
||||
state.isShowLockScreen = true;
|
||||
setLocalThemeConfig();
|
||||
return false;
|
||||
}
|
||||
store.state.themeConfig.themeConfig.lockScreenTime--;
|
||||
}, 1000);
|
||||
} else {
|
||||
clearInterval(state.isShowLockScreenIntervalTime);
|
||||
}
|
||||
};
|
||||
// 存储布局配置
|
||||
const setLocalThemeConfig = () => {
|
||||
store.state.themeConfig.themeConfig.isDrawer = false;
|
||||
setLocal('themeConfig', store.state.themeConfig.themeConfig);
|
||||
};
|
||||
// 密码输入点击事件
|
||||
const onLockScreenSubmit = () => {
|
||||
store.state.themeConfig.themeConfig.isLockScreen = false;
|
||||
store.state.themeConfig.themeConfig.lockScreenTime = 30;
|
||||
setLocalThemeConfig();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetElement();
|
||||
initSetTime();
|
||||
initLockScreen();
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(state.setIntervalTime);
|
||||
window.clearInterval(state.isShowLockScreenIntervalTime);
|
||||
});
|
||||
return {
|
||||
layoutLockScreenInputRef,
|
||||
onDown,
|
||||
onMove,
|
||||
onEnd,
|
||||
onLockScreenSubmit,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
<script setup lang="ts" name="layoutLockScreen">
|
||||
import { nextTick, onMounted, reactive, ref, onUnmounted } from 'vue';
|
||||
import { formatDate } from '@/common/utils/format';
|
||||
import { setLocal } from '@/common/utils/storage';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
|
||||
// 定义变量内容
|
||||
const layoutLockScreenDateRef = ref<null>();
|
||||
const layoutLockScreenInputRef = ref();
|
||||
const storesThemeConfig = useThemeConfig();
|
||||
const { themeConfig } = storeToRefs(storesThemeConfig);
|
||||
const state = reactive({
|
||||
transparency: 1,
|
||||
downClientY: 0,
|
||||
moveDifference: 0,
|
||||
isShowLoockLogin: false,
|
||||
isFlags: false,
|
||||
querySelectorEl: '' as any,
|
||||
time: {
|
||||
hm: '',
|
||||
s: '',
|
||||
mdq: '',
|
||||
},
|
||||
setIntervalTime: 0,
|
||||
isShowLockScreen: false,
|
||||
isShowLockScreenIntervalTime: 0,
|
||||
lockScreenPassword: '',
|
||||
});
|
||||
|
||||
// 鼠标按下 pc
|
||||
const onDownPc = (down: MouseEvent) => {
|
||||
state.isFlags = true;
|
||||
state.downClientY = down.clientY;
|
||||
};
|
||||
// 鼠标按下 app
|
||||
const onDownApp = (down: TouchEvent) => {
|
||||
state.isFlags = true;
|
||||
state.downClientY = down.touches[0].clientY;
|
||||
};
|
||||
// 鼠标移动 pc
|
||||
const onMovePc = (move: MouseEvent) => {
|
||||
state.moveDifference = move.clientY - state.downClientY;
|
||||
onMove();
|
||||
};
|
||||
// 鼠标移动 app
|
||||
const onMoveApp = (move: TouchEvent) => {
|
||||
state.moveDifference = move.touches[0].clientY - state.downClientY;
|
||||
onMove();
|
||||
};
|
||||
// 鼠标移动事件
|
||||
const onMove = () => {
|
||||
if (state.isFlags) {
|
||||
const el = <HTMLElement>state.querySelectorEl;
|
||||
const opacitys = (state.transparency -= 1 / 200);
|
||||
if (state.moveDifference >= 0) return false;
|
||||
el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`);
|
||||
if (state.moveDifference < -400) {
|
||||
el.setAttribute('style', `top:${-el.clientHeight}px;cursor:pointer;transition:all 0.3s ease;`);
|
||||
state.moveDifference = -el.clientHeight;
|
||||
setTimeout(() => {
|
||||
el && el.parentNode?.removeChild(el);
|
||||
}, 300);
|
||||
}
|
||||
if (state.moveDifference === -el.clientHeight) {
|
||||
state.isShowLoockLogin = true;
|
||||
layoutLockScreenInputRef.value.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
// 鼠标松开
|
||||
const onEnd = () => {
|
||||
state.isFlags = false;
|
||||
state.transparency = 1;
|
||||
if (state.moveDifference >= -400) {
|
||||
(<HTMLElement>state.querySelectorEl).setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
|
||||
}
|
||||
};
|
||||
// 获取要拖拽的初始元素
|
||||
const initGetElement = () => {
|
||||
nextTick(() => {
|
||||
state.querySelectorEl = layoutLockScreenDateRef.value;
|
||||
});
|
||||
};
|
||||
// 时间初始化
|
||||
const initTime = () => {
|
||||
state.time.hm = formatDate(new Date(), 'HH:MM');
|
||||
state.time.s = formatDate(new Date(), 'SS');
|
||||
state.time.mdq = formatDate(new Date(), 'mm月dd日,WWW');
|
||||
};
|
||||
// 时间初始化定时器
|
||||
const initSetTime = () => {
|
||||
initTime();
|
||||
state.setIntervalTime = window.setInterval(() => {
|
||||
initTime();
|
||||
}, 1000);
|
||||
};
|
||||
// 锁屏时间定时器
|
||||
const initLockScreen = () => {
|
||||
if (themeConfig.value.isLockScreen) {
|
||||
state.isShowLockScreenIntervalTime = window.setInterval(() => {
|
||||
if (themeConfig.value.lockScreenTime <= 1) {
|
||||
state.isShowLockScreen = true;
|
||||
setLocalThemeConfig();
|
||||
return false;
|
||||
}
|
||||
themeConfig.value.lockScreenTime--;
|
||||
}, 1000);
|
||||
} else {
|
||||
clearInterval(state.isShowLockScreenIntervalTime);
|
||||
}
|
||||
};
|
||||
// 存储布局配置
|
||||
const setLocalThemeConfig = () => {
|
||||
themeConfig.value.isDrawer = false;
|
||||
setLocal('themeConfig', themeConfig.value);
|
||||
};
|
||||
// 密码输入点击事件
|
||||
const onLockScreenSubmit = () => {
|
||||
themeConfig.value.isLockScreen = false;
|
||||
themeConfig.value.lockScreenTime = 30;
|
||||
setLocalThemeConfig();
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initGetElement();
|
||||
initSetTime();
|
||||
initLockScreen();
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
window.clearInterval(state.setIntervalTime);
|
||||
window.clearInterval(state.isShowLockScreenIntervalTime);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-lock-screen-fixed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.layout-lock-screen-filter {
|
||||
filter: blur(5px);
|
||||
transform: scale(1.2);
|
||||
filter: blur(1px);
|
||||
}
|
||||
.layout-lock-screen-mask {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
@extend .layout-lock-screen-fixed;
|
||||
z-index: 9999990;
|
||||
background: var(--el-color-white);
|
||||
@extend .layout-lock-screen-fixed;
|
||||
z-index: 9999990;
|
||||
}
|
||||
.layout-lock-screen-img {
|
||||
@extend .layout-lock-screen-fixed;
|
||||
background-image: url('https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG');
|
||||
background-size: 100% 100%;
|
||||
z-index: 9999991;
|
||||
transition: all ease 0.3s 0.3s;
|
||||
@extend .layout-lock-screen-fixed;
|
||||
background-image: url('@/assets/image/bg-login.png');
|
||||
background-size: 100% 100%;
|
||||
z-index: 9999991;
|
||||
}
|
||||
.layout-lock-screen {
|
||||
@extend .layout-lock-screen-fixed;
|
||||
z-index: 9999992;
|
||||
&-date {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #ffffff;
|
||||
z-index: 9999993;
|
||||
user-select: none;
|
||||
&-box {
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
bottom: 50px;
|
||||
&-time {
|
||||
font-size: 100px;
|
||||
}
|
||||
&-info {
|
||||
font-size: 40px;
|
||||
}
|
||||
&-minutes {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&-login {
|
||||
position: relative;
|
||||
z-index: 9999994;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
&-box {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
&-img {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
margin: auto;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
&-name {
|
||||
font-size: 26px;
|
||||
margin: 15px 0 30px;
|
||||
}
|
||||
}
|
||||
&-icon {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 30px;
|
||||
i {
|
||||
font-size: 20px;
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@extend .layout-lock-screen-fixed;
|
||||
z-index: 9999992;
|
||||
&-date {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: var(--el-color-white);
|
||||
z-index: 9999993;
|
||||
user-select: none;
|
||||
&-box {
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
bottom: 50px;
|
||||
&-time {
|
||||
font-size: 100px;
|
||||
color: var(--el-color-white);
|
||||
}
|
||||
&-info {
|
||||
font-size: 40px;
|
||||
color: var(--el-color-white);
|
||||
}
|
||||
&-minutes {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
&-top {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border-radius: 100%;
|
||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: var(--el-color-white);
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 50px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
i {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
&-text {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 150%;
|
||||
font-size: 12px;
|
||||
color: var(--el-color-white);
|
||||
left: 50%;
|
||||
line-height: 1.2;
|
||||
transform: translate(-50%, -50%);
|
||||
transition: all 0.3s ease;
|
||||
width: 35px;
|
||||
}
|
||||
&:hover {
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 0 12px 0 rgba(255, 255, 255, 0.5);
|
||||
color: var(--el-color-white);
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease;
|
||||
i {
|
||||
transform: translateY(-40px);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.layout-lock-screen-date-top-text {
|
||||
opacity: 1;
|
||||
top: 50%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&-login {
|
||||
position: relative;
|
||||
z-index: 9999994;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
color: var(--el-color-white);
|
||||
&-box {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
&-img {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
margin: auto;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
&-name {
|
||||
font-size: 26px;
|
||||
margin: 15px 0 30px;
|
||||
}
|
||||
}
|
||||
&-icon {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
bottom: 30px;
|
||||
i {
|
||||
font-size: 20px;
|
||||
margin-left: 15px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep(.el-input-group__append) {
|
||||
background: #ffffff;
|
||||
padding: 0px 15px;
|
||||
:deep(.el-input-group__append) {
|
||||
background: var(--el-color-white);
|
||||
padding: 0px 15px;
|
||||
}
|
||||
::v-deep(.el-input__inner) {
|
||||
border-right-color: #f6f6f6;
|
||||
&:hover {
|
||||
border-color: #f6f6f6;
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
border-right-color: var(--el-border-color-extra-light);
|
||||
&:hover {
|
||||
border-color: var(--el-border-color-extra-light);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,42 +1,35 @@
|
||||
<template>
|
||||
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
|
||||
<img src="@/assets/image/logo.svg" class="layout-logo-medium-img" />
|
||||
<span>{{ getThemeConfig.globalTitle }}</span>
|
||||
<span>
|
||||
{{ `${themeConfig.globalTitle}` }}
|
||||
<sub><span style="font-size: 10px;color:goldenrod">{{ ` ${config.version}` }}</span></sub>
|
||||
</span>
|
||||
</div>
|
||||
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
|
||||
<img src="@/assets/image/logo.svg" class="layout-logo-size-img" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, getCurrentInstance } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
export default {
|
||||
name: 'layoutLogo',
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 设置 logo 的显示。classic 经典布局默认显示 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let { isCollapse, layout } = store.state.themeConfig.themeConfig;
|
||||
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
|
||||
});
|
||||
// logo 点击实现菜单展开/收起
|
||||
const onThemeConfigChange = () => {
|
||||
if (store.state.themeConfig.themeConfig.layout === 'transverse') return false;
|
||||
proxy.mittBus.emit('onMenuClick');
|
||||
store.state.themeConfig.themeConfig.isCollapse = !store.state.themeConfig.themeConfig.isCollapse;
|
||||
};
|
||||
return {
|
||||
setShowLogo,
|
||||
getThemeConfig,
|
||||
onThemeConfigChange,
|
||||
};
|
||||
},
|
||||
<script setup lang="ts" name="layoutLogo">
|
||||
import { computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import config from '@/common/config';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
|
||||
// 设置 logo 的显示。classic 经典布局默认显示 logo
|
||||
const setShowLogo = computed(() => {
|
||||
let { isCollapse, layout } = themeConfig.value;
|
||||
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
|
||||
});
|
||||
// logo 点击实现菜单展开/收起
|
||||
const onThemeConfigChange = () => {
|
||||
if (themeConfig.value.layout === 'transverse') return false;
|
||||
mittBus.emit('onMenuClick');
|
||||
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -52,26 +45,31 @@ export default {
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
span {
|
||||
color: var(--color-primary-light-2);
|
||||
}
|
||||
}
|
||||
|
||||
&-medium-img {
|
||||
width: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-logo-size {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
|
||||
&-img {
|
||||
width: 20px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
img {
|
||||
animation: logoAnimation 0.3s ease-in-out;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<el-container class="layout-mian-height-50">
|
||||
<Aside />
|
||||
<div class="flex-center layout-backtop">
|
||||
<TagsView v-if="getThemeConfig.isTagsview" />
|
||||
<TagsView v-if="themeConfig.isTagsview" />
|
||||
<Main />
|
||||
</div>
|
||||
</el-container>
|
||||
@@ -12,25 +12,13 @@
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
<script lang="ts" setup name="layoutClassic">
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import Aside from '@/views/layout/component/aside.vue';
|
||||
import Header from '@/views/layout/component/header.vue';
|
||||
import Main from '@/views/layout/component/main.vue';
|
||||
import TagsView from '@/views/layout/navBars/tagsView/tagsView.vue';
|
||||
export default {
|
||||
name: 'layoutClassic',
|
||||
components: { Aside, Header, Main, TagsView },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
return {
|
||||
getThemeConfig,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
</script>
|
||||
|
||||
@@ -17,18 +17,17 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import Aside from '@/views/layout/component/aside.vue';
|
||||
import Header from '@/views/layout/component/header.vue';
|
||||
import Main from '@/views/layout/component/main.vue';
|
||||
import ColumnsAside from '@/views/layout/component/columnsAside.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
export default {
|
||||
name: 'layoutColumns',
|
||||
components: { Aside, Header, Main, ColumnsAside },
|
||||
setup() {
|
||||
const store = useStore();
|
||||
const isFixedHeader = computed(() => {
|
||||
return store.state.themeConfig.themeConfig.isFixedHeader;
|
||||
return useThemeConfig().themeConfig.isFixedHeader;
|
||||
});
|
||||
return {
|
||||
isFixedHeader,
|
||||
|
||||
@@ -15,19 +15,18 @@
|
||||
<script lang="ts">
|
||||
import { computed, getCurrentInstance, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import Aside from '@/views/layout/component/aside.vue';
|
||||
import Header from '@/views/layout/component/header.vue';
|
||||
import Main from '@/views/layout/component/main.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
export default {
|
||||
name: 'layoutDefaults',
|
||||
components: { Aside, Header, Main },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const isFixedHeader = computed(() => {
|
||||
return store.state.themeConfig.themeConfig.isFixedHeader;
|
||||
return useThemeConfig().themeConfig.isFixedHeader;
|
||||
});
|
||||
// 监听路由的变化
|
||||
watch(
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb" v-show="getThemeConfig.isBreadcrumb">
|
||||
<SvgIcon class="layout-navbars-breadcrumb-icon" :name="getThemeConfig.isCollapse ? 'expand' : 'fold'" @click="onThemeConfigChange" />
|
||||
<div class="layout-navbars-breadcrumb" v-show="themeConfig.isBreadcrumb">
|
||||
<SvgIcon class="layout-navbars-breadcrumb-icon" :name="themeConfig.isCollapse ? 'expand' : 'fold'"
|
||||
@click="onThemeConfigChange" />
|
||||
<el-breadcrumb class="layout-navbars-breadcrumb-hide">
|
||||
<transition-group name="breadcrumb" mode="out-in">
|
||||
<el-breadcrumb-item v-for="(v, k) in breadcrumbList" :key="v.meta.title">
|
||||
<span v-if="k === breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon" />
|
||||
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="v.meta.title">
|
||||
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont"
|
||||
v-if="themeConfig.isBreadcrumbIcon" />
|
||||
{{ v.meta.title }}
|
||||
</span>
|
||||
<a v-else @click.prevent="onBreadcrumbClick(v)">
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="getThemeConfig.isBreadcrumbIcon" />
|
||||
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont"
|
||||
v-if="themeConfig.isBreadcrumbIcon" />
|
||||
{{ v.meta.title }}
|
||||
</a>
|
||||
</el-breadcrumb-item>
|
||||
@@ -18,77 +21,67 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, computed, getCurrentInstance, onMounted } from 'vue';
|
||||
<script lang="ts" setup name="layoutBreadcrumb">
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
export default {
|
||||
name: 'layoutBreadcrumb',
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
breadcrumbList: [],
|
||||
routeSplit: [],
|
||||
routeSplitFirst: '',
|
||||
routeSplitIndex: 1,
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 面包屑点击时
|
||||
const onBreadcrumbClick = (v: any) => {
|
||||
const { redirect, path } = v;
|
||||
if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
};
|
||||
// 展开/收起左侧菜单点击
|
||||
const onThemeConfigChange = () => {
|
||||
proxy.mittBus.emit('onMenuClick');
|
||||
store.state.themeConfig.themeConfig.isCollapse = !store.state.themeConfig.themeConfig.isCollapse;
|
||||
};
|
||||
// 处理面包屑数据
|
||||
const getBreadcrumbList = (arr: Array<object>) => {
|
||||
arr.map((item: any) => {
|
||||
state.routeSplit.map((v: any, k: number, arrs: any) => {
|
||||
if (state.routeSplitFirst === item.path) {
|
||||
state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
|
||||
state.breadcrumbList.push(item);
|
||||
state.routeSplitIndex++;
|
||||
if (item.children) getBreadcrumbList(item.children);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// 当前路由字符串切割成数组,并删除第一项空内容
|
||||
const initRouteSplit = (path: string) => {
|
||||
if (!store.state.themeConfig.themeConfig.isBreadcrumb) return false;
|
||||
state.breadcrumbList = [store.state.routesList.routesList[0]];
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
state.routeSplitFirst = `/${state.routeSplit[0]}`;
|
||||
state.routeSplitIndex = 1;
|
||||
getBreadcrumbList(store.state.routesList.routesList);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initRouteSplit(route.path);
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
initRouteSplit(to.path);
|
||||
});
|
||||
return {
|
||||
onThemeConfigChange,
|
||||
getThemeConfig,
|
||||
onBreadcrumbClick,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
const { routesList } = storeToRefs(useRoutesList());
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
breadcrumbList: [],
|
||||
routeSplit: [],
|
||||
routeSplitFirst: '',
|
||||
routeSplitIndex: 1,
|
||||
});
|
||||
|
||||
// 面包屑点击时
|
||||
const onBreadcrumbClick = (v: any) => {
|
||||
const { redirect, path } = v;
|
||||
if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
};
|
||||
// 展开/收起左侧菜单点击
|
||||
const onThemeConfigChange = () => {
|
||||
mittBus.emit('onMenuClick');
|
||||
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
|
||||
};
|
||||
// 处理面包屑数据
|
||||
const getBreadcrumbList = (arr: Array<object>) => {
|
||||
arr.map((item: any) => {
|
||||
state.routeSplit.map((v: any, k: number, arrs: any) => {
|
||||
if (state.routeSplitFirst === item.path) {
|
||||
state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
|
||||
state.breadcrumbList.push(item);
|
||||
state.routeSplitIndex++;
|
||||
if (item.children) getBreadcrumbList(item.children);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
// 当前路由字符串切割成数组,并删除第一项空内容
|
||||
const initRouteSplit = (path: string) => {
|
||||
if (!themeConfig.value.isBreadcrumb) return false;
|
||||
state.breadcrumbList = [routesList.value[0]];
|
||||
state.routeSplit = path.split('/');
|
||||
state.routeSplit.shift();
|
||||
state.routeSplitFirst = `/${state.routeSplit[0]}`;
|
||||
state.routeSplitIndex = 1;
|
||||
getBreadcrumbList(routesList.value);
|
||||
};
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
initRouteSplit(route.path);
|
||||
});
|
||||
// 路由更新时
|
||||
onBeforeRouteUpdate((to) => {
|
||||
initRouteSplit(to.path);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -98,20 +91,24 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 15px;
|
||||
|
||||
.layout-navbars-breadcrumb-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
margin-right: 15px;
|
||||
color: var(--bg-topBarColor);
|
||||
}
|
||||
|
||||
.layout-navbars-breadcrumb-span {
|
||||
opacity: 0.7;
|
||||
color: var(--bg-topBarColor);
|
||||
}
|
||||
|
||||
.layout-navbars-breadcrumb-iconfont {
|
||||
font-size: 14px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
::v-deep(.el-breadcrumb__separator) {
|
||||
opacity: 0.7;
|
||||
color: var(--bg-topBarColor);
|
||||
|
||||
@@ -2,109 +2,100 @@
|
||||
<div class="layout-navbars-breadcrumb-index">
|
||||
<Logo v-if="setIsShowLogo" />
|
||||
<Breadcrumb />
|
||||
<Horizontal :menuList="menuList" v-if="isLayoutTransverse" />
|
||||
<Horizontal :menuList="state.menuList" v-if="isLayoutTransverse" />
|
||||
<User />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, reactive, toRefs, onMounted, onUnmounted, getCurrentInstance, watch } from 'vue';
|
||||
<script lang="ts" setup name="layoutBreadcrumbIndex">
|
||||
import { computed, reactive, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
import pinia from '@/store/index';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
import Breadcrumb from '@/views/layout/navBars/breadcrumb/breadcrumb.vue';
|
||||
import User from '@/views/layout/navBars/breadcrumb/user.vue';
|
||||
import Logo from '@/views/layout/logo/index.vue';
|
||||
import Horizontal from '@/views/layout/navMenu/horizontal.vue';
|
||||
export default {
|
||||
name: 'layoutBreadcrumbIndex',
|
||||
components: { Breadcrumb, User, Logo, Horizontal },
|
||||
setup() {
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const state: any = reactive({
|
||||
menuList: [],
|
||||
});
|
||||
// 获取布局配置信息
|
||||
const getThemeConfig = computed(() => {
|
||||
return store.state.themeConfig.themeConfig;
|
||||
});
|
||||
// 设置 logo 显示/隐藏
|
||||
const setIsShowLogo = computed(() => {
|
||||
let { isShowLogo, layout } = store.state.themeConfig.themeConfig;
|
||||
return (isShowLogo && layout === 'classic') || (isShowLogo && layout === 'transverse');
|
||||
});
|
||||
// 设置是否显示横向导航菜单
|
||||
const isLayoutTransverse = computed(() => {
|
||||
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
|
||||
return layout === 'transverse' || (isClassicSplitMenu && layout === 'classic');
|
||||
});
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = delClassicChildren(filterRoutesFun(store.state.routesList.routesList));
|
||||
const resData = setSendClassicChildren(route.path);
|
||||
proxy.mittBus.emit('setSendClassicChildren', resData);
|
||||
} else {
|
||||
state.menuList = filterRoutesFun(store.state.routesList.routesList);
|
||||
}
|
||||
};
|
||||
// 设置了分割菜单时,删除底下 children
|
||||
const delClassicChildren = (arr: Array<object>) => {
|
||||
arr.map((v: any) => {
|
||||
if (v.children) delete v.children;
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendClassicChildren = (path: string) => {
|
||||
const currentPathSplit = path.split('/');
|
||||
let currentData: any = {};
|
||||
filterRoutesFun(store.state.routesList.routesList).map((v, k) => {
|
||||
if (v.path === `/${currentPathSplit[1]}`) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = [{ ...v }];
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(store.state, (val) => {
|
||||
if (val.routesList.routesList.length === state.menuList.length) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
setFilterRoutes();
|
||||
proxy.mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
proxy.mittBus.off('getBreadcrumbIndexSetFilterRoutes');
|
||||
});
|
||||
return {
|
||||
getThemeConfig,
|
||||
setIsShowLogo,
|
||||
isLayoutTransverse,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
import mittBus from '@/common/utils/mitt';
|
||||
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
const { routesList } = storeToRefs(useRoutesList());
|
||||
const route = useRoute();
|
||||
const state: any = reactive({
|
||||
menuList: [],
|
||||
});
|
||||
|
||||
// 设置 logo 显示/隐藏
|
||||
const setIsShowLogo = computed(() => {
|
||||
let { isShowLogo, layout } = themeConfig.value;
|
||||
return (isShowLogo && layout === 'classic') || (isShowLogo && layout === 'transverse');
|
||||
});
|
||||
// 设置是否显示横向导航菜单
|
||||
const isLayoutTransverse = computed(() => {
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
return layout === 'transverse' || (isClassicSplitMenu && layout === 'classic');
|
||||
});
|
||||
// 设置/过滤路由(非静态路由/是否显示在菜单中)
|
||||
const setFilterRoutes = () => {
|
||||
let { layout, isClassicSplitMenu } = themeConfig.value;
|
||||
if (layout === 'classic' && isClassicSplitMenu) {
|
||||
state.menuList = delClassicChildren(filterRoutesFun(routesList.value));
|
||||
const resData = setSendClassicChildren(route.path);
|
||||
mittBus.emit('setSendClassicChildren', resData);
|
||||
} else {
|
||||
state.menuList = filterRoutesFun(routesList.value);
|
||||
}
|
||||
};
|
||||
// 设置了分割菜单时,删除底下 children
|
||||
const delClassicChildren = (arr: Array<object>) => {
|
||||
arr.map((v: any) => {
|
||||
if (v.children) delete v.children;
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
// 路由过滤递归函数
|
||||
const filterRoutesFun = (arr: Array<object>) => {
|
||||
return arr
|
||||
.filter((item: any) => !item.meta.isHide)
|
||||
.map((item: any) => {
|
||||
item = Object.assign({}, item);
|
||||
if (item.children) item.children = filterRoutesFun(item.children);
|
||||
return item;
|
||||
});
|
||||
};
|
||||
// 传送当前子级数据到菜单中
|
||||
const setSendClassicChildren = (path: string) => {
|
||||
const currentPathSplit = path.split('/');
|
||||
let currentData: any = {};
|
||||
filterRoutesFun(routesList.value).map((v, k) => {
|
||||
if (v.path === `/${currentPathSplit[1]}`) {
|
||||
v['k'] = k;
|
||||
currentData['item'] = [{ ...v }];
|
||||
currentData['children'] = [{ ...v }];
|
||||
if (v.children) currentData['children'] = v.children;
|
||||
}
|
||||
});
|
||||
return currentData;
|
||||
};
|
||||
// 监听路由的变化,动态赋值给菜单中
|
||||
watch(pinia.state, (val) => {
|
||||
if (val.routesList.routesList.length === state.menuList.length) return false;
|
||||
setFilterRoutes();
|
||||
});
|
||||
// 页面加载时
|
||||
onMounted(() => {
|
||||
setFilterRoutes();
|
||||
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
|
||||
setFilterRoutes();
|
||||
});
|
||||
});
|
||||
// 页面卸载时
|
||||
onUnmounted(() => {
|
||||
mittBus.off('getBreadcrumbIndexSetFilterRoutes');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,119 +1,103 @@
|
||||
<template>
|
||||
<div class="layout-search-dialog">
|
||||
<el-dialog v-model="isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen :show-close="false">
|
||||
<el-autocomplete
|
||||
v-model="menuQuery"
|
||||
:fetch-suggestions="menuSearch"
|
||||
placeholder="菜单搜索"
|
||||
prefix-icon="el-icon-search"
|
||||
ref="layoutMenuAutocompleteRef"
|
||||
@select="onHandleSelect"
|
||||
@blur="onSearchBlur"
|
||||
>
|
||||
<el-dialog v-model="state.isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen :show-close="false">
|
||||
<el-autocomplete v-model="state.menuQuery" :fetch-suggestions="menuSearch" placeholder="菜单搜索"
|
||||
prefix-icon="el-icon-search" ref="layoutMenuAutocompleteRef" @select="onHandleSelect" @blur="onSearchBlur">
|
||||
<template #prefix>
|
||||
<el-icon class="el-input__icon">
|
||||
<search />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template #default="{ item }">
|
||||
<div><SvgIcon :name="item.meta.icon" class="mr5" />{{ item.meta.title }}</div>
|
||||
<div>
|
||||
<SvgIcon :name="item.meta.icon" class="mr5" />{{ item.meta.title }}
|
||||
</div>
|
||||
</template>
|
||||
</el-autocomplete>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { reactive, toRefs, defineComponent, ref, nextTick } from 'vue';
|
||||
<script lang="ts" setup name="layoutBreadcrumbSearch">
|
||||
import { reactive, ref, nextTick } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useStore } from '@/store/index.ts';
|
||||
export default defineComponent({
|
||||
name: 'layoutBreadcrumbSearch',
|
||||
setup() {
|
||||
const layoutMenuAutocompleteRef = ref();
|
||||
const store = useStore();
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
isShowSearch: false,
|
||||
menuQuery: '',
|
||||
tagsViewList: [],
|
||||
});
|
||||
// 搜索弹窗打开
|
||||
const openSearch = () => {
|
||||
state.menuQuery = '';
|
||||
state.isShowSearch = true;
|
||||
initTageView();
|
||||
nextTick(() => {
|
||||
layoutMenuAutocompleteRef.value.focus();
|
||||
});
|
||||
};
|
||||
// 搜索弹窗关闭
|
||||
const closeSearch = () => {
|
||||
state.isShowSearch = false;
|
||||
};
|
||||
// 菜单搜索数据过滤
|
||||
const menuSearch = (queryString: any, cb: any) => {
|
||||
let results = queryString ? state.tagsViewList.filter(createFilter(queryString)) : state.tagsViewList;
|
||||
cb(results);
|
||||
};
|
||||
// 菜单搜索过滤
|
||||
const createFilter = (queryString: any) => {
|
||||
return (restaurant: any) => {
|
||||
return (
|
||||
restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
|
||||
restaurant.meta.title.toLowerCase().indexOf(queryString.toLowerCase()) > -1
|
||||
);
|
||||
};
|
||||
};
|
||||
// 初始化菜单数据
|
||||
const initTageView = () => {
|
||||
if (state.tagsViewList.length > 0) return false;
|
||||
console.log(getRoutes(store.state.routesList.routesList));
|
||||
getRoutes(store.state.routesList.routesList).map((v: any) => {
|
||||
if (!v.meta.isHide) {
|
||||
state.tagsViewList.push({ ...v });
|
||||
}
|
||||
});
|
||||
};
|
||||
// 获取所有根节点的route,即可访问的route
|
||||
const getRoutes = (routes: any) => {
|
||||
const menu: any = [];
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
const item = { ...routes[i] };
|
||||
if (item.children) {
|
||||
getRoutes(item.children).forEach((r: any) => {
|
||||
menu.push(r);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
menu.push(item);
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
import { useRoutesList } from '@/store/routesList';
|
||||
|
||||
// 当前菜单选中时
|
||||
const onHandleSelect = (item: any) => {
|
||||
let { path, redirect } = item;
|
||||
if (item.meta.link && !item.meta.isIframe) window.open(item.meta.link);
|
||||
else if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
closeSearch();
|
||||
};
|
||||
// input 失去焦点时
|
||||
const onSearchBlur = () => {
|
||||
closeSearch();
|
||||
};
|
||||
return {
|
||||
layoutMenuAutocompleteRef,
|
||||
openSearch,
|
||||
closeSearch,
|
||||
menuSearch,
|
||||
onHandleSelect,
|
||||
onSearchBlur,
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
const layoutMenuAutocompleteRef: any = ref(null);;
|
||||
const router = useRouter();
|
||||
const state: any = reactive({
|
||||
isShowSearch: false,
|
||||
menuQuery: '',
|
||||
tagsViewList: [],
|
||||
});
|
||||
// 搜索弹窗打开
|
||||
const openSearch = () => {
|
||||
state.menuQuery = '';
|
||||
state.isShowSearch = true;
|
||||
initTageView();
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
layoutMenuAutocompleteRef.value.focus();
|
||||
});
|
||||
});
|
||||
};
|
||||
// 搜索弹窗关闭
|
||||
const closeSearch = () => {
|
||||
state.isShowSearch = false;
|
||||
};
|
||||
// 菜单搜索数据过滤
|
||||
const menuSearch = (queryString: any, cb: any) => {
|
||||
let results = queryString ? state.tagsViewList.filter(createFilter(queryString)) : state.tagsViewList;
|
||||
cb(results);
|
||||
};
|
||||
// 菜单搜索过滤
|
||||
const createFilter = (queryString: any) => {
|
||||
return (restaurant: any) => {
|
||||
return (
|
||||
restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
|
||||
restaurant.meta.title.toLowerCase().indexOf(queryString.toLowerCase()) > -1
|
||||
);
|
||||
};
|
||||
};
|
||||
// 初始化菜单数据
|
||||
const initTageView = () => {
|
||||
if (state.tagsViewList.length > 0) return false;
|
||||
getRoutes(useRoutesList().routesList).map((v: any) => {
|
||||
if (!v.meta.isHide) {
|
||||
state.tagsViewList.push({ ...v });
|
||||
}
|
||||
});
|
||||
};
|
||||
// 获取所有根节点的route,即可访问的route
|
||||
const getRoutes = (routes: any) => {
|
||||
const menu: any = [];
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
const item = { ...routes[i] };
|
||||
if (item.children) {
|
||||
getRoutes(item.children).forEach((r: any) => {
|
||||
menu.push(r);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
menu.push(item);
|
||||
}
|
||||
return menu;
|
||||
};
|
||||
|
||||
// 当前菜单选中时
|
||||
const onHandleSelect = (item: any) => {
|
||||
let { path, redirect } = item;
|
||||
if (item.meta.link && item.meta.linkType == 2) window.open(item.meta.link);
|
||||
else if (redirect) router.push(redirect);
|
||||
else router.push(path);
|
||||
closeSearch();
|
||||
};
|
||||
// input 失去焦点时
|
||||
const onSearchBlur = () => {
|
||||
closeSearch();
|
||||
};
|
||||
|
||||
defineExpose({openSearch})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -123,6 +107,7 @@ export default defineComponent({
|
||||
border-radius: 0 !important;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
::v-deep(.el-autocomplete) {
|
||||
width: 560px;
|
||||
position: absolute;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user