使用常量标记状态和Redis的key,增加了文件管理功能,修改tinymce页面增加文件上传功能

This commit is contained in:
sysnix 2023-09-24 21:51:12 +08:00
parent e1d0017257
commit a2d868d354
18 changed files with 388 additions and 57 deletions

View File

@ -1,6 +1,7 @@
package async package async
import ( import (
"blog/internal/consts"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
@ -27,7 +28,7 @@ func init() {
database.GormTemplate.Table("blog_articles").Where("id = ?", id).UpdateColumn("publish_time", time) database.GormTemplate.Table("blog_articles").Where("id = ?", id).UpdateColumn("publish_time", time)
database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(article) database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(article)
database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(article.PublishTime), Member: article}) database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(article.PublishTime), Member: article})
} }
}() }()
} }

View File

@ -1,6 +1,7 @@
package async package async
import ( import (
"blog/internal/consts"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
@ -22,7 +23,7 @@ func init() {
var diary blog.BlogDiary var diary blog.BlogDiary
database.GormTemplate.Table("blog_diaries").Where("id = ?", id).First(&diary) database.GormTemplate.Table("blog_diaries").Where("id = ?", id).First(&diary)
database.RedisTemplate.ZAdd(ctx, "blog:diary:latest", redis.Z{Score: float64(diary.PublishTime), Member: diary}) database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_DIARY_LATEST, redis.Z{Score: float64(diary.PublishTime), Member: diary})
} }
}() }()
} }

View File

@ -1,6 +1,7 @@
package async package async
import ( import (
"blog/internal/consts"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
"log" "log"
@ -30,7 +31,7 @@ func init() {
record.Id = uuid.NewString() record.Id = uuid.NewString()
database.GormTemplate.Table("blog_view_record").Create(&record) database.GormTemplate.Table("blog_view_record").Create(&record)
ctx := context.Background() ctx := context.Background()
database.RedisTemplate.HIncrBy(ctx, "blog:view:record", relId, 1) database.RedisTemplate.HIncrBy(ctx, consts.REDIS_BLOG_VIEW_RECORD, relId, 1)
} }

View File

@ -0,0 +1,8 @@
package consts
//文章状态常量
const ARTICLE_STATE_PUBLISH string = "publish"
const ARTICLE_STATE_DRAFT string = "draft"
const ARTICLE_CONTENT_TYPE_MARKDOWN string = "markdown"
const ARTICLE_CONTENT_TYPE_TINYMCE string = "tinymce"

View File

@ -0,0 +1,6 @@
package consts
//文本状态常量
const CONTENT_STATE_PUBLISH string = "publish"
const CONTENT_STATE_DOWN string = "down"

View File

@ -0,0 +1,21 @@
package consts
//Redis
//文章相关Key常量
const REDIS_BLOG_ARTICLE string = "blog:article:"
const REDIS_BLOG_ARTICLE_LATEST string = "blog:article:latest"
//日记相关Key常量
const REDIS_BLOG_DIARY string = "blog:diary:"
const REDIS_BLOG_DIARY_LATEST string = "blog:diary:latest"
//文件相关Key常量
const REDIS_FILE string = "blog:file:"
const REDIS_FILE_BYTES string = "blog:file:bytes"
//文本相关Key常量
const REDIS_BLOG_CONTENT string = "blog:content:"
//点击量相关Key常量
const REDIS_BLOG_VIEW_RECORD string = "blog:view:record"

View File

@ -2,6 +2,7 @@ package controller
import ( import (
"blog/internal/async" "blog/internal/async"
"blog/internal/consts"
"blog/internal/model/AjaxResult" "blog/internal/model/AjaxResult"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/internal/service" "blog/internal/service"
@ -28,7 +29,7 @@ func (ctrl *ArticleController) Get() {
func (ctrl *ArticleController) GetLatest() { func (ctrl *ArticleController) GetLatest() {
ctx := context.Background() ctx := context.Background()
var slices []blog.BlogArticle var slices []blog.BlogArticle
err := database.RedisTemplate.ZRevRange(ctx, "blog:article:latest", 0, 10).ScanSlice(&slices) err := database.RedisTemplate.ZRevRange(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, 0, 10).ScanSlice(&slices)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
ctrl.Ctx.JSON(AjaxResult.Error("加载错误")) ctrl.Ctx.JSON(AjaxResult.Error("加载错误"))
@ -38,7 +39,7 @@ func (ctrl *ArticleController) GetLatest() {
for i := range slices { for i := range slices {
article := &slices[i] article := &slices[i]
id := article.Id id := article.Id
viewRecord, _ := database.RedisTemplate.HGet(ctx, "blog:view:record", id).Int() viewRecord, _ := database.RedisTemplate.HGet(ctx, consts.REDIS_BLOG_VIEW_RECORD, id).Int()
article.ViewRecord = viewRecord article.ViewRecord = viewRecord
} }
@ -58,7 +59,7 @@ func (ctrl *ArticleController) ViewArticle() {
content := service.ContentService.GetContentByCache(articleId) content := service.ContentService.GetContentByCache(articleId)
ctrl.Ctx.ViewData("article", article) ctrl.Ctx.ViewData("article", article)
ctrl.Ctx.ViewData("content", content) ctrl.Ctx.ViewData("content", content)
if article.ContentType == "markdown" { if article.ContentType == consts.ARTICLE_CONTENT_TYPE_MARKDOWN {
ctrl.Ctx.View("blog/article/article_md.html") ctrl.Ctx.View("blog/article/article_md.html")
return return
} }

View File

@ -1,6 +1,7 @@
package controller package controller
import ( import (
"blog/internal/consts"
"blog/internal/model/AjaxResult" "blog/internal/model/AjaxResult"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/internal/service" "blog/internal/service"
@ -24,7 +25,7 @@ func (ctrl *DiaryController) Get() {
func (ctrl *DiaryController) GetLatest() { func (ctrl *DiaryController) GetLatest() {
ctx := context.Background() ctx := context.Background()
var slices []blog.BlogDiary var slices []blog.BlogDiary
err := database.RedisTemplate.ZRevRange(ctx, "blog:diary:latest", 0, 5).ScanSlice(&slices) err := database.RedisTemplate.ZRevRange(ctx, consts.REDIS_BLOG_DIARY_LATEST, 0, 5).ScanSlice(&slices)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
ctrl.Ctx.JSON(AjaxResult.Error("加载错误")) ctrl.Ctx.JSON(AjaxResult.Error("加载错误"))

View File

@ -2,6 +2,7 @@ package controller
import ( import (
"blog/internal/service" "blog/internal/service"
"net/url"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/mvc" "github.com/kataras/iris/v12/mvc"
@ -14,10 +15,13 @@ type FileController struct {
} }
func (ctrl *FileController) ViewFile() { func (ctrl *FileController) ViewFile() {
relId := ctrl.Ctx.Params().Get("id") id := ctrl.Ctx.Params().Get("id")
bytes := service.FileService.GetFile(relId) file, err := service.FileService.GetFile(id)
ctrl.Ctx.Write(bytes) if err != nil {
return
}
ctrl.Ctx.Header("Content-Disposition", "attachment;filename="+url.QueryEscape(file.FileName))
ctrl.Ctx.Write(file.Data)
} }
func (ctrl *FileController) BeforeActivation(activation mvc.BeforeActivation) { func (ctrl *FileController) BeforeActivation(activation mvc.BeforeActivation) {

View File

@ -2,6 +2,8 @@ package admin
import ( import (
"blog/internal/model/admin" "blog/internal/model/admin"
"blog/internal/service"
"blog/third_party/SessionUtil"
"blog/third_party/database" "blog/third_party/database"
"time" "time"
@ -23,6 +25,18 @@ type uploadResponse struct {
Location string `json:"location"` Location string `json:"location"`
} }
func (ctrl *FileController) Get() {
ctrl.Ctx.View("/admin/file/index.html")
}
func (ctrl *FileController) GetList() {
page := ctrl.Ctx.URLParamIntDefault("page", 0)
itemsPerPage := ctrl.Ctx.URLParamIntDefault("itemsPerPage", 10)
pages := service.FileService.PageSysFiles(page, itemsPerPage)
ctrl.Ctx.JSON(pages)
}
func (ctrl *FileController) PostUpload() { func (ctrl *FileController) PostUpload() {
file, fileHeader, err := ctrl.Ctx.FormFile("editormd-image-file") file, fileHeader, err := ctrl.Ctx.FormFile("editormd-image-file")
if err != nil { if err != nil {
@ -31,13 +45,22 @@ func (ctrl *FileController) PostUpload() {
return return
} }
} }
user := SessionUtil.GetUser(ctrl.Session)
fileId := uuid.NewString() fileId := uuid.NewString()
fileName := fileHeader.Filename fileName := fileHeader.Filename
fileSize := fileHeader.Size fileSize := fileHeader.Size
var bytes []byte = make([]byte, fileSize) var bytes []byte = make([]byte, fileSize)
file.Read(bytes) file.Read(bytes)
sysFile := admin.SysFile{Id: fileId, FileName: fileName, FileSize: fileSize, CreateTime: time.Now().UnixMilli(), Data: bytes, State: "1", Del: 0} sysFile := admin.SysFile{
Id: fileId,
FileName: fileName,
FileSize: fileSize,
CreateBy: user.Username,
CreateTime: time.Now().UnixMilli(),
Data: bytes, State: "1",
Del: 0,
}
err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { err = database.GormTemplate.Transaction(func(tx *gorm.DB) error {
err2 := tx.Table("common_files").Create(sysFile).Error err2 := tx.Table("common_files").Create(sysFile).Error
return err2 return err2

View File

@ -5,7 +5,7 @@ import "encoding/json"
type CommonFiles struct { type CommonFiles struct {
Id string `json:"id" gorm:"primary_key"` Id string `json:"id" gorm:"primary_key"`
FileName string `json:"fileName"` FileName string `json:"fileName"`
Data []byte Data []byte `json:"-"`
Sort int32 `json:"sort"` Sort int32 `json:"sort"`
CreateBy string `json:"-"` CreateBy string `json:"-"`
CreateTime int64 `json:"-"` CreateTime int64 `json:"-"`
@ -15,11 +15,11 @@ type CommonFiles struct {
Del int `json:"-"` Del int `json:"-"`
} }
func (article *CommonFiles) MarshalBinary() (data []byte, err error) { func (file *CommonFiles) MarshalBinary() (data []byte, err error) {
// encoding.BinaryMarshaler // encoding.BinaryMarshaler
return json.Marshal(article) return json.Marshal(file)
} }
func (article *CommonFiles) UnmarshalBinary(data []byte) (err error) { func (file *CommonFiles) UnmarshalBinary(data []byte) (err error) {
return json.Unmarshal(data, article) return json.Unmarshal(data, file)
} }

View File

@ -1,6 +1,7 @@
package service package service
import ( import (
"blog/internal/consts"
"blog/internal/model/admin" "blog/internal/model/admin"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/internal/model/vo" "blog/internal/model/vo"
@ -23,13 +24,13 @@ var ArticleService articleService
func (*articleService) InitArticleData() { func (*articleService) InitArticleData() {
var articleSlice []blog.BlogArticle var articleSlice []blog.BlogArticle
result := database.GormTemplate.Where("state = ?", "publish").Find(&articleSlice) result := database.GormTemplate.Where("state = ?", consts.ARTICLE_STATE_PUBLISH).Find(&articleSlice)
log.Println("文章初始化数据加载量:", result.RowsAffected) log.Println("文章初始化数据加载量:", result.RowsAffected)
ctx := context.Background() ctx := context.Background()
for _, article := range articleSlice { for _, article := range articleSlice {
// log.Println(article) // log.Println(article)
publishTime := article.PublishTime publishTime := article.PublishTime
err := database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(publishTime), Member: &article}).Err() err := database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(publishTime), Member: &article}).Err()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
@ -43,14 +44,14 @@ func (*articleService) InitArticleData() {
database.GormTemplate.Table("blog_view_record").Select("rel_id ,count(1) counts").Group("rel_id").Find(&records) database.GormTemplate.Table("blog_view_record").Select("rel_id ,count(1) counts").Group("rel_id").Find(&records)
for _, val := range records { for _, val := range records {
database.RedisTemplate.HSet(ctx, "blog:view:record", map[string]any{val.RelId: val.Counts}) database.RedisTemplate.HSet(ctx, consts.REDIS_BLOG_VIEW_RECORD, map[string]any{val.RelId: val.Counts})
} }
} }
func (*articleService) GetBlogArticle(id string) blog.BlogArticle { func (*articleService) GetBlogArticle(id string) blog.BlogArticle {
ctx := context.Background() ctx := context.Background()
var article blog.BlogArticle var article blog.BlogArticle
key := "blog:article:" + id key := consts.REDIS_BLOG_ARTICLE + id
err := database.RedisTemplate.HGetAll(ctx, key).Scan(&article) err := database.RedisTemplate.HGetAll(ctx, key).Scan(&article)
if err != nil || article.Id == "" { if err != nil || article.Id == "" {
database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(&article) database.GormTemplate.Table("blog_articles").Where("id = ?", id).First(&article)
@ -93,7 +94,7 @@ func (*articleService) CreateArticle(articel *admin.AdminArticle) (string, error
articel.CreateTime = time articel.CreateTime = time
articel.UpdateTime = time articel.UpdateTime = time
articel.Del = 0 articel.Del = 0
articel.State = "draft" articel.State = consts.ARTICLE_STATE_DRAFT
err := database.GormTemplate.Transaction(func(tx *gorm.DB) error { err := database.GormTemplate.Transaction(func(tx *gorm.DB) error {
contentId := uuid.NewString() contentId := uuid.NewString()
@ -134,19 +135,19 @@ func (*articleService) PublishArticle(id string) error {
return err return err
} }
if article.State != "draft" { if article.State != consts.ARTICLE_STATE_DRAFT {
return errors.New("发布失败,文章状态不是起草状态,无法发布") return errors.New("发布失败,文章状态不是起草状态,无法发布")
} }
article.PublishTime = now article.PublishTime = now
article.State = "publish" article.State = consts.ARTICLE_STATE_PUBLISH
err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { err = database.GormTemplate.Transaction(func(tx *gorm.DB) error {
var txErr error = tx.Table("blog_articles").Updates(&article).Error var txErr error = tx.Table("blog_articles").Updates(&article).Error
if txErr != nil { if txErr != nil {
return errors.New("文章更新错误") return errors.New("文章更新错误")
} }
txErr = ContentService.UpdataState(id, "publish") txErr = ContentService.UpdataState(id, consts.CONTENT_STATE_PUBLISH)
if txErr != nil { if txErr != nil {
return errors.New("文本状态更新错误") return errors.New("文本状态更新错误")
} }
@ -156,11 +157,11 @@ func (*articleService) PublishArticle(id string) error {
} }
ctx := context.Background() ctx := context.Background()
txErr = database.RedisTemplate.ZAdd(ctx, "blog:article:latest", redis.Z{Score: float64(article.PublishTime), Member: &article}).Err() txErr = database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_ARTICLE_LATEST, redis.Z{Score: float64(article.PublishTime), Member: &article}).Err()
if txErr != nil { if txErr != nil {
return errors.New("缓存错误") return errors.New("缓存错误")
} }
txErr = database.RedisTemplate.Set(ctx, "blog:content:"+id, content.Content, time.Duration(0)).Err() txErr = database.RedisTemplate.Set(ctx, consts.REDIS_BLOG_CONTENT+id, content.Content, time.Duration(0)).Err()
return txErr return txErr
}) })
@ -175,14 +176,14 @@ func (*articleService) UnPublishArticle(id string) error {
return err return err
} }
if article.State != "publish" { if article.State != consts.CONTENT_STATE_PUBLISH {
return errors.New("撤下失败,文章状态不是已发布状态,无法撤下") return errors.New("撤下失败,文章状态不是已发布状态,无法撤下")
} }
err = database.GormTemplate.Transaction(func(tx *gorm.DB) error { err = database.GormTemplate.Transaction(func(tx *gorm.DB) error {
var txErr error var txErr error
ctx := context.Background() ctx := context.Background()
txErr = database.RedisTemplate.ZRemRangeByScore(ctx, "blog:article:latest", txErr = database.RedisTemplate.ZRemRangeByScore(ctx, consts.REDIS_BLOG_ARTICLE_LATEST,
strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64), strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64),
strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64), strconv.FormatFloat(float64(article.PublishTime), 'E', -1, 64),
).Err() ).Err()
@ -191,18 +192,18 @@ func (*articleService) UnPublishArticle(id string) error {
return errors.New("缓存错误") return errors.New("缓存错误")
} }
txErr = database.RedisTemplate.Del(ctx, "blog:content:"+id).Err() txErr = database.RedisTemplate.Del(ctx, consts.REDIS_BLOG_CONTENT+id).Err()
if txErr != nil { if txErr != nil {
return errors.New("缓存错误") return errors.New("缓存错误")
} }
article.State = "draft" article.State = consts.ARTICLE_STATE_DRAFT
article.PublishTime = -1 article.PublishTime = -1
txErr = tx.Table("blog_articles").Updates(&article).Error txErr = tx.Table("blog_articles").Updates(&article).Error
if txErr != nil { if txErr != nil {
return errors.New("文章状态更新错误") return errors.New("文章状态更新错误")
} }
txErr = ContentService.UpdataState(id, "down") txErr = ContentService.UpdataState(id, consts.CONTENT_STATE_DOWN)
return txErr return txErr
}) })

View File

@ -1,6 +1,7 @@
package service package service
import ( import (
"blog/internal/consts"
"blog/internal/model/vo" "blog/internal/model/vo"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
@ -15,12 +16,12 @@ var ContentService contentService
func (*contentService) InitContentData() { func (*contentService) InitContentData() {
var contentSlice []vo.CommonContent var contentSlice []vo.CommonContent
result := database.GormTemplate.Table("common_contents").Where("state = ?", "publish").Find(&contentSlice) result := database.GormTemplate.Table("common_contents").Where("state = ?", consts.CONTENT_STATE_PUBLISH).Find(&contentSlice)
log.Println("大文本初始化数据加载量:", result.RowsAffected) log.Println("大文本初始化数据加载量:", result.RowsAffected)
ctx := context.Background() ctx := context.Background()
for _, content := range contentSlice { for _, content := range contentSlice {
database.RedisTemplate.Set(ctx, "blog:content:"+content.RelId, content.Content, time.Duration(0)) database.RedisTemplate.Set(ctx, consts.REDIS_BLOG_CONTENT+content.RelId, content.Content, time.Duration(0))
} }
} }
@ -28,7 +29,7 @@ func (*contentService) InitContentData() {
func (*contentService) GetContentByCache(relId string) string { func (*contentService) GetContentByCache(relId string) string {
ctx := context.Background() ctx := context.Background()
var content string var content string
database.RedisTemplate.Get(ctx, "blog:content:"+relId).Scan(&content) database.RedisTemplate.Get(ctx, consts.REDIS_BLOG_CONTENT+relId).Scan(&content)
return content return content
} }

View File

@ -1,6 +1,7 @@
package service package service
import ( import (
"blog/internal/consts"
"blog/internal/model/blog" "blog/internal/model/blog"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
@ -23,7 +24,7 @@ func (*diaryService) InitDiaryData() {
for _, diary := range diarySlice { for _, diary := range diarySlice {
// log.Println(article) // log.Println(article)
publishTime := diary.PublishTime publishTime := diary.PublishTime
err := database.RedisTemplate.ZAdd(ctx, "blog:diary:latest", redis.Z{Score: float64(publishTime), Member: &diary}).Err() err := database.RedisTemplate.ZAdd(ctx, consts.REDIS_BLOG_DIARY_LATEST, redis.Z{Score: float64(publishTime), Member: &diary}).Err()
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }

View File

@ -1,9 +1,12 @@
package service package service
import ( import (
"blog/internal/consts"
"blog/internal/model/admin"
"blog/internal/model/vo" "blog/internal/model/vo"
"blog/third_party/database" "blog/third_party/database"
"context" "context"
"log"
"time" "time"
) )
@ -12,15 +15,46 @@ type fileService struct {
var FileService fileService var FileService fileService
func (*fileService) GetFile(relId string) []byte { func (*fileService) GetFile(id string) (vo.CommonFiles, error) {
ctx := context.Background() ctx := context.Background()
key := "blog:file:" + relId
bytes, err := database.RedisTemplate.Get(ctx, key).Bytes()
if err != nil {
var file vo.CommonFiles var file vo.CommonFiles
database.GormTemplate.Table("common_files").Where("id = ?", relId).First(&file) var bytes []byte
err := database.RedisTemplate.Get(ctx, consts.REDIS_FILE+id).Scan(&file)
//如果缓存不存在则查数据库并存入Redis
if err != nil || file.Id == "" {
database.GormTemplate.Table("common_files").Where("id = ?", id).First(&file)
bytes = file.Data bytes = file.Data
database.RedisTemplate.Set(ctx, key, bytes, time.Duration(0)) err = database.RedisTemplate.Set(ctx, consts.REDIS_FILE+id, &file, time.Duration(0)).Err()
if err != nil {
log.Println(err)
} }
return bytes err = database.RedisTemplate.Set(ctx, consts.REDIS_FILE_BYTES+id, bytes, time.Duration(0)).Err()
if err != nil {
log.Println(err)
}
return file, nil
}
bytes, err = database.RedisTemplate.Get(ctx, consts.REDIS_FILE_BYTES+id).Bytes()
file.Data = bytes
return file, err
}
func (*fileService) PageSysFiles(page int, itemsPerPage int) vo.Page[admin.SysFile] {
var content []admin.SysFile
var totalElements int64
database.GormTemplate.Table("common_files").Count(&totalElements)
database.GormTemplate.Table("common_files").
Where("del != ?", 1).
Offset((page - 1) * itemsPerPage).
Limit(itemsPerPage).
Order("create_time DESC").
Find(&content)
pre := int(totalElements) % itemsPerPage
if pre > 0 {
pre = 1
}
var totalPages int = int(totalElements)/itemsPerPage + pre
return vo.Page[admin.SysFile]{TotalElements: totalElements, TotalPages: totalPages, Number: totalPages, Content: content}
} }

View File

@ -21,6 +21,8 @@ import (
// func main() { // func main() {
// i := time.Now().UnixMilli() // i := time.Now().UnixMilli()
// log.Println(i) // log.Println(i)
// b, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
// log.Println(string(b))
// } // }
func main() { func main() {

View File

@ -1,11 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Admin Console</title> <title>Admin Console</title>
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon"/> <link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
<link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon"/> <link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon" />
<link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet"/> <link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet" />
<link href="/assets/vuetify-v2.6.9/materialdesignicons.min.css" rel="stylesheet"> <link href="/assets/vuetify-v2.6.9/materialdesignicons.min.css" rel="stylesheet">
<link href="/assets/input.css" rel="stylesheet"> <link href="/assets/input.css" rel="stylesheet">
@ -15,8 +16,9 @@
<script src="/assets/axios/axios.min.js"></script> <script src="/assets/axios/axios.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
</body> </body>
<script type="text/x-template" id="app-template"> <script type="text/x-template" id="app-template">
<v-app id="inspire"> <v-app id="inspire">
@ -49,6 +51,7 @@
<v-col cols="12" sm="10"> <v-col cols="12" sm="10">
<v-sheet min-height="100%"> <v-sheet min-height="100%">
<textarea id="tinyMceEditor"></textarea> <textarea id="tinyMceEditor"></textarea>
<input id="i-files" name="file" type="file" style="display:none"/>
</v-sheet> </v-sheet>
</v-col> </v-col>
</v-row> </v-row>
@ -65,8 +68,8 @@
new Vue({ new Vue({
el: '#app', el: '#app',
template: '#app-template', template: '#app-template',
computed:{ computed: {
tags:{ tags: {
get() { get() {
let result = [] let result = []
if (this.article.tags.length == 0) { if (this.article.tags.length == 0) {
@ -144,16 +147,36 @@
],*/ ],*/
fontsize_formats: '12px 14px 16px 18px 24px 36px 48px 56px 72px', fontsize_formats: '12px 14px 16px 18px 24px 36px 48px 56px 72px',
font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;知乎配置=BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif;小米配置=Helvetica Neue,Helvetica,Arial,Microsoft Yahei,Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif', font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats;知乎配置=BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, WenQuanYi Micro Hei, sans-serif;小米配置=Helvetica Neue,Helvetica,Arial,Microsoft Yahei,Hiragino Sans GB,Heiti SC,WenQuanYi Micro Hei,sans-serif',
link_list: [ // link_list: [
// { title: '预置链接1', value: 'http://www.tinymce.com' }, // // { title: '预置链接1', value: 'http://www.tinymce.com' },
// { title: '预置链接2', value: 'http://tinymce.ax-z.cn' } // // { title: '预置链接2', value: 'http://tinymce.ax-z.cn' }
], // ],
//importcss_append: true, //importcss_append: true,
//自定义文件选择器的回调内容 //自定义文件选择器的回调内容
file_picker_callback: function (callback, value, meta) { file_picker_callback: function (callback, value, meta) {
let files = document.getElementById('i-files')
files.click()
files.onchange = () => {
let formData = new FormData();
console.log(files.files);
for (let index = 0; index < files.files.length; index++) {
const element = files.files[index];
formData.append("file", element);
}
axios.post('/admin/file/upload', formData, { headers: { "Content-Type": "multipart/form-data" } }).then(res => {
let data = res.data
console.log(data)
// location.href = '/diary'
callback(`${data.url}`, { text: '附件下载' });
}).catch(err => {
console.error(err)
});
}
// if (meta.filetype === 'file') { // if (meta.filetype === 'file') {
// callback('https://www.baidu.com/img/bd_logo1.png', { text: 'My text' }); // callback('https://www.baidu.com/img/bd_logo1.png', {val:"value", text: 'My text',alt: 'My alt text' });
// } // }
// if (meta.filetype === 'image') { // if (meta.filetype === 'image') {
// callback('https://www.baidu.com/img/bd_logo1.png', { alt: 'My alt text' }); // callback('https://www.baidu.com/img/bd_logo1.png', { alt: 'My alt text' });
@ -177,8 +200,8 @@
// { title: '预置图片2', value: 'https://www.baidu.com/img/bd_logo1.png' } // { title: '预置图片2', value: 'https://www.baidu.com/img/bd_logo1.png' }
], ],
image_class_list: [ image_class_list: [
{title: 'None', value: ''}, { title: 'None', value: '' },
{title: 'Some class', value: 'class-name'} { title: 'Some class', value: 'class-name' }
], ],
convert_urls: false, convert_urls: false,
images_upload_credentials: true, images_upload_credentials: true,
@ -207,4 +230,5 @@
</script> </script>
</html> </html>

View File

@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Admin Console</title>
<link rel="icon" href="/assets/favicon.ico" type="image/x-icon" />
<link rel="bookmark" href="/assets/favicon.ico" type="image/x-icon" />
<link href="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.css" rel="stylesheet" />
<link href="/assets/vuetify-v2.6.9/materialdesignicons.min.css" rel="stylesheet">
<script src="/assets/vue/vue.min.js"></script>
<script src="/assets/vuetify-v2.6.9/vuetify-v2.6.9.min.js"></script>
<script src="/assets/axios/axios.min.js"></script>
<script src="/assets/moment/moment.js"></script>
<script src="/assets/qs/qs.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
<style>
.over-display {
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script type="text/x-template" id="app-template">
<v-app id="inspire">
#{ render "common/bar-admin.html" . }
<!-- <#include "../../common/drawer-admin.ftl"> -->
<v-dialog v-model="dialog.show" width="500" v-if="dialog.obj">
<v-card>
<v-card-title class="text-h5 grey lighten-2">{{dialog.title}}</v-card-title>
<v-card-text>{{dialog.context}}</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn text @click="dialog.show = false">取消</v-btn>
<v-spacer></v-spacer>
<v-btn color="primary" text @click="confirm">确认</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-main class="grey lighten-3">
<v-container style="height: 100%">
<v-row style="height: 100%">
<!-- <v-col cols="12" sm="2" class="">
<v-sheet min-height="100%">
</v-sheet>
</v-col> -->
<v-col cols="12" sm="12" md="12">
<v-sheet min-height="100%">
<v-data-table class="elevation-1"
:headers="headers"
:items="itemList"
height="700px"
:page.sync="pageable.page"
:items-per-page="pageable.itemsPerPage"
:loading="loading"
loading-text="数据加载中"
hide-default-footer>
<template #item.createTime="{ item }">
{{item.createTime?moment(item.createTime).format('YYYY/MM/DD HH:mm:ss'):''}}
</template>
<template #item.fileName="{ item }">
<span class="over-display">{{item.fileName}}</span>
</template>
<template #item.fileSize="{ item }">
{{Math.round(item.fileSize/1024)}}KB
</template>
<template #item.actions="{ item }">
<v-btn text color="primary" :href="`/file/${item.id}`" target="_blank">下载</v-btn>
</template>
</v-data-table>
<div class="pt-2 float-right" style="display: flex">
<v-pagination v-model="pageable.page" :length="pageable.totalPages" :total-visible="7"
style="min-width: 200px;" @previous="previousPage" @next="nextPage"
@input="goPage"></v-pagination>
<v-select :items="[5,10,20,50,100]" label="分页大小" style="width: 100px"
v-model="pageable.itemsPerPage" outlined dense
@input="pageSizeChange"></v-select>
</div>
</v-sheet>
</v-col>
<!-- <v-col cols="12" sm="2">
<v-sheet min-height="100%">
</v-sheet>
</v-col> -->
</v-row>
</v-container>
</v-main>
</v-app>
</script>
<script>
var qs = Qs
new Vue({
el: '#app',
template: '#app-template',
data: {
group: null,
drawer: false,
loading: false,
dialog: {
show: false,
title: '',
context: '',
obj: null
},
pageable: {
page: 1,
itemsPerPage: 20,
totalElements: 0,
totalPages: 1
},
param: {
id: null,
title: null,
subTitle: null,
},
headers: [
{ text: 'ID', value: 'id', align: 'start', sortable: false },
{ text: '文件名', value: 'fileName', align: 'start', sortable: false },
{ text: '文件大小', value: 'fileSize', align: 'start', sortable: false },
{ text: '上传人', value: 'createBy', align: 'start', sortable: false },
{ text: '上传时间', value: 'createTime', align: 'start', sortable: false },
{ text: '操作', value: 'actions', align: 'center', sortable: false },
],
itemList: [],
},
methods: {
pageSizeChange(size) {
this.pageable.page = 1
this.list()
},
nextPage() {
this.pageable.page = this.pageable.page + 1
// this.list()
},
previousPage() {
this.pageable.page = this.pageable.page - 1
// this.list()
},
goPage(num) {
this.pageable.page = num
this.list()
},
list() {
let ths = this;
this.loading = true;
let { page, itemsPerPage } = this.pageable;
let pageQuery = qs.stringify({ page, itemsPerPage, ...this.param }, { skipNulls: true });
// let paramQuery = qs.stringify(this.param, {skipNulls: true});
axios.get('/admin/file/list?' + pageQuery).then(function (response) {
// console.log(response);
let data = response.data;
ths.itemList = data.content;
ths.pageable.totalElements = data.totalElements
ths.pageable.totalPages = data.totalPages
ths.pageable.page = data.number + 1
ths.loading = false;
}).catch(function (error) {
console.log(error);
ths.loading = false;
});
},
download(id) {
console.log(id);
}
},
mounted() {
this.list();
},
vuetify: new Vuetify(),
})
</script>
</html>