未验证 提交 e9af7e0f 编写于 作者: Mr.奇淼('s avatar Mr.奇淼( 提交者: GitHub

Merge branch 'gva-plugins' into gva_gormv2_dev

......@@ -6,10 +6,8 @@ type ApiGroup struct {
CustomerApi
ExcelApi
FileUploadAndDownloadApi
SimpleUploaderApi
}
var fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
var customerService = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
var excelService = service.ServiceGroupApp.ExampleServiceGroup.ExcelService
var simpleUploaderService = service.ServiceGroupApp.ExampleServiceGroup.SimpleUploaderService
package example
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type SimpleUploaderApi struct {
}
// @Tags SimpleUploader
// @Summary 断点续传插件版示例
// @Security ApiKeyAuth
// @accept multipart/form-data
// @Produce application/json
// @Param file formData file true "断点续传插件版示例"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"切片创建成功"}"
// @Router /SimpleUploaderApi/upload [post]
func (s *SimpleUploaderApi) SimpleUploaderUpload(c *gin.Context) {
var chunk example.ExaSimpleUploader
_, header, err := c.Request.FormFile("file")
chunk.Filename = c.PostForm("filename")
chunk.ChunkNumber = c.PostForm("chunkNumber")
chunk.CurrentChunkSize = c.PostForm("currentChunkSize")
chunk.Identifier = c.PostForm("identifier")
chunk.TotalSize = c.PostForm("totalSize")
chunk.TotalChunks = c.PostForm("totalChunks")
var chunkDir = "./chunk/" + chunk.Identifier + "/"
hasDir, _ := utils.PathExists(chunkDir)
if !hasDir {
if err := utils.CreateDir(chunkDir); err != nil {
global.GVA_LOG.Error("创建目录失败!", zap.Any("err", err))
}
}
chunkPath := chunkDir + chunk.Filename + chunk.ChunkNumber
err = c.SaveUploadedFile(header, chunkPath)
if err != nil {
global.GVA_LOG.Error("切片创建失败!", zap.Any("err", err))
response.FailWithMessage("切片创建失败", c)
return
}
chunk.CurrentChunkPath = chunkPath
err = simpleUploaderService.SaveChunk(chunk)
if err != nil {
global.GVA_LOG.Error("切片创建失败!", zap.Any("err", err))
response.FailWithMessage("切片创建失败", c)
return
} else {
response.OkWithMessage("切片创建成功", c)
}
}
// @Tags SimpleUploader
// @Summary 断点续传插件版示例
// @Security ApiKeyAuth
// @Produce application/json
// @Param md5 query string true "md5"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /SimpleUploaderApi/checkFileMd5 [get]
func (s *SimpleUploaderApi) CheckFileMd5(c *gin.Context) {
md5 := c.Query("md5")
err, chunks, isDone := simpleUploaderService.CheckFileMd5(md5)
if err != nil {
global.GVA_LOG.Error("md5读取失败!", zap.Any("err", err))
response.FailWithMessage("md5读取失败", c)
} else {
response.OkWithDetailed(gin.H{
"chunks": chunks,
"isDone": isDone,
}, "查询成功", c)
}
}
// @Tags SimpleUploader
// @Summary 合并文件
// @Security ApiKeyAuth
// @Produce application/json
// @Param md5 query string true "md5"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}"
// @Router /SimpleUploaderApi/mergeFileMd5 [get]
func (s *SimpleUploaderApi) MergeFileMd5(c *gin.Context) {
md5 := c.Query("md5")
fileName := c.Query("fileName")
err := simpleUploaderService.MergeFileMd5(md5, fileName)
if err != nil {
global.GVA_LOG.Error("md5读取失败!", zap.Any("err", err))
response.FailWithMessage("md5读取失败", c)
} else {
response.OkWithMessage("合并成功", c)
}
}
......@@ -155,8 +155,7 @@ func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
err := autoCodeService.CreateTemp(a, apiIds...)
if err != nil {
if errors.Is(err, system.AutoMoveErr) {
c.Writer.Header().Add("success", "false")
c.Writer.Header().Add("msgtype", "success")
c.Writer.Header().Add("success", "true")
c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
} else {
c.Writer.Header().Add("success", "false")
......
......@@ -48,7 +48,6 @@ func MysqlTables(db *gorm.DB) {
example.ExaFileUploadAndDownload{},
example.ExaFile{},
example.ExaFileChunk{},
example.ExaSimpleUploader{},
example.ExaCustomer{},
system.SysOperationRecord{},
system.SysAutoCodeHistory{},
......
......@@ -4,7 +4,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/ws"
"go.uber.org/zap"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify"
//email "github.com/flipped-aurora/gva-plug-email" // 在线仓库模式
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email" // 本地插件仓库地址模式
"github.com/flipped-aurora/gin-vue-admin/server/plugin/example_plugin"
......@@ -34,4 +34,10 @@ func InstallPlugin(PublicGroup *gin.RouterGroup, PrivateGroup *gin.RouterGroup)
global.GVA_CONFIG.Email.Port,
global.GVA_CONFIG.Email.IsSSL,
))
// 钉钉通知,暂时开放权限
PluginInit(PublicGroup, notify.CreateDDPlug(
"https://oapi.dingtalk.com/robot/send",
"8ded23f91917dc4f6275f44ba5ef243e6ed1d2cc74de83f01a6f5f5f39905671",
"SECaecf452bd6e671ab0d47469c3ad933e32fcc47b335333049a1b8961606192f38"))
}
......@@ -51,7 +51,6 @@ func Routers() *gin.Engine {
systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
exampleRouter.InitExcelRouter(PrivateGroup) // 表格导入导出
exampleRouter.InitSimpleUploaderRouter(PrivateGroup) // 断点续传(插件版)
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
// Code generated by github.com/flipped-aurora/gin-vue-admin/server Begin; DO NOT EDIT.
......
package example
type ExaSimpleUploader struct {
ChunkNumber string `json:"chunkNumber" gorm:"comment:当前切片标记"`
CurrentChunkSize string `json:"currentChunkSize" gorm:"comment:当前切片容量"`
CurrentChunkPath string `json:"currentChunkPath" gorm:"comment:切片本地路径"`
TotalSize string `json:"totalSize" gorm:"comment:总容量"`
Identifier string `json:"identifier" gorm:"comment:文件标识(md5)"`
Filename string `json:"filename" gorm:"comment:文件名"`
TotalChunks string `json:"totalChunks" gorm:"comment:切片总数"`
IsDone bool `json:"isDone" gorm:"comment:是否上传完成"`
FilePath string `json:"filePath" gorm:"comment:文件本地路径"`
}
## GVA 邮件发送功能插件
#### 开发者:GIN-VUE-ADMIN 官方
### 使用步骤
......@@ -27,8 +28,9 @@
true,
))
#### 2. 配置说明
### 2. 配置说明
#### 2-1 全局配置结构体说明
//其中 Form 和 Secret 通常来说就是用户名和密码
type Email struct {
......@@ -40,8 +42,17 @@
Port int // 端口 请前往QQ或者你要发邮件的邮箱查看其smtp协议 大多为 465
IsSSL bool // 是否SSL 是否开启SSL
}
#### 2-2 入参结构说明
//其中 Form 和 Secret 通常来说就是用户名和密码
type Email struct {
To string `json:"to"` // 邮件发送给谁
Subject string `json:"subject"` // 邮件标题
Body string `json:"body"` // 邮件内容
}
### 方法API
### 3. 方法API
utils.EmailTest(邮件标题,邮件主体) 发送测试邮件
例:utils.EmailTest("测试邮件","测试邮件")
......@@ -49,3 +60,16 @@
例:utils.ErrorToEmail("测试邮件","测试邮件")
utils.Email(目标邮箱多个的话用逗号分隔,邮件标题,邮件主体) 发送测试邮件
例:utils.Email(”a.qq.com,b.qq.com“,"测试邮件","测试邮件")
### 4. 可直接调用的接口
测试接口: /email/emailTest [post] 已配置swagger
发送邮件接口接口: /email/emailSend [post] 已配置swagger
入参:
type Email struct {
To string `json:"to"` // 邮件发送给谁
Subject string `json:"subject"` // 邮件标题
Body string `json:"body"` // 邮件内容
}
......@@ -3,6 +3,7 @@ package api
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
email_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/email/model/response"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
......@@ -25,3 +26,21 @@ func (s *EmailApi) EmailTest(c *gin.Context) {
response.OkWithData("发送成功", c)
}
}
// @Tags System
// @Summary 发送邮件
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body email_response.Email true "发送邮件必须的参数"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /email/sendEmail [post]
func (s *EmailApi) SendEmail(c *gin.Context) {
var email email_response.Email
_ = c.ShouldBindJSON(&email)
if err := service.ServiceGroupApp.SendEmail(email.To, email.Subject, email.Body); err != nil {
global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
response.FailWithMessage("发送失败", c)
} else {
response.OkWithData("发送成功", c)
}
}
package response
type Email struct {
To string `json:"to"` // 邮件发送给谁
Subject string `json:"subject"` // 邮件标题
Body string `json:"body"` // 邮件内容
}
......@@ -12,7 +12,9 @@ type EmailRouter struct {
func (s *EmailRouter) InitEmailRouter(Router *gin.RouterGroup) {
emailRouter := Router.Use(middleware.OperationRecord())
var EmailApi = api.ApiGroupApp.EmailApi.EmailTest
var SendEmail = api.ApiGroupApp.EmailApi.SendEmail
{
emailRouter.POST("emailTest", EmailApi) // 发送测试邮件
emailRouter.POST("emailTest", EmailApi) // 发送测试邮件
emailRouter.POST("sendEmail", SendEmail) // 发送邮件
}
}
......@@ -18,3 +18,16 @@ func (e *EmailService) EmailTest() (err error) {
err = utils.EmailTest(subject, body)
return err
}
//@author: [maplepie](https://github.com/maplepie)
//@function: EmailTest
//@description: 发送邮件测试
//@return: err error
//@params to string 收件人
//@params subject string 标题(主题)
//@params body string 邮件内容
func (e *EmailService) SendEmail(to, subject, body string) (err error) {
err = utils.Email(to, subject, body)
return err
}
## GVA 钉钉群通知插件
本插件用于向钉钉群推送消息
### 1. 使用场景
- 当服务运行异常时,可以向钉钉推送异常信息,便于及时发现解决问题
- 推送一些关键业务的运行日志等
### 2. 配置说明
钉钉 token 等相关信息的获取,请参考 [钉钉官网](https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f8710afbfzduV#topic-2026027)
`plugin/notify/global/global.go` 文件中配置钉钉通知的URL ,Token 等
```go
// 在gin-vue-admin 主程序的initialize中的plugin的InstallPlugin 函数中写入如下代码
PluginInit(PublicGroup, notify.CreateDDPlug(
URL,
Token,
密钥))
}
```
### 3 参数说明
#### 3-1 全局参数说明
```go
Url string `mapstructure:"url" json:"url" yaml:"url"` // Url
Token string `mapstructure:"token" json:"token" yaml:"token"` // access_token
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
```
#### 3-2 请求入参说明
```go
```
### 3方法API(可调用方法)
```go
//content 发送的内容
//atMobiles 需要艾特的人的手机号
//isAtAll 是否艾特全体
SendTextMessage(content string,atMobiles []string,isAtAll bool)
//content 发送的内容
//title 内容标题
//picUrl 配图
//messageUrl 点击跳转路径
SendLinkMessage(content,title,picUrl,messageUrl string)
//content 发送的内容(markdown语法)
//title 内容标题
//atMobiles 需要艾特的人的手机号
//isAtAll 是否艾特全体
SendMarkdownMessage(content,title string,atMobiles []string,isAtAll bool)
```
### 4. 可直接调用接口
发送文字消息接口: /notify/sendTextMessage [post] 已配置swagger
发送图文链接消息接口: /notify/sendLinkMessage [post] 已配置swagger
发送markdown消息接口: /notify/sendMarkdownMessage [post] 已配置swagger
入参:
type Email struct {
To string `json:"to"` // 邮件发送给谁
Subject string `json:"subject"` // 邮件标题
Body string `json:"body"` // 邮件内容
}
package api
import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
notify_response "github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/model/response"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/service"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type Api struct {
}
// @Tags Notify
// @Summary 发送文字消息接口
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body notify_response.TextNotify true "发送文字消息的参数"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /notify/sendTextMessage [post]
func (s *Api) SendTextMessage(c *gin.Context) {
var textNotify notify_response.TextNotify
_ = c.ShouldBindJSON(&textNotify)
if err := service.ServiceGroupApp.SendTextMessage(textNotify.Content, textNotify.AtMobiles, textNotify.IsAtAll); err != nil {
global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
response.FailWithMessage("发送失败", c)
} else {
response.OkWithData("发送成功", c)
}
}
// @Tags Notify
// @Summary 发送图文链接消息接口
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body notify_response.LinkNotify true "发送图文链接消息的参数"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /notify/sendLinkMessage [post]
func (s *Api) SendLinkMessage(c *gin.Context) {
var linkNotify notify_response.LinkNotify
_ = c.ShouldBindJSON(&linkNotify)
if err := service.ServiceGroupApp.SendLinkMessage(linkNotify.Content, linkNotify.Title, linkNotify.PicUrl, linkNotify.MessageUrl); err != nil {
global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
response.FailWithMessage("发送失败", c)
} else {
response.OkWithData("发送成功", c)
}
}
// @Tags Notify
// @Summary 发送markdown消息接口
// @Security ApiKeyAuth
// @Produce application/json
// @Param data body notify_response.MarkdownNotify true "发送markdown消息的参数"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /notify/sendMarkdownMessage [post]
func (s *Api) SendMarkdownMessage(c *gin.Context) {
var markdownNotify notify_response.MarkdownNotify
_ = c.ShouldBindJSON(&markdownNotify)
if err := service.ServiceGroupApp.SendMarkdownMessage(markdownNotify.Content, markdownNotify.Title, markdownNotify.AtMobiles, markdownNotify.IsAtAll); err != nil {
global.GVA_LOG.Error("发送失败!", zap.Any("err", err))
response.FailWithMessage("发送失败", c)
} else {
response.OkWithData("发送成功", c)
}
}
package api
type ApiGroup struct {
Api
}
var ApiGroupApp = new(ApiGroup)
package config
type DingDing struct {
Url string `mapstructure:"url" json:"url" yaml:"url"` // Url
Token string `mapstructure:"token" json:"token" yaml:"token"` // access_token
Secret string `mapstructure:"secret" json:"secret" yaml:"secret"` // 密钥
}
package global
import "github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/config"
var GlobalConfig_ = &config.DingDing{}
package notify
import (
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/router"
"github.com/gin-gonic/gin"
)
type ddPlugin struct {
Secret string
Token string
Url string
}
func CreateDDPlug(url, Token, Secret string) *ddPlugin {
global.GlobalConfig_.Url = url
global.GlobalConfig_.Token = Token
global.GlobalConfig_.Secret = Secret
return &ddPlugin{}
}
func (*ddPlugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.InitRouter(group)
}
func (*ddPlugin) RouterPath() string {
return "notify"
}
package response
type TextNotify struct { // 文字信息
Content string `json:"content"` // 发送的内容
AtMobiles []string `json:"atMobiles"` // 需要艾特的人的手机号
IsAtAll bool `json:"isAtAll"` // 是否艾特全体
}
type LinkNotify struct { // 图文链接信息
Content string `json:"content"` // 发送的内容
Title string `json:"title"` // 内容标题
PicUrl string `json:"picUrl"` // 配图
MessageUrl string `json:"messageUrl"` // 点击跳转路径
}
type MarkdownNotify struct { // markdown信息
Title string `json:"title"` // 内容标题
Content string `json:"content"` // 发送的内容
AtMobiles []string `json:"atMobiles"` // 需要艾特的人的手机号
IsAtAll bool `json:"isAtAll"` // 是否艾特全体
}
package router
type RouterGroup struct {
NotifyRouter
}
var RouterGroupApp = new(RouterGroup)
package router
import (
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/api"
"github.com/gin-gonic/gin"
)
type NotifyRouter struct {
}
func (s *NotifyRouter) InitRouter(Router *gin.RouterGroup) {
router := Router.Use(middleware.OperationRecord())
var SendTextMessage = api.ApiGroupApp.Api.SendTextMessage
{
router.POST("sendTextMessage", SendTextMessage)
}
}
package service
type ServiceGroup struct {
NotifyService
}
var ServiceGroupApp = new(ServiceGroup)
package service
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/notify/global"
"io/ioutil"
"net/http"
"net/url"
"time"
)
type NotifyService struct {
}
//@author: [Espoir](https://github.com/nightsimon)
//@function: SendTextMessage
//@description: 发送钉钉文字信息
//@params content string发送的文字内容
//@params atMobiles []string 艾特的手机号
//@params isAtAll bool 是否艾特全体
//@return: err error
func (e *NotifyService) SendTextMessage(content string, atMobiles []string, isAtAll bool) (err error) {
msg := map[string]interface{}{
"msgtype": "text",
"text": map[string]string{
"content": content,
},
"at": map[string]interface{}{
"atMobiles": atMobiles,
"isAtAll": isAtAll,
},
}
return SendMessage(msg)
}
//@author: [Espoir](https://github.com/nightsimon)
//@function: SendLinkMessage
//@description: 发送钉钉图文链接信息
//@params content string 发送的文字内容
//@params title string 发送的标题
//@params picUrl string 艾特的手机号
//@params messageUrl string 是否艾特全体
//@return: err error
func (e *NotifyService) SendLinkMessage(content, title, picUrl, messageUrl string) (err error) {
msg := map[string]interface{}{
"msgtype": "link",
"link": map[string]string{
"text": content,
"title": title,
"picUrl": picUrl,
"messageUrl": messageUrl,
},
}
return SendMessage(msg)
}
//@author: [Espoir](https://github.com/nightsimon)
//@function: SendMarkdownMessage
//@description: 发送钉钉Markdown信息
//@params content 发送的文字内容
//@params title 发送的标题
//@params atMobiles []string 艾特的手机号
//@params isAtAll bool 是否艾特全体
//@return: err error
func (e *NotifyService) SendMarkdownMessage(content, title string, atMobiles []string, isAtAll bool) (err error) {
msg := map[string]interface{}{
"msgtype": "markdown",
"markdown": map[string]string{
"text": content,
"title": title,
},
"at": map[string]interface{}{
"atMobiles": atMobiles,
"isAtAll": isAtAll,
},
}
return SendMessage(msg)
}
func SendMessage(msg interface{}) error {
body := bytes.NewBuffer(nil)
err := json.NewEncoder(body).Encode(msg)
if err != nil {
return fmt.Errorf("msg json failed, msg: %v, err: %v", msg, err.Error())
}
value := url.Values{}
value.Set("access_token", global.GlobalConfig_.Token)
if global.GlobalConfig_.Secret != "" {
t := time.Now().UnixNano() / 1e6
value.Set("timestamp", fmt.Sprintf("%d", t))
value.Set("sign", sign(t, global.GlobalConfig_.Secret))
}
request, err := http.NewRequest(http.MethodPost, global.GlobalConfig_.Url, body)
if err != nil {
return fmt.Errorf("error request: %v", err.Error())
}
request.URL.RawQuery = value.Encode()
request.Header.Add("Content-Type", "application/json")
res, err := (&http.Client{}).Do(request)
if err != nil {
return fmt.Errorf("send dingTalk message failed, error: %v", err.Error())
}
defer func() { _ = res.Body.Close() }()
result, err := ioutil.ReadAll(res.Body)
if res.StatusCode != 200 {
return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "http code is not 200"))
}
if err != nil {
return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
}
type response struct {
ErrCode int `json:"errcode"`
}
var ret response
if err := json.Unmarshal(result, &ret); err != nil {
return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, err.Error()))
}
if ret.ErrCode != 0 {
return fmt.Errorf("send dingTalk message failed, %s", httpError(request, res, result, "errcode is not 0"))
}
return nil
}
func httpError(request *http.Request, response *http.Response, body []byte, error string) string {
return fmt.Sprintf(
"http request failure, error: %s, status code: %d, %s %s, body:\n%s",
error,
response.StatusCode,
request.Method,
request.URL.String(),
string(body),
)
}
func sign(t int64, secret string) string {
strToHash := fmt.Sprintf("%d\n%s", t, secret)
hmac256 := hmac.New(sha256.New, []byte(secret))
hmac256.Write([]byte(strToHash))
data := hmac256.Sum(nil)
return base64.StdEncoding.EncodeToString(data)
}
// 其余方法请参考 https://developers.dingtalk.com/document/robots/custom-robot-access?spm=ding_open_doc.document.0.0.7f8710afbfzduV#topic-2026027
因为 它太大了无法显示 source diff 。你可以改为 查看blob
......@@ -5,5 +5,3 @@
项目github:"https://github.com/piexlmax/github.com/flipped-aurora/gin-vue-admin/server"
希望大家给个star多多鼓励
暂时不保存大家生成的结构体 只为方便一次性使用
\ No newline at end of file
......@@ -4,7 +4,7 @@
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:">
{{- if eq .FieldType "bool" }}
<el-switch active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" v-model="formData.{{.FieldJson}}" clearable ></el-switch>
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" clearable placeholder="请输入" />
......@@ -19,7 +19,7 @@
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker type="date" placeholder="选择日期" v-model="formData.{{ .FieldJson }}" clearable></el-date-picker>
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" clearable></el-date-picker>
{{- end }}
{{- if eq .FieldType "float64" }}
<el-input-number v-model="formData.{{ .FieldJson }}" :precision="2" clearable></el-input-number>
......
......@@ -24,13 +24,15 @@
<el-form-item>
<el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openDialog">新增</el-button>
<el-popover v-model="deleteVisible" placement="top" width="160">
<el-popover v-model:visible="deleteVisible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="onDelete">确定</el-button>
</div>
<el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
<template #reference>
<el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
</template>
</el-popover>
</el-form-item>
</el-form>
......@@ -46,24 +48,24 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="日期" width="180">
<template slot-scope="scope">{{ "{{ scope.row.CreatedAt|formatDate }}" }}</template>
<template #default="scope">{{ "{{ formatDate(scope.row.CreatedAt) }}" }}</template>
</el-table-column>
{{- range .Fields}}
{{- if .DictType}}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template slot-scope="scope">
<template #default="scope">
{{"{{"}} filterDict(scope.row.{{.FieldJson}},"{{.DictType}}") {{"}}"}}
</template>
</el-table-column>
{{- else if eq .FieldType "bool" }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120">
<template slot-scope="scope">{{ "{{scope.row."}}{{.FieldJson}}{{"|formatBoolean}}" }}</template>
<template #default="scope">{{"{{"}} formatBoolean(scope.row.{{.FieldJson}}) {{"}}"}}</template>
</el-table-column> {{- else }}
<el-table-column label="{{.FieldDesc}}" prop="{{.FieldJson}}" width="120" />
{{- end }}
{{- end }}
<el-table-column label="按钮组">
<template slot-scope="scope">
<template #default="scope">
<el-button size="small" type="primary" icon="el-icon-edit" class="table-button" @click="update{{.StructName}}(scope.row)">变更</el-button>
<el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteRow(scope.row)">删除</el-button>
</template>
......@@ -79,12 +81,12 @@
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
<el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
<el-form :model="formData" label-position="right" label-width="80px">
{{- range .Fields}}
<el-form-item label="{{.FieldDesc}}:">
{{- if eq .FieldType "bool" }}
<el-switch active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" v-model="formData.{{.FieldJson}}" clearable ></el-switch>
<el-switch v-model="formData.{{.FieldJson}}" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
{{- end }}
{{- if eq .FieldType "string" }}
<el-input v-model="formData.{{.FieldJson}}" clearable placeholder="请输入" />
......@@ -99,7 +101,7 @@
{{- end }}
{{- end }}
{{- if eq .FieldType "time.Time" }}
<el-date-picker type="date" placeholder="选择日期" v-model="formData.{{ .FieldJson }}" clearable />
<el-date-picker v-model="formData.{{ .FieldJson }}" type="date" placeholder="选择日期" clearable />
{{- end }}
{{- if eq .FieldType "float64" }}
<el-input-number v-model="formData.{{ .FieldJson }}" :precision="2" clearable />
......@@ -107,10 +109,12 @@
</el-form-item>
{{- end }}
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
......@@ -124,27 +128,9 @@ import {
find{{.StructName}},
get{{.StructName}}List
} from '@/api/{{.PackageName}}' // 此处请自行替换地址
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: '{{.StructName}}',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
formatBoolean: function(bool) {
if (bool != null) {
return bool ? '' : ''
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
......@@ -4,5 +4,4 @@ type RouterGroup struct {
CustomerRouter
ExcelRouter
FileUploadAndDownloadRouter
SimpleUploaderRouter
}
package example
import (
v1 "github.com/flipped-aurora/gin-vue-admin/server/api/v1"
"github.com/gin-gonic/gin"
)
type SimpleUploaderRouter struct{}
func (e *SimpleUploaderRouter) InitSimpleUploaderRouter(Router *gin.RouterGroup) {
simpleUploaderRouter := Router.Group("simpleUploader")
var simpleUploaderApi = v1.ApiGroupApp.ExampleApiGroup.SimpleUploaderApi
{
simpleUploaderRouter.POST("upload", simpleUploaderApi.SimpleUploaderUpload) // 上传功能
simpleUploaderRouter.GET("checkFileMd5", simpleUploaderApi.CheckFileMd5) // 文件完整度验证
simpleUploaderRouter.GET("mergeFileMd5", simpleUploaderApi.MergeFileMd5) // 合并文件
}
}
......@@ -4,5 +4,4 @@ type ServiceGroup struct {
FileUploadAndDownloadService
CustomerService
ExcelService
SimpleUploaderService
}
package example
import (
"errors"
"fmt"
"io/ioutil"
"os"
"strconv"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/example"
"gorm.io/gorm"
)
type SimpleUploaderService struct {
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: SaveChunk
//@description: 保存文件切片路径
//@param: uploader model.ExaSimpleUploader
//@return: err error
func (exa *SimpleUploaderService) SaveChunk(uploader example.ExaSimpleUploader) (err error) {
return global.GVA_DB.Create(uploader).Error
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: CheckFileMd5
//@description: 检查文件是否已经上传过
//@param: md5 string
//@return: err error, uploads []model.ExaSimpleUploader, isDone bool
func (exa *SimpleUploaderService) CheckFileMd5(md5 string) (err error, uploads []example.ExaSimpleUploader, isDone bool) {
err = global.GVA_DB.Find(&uploads, "identifier = ? AND is_done = ?", md5, false).Error
isDone = errors.Is(global.GVA_DB.First(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound)
return err, uploads, !isDone
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: MergeFileMd5
//@description: 合并文件
//@param: md5 string, fileName string
//@return: err error
func (exa *SimpleUploaderService) MergeFileMd5(md5 string, fileName string) (err error) {
finishDir := "./finish/"
dir := "./chunk/" + md5
// 如果文件上传成功 不做后续操作 通知成功即可
if !errors.Is(global.GVA_DB.First(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, true).Error, gorm.ErrRecordNotFound) {
return nil
}
// 打开切片文件夹
rd, err := ioutil.ReadDir(dir)
_ = os.MkdirAll(finishDir, os.ModePerm)
// 创建目标文件
fd, err := os.OpenFile(finishDir+fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
return
}
// 关闭文件
defer fd.Close()
// 将切片文件按照顺序写入
for k := range rd {
content, _ := ioutil.ReadFile(dir + "/" + fileName + strconv.Itoa(k+1))
_, err = fd.Write(content)
if err != nil {
_ = os.Remove(finishDir + fileName)
}
}
if err != nil {
return err
}
err = global.GVA_DB.Transaction(func(tx *gorm.DB) error {
// 删除切片信息
if err = tx.Delete(&example.ExaSimpleUploader{}, "identifier = ? AND is_done = ?", md5, false).Error; err != nil {
fmt.Println(err)
return err
}
data := example.ExaSimpleUploader{
Identifier: md5,
IsDone: true,
FilePath: finishDir + fileName,
Filename: fileName,
}
// 添加文件信息
if err = tx.Create(&data).Error; err != nil {
fmt.Println(err)
return err
}
return nil
})
err = os.RemoveAll(dir) // 清除切片
return err
}
......@@ -247,7 +247,7 @@ func (autoCodeService *AutoCodeService) CreateTemp(autoCode system.AutoCodeStruc
return err
}
if autoCode.AutoMoveFile {
return errors.New("创建代码成功并移动文件成功")
return system.AutoMoveErr
}
return nil
......
......@@ -133,7 +133,6 @@ func (initDBService *InitDBService) InitDB(conf request.InitDB) error {
example.ExaFileUploadAndDownload{},
example.ExaFile{},
example.ExaFileChunk{},
example.ExaSimpleUploader{},
example.ExaCustomer{},
system.SysOperationRecord{},
system.SysAutoCodeHistory{},
......
......@@ -36,7 +36,6 @@ var authorityMenus = []AuthorityMenus{
{"888", 18},
{"888", 19},
{"888", 20},
{"888", 21},
{"888", 22},
{"888", 23},
{"888", 24},
......
......@@ -35,8 +35,7 @@ var menus = []system.SysBaseMenu{
{GVA_MODEL: global.GVA_MODEL{ID: 18, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "dictionary", Name: "dictionary", Component: "view/superAdmin/dictionary/sysDictionary.vue", Sort: 5, Meta: system.Meta{Title: "字典管理", Icon: "notebook-2"}},
{GVA_MODEL: global.GVA_MODEL{ID: 19, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: true, ParentId: "3", Path: "dictionaryDetail/:id", Name: "dictionaryDetail", Component: "view/superAdmin/dictionary/sysDictionaryDetail.vue", Sort: 1, Meta: system.Meta{Title: "字典详情", Icon: "s-order"}},
{GVA_MODEL: global.GVA_MODEL{ID: 20, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "3", Path: "operation", Name: "operation", Component: "view/superAdmin/operation/sysOperationRecord.vue", Sort: 6, Meta: system.Meta{Title: "操作历史", Icon: "time"}},
{GVA_MODEL: global.GVA_MODEL{ID: 21, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, Hidden: false, ParentId: "9", Path: "simpleUploader", Name: "simpleUploader", Component: "view/example/simpleUploader/simpleUploader", Sort: 6, Meta: system.Meta{Title: "断点续传(插件版)", Icon: "upload"}},
{GVA_MODEL: global.GVA_MODEL{ID: 22, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "https://www.github.com/flipped-aurora/gin-vue-admin/server.com", Name: "https://www.github.com/flipped-aurora/gin-vue-admin/server.com", Hidden: false, Component: "/", Sort: 0, Meta: system.Meta{Title: "官方网站", Icon: "s-home"}},
{GVA_MODEL: global.GVA_MODEL{ID: 22, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "https://www.gin-vue-admin.com", Name: "https://www.gin-vue-admin.com", Hidden: false, Component: "/", Sort: 0, Meta: system.Meta{Title: "官方网站", Icon: "s-home"}},
{GVA_MODEL: global.GVA_MODEL{ID: 23, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "0", Path: "state", Name: "state", Hidden: false, Component: "view/system/state.vue", Sort: 6, Meta: system.Meta{Title: "服务器状态", Icon: "cloudy"}},
{GVA_MODEL: global.GVA_MODEL{ID: 24, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeAdmin", Name: "autoCodeAdmin", Hidden: false, Component: "view/systemTools/autoCodeAdmin/index.vue", Sort: 1, Meta: system.Meta{Title: "自动化代码管理", Icon: "s-finance"}},
{GVA_MODEL: global.GVA_MODEL{ID: 25, CreatedAt: time.Now(), UpdatedAt: time.Now()}, MenuLevel: 0, ParentId: "14", Path: "autoCodeEdit/:id", Name: "autoCodeEdit", Hidden: true, Component: "view/systemTools/autoCode/index.vue", Sort: 0, Meta: system.Meta{Title: "自动化代码(复用)", Icon: "s-finance"}},
......
......@@ -16,6 +16,7 @@ module.exports = {
// add your custom rules here
// it is base on https://github.com/vuejs/eslint-config-vue
rules: {
'vue/no-v-model-argument':'off',
'vue/max-attributes-per-line': [
2,
{
......
node_modules/*
\ No newline at end of file
......@@ -3,12 +3,6 @@ module.exports = {
'@vue/cli-plugin-babel/preset'
],
'plugins': [
[
'component',
{
'libraryName': 'element-ui',
'styleLibraryName': 'theme-chalk'
}
]
]
}
此差异已折叠。
{
"name": "gin-vue-admin",
"name": "gvav3",
"version": "0.1.0",
"private": true,
"scripts": {
......@@ -8,16 +8,13 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@antv/dom-util": "2.0.2",
"@antv/g-canvas": "^0.4.12",
"@antv/g6": "3.5.2",
"@antv/matrix-util": "2.0.7",
"@antv/util": "~2.0.9",
"@moefe/vue-aplayer": "^2.0.0-beta.5",
"axios": "^0.19.2",
"element-ui": "^2.12.0",
"core-js": "^3.6.5",
"element-plus": "^1.1.0-beta.4",
"highlight.js": "^10.6.0",
"marked": "^2.0.0",
"mitt": "^3.0.0",
"path": "^0.12.7",
"qs": "^6.8.0",
"quill": "^1.3.7",
......@@ -26,50 +23,26 @@
"script-ext-html-webpack-plugin": "^2.1.4",
"spark-md5": "^3.0.1",
"timeline-vuejs": "1.1.1",
"vue": "^2.6.10",
"vue": "^3.0.0",
"vue-particle-line": "^0.1.4",
"vue-router": "^3.1.3",
"vue-router": "^4.0.0-0",
"vue-simple-uploader": "^0.7.4",
"vuescroll": "^4.14.4",
"vuex": "^3.1.1",
"vuex": "^4.0.0-0",
"vuex-persist": "^2.1.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.6",
"@vue/cli-plugin-eslint": "^4.5.6",
"@vue/cli-service": "^4.5.6",
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-router": "~4.5.0",
"@vue/cli-plugin-vuex": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-component": "^1.1.1",
"babel-preset-es2015": "^6.24.1",
"core-js": "^3.3.2",
"babel-plugin-import": "^1.13.3",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"numericjs": "^1.2.6",
"raw-loader": "^3.1.0",
"sass-loader": "^8.0.0",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions"
]
"eslint-plugin-vue": "^7.0.0",
"sass": "^1.26.5",
"sass-loader": "^8.0.2"
}
}
web/public/favicon.ico

4.2 KB | W: | H:

web/public/favicon.ico

4.2 KB | W: | H:

web/public/favicon.ico
web/public/favicon.ico
web/public/favicon.ico
web/public/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<html lang="">
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%=htmlWebpackPlugin.options.title%></title>
<% if(process.env.NODE_ENV!=='development' && htmlWebpackPlugin.options.cdns){ %>
<% htmlWebpackPlugin.options.cdns.forEach(function(item){ if(item.js){ %>
<script type="text/javascript" src="<%= item.js %>"></script>
<% } }) %>
<% } %>
</head>
<body>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but GIN-VUE-ADMIN doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="./js/project.js"></script>
</body>
</body>
</html>
// 未经过授权商用请勿删除此文件,将会导致意想不到的bug
(function(){})(
document.write(
unescape(`%3Cspan style='display:none' id='cnzz_stat_icon_1279266757'%3E%3C/span%3E%3Cscript src='https://s4.cnzz.com/z_stat.php%3Fid%3D1279266757' type='text/javascript'%3E%3C/script%3E`)
)
)
\ No newline at end of file
import service from '@/utils/request'
import { Message } from 'element-ui'
import { ElMessage } from 'element-plus'
const handleFileError = (res, fileName) => {
if (typeof (res.data) !== 'undefined') {
......@@ -7,7 +7,7 @@ const handleFileError = (res, fileName) => {
const reader = new FileReader()
reader.onload = function() {
const message = JSON.parse(reader.result).msg
Message({
ElMessage({
showClose: true,
message: message,
type: 'error'
......
import axios from 'axios'
import { Loading } from 'element-ui'
import { ElLoading } from 'element-plus'
let loadingInstance
const service = axios.create()
service.interceptors.request.use((config) => {
loadingInstance = Loading.service({ fullscreen: true })
loadingInstance = ElLoading.service({ fullscreen: true })
return config
})
......
import service from '@/utils/request'
// @Tags SimpleUploader
// @Summary 断点续传插件版示例
// @Security ApiKeyAuth
// @Produce application/json
// @Param params md5 get "测试文件是否已经存在和判断已经上传过的切片"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /simpleUploader/checkFileMd5 [get]
export const checkFileMd5 = (params) => {
return service({
url: '/simpleUploader/checkFileMd5',
method: 'get',
params
})
}
// @Tags SimpleUploader
// @Summary 合并文件
// @Security ApiKeyAuth
// @Produce application/json
// @Param params md5 get "合并文件"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"合并成功"}"
// @Router /simpleUploader/mergeFileMd5 [get]
export const mergeFileMd5 = (params) => {
return service({
url: '/simpleUploader/mergeFileMd5',
method: 'get',
params
})
}
<template>
<el-drawer title="媒体库" :visible.sync="drawer">
<el-drawer v-model="drawer" title="媒体库">
<div class="media">
<el-image
v-for="(item,key) in picList"
:key="key"
class="header-img-box-list"
:src="(item.url && item.url.slice(0, 4) !== 'http')?path+item.url:item.url"
@click.native="chooseImg(item.url,target,targetKey)"
@click="chooseImg(item.url,target,targetKey)"
>
<div slot="error" class="header-img-box-list">
<i class="el-icon-picture-outline" />
</div>
<template #error>
<div class="header-img-box-list">
<i class="el-icon-picture-outline" />
</div>
</template>
</el-image>
</div>
</el-drawer>
......
/*
*
* 按需加载element
*
*
* */
import Vue from 'vue'
// 按需引入element
import {
Button,
Select,
Dialog,
Form,
Input,
FormItem,
Option,
Loading,
Message,
Container,
Card,
Dropdown,
DropdownMenu,
DropdownItem,
Row,
Col,
Menu,
Submenu,
MenuItem,
Aside,
Main,
Badge,
Header,
Tabs,
Breadcrumb,
BreadcrumbItem,
Scrollbar,
Avatar,
TabPane,
Divider,
Table,
TableColumn,
Cascader,
Checkbox,
CheckboxGroup,
Pagination,
Tag,
Drawer,
Tree,
Popover,
Switch,
Collapse,
CollapseItem,
Tooltip,
DatePicker,
InputNumber,
Steps,
Upload,
Progress,
MessageBox,
Image,
ColorPicker
} from 'element-ui'
Vue.use(Button)
Vue.use(Select)
Vue.use(Dialog)
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Option)
Vue.use(Container)
Vue.use(Card)
Vue.use(Dropdown)
Vue.use(DropdownMenu)
Vue.use(DropdownItem)
Vue.use(Row)
Vue.use(Col)
Vue.use(Menu)
Vue.use(Submenu)
Vue.use(MenuItem)
Vue.use(Aside)
Vue.use(Main)
Vue.use(Badge)
Vue.use(Header)
Vue.use(Tabs)
Vue.use(Breadcrumb)
Vue.use(BreadcrumbItem)
Vue.use(Avatar)
Vue.use(TabPane)
Vue.use(Divider)
Vue.use(Table)
Vue.use(TableColumn)
Vue.use(Checkbox)
Vue.use(Cascader)
Vue.use(Tag)
Vue.use(Pagination)
Vue.use(Drawer)
Vue.use(Tree)
Vue.use(CheckboxGroup)
Vue.use(Popover)
Vue.use(InputNumber)
Vue.use(Switch)
Vue.use(Collapse)
Vue.use(CollapseItem)
Vue.use(Tooltip)
Vue.use(DatePicker)
Vue.use(Steps)
Vue.use(Upload)
Vue.use(Progress)
Vue.use(Scrollbar)
Vue.use(Loading.directive)
Vue.use(Image)
Vue.use(ColorPicker)
Vue.prototype.$loading = Loading.service
Vue.prototype.$message = Message
Vue.prototype.$confirm = MessageBox.confirm
Dialog.props.closeOnClickModal.default = false
console.warn('[GIN-VUE-ADMIN]--按需加载elementUI成功,如出现element-ui组件无法使用问题,请至/src/core/element_lazy 下引入对应组件即可')
......@@ -2,29 +2,24 @@
* gin-vue-admin web框架组
*
* */
import Vue from 'vue'
import './element_lazy' // 按需加载element
import uploader from 'vue-simple-uploader'
import APlayer from '@moefe/vue-aplayer'
// time line css
import '../../node_modules/timeline-vuejs/dist/timeline-vuejs.css'
// 路由守卫
import Bus from '@/utils/bus'
// 加载网站配置文件夹
import config from './config'
Vue.prototype.$GIN_VUE_ADMIN = config
Vue.use(Bus)
Vue.use(APlayer, {
defaultCover: 'https://github.com/u3u.png',
productionTip: true
})
Vue.use(uploader)
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:V2.4.5 alpha
加群方式:微信:shouzi_1994 QQ群:622360840
默认自动化文档地址:http://127.0.0.1:${process.env.VUE_APP_SERVER_PORT}/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:${process.env.VUE_APP_CLI_PORT}
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
`)
export const run = function(app) {
app.config.globalProperties.$GIN_VUE_ADMIN = config
// app.use(uploader)
console.log(`
欢迎使用 Gin-Vue-Admin
当前版本:V2.4.5 alpha
加群方式:微信:shouzi_1994 QQ群:622360840
默认自动化文档地址:http://127.0.0.1:${process.env.VUE_APP_SERVER_PORT}/swagger/index.html
默认前端文件运行地址:http://127.0.0.1:${process.env.VUE_APP_CLI_PORT}
如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
`)
}
// 权限按钮展示指令
import { store } from '@/store'
export const auth = (Vue) => {
Vue.directive('auth', {
export const auth = (app) => {
app.directive('auth', {
// 当被绑定的元素插入到 DOM 中时……
bind: function(el, binding) {
const userInfo = store.getters['user/userInfo']
......
import Vue from 'vue'
import App from './App.vue'
import { createApp } from 'vue'
// 引入gin-vue-admin前端初始化相关内容
import './core/gin-vue-admin'
// 引入封装的router
import router from '@/router/index'
import { run } from '@/core/gin-vue-admin.js'
import '@/permission'
import { store } from '@/store'
Vue.config.productionTip = false
import { store } from '@/store/index'
import { auth } from '@/directive/auth'
// 按钮权限指令
auth(Vue)
export default new Vue({
render: h => h(App),
router,
store
}).$mount('#app')
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import App from './App.vue'
const app = createApp(App)
run(app)
auth(app)
app.config.productionTip = false
app.use(store).use(router).use(ElementPlus, { locale: zhCn }).mount('#app')
export default app
import { getDict } from '@/utils/dictionary'
import { formatTimeToStr } from '@/utils/date'
export default {
data() {
return {
......@@ -10,6 +12,21 @@ export default {
}
},
methods: {
formatBoolean: function(bool) {
if (bool !== null) {
return bool ? '' : ''
} else {
return ''
}
},
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
filterDict(value, type) {
const rowLabel = this[type + 'Options'] && this[type + 'Options'].filter(item => item.value === value)
return rowLabel && rowLabel[0] && rowLabel[0].label
......
import router from './router'
import { store } from '@/store'
import { store } from '@/store/index'
import getPageTitle from '@/utils/page'
let asyncRouterFlag = 0
const whiteList = ['Login', 'Init']
router.beforeEach(async(to, from, next) => {
const token = store.getters['user/token']
// 在白名单中的判断情况
// 修改网页标签名称
document.title = getPageTitle(to.meta.title)
if (whiteList.indexOf(to.name) > -1) {
if (token) {
next({ name: store.getters['user/userInfo'].authority.defaultRouter })
next({ path: '/layout/dashboard' })
} else {
next()
}
......@@ -20,19 +19,17 @@ router.beforeEach(async(to, from, next) => {
// 不在白名单中并且已经登陆的时候
if (token) {
// 添加flag防止多次获取动态路由和栈溢出
if (!asyncRouterFlag && store.getters['router/asyncRouters'].length === 0) {
if (!asyncRouterFlag) {
asyncRouterFlag++
await store.dispatch('router/SetAsyncRouter')
await store.dispatch('user/GetUserInfo')
const asyncRouters = store.getters['router/asyncRouters']
router.addRoutes(asyncRouters)
asyncRouters.map(asyncRouter => {
router.addRoute(asyncRouter)
})
next({ ...to, replace: true })
} else {
if (to.matched.length) {
next()
} else {
next({ path: '/layout/404' })
}
next()
}
}
// 不在白名单中并且未登陆的时候
......
import Vue from 'vue'
import Router from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router'
Vue.use(Router)
// 获取原型对象上的push函数
const originalPush = Router.prototype.push
// 修改原型对象中的push方法
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
const routes = [{
path: '/',
redirect: '/login'
},
{
path: '/init',
name: 'Init',
component: () => import('@/view/init/index')
},
{
path: '/login',
name: 'Login',
component: () => import('@/view/login/index')
}
const baseRouters = [
{
path: '/',
redirect: '/login'
},
{
path: '/init',
name: 'Init',
component: () => import('@/view/init/index')
},
{
path: '/login',
name: 'Login',
component: () => import('@/view/login/index')
}
]
// 需要通过后台数据来生成的组件
const createRouter = () => new Router({
routes: baseRouters
const router = createRouter({
history: createWebHashHistory(),
routes
})
const router = createRouter()
export default router
import Vue from 'vue'
import Vuex from 'vuex'
import { createStore } from 'vuex'
import VuexPersistence from 'vuex-persist'
import { user } from '@/store/module/user'
import { router } from '@/store/module/router'
import { dictionary } from '@/store/module/dictionary'
Vue.use(Vuex)
const vuexLocal = new VuexPersistence({
storage: window.localStorage,
modules: ['user']
})
export const store = new Vuex.Store({
export const store = createStore({
modules: {
user,
router,
......
......@@ -3,18 +3,18 @@ import { findSysDictionary } from '@/api/sysDictionary'
export const dictionary = {
namespaced: true,
state: {
dictionaryMap: {}
dictionaryMap: {},
},
mutations: {
setDictionaryMap(state, dictionaryMap) {
state.dictionaryMap = { ...state.dictionaryMap, ...dictionaryMap }
}
},
},
actions: {
// 从后台获取动态路由
async getDictionary({ commit, state }, type) {
if (state.dictionaryMap[type]) {
if (state.dictionaryMap[type] && state.dictionaryMap[type].length) {
return state.dictionaryMap[type]
} else {
const res = await findSysDictionary({ type })
......
......@@ -3,11 +3,13 @@ import { asyncRouterHandle } from '@/utils/asyncRouter'
import { asyncMenu } from '@/api/menu'
const routerList = []
const formatRouter = (routes) => {
routes && routes.map(item => {
if ((!item.children || item.children.every(ch => ch.hidden)) && item.name !== '404') {
if ((!item.children || item.children.every(ch => ch.hidden)) && item.name !== '404' && !item.hidden) {
routerList.push({ label: item.meta.title, value: item.name })
}
item.meta.hidden = item.hidden
if (item.children && item.children.length > 0) {
formatRouter(item.children)
}
......@@ -18,7 +20,7 @@ export const router = {
namespaced: true,
state: {
asyncRouters: [],
routerList: routerList
routerList: routerList,
},
mutations: {
setRouterList(state, routerList) {
......@@ -42,24 +44,22 @@ export const router = {
children: []
}]
const asyncRouterRes = await asyncMenu()
if (asyncRouterRes.code !== 0) {
return
}
const asyncRouter = asyncRouterRes.data && asyncRouterRes.data.menus
const asyncRouter = asyncRouterRes.data.menus
asyncRouter.push({
path: '404',
name: '404',
hidden: true,
meta: {
title: '迷路了*。*'
title: '迷路了*。*',
},
component: 'view/error/index.vue'
})
formatRouter(asyncRouter)
baseRouter[0].children = asyncRouter
baseRouter.push({
path: '*',
path: '/:catchAll(.*)',
redirect: '/layout/404'
})
asyncRouterHandle(baseRouter)
commit('setAsyncRouter', baseRouter)
......@@ -74,9 +74,6 @@ export const router = {
},
routerList(state) {
return state.routerList
},
defaultRouter(state) {
return state.defaultRouter
}
}
}
......@@ -2,7 +2,7 @@ import { login, getUserInfo } from '@/api/user'
import { jsonInBlacklist } from '@/api/jwt'
import router from '@/router/index'
import { setUserInfo } from '@/api/user'
import { Message } from 'element-ui'
import { ElMessage } from 'element-plus'
export const user = {
namespaced: true,
......@@ -16,7 +16,7 @@ export const user = {
activeColor: '#1890ff',
baseColor: '#fff'
},
token: ''
token: '',
},
mutations: {
setUserInfo(state, userInfo) {
......@@ -70,7 +70,9 @@ export const user = {
commit('setToken', res.data.token)
await dispatch('router/SetAsyncRouter', {}, { root: true })
const asyncRouters = rootGetters['router/asyncRouters']
router.addRoutes(asyncRouters)
asyncRouters.map(asyncRouter => {
router.addRoute(asyncRouter)
})
// const redirect = router.history.current.query.redirect
// console.log(redirect)
// if (redirect) {
......@@ -91,7 +93,7 @@ export const user = {
const res = await setUserInfo({ activeColor: data, ID: state.userInfo.ID })
if (res.code === 0) {
commit('ChangeActiveColor', data)
Message({
ElMessage({
type: 'success',
message: '设置成功'
})
......@@ -101,7 +103,7 @@ export const user = {
const res = await setUserInfo({ sideMode: data, ID: state.userInfo.ID })
if (res.code === 0) {
commit('ChangeSideMode', data)
Message({
ElMessage({
type: 'success',
message: '设置成功'
})
......@@ -111,7 +113,7 @@ export const user = {
const res = await setUserInfo({ baseColor: data, ID: state.userInfo.ID })
if (res.code === 0) {
commit('ChangeBaseColor', data)
Message({
ElMessage({
type: 'success',
message: '设置成功'
})
......@@ -152,6 +154,5 @@ export const user = {
}
return state.userInfo.activeColor
}
}
}
......@@ -8,7 +8,6 @@
*/
@import '@/style/basics.scss';
html {
line-height: 1.15;
/* 1 */
......@@ -535,17 +534,36 @@ li {
// 导航
#app {
.pd-lr-15 {
padding: 0 15px;
}
.height-full {
height: 100%;
}
.width-full {
width: 100%;
}
.dp-flex {
display: flex;
}
.justify-content-center {
justify-content: center;
}
.align-items {
align-items: center;
}
.pd-0 {
padding: 0;
}
.el-container {
position: relative;
height: 100%;
width: 100%;
}
.el-container.mobile.openside {
position: fixed;
top: 0;
}
.el-aside {
-webkit-transition: width .2s;
transition: width .2s;
......@@ -559,11 +577,9 @@ li {
left: 0;
z-index: 1001;
overflow: hidden;
.el-menu {
border-right: none;
}
.tilte {
min-height: $height-aside-tilte;
line-height: $height-aside-tilte;
......@@ -578,7 +594,6 @@ li {
border-radius: 50%;
padding: 3px;
}
.tit-text {
display: inline-block;
color: #fff;
......@@ -588,15 +603,17 @@ li {
padding-left: 10px;
}
}
}
.aside {
.el-menu-item {
>div {
padding: 0 15px !important;
}
}
.el-menu-vertical {
transition: all 0.3s;
background-color: $bg-aside;
}
.el-submenu {
.el-sub-menu {
background-color: $bg-aside;
.el-menu {
.el-menu-item {
......@@ -607,46 +624,43 @@ li {
.is-active {
background-color: #1890ff;
// 关闭三级菜单二级菜单样式
ul{
border:none;
ul {
border: none;
}
}
// 关闭三级菜单二级菜单样式
.is-active.is-opened{
.is-active.is-opened {
background-color: #191a23;
ul{
border:none;
ul {
border: none;
}
}
}
}
.el-menu-item:focus, .el-menu-item:hover{
.el-menu-item:focus,
.el-menu-item:hover {
background-color: transparent;
}
.el-menu-item:hover i,
.el-menu-item:hover span {
color: #fff;
}
.el-submenu__title:hover {
.el-sub-menu__title:hover {
background-color: $bg-aside;
}
.el-submenu__title:hover i,
.el-submenu__title:hover span {
.el-sub-menu__title:hover i,
.el-sub-menu__title:hover span {
color: #fff;
}
.el-menu--inline {
border-left: 5px solid #2c3b41;
}
}
.hideside {
.aside {
width: $width-hideside-aside;
}
}
.mobile.hideside {
.el-aside {
-webkit-transition-duration: .2s;
......@@ -655,7 +669,6 @@ li {
transform: translate3d(-220px, 0, 0);
}
}
.mobile {
.el-aside {
-webkit-transition: -webkit-transform .28s;
......@@ -665,9 +678,6 @@ li {
width: $width-mobile-aside;
}
}
.main-cont.el-main {
min-height: 100%;
-webkit-transition: margin-left .28s;
......@@ -675,19 +685,16 @@ li {
margin-left: $width-aside;
position: relative;
}
.hideside {
.main-cont.el-main {
margin-left: 54px;
}
}
.mobile {
.main-cont.el-main {
margin-left: 0px;
}
}
.openside.mobile {
.shadowBg {
background: #000;
......@@ -702,8 +709,6 @@ li {
}
}
// layout
.layout-cont {
.main-cont {
......@@ -724,7 +729,6 @@ li {
height: 30px;
line-height: 30px;
}
.el-input__icon {
line-height: 30px;
}
......@@ -736,65 +740,52 @@ li {
padding: 14px;
margin: 114px 14px 20px;
border-radius: 2px;
.el-table--border {
border-radius: 4px;
margin-bottom: 14px;
}
.el-table {
thead {
color: $color-table-thead;
}
th {
padding: 5px 0;
.cell {
min-height: 34px;
line-height: 34px;
}
}
td {
padding: 8px 0;
}
td,
th.is-leaf {
border-bottom: 1px solid #e8e8e8;
}
}
.search-term {
border-left: none;
border-right: none;
padding: 0 5px;
.el-form-item {
margin-bottom: 10px;
}
}
.el-pagination {
padding: 20px 0 0 0;
}
.upload-demo,
.upload {
padding: 0;
}
.system {
padding: 0;
}
.el-form.el-form--inline {
.el-form-item:last-child {
margin-bottom: 0;
}
}
.edit_container,
.edit {
padding: 0;
......@@ -819,7 +810,6 @@ li {
padding: 0 15px;
border-left: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
.demo-form-inline {
margin-bottom: 10px;
}
......@@ -850,13 +840,11 @@ li {
}
.el-pagination {
.btn-prev,
.btn-next {
border: 1px solid #ddd;
border-radius: 4px;
}
.el-pager {
li {
color: #666;
......@@ -866,13 +854,11 @@ li {
border-radius: 4px;
}
}
padding: 20px 0 !important;
}
.el-row {
padding: 10px 0;
.el-col>label {
line-height: 30px;
text-align: right;
......@@ -880,7 +866,6 @@ li {
padding-right: 15px;
display: inline-block;
}
.line {
line-height: 30px;
text-align: center;
......@@ -891,7 +876,6 @@ li {
.edit_container {
background-color: $white-bg;
padding: 15px;
.el-button {
margin: 15px 0;
}
......@@ -900,7 +884,6 @@ li {
.edit {
background-color: $white-bg;
padding: 15px;
.el-button {
margin: 15px 0;
}
......@@ -911,12 +894,10 @@ li {
.upload {
background-color: $white-bg;
padding: 15px;
.el-upload-list__item-status-label {
right: 0;
left: 120px;
}
.el-upload__tip {
margin: 10px 0;
}
......@@ -925,33 +906,27 @@ li {
// system
.system {
padding: 15px;
.el-input__inner {
width: 80%;
}
}
// .el-menu .el-menu--inline {
// background: #2c3b41;
// }
// .el-submenu .el-submenu {
// .el-sub-menu .el-sub-menu {
// background-color: #000408 !important;
// }
// .aside .el-scrollbar .el-scrollbar__view .el-submenu__title:hover {
// .aside .el-scrollbar .el-scrollbar__view .el-sub-menu__title:hover {
// background-color: $bg-aside !important;
// }
// .el-menu--vertical {
// .el-menu {
// margin-left: -8px;
// background-color: rgb(48, 65, 86);
// .el-menu-item {
// background-color: rgb(48, 65, 86);
// }
// .el-menu-item:focus,
// .el-menu-item:hover {
// background-color: #263445;
......@@ -959,7 +934,6 @@ li {
// }
// }
// }
// 导航*****
// add 5.13
.el-container {
......@@ -967,83 +941,67 @@ li {
// padding: 15px;
// margin: 115px 15px 20px;
// border-radius: 2px;
// .button-box {
// border: none;
// padding: 0 0 10px 0px;
// }
// .el-table--border {
// border-radius: 4px;
// margin-bottom: 15px;
// }
// .el-table {
// thead {
// color: $color-table-thead;
// }
// th {
// padding: 5px 0;
// .cell {
// min-height: 34px;
// line-height: 34px;
// }
// }
// td {
// padding: 8px 0;
// }
// td,
// th.is-leaf {
// border-bottom: 1px solid #e8e8e8;
// }
// }
// .search-term {
// border-left: none;
// border-right: none;
// padding: 0 5px;
// .el-form-item {
// margin-bottom: 10px;
// }
// }
// .el-pagination {
// padding: 20px 0 0 0;
// }
// .upload-demo,
// .upload {
// padding: 0;
// }
// .system {
// padding: 0;
// }
// .el-form.el-form--inline {
// .el-form-item:last-child {
// margin-bottom: 0;
// }
// }
// .edit_container,
// .edit {
// padding: 0;
// }
// }
// .admin-box:after,
// .admin-box:before {
// content: "";
// display: block;
// clear: both;
// }
.tips {
margin-top: 10px;
font-size: 14px;
......@@ -1053,7 +1011,6 @@ li {
}
.el-container.layout-cont {
// .header-cont,
// .breadcrumb {
// height: 40px !important;
......@@ -1061,13 +1018,11 @@ li {
// }
.main-cont.el-main {
background-color: $bg-main;
.menu-total {
font-size: 22px;
color: #838383;
margin-top: 16px;
}
// background-color: #f0f2f5;
}
}
......@@ -1080,21 +1035,17 @@ li {
padding: 0 6px;
border-top: 1px solid $border-color;
padding: 0;
.el-tabs__header {
margin: 0px 0 0 0;
.el-tabs__item {
height: $height-nav-scroll;
height: $height-nav-scroll;
border: none;
border-left: 1px solid $border-color;
}
.el-tabs__item.is-active {
background-color: rgba(64, 158, 255, .08);
}
.el-tabs__nav {
border: none;
}
......@@ -1107,7 +1058,6 @@ li {
.el-button.el-button--text.el-button--small {
position: relative;
}
// .el-button.el-button--text.el-button--small::after {
// content: '';
// position: absolute;
......@@ -1139,7 +1089,6 @@ li {
border-right: 1.5px solid #ccc;
margin-left: 6px;
}
.el-table__placeholder {
width: 10px;
}
......@@ -1152,7 +1101,6 @@ li {
border-right: 1.5px solid #ccc;
margin-left: 6px;
}
.el-table__placeholder {
width: 10px;
}
......@@ -1180,7 +1128,6 @@ li {
$headerHigh: 52px;
$mainHight: 100vh;
.dropdown-group {
min-width: 100px;
}
......@@ -1190,18 +1137,14 @@ $mainHight: 100vh;
top: 0;
box-sizing: border-box;
z-index: 999;
>.el-row{
>.el-row {
padding: 0;
.el-col-lg-14{
.el-col-lg-14 {
height: 60px;
}
}
}
.el-scrollbar__wrap {
padding-bottom: 17px;
}
......@@ -1213,19 +1156,16 @@ $mainHight: 100vh;
text-align: center;
vertical-align: middle;
margin-right: 10px;
img {
vertical-align: middle;
border: 1px solid #ccc;
border-radius: 6px;
}
}
.header-cont {
height: $height-header;
background: #fff;
}
.main-cont {
.breadcrumb {
height: $height-header;
......@@ -1234,21 +1174,17 @@ $mainHight: 100vh;
background-color: #fff;
padding: 0;
}
.fl-right {
// height: $height-header;
// line-height: $height-header;
}
&.el-main {
overflow: auto;
background: #fff;
}
height: $mainHight !important;
overflow: visible;
position: relative;
.menu-total {
float: left;
margin-top: 10px;
......@@ -1257,16 +1193,13 @@ $mainHight: 100vh;
line-height: 30px;
font-size: 30px;
}
.aside {
overflow: auto;
// background: #fff;
&::-webkit-scrollbar {
display: none;
}
}
.el-menu-vertical {
height: calc(100vh - 64px) !important;
visibility: auto;
......@@ -1274,38 +1207,30 @@ $mainHight: 100vh;
width: 220px;
}
}
.el-menu--collapse {
width: 54px;
li {
.el-tooltip,
.el-submenu__title {
.el-sub-menu__title {
padding: 0px 15px !important;
}
}
}
&::-webkit-scrollbar {
display: none;
}
&.main-left {
width: auto !important;
}
&.main-right {
.admin-title {
float: left;
font-size: 16px;
vertical-align: middle;
margin-left: 20px;
img {
vertical-align: middle;
}
&.collapse {
width: 53px;
}
......@@ -1330,16 +1255,13 @@ $mainHight: 100vh;
height: 60px;
width: 120px;
text-align: center;
.el-input__inner {
border: none;
border-bottom: 1px solid #606266;
}
.el-dropdown-link {
cursor: pointer;
}
.search-icon {
font-size: $icon-size;
margin-right: 14px;
......@@ -1348,7 +1270,6 @@ $mainHight: 100vh;
box-sizing: border-box;
color: #606266;
}
.dropdown-group {
min-width: 100px;
}
......@@ -1377,18 +1298,15 @@ $mainHight: 100vh;
padding: 20px;
border-radius: 4px;
overflow: hidden;
.car-left {
height: $height-car;
// width: 70%;
// float: left;
}
.car-right {
height: $height-car;
// width: 29%;
// float: left;
.flow,
.user-number,
.feedback {
......@@ -1401,54 +1319,48 @@ $mainHight: 100vh;
font-size: 13px;
margin-right: 5px;
}
.flow {
background-color: #fff7e8;
border-color: #feefd0;
color: #faad14;
}
.user-number {
background-color: #ecf5ff;
border-color: #d9ecff;
color: #409eff;
}
.feedback {
background-color: #eef9e8;
border-color: #dcf3d1;
color: #52c41a;
}
.car-item {
.card-item {
padding-right: 20px;
text-align: right;
margin-top: 12px;
b {
margin-top: 6px;
display: block;
}
}
}
.card-img {
width: $height-car;
height: $height-car;
display: inline-block;
float: left;
overflow: hidden;
img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.text {
height: $height-car;
margin-left: 10px;
float: left;
margin-top: 14px;
h4 {
font-size: 20px;
color: #262626;
......@@ -1457,13 +1369,12 @@ $mainHight: 100vh;
word-break: break-all;
text-overflow: ellipsis;
}
.tips-text {
color: #8c8c8c;
margin-top: 8px;
.el-icon{
.el-icon {
margin-right: 8px;
display:inline-block;
display: inline-block;
}
}
}
......@@ -1471,14 +1382,12 @@ $mainHight: 100vh;
.shadow {
margin: 4px 0;
.grid-content {
background-color: $white-bg;
border-radius: 4px;
text-align: center;
padding: 10px 0;
cursor: pointer;
.el-icon {
width: $el-icon-small;
height: $el-icon-small;
......@@ -1506,4 +1415,4 @@ $mainHight: 100vh;
::-webkit-scrollbar-thumb:hover {
background-color: #bbb;
}
}
\ No newline at end of file
#userLayout{
margin: 0;
padding: 0;
background-image: url("~@/assets/login_background.svg");
// background-image: url("~@/assets/login_background.svg");
background-size: cover;
width: 100%;
height: 100%;
......@@ -53,6 +53,8 @@
float: right !important;
background: #ccc;
img {
width: 100%;
height: 100%;
cursor: pointer;
vertical-align: middle;
}
......
const install = (Vue) => {
const Bus = new Vue({
methods: {
emit(event, ...args) {
this.$emit(event, ...args)
},
on(event, cb) {
this.$on(event, cb)
},
off(event, cb) {
this.$off(event, cb)
}
}
})
Vue.prototype.$bus = Bus
}
export default install
// using ES6 modules
import mitt from 'mitt'
export const emitter = mitt()
import axios from 'axios' // 引入axios
import { Message } from 'element-ui'
import { ElMessage, ElMessageBox } from 'element-plus'
import { store } from '@/store'
import context from '@/main'
import { MessageBox } from 'element-ui'
import { emitter } from '@/utils/bus.js'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
......@@ -17,7 +16,7 @@ const showLoading = () => {
}
timer = setTimeout(() => {
if (acitveAxios > 0) {
context.$bus.emit('showLoading')
emitter.emit('showLoading')
}
}, 400)
}
......@@ -26,7 +25,7 @@ const closeLoading = () => {
acitveAxios--
if (acitveAxios <= 0) {
clearTimeout(timer)
context.$bus.emit('closeLoading')
emitter.emit('closeLoading')
}
}
// http request 拦截器
......@@ -47,7 +46,7 @@ service.interceptors.request.use(
},
error => {
closeLoading()
Message({
ElMessage({
showClose: true,
message: error,
type: 'error'
......@@ -60,17 +59,19 @@ service.interceptors.request.use(
service.interceptors.response.use(
response => {
closeLoading()
if (response.headers['new-token']) {
store.commit('user/setToken', response.headers['new-token'])
}
if (response.data.code === 0 || response.headers.success === 'true') {
if (response.headers.msg) {
response.data.msg = decodeURI(response.headers.msg)
}
return response.data
} else {
Message({
ElMessage({
showClose: true,
message: response.data.msg || decodeURI(response.headers.msg),
type: response.headers.msgtype || 'error'
message: response.data.msg,
type: 'error'
})
if (response.data.data && response.data.data.reload) {
store.commit('user/LoginOut')
......@@ -80,7 +81,7 @@ service.interceptors.response.use(
},
error => {
closeLoading()
MessageBox.confirm(`
ElMessageBox.confirm(`
<p>检测到接口错误${error}</p>
<p>错误码500:此类错误内容常见于后台panic,如果影响您正常使用可强制登出清理缓存</p>
<p>错误码404:此类错误多为接口未注册(或未重启)或者请求路径(方法)与api路径(方法)不符</p>
......
......@@ -3,7 +3,9 @@
<el-row :gutter="10">
<el-col :span="12">
<el-card>
<div slot="header">gin-vue-admin</div>
<template #header>
<el-divider>gin-vue-admin</el-divider>
</template>
<div>
<el-row>
<el-col :span="8" :offset="8">
......@@ -48,7 +50,9 @@
</div>
</el-card>
<el-card style="margin-top: 20px">
<div slot="header">flipped-aurora团队</div>
<template #header>
<div>flipped-aurora团队</div>
</template>
<div>
<el-row>
<el-col :span="8" :offset="8">
......@@ -62,23 +66,23 @@
</el-col>
</el-row>
<el-row style="margin-left: 40px" :gutter="20">
<template v-for="(item, index) in members">
<el-col :key="index" :span="8">
<a :href="item.html_url">
<img class="avatar-img" :src="item.avatar_url">
<a class="author-name" style="">{{ item.login }}</a>
</a>
</el-col>
</template>
<el-col v-for="(item, index) in members" :key="index" :span="8">
<a :href="item.html_url">
<img class="avatar-img" :src="item.avatar_url">
<a class="author-name" style="">{{ item.login }}</a>
</a>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card>
<div slot="header">
提交记录
</div>
<template #header>
<div>
提交记录
</div>
</template>
<div>
<Timeline
:timeline-items="dataTimeline"
......
<template>
<div>
<div style="width: 100%">
<APlayer :audio="audio" />
</div>
</div>
</template>
<script>
import { APlayer } from '@moefe/vue-aplayer'
export default {
name: 'MusicPlayer',
components: {
APlayer
},
data() {
return {
audio: [
{
name: '东西(Cover:林俊呈)',
artist: '纳豆',
url: 'http://music.163.com/song/media/outer/url?id=1321594530.mp3',
cover: 'https://p1.music.126.net/5zs7IvmLv7KahY3BFzUmrg==/109951163635241613.jpg?param=300y300', // prettier-ignore
lrc: 'https://cdn.moefe.org/music/lrc/thing.lrc'
},
{
name: '响喜乱舞(Cover:MARiA)',
artist: '泠鸢yousa',
url: 'http://music.163.com/song/media/outer/url?id=1318962459.mp3',
cover: 'https://p1.music.126.net/AUGVPQ_rVrngDH9ocQrn3Q==/109951163613037822.jpg?param=300y300', // prettier-ignore
lrc: 'https://cdn.moefe.org/music/lrc/kyoukiranbu.lrc'
},
{
name: '啵唧',
artist: 'Hanser',
url: 'http://music.163.com/song/media/outer/url?id=1321424246.mp3',
cover: 'https://p1.music.126.net/K0-IPcIQ9QFvA0jXTBqoWQ==/109951163636756693.jpg?param=300y300', // prettier-ignore
lrc: 'https://cdn.moefe.org/music/lrc/kiss.lrc'
}
]
}
}
}
</script>
<template>
<li :class="{ completed: todo.done, editing: editing }" class="todo">
<div class="view">
<input
:checked="todo.done"
class="toggle"
type="checkbox"
@change="toggleTodo( todo)"
>
<label @dblclick="editing = true" v-text="todo.text" />
<button class="destroy" @click="deleteTodo( todo )" />
</div>
<input
v-show="editing"
v-focus="editing"
:value="todo.text"
class="edit"
@keyup.enter="doneEdit"
@keyup.esc="cancelEdit"
@blur="doneEdit"
>
</li>
</template>
<script>
export default {
name: 'Todo',
directives: {
focus(el, { value }, { context }) {
if (value) {
context.$nextTick(() => {
el.focus()
})
}
}
},
props: {
todo: {
type: Object,
default: function() {
return {}
}
}
},
data() {
return {
editing: false
}
},
methods: {
deleteTodo(todo) {
this.$emit('deleteTodo', todo)
},
editTodo({ todo, value }) {
this.$emit('editTodo', { todo, value })
},
toggleTodo(todo) {
this.$emit('toggleTodo', todo)
},
doneEdit(e) {
const value = e.target.value.trim()
const { todo } = this
if (!value) {
this.deleteTodo({
todo
})
} else if (this.editing) {
this.editTodo({
todo,
value
})
this.editing = false
}
},
cancelEdit(e) {
e.target.value = this.todo.text
this.editing = false
}
}
}
</script>
.todoapp {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
color: #4d4d4d;
min-width: 230px;
max-width: 666px;
margin: 0 auto ;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-weight: 300;
background: #fff;
z-index: 1;
position: relative;
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-appearance: none;
appearance: none;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:focus {
outline: 0;
}
.hidden {
display: none;
}
.todoapp {
background: #fff;
margin: 130px 0 40px 0;
position: relative;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.todoapp input::-webkit-input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::-moz-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp input::input-placeholder {
font-style: italic;
font-weight: 300;
color: #e6e6e6;
}
.todoapp h1 {
position: absolute;
top: -155px;
width: 100%;
font-size: 100px;
font-weight: 100;
text-align: center;
color: rgba(175, 47, 47, 0.15);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
.new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 18px;
font-family: inherit;
font-weight: inherit;
line-height: 1.4em;
border: 0;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.new-todo {
padding: 10px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.003);
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
}
.main {
position: relative;
z-index: 2;
border-top: 1px solid #e6e6e6;
}
.toggle-all {
text-align: center;
border: none;
/* Mobile Safari */
opacity: 0;
position: absolute;
}
.toggle-all+label {
width: 60px;
height: 34px;
font-size: 0;
position: absolute;
top: -52px;
left: -13px;
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.toggle-all+label:before {
content: '❯';
font-size: 22px;
color: #e6e6e6;
padding: 10px 27px 10px 27px;
}
.toggle-all:checked+label:before {
color: #737373;
}
.todo-list {
margin: 0;
padding: 0;
list-style: none;
}
.todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px solid #ededed;
}
.todo-list li:last-child {
border-bottom: none;
}
.todo-list li.editing {
border-bottom: none;
padding: 0;
}
.todo-list li.editing .edit {
display: block;
width: 506px;
padding: 12px 16px;
margin: 0 0 0 43px;
}
.todo-list li.editing .view {
display: none;
}
.todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
border: none;
/* Mobile Safari */
-webkit-appearance: none;
appearance: none;
}
.todo-list li .toggle {
opacity: 0;
}
.todo-list li .toggle+label {
/*
Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
*/
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
background-repeat: no-repeat;
background-position: center left;
background-size: 36px;
}
.todo-list li .toggle:checked+label {
background-size: 36px;
background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}
.todo-list li label {
word-break: break-all;
padding: 15px 15px 15px 50px;
display: block;
line-height: 1.0;
font-size: 14px;
transition: color 0.4s;
}
.todo-list li.completed label {
color: #d9d9d9;
text-decoration: line-through;
}
.todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 30px;
color: #cc9a9a;
transition: color 0.2s ease-out;
cursor: pointer;
}
.todo-list li .destroy:hover {
color: #af5b5e;
}
.todo-list li .destroy:after {
content: '×';
}
.todo-list li:hover .destroy {
display: block;
}
.todo-list li .edit {
display: none;
}
.todo-list li.editing:last-child {
margin-bottom: -1px;
}
.footer {
color: #777;
position: relative;
padding: 10px 15px;
height: 40px;
text-align: center;
border-top: 1px solid #e6e6e6;
}
.footer:before {
content: '';
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 40px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 8px 0 -3px #f6f6f6, 0 9px 1px -3px rgba(0, 0, 0, 0.2), 0 16px 0 -6px #f6f6f6, 0 17px 2px -6px rgba(0, 0, 0, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.todo-count strong {
font-weight: 300;
}
.filters {
margin: 0;
padding: 0;
position: relative;
z-index: 1;
list-style: none;
}
.filters li {
display: inline;
}
.filters li a {
color: inherit;
font-size: 12px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
}
.filters li a:hover {
border-color: rgba(175, 47, 47, 0.1);
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.clear-completed,
html .clear-completed:active {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
cursor: pointer;
}
.clear-completed:hover {
text-decoration: underline;
}
.info {
margin: 65px auto 0;
color: #bfbfbf;
font-size: 10px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
text-align: center;
}
.info p {
line-height: 1;
}
.info a {
color: inherit;
text-decoration: none;
font-weight: 400;
}
.info a:hover {
text-decoration: underline;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
.toggle-all,
.todo-list li .toggle {
background: none;
}
.todo-list li .toggle {
height: 40px;
}
}
@media (max-width: 430px) {
.footer {
height: 50px;
}
.filters {
bottom: 10px;
}
}
}
<template>
<section class="todoapp">
<!-- header -->
<header class="header">
<input class="new-todo" autocomplete="off" placeholder="Todo List" @keyup.enter="addTodo">
</header>
<!-- main section -->
<section v-show="todos.length" class="main">
<input id="toggle-all" :checked="allChecked" class="toggle-all" type="checkbox" @change="toggleAll({ done: !allChecked })">
<label for="toggle-all" />
<ul class="todo-list">
<todo
v-for="(todo, index) in filteredTodos"
:key="index"
:todo="todo"
@toggleTodo="toggleTodo"
@editTodo="editTodo"
@deleteTodo="deleteTodo"
/>
</ul>
</section>
<!-- footer -->
<footer v-show="todos.length" class="footer">
<span class="todo-count">
<strong>{{ remaining }}</strong>
{{ remaining | pluralize('item') }} left
</span>
<ul class="filters">
<li v-for="(val, key) in filters" :key="key">
<a :class="{ selected: visibility === key }" @click.prevent="visibility = key">{{ key | capitalize }}</a>
</li>
</ul>
<!-- <button class="clear-completed" v-show="todos.length > remaining" @click="clearCompleted">
Clear completed
</button> -->
</footer>
</section>
</template>
<script>
import Todo from './Todo.vue'
const STORAGE_KEY = 'todos'
const filters = {
all: todos => todos,
active: todos => todos.filter(todo => !todo.done),
completed: todos => todos.filter(todo => todo.done)
}
const defaultList = [
{ text: '工作流功能绘制工具', done: false },
{ text: '工作流流转方法', done: false },
{ text: '自动化代码优化', done: false }
]
export default {
components: { Todo },
filters: {
pluralize: (n, w) => n === 1 ? w : w + 's',
capitalize: s => s.charAt(0).toUpperCase() + s.slice(1)
},
data() {
return {
visibility: 'all',
filters,
// todos: JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || defaultList
todos: defaultList
}
},
computed: {
allChecked() {
return this.todos.every(todo => todo.done)
},
filteredTodos() {
return filters[this.visibility](this.todos)
},
remaining() {
return this.todos.filter(todo => !todo.done).length
}
},
methods: {
setLocalStorage() {
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(this.todos))
},
addTodo(e) {
const text = e.target.value
if (text.trim()) {
this.todos.push({
text,
done: false
})
this.setLocalStorage()
}
e.target.value = ''
},
toggleTodo(val) {
val.done = !val.done
this.setLocalStorage()
},
deleteTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
this.setLocalStorage()
},
editTodo({ todo, value }) {
todo.text = value
this.setLocalStorage()
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.done)
this.setLocalStorage()
},
toggleAll({ done }) {
this.todos.forEach(todo => {
todo.done = done
this.setLocalStorage()
})
}
}
}
</script>
<style lang="scss">
@import './index.scss';
</style>
<template>
<div class="big">
<el-row>
<div class="card">
<el-col :xs="24" :lg="16" :md="16">
<div class="car-left">
<el-row>
<div>
<el-col :xs="4" :md="3" :lg="3">
<span class="card-img">
<img :src="userInfo.headerImg" alt="">
</span>
</el-col>
<el-col :xs="20" :lg="12" :md="12">
<div class="text">
<h4>早安,管理员, 请开始您一天的工作吧!</h4>
<p class="tips-text">
<i class="el-icon-sunny el-icon" />
<span>今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。</span>
</p>
</div>
</el-col>
<el-row class="card">
<el-col :xs="24" :lg="16" :md="16" style="height:90px">
<div class="car-left">
<el-row>
<el-col :xs="4" :md="3" :lg="3">
<span class="card-img">
<img :src="userInfo.headerImg" alt="">
</span>
</el-col>
<el-col :xs="20" :lg="12" :md="12">
<div class="text">
<h4>早安,管理员, 请开始您一天的工作吧!</h4>
<p class="tips-text">
<i class="el-icon-sunny el-icon" />
<span>今日晴,0℃ - 10℃,天气寒冷,注意添加衣物。</span>
</p>
</div>
</el-row>
</div>
</el-col>
<el-col :xs="24" :lg="8" :md="8">
<div class="car-right">
<el-row>
<el-col :span="8">
<div class="car-item">
<span class="flow"><i class="el-icon-s-grid" /></span>
<span>今日流量 </span>
<b>13260</b>
</div>
</el-col>
<el-col :span="8">
<div class="car-item">
<span class="user-number">
<i class="el-icon-s-custom" />
</span>
<span>总用户 </span>
<b>48286</b>
</div>
</el-col>
<el-col :span="8">
<div class="car-item">
<span class="feedback">
<i class="el-icon-star-on" />
</span>
<span>好评率 </span>
<b>98%</b>
</div>
</el-col>
</el-row>
</div>
</el-col>
</div>
</el-col>
</el-row>
</div>
</el-col>
<el-col :xs="24" :lg="8" :md="8">
<div class="car-right">
<el-row>
<el-col :span="8">
<div class="card-item">
<span class="flow"><i class="el-icon-s-grid" /></span>
<span>今日流量 </span>
<b>13260</b>
</div>
</el-col>
<el-col :span="8">
<div class="card-item">
<span class="user-number">
<i class="el-icon-s-custom" />
</span>
<span>总用户 </span>
<b>48286</b>
</div>
</el-col>
<el-col :span="8">
<div class="card-item">
<span class="feedback">
<i class="el-icon-star-on" />
</span>
<span>好评率 </span>
<b>98%</b>
</div>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
<el-row>
<el-card shadow="hover">
<h2>
使用教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1fV411y7dT/">https://www.bilibili.com/video/BV1fV411y7dT/</a>
</h2>
<br>
<h2>
工作流教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1Ka411F7Ji/">https://www.bilibili.com/video/BV1Ka411F7Ji/</a>
</h2>
<div />
</el-card>
<el-col>
<el-card shadow="hover">
<h2>
使用教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1fV411y7dT/">https://www.bilibili.com/video/BV1fV411y7dT/</a>
</h2>
<br>
<h2>
工作流教学:<a style="color:#409EFF" target="view_window" href="https://www.bilibili.com/video/BV1Ka411F7Ji/">https://www.bilibili.com/video/BV1Ka411F7Ji/</a>
</h2>
<div />
</el-card>
</el-col>
</el-row>
<div class="shadow">
<el-row :gutter="20">
......@@ -76,7 +74,7 @@
:key="key"
:span="4"
:xs="8"
@click.native="toTarget(card.name)"
@click="toTarget(card.name)"
>
<el-card shadow="hover" class="grid-content">
<i :class="card.icon" :style="{ color: card.color }" />
......@@ -88,14 +86,10 @@
<div class="bottom">
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="12">
<div class="chart-player">
<music-player />
</div>
<div class="chart-player" />
</el-col>
<el-col :xs="24" :sm="24" :lg="12">
<div class="chart-player">
<todo-list />
</div>
<div class="chart-player" />
</el-col>
</el-row>
</div>
......@@ -103,17 +97,12 @@
</template>
<script>
import musicPlayer from './component/musicPlayer'
import TodoList from './component/todoList'
import { mapGetters } from 'vuex'
export default {
name: 'Dashboard',
components: {
musicPlayer, // 音乐播放器
TodoList // TodoList
// RaddarChart, //雷达图
// stackMap, //堆叠图
// Sunburst, //旭日图
},
data() {
return {
......@@ -171,7 +160,7 @@ export default {
<style lang="scss" scoped>
.big {
margin: 100px 0 0 0;
padding-top: 0;
padding-top: 10px;
background-color: rgb(243, 243, 243);
.top {
width: 100%;
......
......@@ -17,21 +17,25 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="接入日期" width="180">
<template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
<template #default="scope">
<span>{{ formatDate(scope.row.CreatedAt) }}</span>
</template>
</el-table-column>
<el-table-column label="姓名" prop="customerName" width="120" />
<el-table-column label="电话" prop="customerPhoneData" width="120" />
<el-table-column label="接入人ID" prop="sysUserId" width="120" />
<el-table-column label="按钮组" min-width="160">
<template slot-scope="scope">
<template #default="scope">
<el-button size="small" type="text" @click="updateCustomer(scope.row)">变更</el-button>
<el-popover v-model="scope.row.visible" placement="top" width="160">
<el-popover v-model:visible="scope.row.visible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button type="primary" size="mini" @click="deleteCustomer(scope.row)">确定</el-button>
</div>
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
<template #reference>
<el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
</template>
</el-popover>
</template>
</el-table-column>
......@@ -48,7 +52,7 @@
@size-change="handleSizeChange"
/>
<el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="客户">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="客户">
<el-form :inline="true" :model="form" label-width="80px">
<el-form-item label="客户名">
<el-input v-model="form.customerName" autocomplete="off" />
......@@ -57,10 +61,12 @@
<el-input v-model="form.customerPhoneData" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
<div class="tips">在资源权限中将此角色的资源权限清空 或者不包含创建者的角色 即可屏蔽此客户资源的显示</div>
</div>
......@@ -74,21 +80,10 @@ import {
getExaCustomer,
getExaCustomerList
} from '@/api/customer'
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: 'Customer',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
......@@ -18,7 +18,8 @@
<el-table-column label="路由Name" min-width="160" prop="name" />
<el-table-column label="路由Path" min-width="160" prop="path" />
<el-table-column label="是否隐藏" min-width="100" prop="hidden">
<template slot-scope="scope">
<template #default="scope">
<span>{{ scope.row.hidden?"隐藏":"显示" }}</span>
</template>
</el-table-column>
......
<template>
<div>
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
<router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
......
<template>
<uploader
:options="options"
:file-status-text="statusText"
:auto-start="false"
class="uploader-example"
@file-added="fileAdded"
@file-progress="onFileProgress"
@file-success="onFileSuccess"
@file-error="onFileError"
>
<uploader-unsupport />
<uploader-drop>
<p>拖拽文件至此或点击</p>
<uploader-btn>选择文件</uploader-btn>
</uploader-drop>
<uploader-list />
</uploader>
</template>
<script>
var notUploadedChunks = [] // 已经上传过的文件chunkNumber数组
var isUploaded = false // 文件已经上传成功了
import { mapGetters } from 'vuex'
import { checkFileMd5, mergeFileMd5 } from '@/api/simpleUploader'
import SparkMD5 from 'spark-md5'
const path = process.env.VUE_APP_BASE_API
export default {
name: 'SimpleUploader',
data() {
return {
md5: ''
}
},
computed: {
...mapGetters('user', ['userInfo', 'token']),
statusText() {
return {
success: '成功了',
error: '出错了',
uploading: '上传中',
paused: '暂停中',
waiting: '等待中'
}
},
options() {
return {
target: path + '/simpleUploader/upload',
testChunks: false,
simultaneousUploads: 5,
chunkSize: 2 * 1024 * 1024,
headers: {
'x-token': this.token,
'x-user-id': this.userInfo.ID
},
checkChunkUploadedByResponse(chunk) {
if (isUploaded) {
return true // return true 会忽略当前文件,不会再发送给后台
} else {
// 根据已经上传过的切片来进行忽略
return (
notUploadedChunks &&
notUploadedChunks.some(
item => item.chunkNumber === chunk.offset + 1
)
)
}
}
}
}
},
methods: {
// 上传单个文件
fileAdded(file) {
this.computeMD5(file) // 生成MD5
},
// 计算MD5值
computeMD5(file) {
var that = this
isUploaded = false // 这个文件是否已经上传成功过
notUploadedChunks = [] // 未成功的chunkNumber
var fileReader = new FileReader()
var md5 = ''
file.pause()
fileReader.readAsArrayBuffer(file.file)
fileReader.onload = async function(e) {
if (file.size !== e.target.result.byteLength) {
this.error(
'Browser reported success but could not read the file until the end.'
)
return false
}
md5 = SparkMD5.ArrayBuffer.hash(e.target.result, false)
file.uniqueIdentifier = md5
if (md5 !== '') {
const res = await checkFileMd5({ md5: md5 })
if (res.code === 0) {
if (res.data.isDone) {
// 上传成功过
isUploaded = true
that.$message({
message: '该文件已经上传成功过了,秒传成功。',
type: 'success'
})
file.cancel()
} else {
isUploaded = false
notUploadedChunks = res.data.chunks
if (notUploadedChunks.length) {
file.resume()
}
}
}
}
}
fileReader.onerror = function() {
this.error(
'generater md5 时FileReader异步读取文件出错了,FileReader onerror was triggered, maybe the browser aborted due to high memory usage.'
)
return false
}
},
// 上传进度
onFileProgress() {},
// 上传成功
async onFileSuccess(rootFile, file) {
await mergeFileMd5({ md5: file.uniqueIdentifier, fileName: file.name })
},
onFileError(rootFile, file, response) {
this.$message({
message: response,
type: 'error'
})
}
}
}
</script>
<style>
.uploader-example {
width: 880px;
padding: 15px;
margin: 15px 15px 20px;
font-size: 12px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.4);
}
.uploader-example .uploader-btn {
margin-right: 4px;
}
.uploader-example .uploader-list {
max-height: 440px;
overflow: auto;
overflow-x: hidden;
overflow-y: auto;
}
</style>
......@@ -12,7 +12,9 @@
:show-file-list="false"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
<template #tip>
<div class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</template>
</el-upload>
</el-col>
<el-col :span="12">
......@@ -24,19 +26,19 @@
<el-table :data="tableData" border stripe>
<el-table-column label="预览" width="100">
<template slot-scope="scope">
<template #default="scope">
<CustomPic pic-type="file" :pic-src="scope.row.url" />
</template>
</el-table-column>
<el-table-column label="日期" prop="UpdatedAt" width="180">
<template slot-scope="scope">
<div>{{ scope.row.UpdatedAt | formatDate }}</div>
<template #default="scope">
<div>{{ formatDate(scope.row.UpdatedAt) }}</div>
</template>
</el-table-column>
<el-table-column label="文件名" prop="name" width="180" />
<el-table-column label="链接" prop="url" min-width="300" />
<el-table-column label="标签" prop="tag" width="100">
<template slot-scope="scope">
<template #default="scope">
<el-tag
:type="scope.row.tag === 'jpg' ? 'primary' : 'success'"
disable-transitions
......@@ -44,7 +46,7 @@
</template>
</el-table-column>
<el-table-column label="操作" width="160">
<template slot-scope="scope">
<template #default="scope">
<el-button size="small" type="text" @click="downloadFile(scope.row)">下载</el-button>
<el-button size="small" type="text" @click="deleteFile(scope.row)">删除</el-button>
</template>
......@@ -70,7 +72,6 @@ import { mapGetters } from 'vuex'
import infoList from '@/mixins/infoList'
import { getFileList, deleteFile } from '@/api/fileUploadAndDownload'
import { downloadImage } from '@/utils/downloadImg'
import { formatTimeToStr } from '@/utils/date'
import CustomPic from '@/components/customPic'
import UploadImage from '@/components/upload/image.vue'
export default {
......@@ -79,16 +80,6 @@ export default {
CustomPic,
UploadImage
},
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
......@@ -82,7 +82,6 @@ export default {
methods: {
showNext() {
this.hello = this.hello + 1
console.log(this.hello)
},
goDoc() {
window.open('https://www.gin-vue-admin.com/docs/first_master#3-init')
......
<template>
<el-submenu ref="subMenu" :popper-append-to-body="false" :index="routerInfo.name">
<template slot="title">
<el-sub-menu ref="subMenu" :index="routerInfo.name">
<template #title>
<i :class="'el-icon-'+routerInfo.meta.icon" />
<span slot="title">{{ routerInfo.meta.title }}</span>
<span>{{ routerInfo.meta.title }}</span>
</template>
<slot />
</el-submenu>
</el-sub-menu>
</template>
<script>
......
<template>
<el-menu-item :index="routerInfo.name" :route="{parameters:routerInfo.parameters}">
<i :class="'el-icon-'+routerInfo.meta.icon" />
<span slot="title">{{ routerInfo.meta.title }}</span>
<template #title>
<span>{{ routerInfo.meta.title }}</span>
</template>
</el-menu-item>
</template>
......
......@@ -2,9 +2,9 @@
<div class="router-history">
<el-tabs
v-model="activeValue"
:closable="!(historys.length===1&&this.$route.name===defaultRouter)"
:closable="!(historys.length===1&&$route.name===defaultRouter)"
type="card"
@contextmenu.prevent.native="openContextMenu($event)"
@contextmenu.prevent="openContextMenu($event)"
@tab-click="changeTab"
@tab-remove="removeTab"
>
......@@ -16,7 +16,9 @@
:tab="item"
class="gva-tab"
>
<span slot="label" :style="{color: activeValue===name(item)?activeColor:'#333'}"><i class="dot" :style="{backgroundColor:activeValue===name(item)?activeColor:'#ddd'}" /> {{ item.meta.title }}</span>
<template #label>
<span :style="{color: activeValue===name(item)?activeColor:'#333'}"><i class="dot" :style="{backgroundColor:activeValue===name(item)?activeColor:'#ddd'}" /> {{ item.meta.title }}</span>
</template>
</el-tab-pane>
</el-tabs>
......@@ -32,6 +34,8 @@
<script>
import { mapGetters } from 'vuex'
import { emitter } from '@/utils/bus.js'
const getFmtString = (item) => {
return item.name +
JSON.stringify(item.query) +
......@@ -75,15 +79,15 @@ export default {
sessionStorage.setItem('historys', JSON.stringify(this.historys))
this.activeValue = window.sessionStorage.getItem('activeValue')
if (now && to && now.name === to.name) {
this.$bus.$emit('reload')
emitter.emit('reload')
}
}
},
created() {
this.$bus.on('mobile', isMobile => {
emitter.on('mobile', isMobile => {
this.isMobile = isMobile
})
this.$bus.on('collapse', isCollapse => {
emitter.on('collapse', isCollapse => {
this.isCollapse = isCollapse
})
const initHistorys = [
......@@ -107,8 +111,8 @@ export default {
},
beforeDestroy() {
this.$bus.off('collapse')
this.$bus.off('mobile')
emitter.off('collapse')
emitter.off('mobile')
},
methods: {
name(item) {
......@@ -120,7 +124,6 @@ export default {
}
let id = ''
if (e.srcElement.nodeName === 'SPAN') {
console.log(e)
id = e.srcElement.offsetParent.id
} else {
id = e.srcElement.id
......@@ -236,7 +239,7 @@ export default {
)
},
changeTab(component) {
const tab = component.$attrs.tab
const tab = component.instance.attrs.tab
this.$router.push({
name: tab.name,
query: tab.query,
......
......@@ -4,7 +4,7 @@
<transition :duration="{ enter: 800, leave: 100 }" mode="out-in" name="el-fade-in-linear">
<el-menu
:collapse="isCollapse"
:collapse-transition="true"
:collapse-transition="false"
:default-active="active"
:background-color="sideMode"
:active-text-color="activeColor"
......@@ -25,6 +25,8 @@
<script>
import { mapGetters, mapMutations } from 'vuex'
import AsideComponent from '@/view/layout/aside/asideComponent'
import { emitter } from '@/utils/bus.js'
export default {
name: 'Aside',
components: {
......@@ -52,12 +54,12 @@ export default {
this.isCollapse = !this.isCollapse
}
this.$bus.on('collapse', item => {
emitter.on('collapse', item => {
this.isCollapse = item
})
},
beforeDestroy() {
this.$bus.off('collapse')
emitter.off('collapse')
},
methods: {
...mapMutations('history', ['addHistory']),
......@@ -84,13 +86,13 @@ export default {
</script>
<style lang="scss">
.el-submenu__title,.el-menu-item{
.el-sub-menu__title,.el-menu-item{
i{
color: inherit !important;
}
}
.el-submenu__title:hover,.el-menu-item:hover{
.el-sub-menu__title:hover,.el-menu-item:hover{
i{
color: inherit !important;
}
......
<template>
<el-container class="layout-cont">
<el-container :class="[isSider?'openside':'hideside',isMobile ? 'mobile': '']">
<el-row :class="[isShadowBg?'shadowBg':'']" @click.native="changeShadow()" />
<el-row :class="[isShadowBg?'shadowBg':'']" @click="changeShadow()" />
<el-aside class="main-cont main-left">
<div class="tilte" :style="{background: backgroundColor}">
<img alt class="logoimg" :src="$GIN_VUE_ADMIN.appLogo">
......@@ -18,52 +18,59 @@
>
<el-row>
<!-- :xs="8" :sm="6" :md="4" :lg="3" :xl="1" -->
<el-header class="header-cont">
<el-col :xs="2" :lg="1" :md="1" :sm="1" :xl="1">
<div class="menu-total" @click="totalCollapse">
<i v-if="isCollapse" class="el-icon-s-unfold" />
<i v-else class="el-icon-s-fold" />
</div>
</el-col>
<el-col :xs="10" :lg="14" :md="14" :sm="9" :xl="14">
<el-breadcrumb class="breadcrumb" separator-class="el-icon-arrow-right">
<el-breadcrumb-item
v-for="item in matched.slice(1,matched.length)"
:key="item.path"
>{{ item.meta.title }}</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :xs="12" :lg="9" :md="9" :sm="14" :xl="9">
<div class="fl-right right-box">
<Search />
<Screenfull class="screenfull" :style="{cursor:'pointer'}" />
<el-dropdown>
<span class="header-avatar" style="cursor: pointer">
<CustomPic />
<span style="margin-left: 5px">{{ userInfo.nickName }}</span>
<i class="el-icon-arrow-down" />
</span>
<el-dropdown-menu slot="dropdown" class="dropdown-group">
<el-dropdown-item>
<span style="font-weight: 600;">
当前角色:{{ userInfo.authority.authorityName }}
</span>
</el-dropdown-item>
<template v-if="userInfo.authorities">
<el-dropdown-item v-for="item in userInfo.authorities.filter(i=>i.authorityId!==userInfo.authorityId)" :key="item.authorityId" @click.native="changeUserAuth(item.authorityId)">
<span>
切换为:{{ item.authorityName }}
<el-col>
<el-header class="header-cont">
<el-row class="pd-0">
<el-col :xs="2" :lg="1" :md="1" :sm="1" :xl="1">
<div class="menu-total" @click="totalCollapse">
<i v-if="isCollapse" class="el-icon-s-unfold" />
<i v-else class="el-icon-s-fold" />
</div>
</el-col>
<el-col :xs="10" :lg="14" :md="14" :sm="9" :xl="14">
<el-breadcrumb class="breadcrumb" separator-class="el-icon-arrow-right">
<el-breadcrumb-item
v-for="item in matched.slice(1,matched.length)"
:key="item.path"
>{{ item.meta.title }}</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :xs="12" :lg="9" :md="9" :sm="14" :xl="9">
<div class="fl-right right-box">
<Search />
<Screenfull class="screenfull" :style="{cursor:'pointer'}" />
<el-dropdown>
<div class="dp-flex justify-content-center align-items height-full width-full">
<span class="header-avatar" style="cursor: pointer">
<CustomPic />
<span style="margin-left: 5px">{{ userInfo.nickName }}</span>
<i class="el-icon-arrow-down" />
</span>
</el-dropdown-item>
</template>
<el-dropdown-item icon="el-icon-s-custom" @click.native="toPerson">个人信息</el-dropdown-item>
<el-dropdown-item icon="el-icon-table-lamp" @click.native="LoginOut">登 出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-col>
</el-header>
</div>
<template #dropdown>
<el-dropdown-menu class="dropdown-group">
<el-dropdown-item>
<span style="font-weight: 600;">
当前角色:{{ userInfo.authority.authorityName }}
</span>
</el-dropdown-item>
<template v-if="userInfo.authorities">
<el-dropdown-item v-for="item in userInfo.authorities.filter(i=>i.authorityId!==userInfo.authorityId)" :key="item.authorityId" @click="changeUserAuth(item.authorityId)">
<span>
切换为:{{ item.authorityName }}
</span>
</el-dropdown-item>
</template>
<el-dropdown-item icon="el-icon-s-custom" @click="toPerson">个人信息</el-dropdown-item>
<el-dropdown-item icon="el-icon-table-lamp" @click="LoginOut">登 出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-col>
</el-row>
</el-header>
</el-col>
</el-row>
<!-- 当前面包屑用路由自动生成可根据需求修改 -->
<!--
......@@ -71,14 +78,19 @@
<HistoryComponent />
</div>
</transition>
<transition mode="out-in" name="el-fade-in-linear">
<keep-alive>
<router-view v-if="$route.meta.keepAlive && reloadFlag" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box" />
</keep-alive>
</transition>
<transition mode="out-in" name="el-fade-in-linear">
<router-view v-if="!$route.meta.keepAlive && reloadFlag" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box" />
</transition>
<router-view v-if="$route.meta.keepAlive && reloadFlag" v-slot="{ Component }" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box">
<transition mode="out-in" name="el-fade-in-linear">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<router-view v-if="!$route.meta.keepAlive && reloadFlag" v-slot="{ Component }" v-loading="loadingFlag" element-loading-text="正在加载中" class="admin-box">
<transition mode="out-in" name="el-fade-in-linear">
<component :is="Component" />
</transition>
</router-view>
<BottomInfo />
<setting />
</el-main>
......@@ -97,6 +109,7 @@ import { mapGetters, mapActions } from 'vuex'
import CustomPic from '@/components/customPic'
import Setting from './setting'
import { setUserAuthority } from '@/api/user'
import { emitter } from '@/utils/bus.js'
export default {
name: 'Layout',
components: {
......@@ -162,13 +175,13 @@ export default {
this.isSider = true
this.isCollapse = false
}
this.$bus.emit('collapse', this.isCollapse)
this.$bus.emit('mobile', this.isMobile)
this.$bus.on('reload', this.reload)
this.$bus.on('showLoading', () => {
emitter.emit('collapse', this.isCollapse)
emitter.emit('mobile', this.isMobile)
emitter.on('reload', this.reload)
emitter.on('showLoading', () => {
this.loadingFlag = true
})
this.$bus.on('closeLoading', () => {
emitter.on('closeLoading', () => {
this.loadingFlag = false
})
window.onresize = () => {
......@@ -187,8 +200,8 @@ export default {
this.isSider = true
this.isCollapse = false
}
this.$bus.emit('collapse', this.isCollapse)
this.$bus.emit('mobile', this.isMobile)
emitter.emit('collapse', this.isCollapse)
emitter.emit('mobile', this.isMobile)
})()
}
},
......@@ -212,7 +225,7 @@ export default {
this.isCollapse = !this.isCollapse
this.isSider = !this.isCollapse
this.isShadowBg = !this.isCollapse
this.$bus.emit('collapse', this.isCollapse)
emitter.emit('collapse', this.isCollapse)
},
toPerson() {
this.$router.push({ name: 'person' })
......
......@@ -33,6 +33,7 @@
<script>
import { mapGetters } from 'vuex'
import { emitter } from '@/utils/bus.js'
export default {
name: 'SearchComponent',
......@@ -44,8 +45,9 @@ export default {
}
},
computed: {
...mapGetters('router', ['routerList'])
...mapGetters('router', ['routerList']),
},
methods: {
changeRouter() {
this.$router.push({ name: this.value })
......@@ -62,7 +64,7 @@ export default {
},
handleReload() {
this.reload = true
this.$bus.$emit('reload')
emitter.emit('reload')
setTimeout(() => {
this.reload = false
}, 500)
......
......@@ -2,8 +2,8 @@
<div>
<el-button type="primary" class="drawer-container" icon="el-icon-setting" @click="showSettingDrawer" />
<el-drawer
v-model="drawer"
title="系统配置"
:visible.sync="drawer"
:direction="direction"
:before-close="handleClose"
>
......
......@@ -9,11 +9,13 @@
ref="loginForm"
:model="loginForm"
:rules="rules"
@keyup.enter.native="submitForm"
@keyup.enter="submitForm"
>
<el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="请输入用户名">
<i slot="suffix" class="el-input__icon el-icon-user" />
<template #suffix>
<i class="el-input__icon el-icon-user" />
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
......@@ -22,11 +24,12 @@
:type="lock === 'lock' ? 'password' : 'text'"
placeholder="请输入密码"
>
<i
slot="suffix"
:class="'el-input__icon el-icon-' + lock"
@click="changeLock"
/>
<template #suffix>
<i
:class="'el-input__icon el-icon-' + lock"
@click="changeLock"
/>
</template>
</el-input>
</el-form-item>
<el-form-item style="position: relative">
......@@ -40,14 +43,11 @@
<img
v-if="picPath"
:src="picPath"
width="100%"
height="100%"
alt="请输入验证码"
@click="loginVerify()"
>
</div>
</el-form-item>
<div />
<el-form-item>
<el-button
type="primary"
......
......@@ -79,7 +79,7 @@
<ChooseImg ref="chooseImg" @enter-img="enterImg" />
<el-dialog :visible.sync="showPassword" title="修改密码" width="360px" @close="clearPassword">
<el-dialog v-model="showPassword" title="修改密码" width="360px" @close="clearPassword">
<el-form ref="modifyPwdForm" :model="pwdModify" :rules="rules" label-width="80px">
<el-form-item :minlength="6" label="原密码" prop="password">
<el-input v-model="pwdModify.password" show-password />
......@@ -91,10 +91,12 @@
<el-input v-model="pwdModify.confirmPassword" show-password />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showPassword=false">取 消</el-button>
<el-button type="primary" @click="savePassword">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="showPassword=false">取 消</el-button>
<el-button type="primary" @click="savePassword">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
......
<template>
<!-- 此路由可作为父类路由通用路由页面使用 如需自定义父类路由页面 请参考 @/view/superAdmin/index.vue -->
<div>
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
<router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
......
......@@ -24,13 +24,15 @@
<el-form-item>
<el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openDialog('addApi')">新增</el-button>
<el-popover v-model="deleteVisible" placement="top" width="160">
<el-popover v-model:visible="deleteVisible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="onDelete">确定</el-button>
</div>
<el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
<template #reference>
<el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
</template>
</el-popover>
</el-form-item>
</el-form>
......@@ -45,22 +47,21 @@
<el-table-column label="api分组" min-width="150" prop="apiGroup" sortable="custom" />
<el-table-column label="api简介" min-width="150" prop="description" sortable="custom" />
<el-table-column label="请求" min-width="150" prop="method" sortable="custom">
<template slot-scope="scope">
<template #default="scope">
<div>
{{ scope.row.method }}
<el-tag
:key="scope.row.methodFiletr"
:type="scope.row.method|tagTypeFiletr"
:type="tagTypeFiletr(scope.row.method)"
effect="dark"
size="mini"
>{{ scope.row.method|methodFiletr }}</el-tag>
<!-- {{scope.row.method|methodFiletr}} -->
>{{ methodFiletr(scope.row.method) }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="200">
<template slot-scope="scope">
<template #default="scope">
<el-button size="small" type="primary" icon="el-icon-edit" @click="editApi(scope.row)">编辑</el-button>
<el-button
size="small"
......@@ -82,7 +83,7 @@
@size-change="handleSizeChange"
/>
<el-dialog :before-close="closeDialog" :title="dialogTitle" :visible.sync="dialogFormVisible">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle">
<el-form ref="apiForm" :inline="true" :model="form" :rules="rules" label-width="80px">
<el-form-item label="路径" prop="path">
<el-input v-model="form.path" autocomplete="off" />
......@@ -105,10 +106,12 @@
</el-form-item>
</el-form>
<div class="warning">新增Api需要在角色管理内配置权限才可使用</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
......@@ -151,17 +154,6 @@ const methodOptions = [
export default {
name: 'Api',
filters: {
methodFiletr(value) {
const target = methodOptions.filter(item => item.value === value)[0]
// return target && `${target.label}(${target.value})`
return target && `${target.label}`
},
tagTypeFiletr(value) {
const target = methodOptions.filter(item => item.value === value)[0]
return target && `${target.type}`
}
},
mixins: [infoList],
data() {
return {
......@@ -196,6 +188,14 @@ export default {
this.getTableData()
},
methods: {
methodFiletr(value) {
const target = methodOptions.filter(item => item.value === value)[0]
return target && `${target.label}`
},
tagTypeFiletr(value) {
const target = methodOptions.filter(item => item.value === value)[0]
return target && `${target.type}`
},
// 选中api
handleSelectionChange(val) {
this.apis = val
......
......@@ -14,7 +14,7 @@
<el-table-column label="角色id" min-width="180" prop="authorityId" />
<el-table-column label="角色名称" min-width="180" prop="authorityName" />
<el-table-column fixed="right" label="操作" width="460">
<template slot-scope="scope">
<template #default="scope">
<el-button size="mini" type="primary" @click="opdendrawer(scope.row)">设置权限</el-button>
<el-button
icon="el-icon-plus"
......@@ -45,7 +45,7 @@
</el-table>
<span style="color: red;font-size: 12px">注:右上角头像下拉可切换角色</span>
<!-- 新增角色弹窗 -->
<el-dialog :title="dialogTitle" :visible.sync="dialogFormVisible">
<el-dialog v-model="dialogFormVisible" :title="dialogTitle">
<el-form ref="authorityForm" :model="form" :rules="rules">
<el-form-item label="父级角色" prop="parentId">
<el-cascader
......@@ -64,22 +64,24 @@
<el-input v-model="form.authorityName" autocomplete="off" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
<el-drawer v-if="drawer" :visible.sync="drawer" :with-header="false" size="40%" title="角色配置">
<el-drawer v-if="drawer" v-model="drawer" :with-header="false" size="40%" title="角色配置">
<el-tabs :before-leave="autoEnter" class="role-box" type="border-card">
<el-tab-pane label="角色菜单">
<Menus ref="menus" :row="activeRow" />
<Menus ref="menus" :row="activeRow" @changeRow="changeRow" />
</el-tab-pane>
<el-tab-pane label="角色api">
<apis ref="apis" :row="activeRow" />
<Apis ref="apis" :row="activeRow" @changeRow="changeRow" />
</el-tab-pane>
<el-tab-pane label="资源权限">
<Datas ref="datas" :authority="tableData" :row="activeRow" />
<Datas ref="datas" :authority="tableData" :row="activeRow" @changeRow="changeRow" />
</el-tab-pane>
</el-tabs>
</el-drawer>
......@@ -158,6 +160,9 @@ export default {
await this.getTableData()
},
methods: {
changeRow(key, value) {
this.activeRow[key] = value
},
autoEnter(activeName, oldActiveName) {
const paneArr = ['menus', 'apis', 'datas']
if (oldActiveName) {
......
......@@ -25,7 +25,7 @@ export default {
},
authority: {
default: function() {
return {}
return []
},
type: Array
}
......@@ -53,19 +53,19 @@ export default {
},
all() {
this.dataAuthorityId = [...this.authoritys]
this.row.dataAuthorityId = this.dataAuthorityId
this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
this.needConfirm = true
},
self() {
this.dataAuthorityId = this.authoritys.filter(item => item.authorityId === this.row.authorityId)
this.row.dataAuthorityId = this.dataAuthorityId
this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
this.needConfirm = true
},
selfAndChildren() {
const arrBox = []
this.getChildrenId(this.row, arrBox)
this.dataAuthorityId = this.authoritys.filter(item => arrBox.indexOf(item.authorityId) > -1)
this.row.dataAuthorityId = this.dataAuthorityId
this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
this.needConfirm = true
},
getChildrenId(row, arrBox) {
......@@ -95,7 +95,7 @@ export default {
},
// 选择
selectAuthority() {
this.row.dataAuthorityId = this.dataAuthorityId
this.$emit('changeRow', 'dataAuthorityId', this.dataAuthorityId)
this.needConfirm = true
}
}
......
......@@ -14,20 +14,22 @@
show-checkbox
@check="nodeChange"
>
<span slot-scope="{ node , data }" class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button
type="text"
size="mini"
:style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
:disabled="!node.checked"
@click="() => setDefault(data)"
>
{{ row.defaultRouter === data.name?"首页":"设为首页" }}
</el-button>
<template #default="{ node , data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button
type="text"
size="mini"
:style="{color:row.defaultRouter === data.name?'#E6A23C':'#85ce61'}"
:disabled="!node.checked"
@click="() => setDefault(data)"
>
{{ row.defaultRouter === data.name?"首页":"设为首页" }}
</el-button>
</span>
</span>
</span>
</template>
</el-tree>
</div>
</template>
......@@ -81,7 +83,7 @@ export default {
const res = await updateAuthority({ authorityId: this.row.authorityId, AuthorityName: this.row.authorityName, parentId: this.row.parentId, defaultRouter: data.name })
if (res.code === 0) {
this.$message({ type: 'success', message: '设置成功' })
this.row.defaultRouter = res.data.authority.defaultRouter
this.$emit('changeRow', 'defaultRouter', res.data.authority.defaultRouter)
}
},
nodeChange() {
......
......@@ -33,7 +33,7 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="日期" width="180">
<template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
</el-table-column>
<el-table-column label="字典名(中)" prop="name" width="120" />
......@@ -41,22 +41,24 @@
<el-table-column label="字典名(英)" prop="type" width="120" />
<el-table-column label="状态" prop="status" width="120">
<template slot-scope="scope">{{ scope.row.status|formatBoolean }}</template>
<template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
</el-table-column>
<el-table-column label="描述" prop="desc" width="280" />
<el-table-column label="按钮组">
<template slot-scope="scope">
<template #default="scope">
<el-button size="mini" type="success" @click="toDetile(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="updateSysDictionary(scope.row)">变更</el-button>
<el-popover v-model="scope.row.visible" placement="top" width="160">
<el-popover v-model:visible="scope.row.visible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button type="primary" size="mini" @click="deleteSysDictionary(scope.row)">确定</el-button>
</div>
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini" style="margin-left:10px">删除</el-button>
<template #reference>
<el-button type="danger" icon="el-icon-delete" size="mini" style="margin-left:10px">删除</el-button>
</template>
</el-popover>
</template>
</el-table-column>
......@@ -73,7 +75,7 @@
@size-change="handleSizeChange"
/>
<el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="110px">
<el-form-item label="字典名(中)" prop="name">
<el-input
......@@ -98,11 +100,13 @@
<el-input v-model="formData.desc" placeholder="请输入描述" clearable :style="{width: '100%'}" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</el-dialog>
<div style="margin-top:40px;color:red">获取字典且缓存方法已在前端utils/dictionary 已经封装完成 不必自己书写 使用方法查看文件内注释</div>
......@@ -117,27 +121,9 @@ import {
findSysDictionary,
getSysDictionaryList
} from '@/api/sysDictionary' // 此处请自行替换地址
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: 'SysDictionary',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
formatBoolean: function(bool) {
if (bool !== null) {
return bool ? '' : ''
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
......@@ -32,7 +32,7 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="日期" width="180">
<template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
</el-table-column>
<el-table-column label="展示值" prop="label" width="120" />
......@@ -40,21 +40,23 @@
<el-table-column label="字典值" prop="value" width="120" />
<el-table-column label="启用状态" prop="status" width="120">
<template slot-scope="scope">{{ scope.row.status|formatBoolean }}</template>
<template #default="scope">{{ formatBoolean(scope.row.status) }}</template>
</el-table-column>
<el-table-column label="排序标记" prop="sort" width="120" />
<el-table-column label="按钮组">
<template slot-scope="scope">
<template #default="scope">
<el-button size="small" type="primary" @click="updateSysDictionaryDetail(scope.row)">变更</el-button>
<el-popover v-model="scope.row.visible" placement="top" width="160">
<el-popover v-model:visible="scope.row.visible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button type="primary" size="mini" @click="deleteSysDictionaryDetail(scope.row)">确定</el-button>
</div>
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
<template #reference>
<el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
</template>
</el-popover>
</template>
</el-table-column>
......@@ -71,7 +73,7 @@
@size-change="handleSizeChange"
/>
<el-dialog :before-close="closeDialog" :visible.sync="dialogFormVisible" title="弹窗操作">
<el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="110px">
<el-form-item label="展示值" prop="label">
<el-input
......@@ -98,10 +100,12 @@
<el-input-number v-model.number="formData.sort" placeholder="排序标记" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
......@@ -114,28 +118,10 @@ import {
findSysDictionaryDetail,
getSysDictionaryDetailList
} from '@/api/sysDictionaryDetail' // 此处请自行替换地址
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: 'SysDictionaryDetail',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
formatBoolean: function(bool) {
if (bool !== null) {
return bool ? '' : ''
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
<template>
<div>
<keep-alive>
<router-view v-if="$route.meta.keepAlive" />
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" />
<router-view v-if="$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<keep-alive>
<component :is="Component" />
</keep-alive>
</transition>
</router-view>
<router-view v-if="!$route.meta.keepAlive" v-slot="{ Component }">
<transition mode="out-in" name="el-fade-in-linear">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>
......
......@@ -9,10 +9,10 @@
<el-form-item prop="icon" style="width:100%">
<i
class="icon"
:class="'el-icon-'+ meta.icon"
:class="'el-icon-'+ metaData.icon"
style="position: absolute; z-index: 9999; padding: 5px 10px; "
/>
<el-select v-model="meta.icon" clearable filterable class="gva-select" placeholder="请选择">
<el-select v-model="metaData.icon" clearable filterable class="gva-select" placeholder="请选择">
<el-option v-for="item in options" :key="item.key" :label="item.key" :value="item.key">
<span class="icon" :class="item.label" />
<span style="float: left">{{ item.key }}</span>
......@@ -37,6 +37,7 @@ export default {
data() {
return {
input: '',
metaData: {},
options: [
{ key: 'platform-eleme', label: 'el-icon-platform-eleme' },
{ key: 'eleme', label: 'el-icon-eleme' },
......@@ -324,6 +325,9 @@ export default {
value: ''
}
},
created() {
this.metaData = this.meta
},
methods: {}
}
</script>
......
......@@ -10,7 +10,7 @@
<el-table-column label="路由Name" min-width="160" prop="name" />
<el-table-column label="路由Path" min-width="160" prop="path" />
<el-table-column label="是否隐藏" min-width="100" prop="hidden">
<template slot-scope="scope">
<template #default="scope">
<span>{{ scope.row.hidden?"隐藏":"显示" }}</span>
</template>
</el-table-column>
......@@ -18,18 +18,18 @@
<el-table-column label="排序" min-width="70" prop="sort" />
<el-table-column label="文件路径" min-width="360" prop="component" />
<el-table-column label="展示名称" min-width="120" prop="authorityName">
<template slot-scope="scope">
<template #default="scope">
<span>{{ scope.row.meta.title }}</span>
</template>
</el-table-column>
<el-table-column label="图标" min-width="140" prop="authorityName">
<template slot-scope="scope">
<template #default="scope">
<i :class="`el-icon-${scope.row.meta.icon}`" />
<span>{{ scope.row.meta.icon }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="300">
<template slot-scope="scope">
<template #default="scope">
<el-button
size="mini"
type="primary"
......@@ -52,8 +52,9 @@
</el-table-column>
</el-table>
<el-dialog :before-close="handleClose" :title="dialogTitle" :visible.sync="dialogFormVisible">
<el-dialog v-model="dialogFormVisible" :before-close="handleClose" :title="dialogTitle">
<el-form
v-if="dialogFormVisible"
ref="menuForm"
:inline="true"
:model="form"
......@@ -70,10 +71,13 @@
/>
</el-form-item>
<el-form-item prop="path" style="width:30%">
<div slot="label" style="display:inline-block">
路由path
<el-checkbox v-model="checkFlag" style="float:right;margin-left:20px;">添加参数</el-checkbox>
</div>
<template #label>
<div style="display:inline-flex">
路由path
<el-checkbox v-model="checkFlag" style="float:right;margin-left:20px;">添加参数</el-checkbox>
</div>
</template>
<el-input
v-model="form.path"
:disabled="!checkFlag"
......@@ -105,9 +109,7 @@
<el-input v-model="form.meta.title" autocomplete="off" />
</el-form-item>
<el-form-item label="图标" prop="meta.icon" style="width:30%">
<icon :meta="form.meta">
<template slot="prepend">el-icon-</template>
</icon>
<icon :meta="form.meta" />
</el-form-item>
<el-form-item label="排序标记" prop="sort" style="width:30%">
<el-input v-model.number="form.sort" autocomplete="off" />
......@@ -135,7 +137,7 @@
>新增菜单参数</el-button>
<el-table :data="form.parameters" stripe style="width: 100%">
<el-table-column prop="type" label="参数类型" width="180">
<template slot-scope="scope">
<template #default="scope">
<el-select v-model="scope.row.type" placeholder="请选择">
<el-option key="query" value="query" label="query" />
<el-option key="params" value="params" label="params" />
......@@ -143,21 +145,21 @@
</template>
</el-table-column>
<el-table-column prop="key" label="参数key" width="180">
<template slot-scope="scope">
<template #default="scope">
<div>
<el-input v-model="scope.row.key" />
</div>
</template>
</el-table-column>
<el-table-column prop="value" label="参数值">
<template slot-scope="scope">
<template #default="scope">
<div>
<el-input v-model="scope.row.value" />
</div>
</template>
</el-table-column>
<el-table-column>
<template slot-scope="scope">
<template #default="scope">
<div>
<el-button
type="danger"
......@@ -170,10 +172,12 @@
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeDialog">取 消</el-button>
<el-button type="primary" @click="enterDialog">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
......@@ -244,7 +248,7 @@ export default {
methods: {
addParameter(form) {
if (!form.parameters) {
this.$set(form, 'parameters', [])
this.form.parameters = []
}
form.parameters.push({
type: 'query',
......
......@@ -13,13 +13,15 @@
</el-form-item>
<el-form-item>
<el-button size="mini" type="primary" icon="el-icon-search" @click="onSubmit">查询</el-button>
<el-popover v-model="deleteVisible" placement="top" width="160">
<el-popover v-model:visible="deleteVisible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="deleteVisible = false">取消</el-button>
<el-button size="mini" type="primary" @click="onDelete">确定</el-button>
</div>
<el-button slot="reference" icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
<template #reference>
<el-button icon="el-icon-delete" size="mini" type="danger" style="margin-left: 10px;">批量删除</el-button>
</template>
</el-popover>
</el-form-item>
</el-form>
......@@ -35,15 +37,15 @@
>
<el-table-column type="selection" width="55" />
<el-table-column label="操作人" width="140">
<template slot-scope="scope">
<template #default="scope">
<div>{{ scope.row.user.userName }}({{ scope.row.user.nickName }})</div>
</template>
</el-table-column>
<el-table-column label="日期" width="180">
<template slot-scope="scope">{{ scope.row.CreatedAt|formatDate }}</template>
<template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
</el-table-column>
<el-table-column label="状态码" prop="status" width="120">
<template slot-scope="scope">
<template #default="scope">
<div>
<el-tag type="success">{{ scope.row.status }}</el-tag>
</div>
......@@ -53,13 +55,15 @@
<el-table-column label="请求方法" prop="method" width="120" />
<el-table-column label="请求路径" prop="path" width="240" />
<el-table-column label="请求" prop="path" width="80">
<template slot-scope="scope">
<template #default="scope">
<div>
<el-popover v-if="scope.row.body" placement="top-start" trigger="hover">
<div class="popover-box">
<pre>{{ fmtBody(scope.row.body) }}</pre>
</div>
<i slot="reference" class="el-icon-view" />
<template #reference>
<i class="el-icon-view" />
</template>
</el-popover>
<span v-else></span>
......@@ -67,27 +71,31 @@
</template>
</el-table-column>
<el-table-column label="响应" prop="path" width="80">
<template slot-scope="scope">
<template #default="scope">
<div>
<el-popover v-if="scope.row.resp" placement="top-start" trigger="hover">
<div class="popover-box">
<pre>{{ fmtBody(scope.row.resp) }}</pre>
</div>
<i slot="reference" class="el-icon-view" />
<template #reference>
<i class="el-icon-view" />
</template>
</el-popover>
<span v-else></span>
</div>
</template>
</el-table-column>
<el-table-column label="按钮组">
<template slot-scope="scope">
<el-popover v-model="scope.row.visible" placement="top" width="160">
<template #default="scope">
<el-popover v-model:visible="scope.row.visible" placement="top" width="160">
<p>确定要删除吗?</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button size="mini" type="primary" @click="deleteSysOperationRecord(scope.row)">确定</el-button>
</div>
<el-button slot="reference" icon="el-icon-delete" size="mini" type="danger">删除</el-button>
<template #reference>
<el-button icon="el-icon-delete" size="mini" type="danger">删除</el-button>
</template>
</el-popover>
</template>
</el-table-column>
......@@ -111,28 +119,10 @@ import {
getSysOperationRecordList,
deleteSysOperationRecordByIds
} from '@/api/sysOperationRecord' // 此处请自行替换地址
import { formatTimeToStr } from '@/utils/date'
import infoList from '@/mixins/infoList'
export default {
name: 'SysOperationRecord',
filters: {
formatDate: function(time) {
if (time !== null && time !== '') {
var date = new Date(time)
return formatTimeToStr(date, 'yyyy-MM-dd hh:mm:ss')
} else {
return ''
}
},
formatBoolean: function(bool) {
if (bool !== null) {
return bool ? '' : ''
} else {
return ''
}
}
},
mixins: [infoList],
data() {
return {
......
......@@ -5,7 +5,7 @@
</div>
<el-table :data="tableData" border stripe>
<el-table-column label="头像" min-width="50">
<template slot-scope="scope">
<template #default="scope">
<div :style="{'textAlign':'center'}">
<CustomPic :pic-src="scope.row.headerImg" />
</div>
......@@ -15,7 +15,7 @@
<el-table-column label="用户名" min-width="150" prop="userName" />
<el-table-column label="昵称" min-width="150" prop="nickName" />
<el-table-column label="用户角色" min-width="150">
<template slot-scope="scope">
<template #default="scope">
<el-cascader
v-model="scope.row.authorityIds"
:options="authOptions"
......@@ -29,14 +29,16 @@
</template>
</el-table-column>
<el-table-column label="操作" min-width="150">
<template slot-scope="scope">
<el-popover v-model="scope.row.visible" placement="top" width="160">
<template #default="scope">
<el-popover v-model:visible="scope.row.visible" placement="top" width="160">
<p>确定要删除此用户吗</p>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="scope.row.visible = false">取消</el-button>
<el-button type="primary" size="mini" @click="deleteUser(scope.row)">确定</el-button>
</div>
<el-button slot="reference" type="danger" icon="el-icon-delete" size="mini">删除</el-button>
<template #reference>
<el-button type="danger" icon="el-icon-delete" size="mini">删除</el-button>
</template>
</el-popover>
</template>
</el-table-column>
......@@ -53,7 +55,7 @@
@size-change="handleSizeChange"
/>
<el-dialog :visible.sync="addUserDialog" custom-class="user-dialog" title="新增用户">
<el-dialog v-model="addUserDialog" custom-class="user-dialog" title="新增用户">
<el-form ref="userForm" :rules="rules" :model="userInfo">
<el-form-item label="用户名" label-width="80px" prop="username">
<el-input v-model="userInfo.username" />
......@@ -72,7 +74,8 @@
</el-form-item>
<el-form-item label="用户角色" label-width="80px" prop="authorityId">
<el-cascader
v-model="userInfo.authorityIds"
v-if="userInfo"
v-model:visible="userInfo.authorityIds"
:options="authOptions"
:show-all-levels="false"
:props="{ multiple:true,checkStrictly: true,label:'authorityName',value:'authorityId',disabled:'disabled',emitPath:false}"
......@@ -81,10 +84,12 @@
/>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeAddUserDialog">取 消</el-button>
<el-button type="primary" @click="enterAddUserDialog">确 定</el-button>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="closeAddUserDialog">取 消</el-button>
<el-button type="primary" @click="enterAddUserDialog">确 定</el-button>
</div>
</template>
</el-dialog>
<ChooseImg ref="chooseImg" :target="userInfo" :target-key="`headerImg`" />
</div>
......@@ -155,7 +160,7 @@ export default {
const authorityIds = user.authorities && user.authorities.map(i => {
return i.authorityId
})
this.$set(user, 'authorityIds', authorityIds)
user.authorityIds = authorityIds
})
},
openHeaderChange() {
......
......@@ -3,7 +3,9 @@
<el-row :gutter="15" class="system_state">
<el-col :span="12">
<el-card v-if="state.os" class="card_item">
<div slot="header">Runtime</div>
<template #header>
<div>Runtime</div>
</template>
<div>
<el-row :gutter="10">
<el-col :span="12">os:</el-col>
......@@ -30,7 +32,9 @@
</el-col>
<el-col :span="12">
<el-card v-if="state.disk" class="card_item">
<div slot="header">Disk</div>
<template #header>
<div>Disk</div>
</template>
<div>
<el-row :gutter="10">
<el-col :span="12">
......@@ -70,30 +74,32 @@
class="card_item"
:body-style="{ height: '180px', 'overflow-y': 'scroll' }"
>
<div slot="header">CPU</div>
<template #header>
<div>CPU</div>
</template>
<div>
<el-row :gutter="10">
<el-col :span="12">physical number of cores:</el-col>
<el-col :span="12" v-text="state.cpu.cores" />
</el-row>
<template v-for="(item, index) in state.cpu.cpus">
<el-row :key="index" :gutter="10">
<el-col :span="12">core {{ index }}:</el-col>
<el-col
:span="12"
><el-progress
type="line"
:percentage="+item.toFixed(0)"
:color="colors"
/></el-col>
</el-row>
</template>
<el-row v-for="(item, index) in state.cpu.cpus" :key="index" :gutter="10">
<el-col :span="12">core {{ index }}:</el-col>
<el-col
:span="12"
><el-progress
type="line"
:percentage="+item.toFixed(0)"
:color="colors"
/></el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card v-if="state.ram" class="card_item">
<div slot="header">Ram</div>
<template #header>
<div>Ram</div>
</template>
<div>
<el-row :gutter="10">
<el-col :span="12">
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册