1
0

4 Revīzijas 76ca467aee ... f619aa1608

Autors SHA1 Ziņojums Datums
  zk f619aa1608 密码修改点击跳转修复 2 gadi atpakaļ
  zk 6acdbcd5d8 问题批示,问题富文本 2 gadi atpakaļ
  zk 2a4dcee4af 问题批示,问题富文本 2 gadi atpakaļ
  zk 3aae44d6d1 问题模糊查询 2 gadi atpakaļ

+ 1 - 0
server/api/v1/autocode/enter.go

@@ -10,5 +10,6 @@ type ApiGroup struct {
 	UnitApi
 	ProblemTypeApi
 	UnitHistoryApi
+	ProblemReplyApi
 	// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
 }

+ 147 - 0
server/api/v1/autocode/problem_reply.go

@@ -0,0 +1,147 @@
+package autocode
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/global"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/autocode"
+	autocodeReq "github.com/flipped-aurora/gin-vue-admin/server/model/autocode/request"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
+	"github.com/flipped-aurora/gin-vue-admin/server/service"
+	"github.com/flipped-aurora/gin-vue-admin/server/utils"
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type ProblemReplyApi struct {
+}
+
+var problemReplyService = service.ServiceGroupApp.AutoCodeServiceGroup.ProblemReplyService
+
+// CreateProblemReply 创建ProblemReply
+// @Tags ProblemReply
+// @Summary 创建ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body autocode.ProblemReply true "创建ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /problemReply/createProblemReply [post]
+func (problemReplyApi *ProblemReplyApi) CreateProblemReply(c *gin.Context) {
+	var problemReply autocode.ProblemReply
+	_ = c.ShouldBindJSON(&problemReply)
+	_, user := service.ServiceGroupApp.SystemServiceGroup.GetUserInfo(utils.GetUserUuid(c))
+	problemReply.HeaderImg = user.HeaderImg
+	problemReply.Uuid = user.UUID
+	problemReply.NickName = user.NickName
+	if err := problemReplyService.CreateProblemReply(problemReply); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Any("err", err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// DeleteProblemReply 删除ProblemReply
+// @Tags ProblemReply
+// @Summary 删除ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body autocode.ProblemReply true "删除ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
+// @Router /problemReply/deleteProblemReply [delete]
+func (problemReplyApi *ProblemReplyApi) DeleteProblemReply(c *gin.Context) {
+	var problemReply autocode.ProblemReply
+	_ = c.ShouldBindJSON(&problemReply)
+	if err := problemReplyService.DeleteProblemReply(problemReply); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Any("err", err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// DeleteProblemReplyByIds 批量删除ProblemReply
+// @Tags ProblemReply
+// @Summary 批量删除ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.IdsReq true "批量删除ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
+// @Router /problemReply/deleteProblemReplyByIds [delete]
+func (problemReplyApi *ProblemReplyApi) DeleteProblemReplyByIds(c *gin.Context) {
+	var IDS request.IdsReq
+	_ = c.ShouldBindJSON(&IDS)
+	if err := problemReplyService.DeleteProblemReplyByIds(IDS); err != nil {
+		global.GVA_LOG.Error("批量删除失败!", zap.Any("err", err))
+		response.FailWithMessage("批量删除失败", c)
+	} else {
+		response.OkWithMessage("批量删除成功", c)
+	}
+}
+
+// UpdateProblemReply 更新ProblemReply
+// @Tags ProblemReply
+// @Summary 更新ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body autocode.ProblemReply true "更新ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
+// @Router /problemReply/updateProblemReply [put]
+func (problemReplyApi *ProblemReplyApi) UpdateProblemReply(c *gin.Context) {
+	var problemReply autocode.ProblemReply
+	_ = c.ShouldBindJSON(&problemReply)
+	if err := problemReplyService.UpdateProblemReply(problemReply); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Any("err", err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// FindProblemReply 用id查询ProblemReply
+// @Tags ProblemReply
+// @Summary 用id查询ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query autocode.ProblemReply true "用id查询ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
+// @Router /problemReply/findProblemReply [get]
+func (problemReplyApi *ProblemReplyApi) FindProblemReply(c *gin.Context) {
+	var problemReply autocode.ProblemReply
+	_ = c.ShouldBindQuery(&problemReply)
+	if err, reproblemReply := problemReplyService.GetProblemReply(problemReply.ID); err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Any("err", err))
+		response.FailWithMessage("查询失败", c)
+	} else {
+		response.OkWithData(gin.H{"reproblemReply": reproblemReply}, c)
+	}
+}
+
+// GetProblemReplyList 分页获取ProblemReply列表
+// @Tags ProblemReply
+// @Summary 分页获取ProblemReply列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query autocodeReq.ProblemReplySearch true "分页获取ProblemReply列表"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /problemReply/getProblemReplyList [get]
+func (problemReplyApi *ProblemReplyApi) GetProblemReplyList(c *gin.Context) {
+	var pageInfo autocodeReq.ProblemReplySearch
+	_ = c.ShouldBindQuery(&pageInfo)
+	if err, list, total := problemReplyService.GetProblemReplyInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Any("err", err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 3 - 3
server/api/v1/system/wechat.go

@@ -71,9 +71,9 @@ func (b *WechatApi) Req(c *gin.Context) {
 			} else {
 				global.GVA_LOG.Info("用户已存在", zap.Any("err", err), zap.Any("sysUser", sysUser))
 			}
-
-			text := message.NewText("【经开创城进行时】感谢您的关注! \r\n <a href='https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx66e0fbb5a735bcc4&redirect_uri=" + url.QueryEscape(
-				global.GVA_CONFIG.Wxxcx.AuthUrl) + "/wechat/auth&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'>点我授权</a>")
+			text := message.NewText("【经开创城进行时】感谢您的关注!")
+			//text := message.NewText("【经开创城进行时】感谢您的关注! \r\n <a href='https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx66e0fbb5a735bcc4&redirect_uri=" + url.QueryEscape(
+			//	global.GVA_CONFIG.Wxxcx.AuthUrl) + "/wechat/auth&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect'>点我授权</a>")
 			return &message.Reply{MsgType: message.MsgTypeText, MsgData: text}
 		}
 		if msg.Content == "授权" {

+ 1 - 0
server/initialize/gorm.go

@@ -56,6 +56,7 @@ func MysqlTables(db *gorm.DB) {
 		autocode.Unit{},
 		autocode.ProblemType{},
 		autocode.UnitHistory{},
+		autocode.ProblemReply{},
 		// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
 	)
 	if err != nil {

+ 1 - 0
server/initialize/router.go

@@ -76,6 +76,7 @@ func Routers() *gin.Engine {
 		autocodeRouter.InitUnitRouter(PrivateGroup)
 		autocodeRouter.InitProblemTypeRouter(PrivateGroup)
 		autocodeRouter.InitUnitHistoryRouter(PrivateGroup)
+		autocodeRouter.InitProblemReplyRouter(PrivateGroup)
 		// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
 	}
 

+ 2 - 2
server/main_test.go

@@ -23,8 +23,8 @@ func TestName(t *testing.T) {
 	//_, resysDictionaryDetail := dictionaryDetailService.GetSysDictionaryDetail(8)
 	//fmt.Printf("\t%v", resysDictionaryDetail)
 	var menuAA []*menu.Button
-	menuAA = append(menuAA, &menu.Button{Type: "miniprogram", Name: "小程序", URL: "http://zk1006.cn", AppID: "wx0dc3e4978f919285", PagePath: "/pages/checking"})
-	menuAA = append(menuAA, &menu.Button{Type: "view", Name: "一键授权", URL: "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx66e0fbb5a735bcc4&redirect_uri=https://cc.zjkduoduo.com/api/wechat/auth&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"})
+	menuAA = append(menuAA, &menu.Button{Type: "miniprogram", Name: "经开创城小程序", URL: "http://zk1006.cn", AppID: "wx0dc3e4978f919285", PagePath: "/pages/checking"})
+	//menuAA = append(menuAA, &menu.Button{Type: "view", Name: "小程序授权登录", URL: "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx66e0fbb5a735bcc4&redirect_uri=https://cc.zjkduoduo.com/api/wechat/auth&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"})
 	err := global.GVA_WECHAT.GetMenu().SetMenu(menuAA)
 	global.GVA_LOG.Info("错误", zap.Any("err", err))
 	//var dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService

+ 23 - 0
server/model/autocode/problem_reply.go

@@ -0,0 +1,23 @@
+// 自动生成模板ProblemReply
+package autocode
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/global"
+	uuid "github.com/satori/go.uuid"
+)
+
+// ProblemReply 结构体
+// 如果含有time.Time 请自行import time包
+type ProblemReply struct {
+	global.GVA_MODEL
+	Uuid      uuid.UUID `json:"uuid" form:"uuid" gorm:"column:uuid;comment:用户UUID;type:varchar(191);"`
+	NickName  string    `json:"nickName" form:"nickName" gorm:"column:nick_name;comment:用户昵称;type:varchar(191);"`
+	HeaderImg string    `json:"headerImg" form:"headerImg" gorm:"column:header_img;comment:用户头像;type:varchar(191);"`
+	ReplyMsg  string    `json:"replyMsg" form:"replyMsg" gorm:"column:reply_msg;comment:用户回复;type:varchar(1000);"`
+	ProblemId *int      `json:"problemId" form:"problemId" gorm:"column:problem_id;comment:问题 id;type:bigint"`
+}
+
+// TableName ProblemReply 表名
+func (ProblemReply) TableName() string {
+	return "problem_reply"
+}

+ 11 - 0
server/model/autocode/request/problem_reply.go

@@ -0,0 +1,11 @@
+package request
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/model/autocode"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
+)
+
+type ProblemReplySearch struct{
+    autocode.ProblemReply
+    request.PageInfo
+}

+ 1 - 0
server/router/autocode/enter.go

@@ -10,5 +10,6 @@ type RouterGroup struct {
 	UnitRouter
 	ProblemTypeRouter
 	UnitHistoryRouter
+	ProblemReplyRouter
 	// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
 }

+ 24 - 0
server/router/autocode/problem_reply.go

@@ -0,0 +1,24 @@
+package autocode
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/api/v1"
+	"github.com/flipped-aurora/gin-vue-admin/server/middleware"
+	"github.com/gin-gonic/gin"
+)
+
+type ProblemReplyRouter struct {
+}
+
+// InitProblemReplyRouter 初始化 ProblemReply 路由信息
+func (s *ProblemReplyRouter) InitProblemReplyRouter(Router *gin.RouterGroup) {
+	problemReplyRouter := Router.Group("problemReply").Use(middleware.OperationRecord())
+	var problemReplyApi = v1.ApiGroupApp.AutoCodeApiGroup.ProblemReplyApi
+	{
+		problemReplyRouter.POST("createProblemReply", problemReplyApi.CreateProblemReply)   // 新建ProblemReply
+		problemReplyRouter.DELETE("deleteProblemReply", problemReplyApi.DeleteProblemReply) // 删除ProblemReply
+		problemReplyRouter.DELETE("deleteProblemReplyByIds", problemReplyApi.DeleteProblemReplyByIds) // 批量删除ProblemReply
+		problemReplyRouter.PUT("updateProblemReply", problemReplyApi.UpdateProblemReply)    // 更新ProblemReply
+		problemReplyRouter.GET("findProblemReply", problemReplyApi.FindProblemReply)        // 根据ID获取ProblemReply
+		problemReplyRouter.GET("getProblemReplyList", problemReplyApi.GetProblemReplyList)  // 获取ProblemReply列表
+	}
+}

+ 1 - 0
server/service/autocode/enter.go

@@ -10,5 +10,6 @@ type ServiceGroup struct {
 	UnitService
 	ProblemTypeService
 	UnitHistoryService
+	ProblemReplyService
 	// Code generated by github.com/flipped-aurora/gin-vue-admin/server End; DO NOT EDIT.
 }

+ 63 - 0
server/service/autocode/problem_reply.go

@@ -0,0 +1,63 @@
+package autocode
+
+import (
+	"github.com/flipped-aurora/gin-vue-admin/server/global"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/autocode"
+	autoCodeReq "github.com/flipped-aurora/gin-vue-admin/server/model/autocode/request"
+	"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
+)
+
+type ProblemReplyService struct {
+}
+
+// CreateProblemReply 创建ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) CreateProblemReply(problemReply autocode.ProblemReply) (err error) {
+	err = global.GVA_DB.Create(&problemReply).Error
+	return err
+}
+
+// DeleteProblemReply 删除ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) DeleteProblemReply(problemReply autocode.ProblemReply) (err error) {
+	err = global.GVA_DB.Delete(&problemReply).Error
+	return err
+}
+
+// DeleteProblemReplyByIds 批量删除ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) DeleteProblemReplyByIds(ids request.IdsReq) (err error) {
+	err = global.GVA_DB.Delete(&[]autocode.ProblemReply{}, "id in ?", ids.Ids).Error
+	return err
+}
+
+// UpdateProblemReply 更新ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) UpdateProblemReply(problemReply autocode.ProblemReply) (err error) {
+	err = global.GVA_DB.Save(&problemReply).Error
+	return err
+}
+
+// GetProblemReply 根据id获取ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) GetProblemReply(id uint) (err error, problemReply autocode.ProblemReply) {
+	err = global.GVA_DB.Where("id = ?", id).First(&problemReply).Error
+	return
+}
+
+// GetProblemReplyInfoList 分页获取ProblemReply记录
+// Author [piexlmax](https://github.com/piexlmax)
+func (problemReplyService *ProblemReplyService) GetProblemReplyInfoList(info autoCodeReq.ProblemReplySearch) (err error, list interface{}, total int64) {
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+	// 创建db
+	db := global.GVA_DB.Model(&autocode.ProblemReply{})
+	var problemReplys []autocode.ProblemReply
+	if info.ProblemId != nil && *info.ProblemId != 0 {
+		db.Where("problem_id=?", info.ProblemId)
+	}
+	// 如果有条件搜索 下方会自动创建搜索语句
+	err = db.Count(&total).Error
+	err = db.Limit(limit).Offset(offset).Find(&problemReplys).Error
+	return err, problemReplys, total
+}

+ 3 - 0
server/service/autocode/problem_type.go

@@ -61,6 +61,9 @@ func (problemTypeService *ProblemTypeService) GetProblemTypeInfoList(info autoCo
 	if info.SiteType != nil {
 		db.Where("site_type = ?", info.SiteType)
 	}
+	if info.Problem != "" {
+		db.Where("problem like ?", "%"+info.Problem+"%")
+	}
 	var problemTypes []autocode.ProblemType
 	// 如果有条件搜索 下方会自动创建搜索语句
 	err = db.Count(&total).Error

+ 58 - 56
web/package-lock.json

@@ -8,6 +8,7 @@
 			"name": "cc",
 			"version": "2.3.5",
 			"dependencies": {
+				"@vueup/vue-quill": "^1.0.0-alpha.40",
 				"axios": "^0.19.2",
 				"core-js": "^3.6.5",
 				"echarts": "4.9.0",
@@ -2921,6 +2922,18 @@
 			"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
 			"dev": true
 		},
+		"node_modules/@vueup/vue-quill": {
+			"version": "1.0.0-alpha.40",
+			"resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.0.0-alpha.40.tgz",
+			"integrity": "sha512-Cn6li+LxOiPQSvmZE+F9jKGsemksQq2VDfTBdgFIlNfNv3+BcBN+DblQstj9NDV5BBF97BHmM2dk7E84yYJI+Q==",
+			"dependencies": {
+				"quill": "^1.3.7",
+				"quill-delta": "^3.6.3"
+			},
+			"peerDependencies": {
+				"vue": "^3.0.11"
+			}
+		},
 		"node_modules/@webassemblyjs/ast": {
 			"version": "1.9.0",
 			"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@@ -3255,7 +3268,7 @@
 			"version": "3.1.2",
 			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
 			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"normalize-path": "^3.0.0",
 				"picomatch": "^2.0.4"
@@ -3443,7 +3456,7 @@
 			"version": "1.0.3",
 			"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
 			"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
-			"dev": true
+			"devOptional": true
 		},
 		"node_modules/async-limiter": {
 			"version": "1.0.1",
@@ -3746,7 +3759,7 @@
 			"version": "2.2.0",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
 			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=8"
 			}
@@ -4243,7 +4256,7 @@
 			"version": "3.5.2",
 			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
 			"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"anymatch": "~3.1.2",
 				"braces": "~3.0.2",
@@ -4264,7 +4277,7 @@
 			"version": "3.0.2",
 			"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
 			"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"fill-range": "^7.0.1"
 			},
@@ -4276,7 +4289,7 @@
 			"version": "7.0.1",
 			"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 			"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"to-regex-range": "^5.0.1"
 			},
@@ -4288,7 +4301,7 @@
 			"version": "7.0.0",
 			"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
 			"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=0.12.0"
 			}
@@ -4297,7 +4310,7 @@
 			"version": "5.0.1",
 			"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 			"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"is-number": "^7.0.0"
 			},
@@ -7430,7 +7443,7 @@
 			"version": "5.1.2",
 			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"is-glob": "^4.0.1"
 			},
@@ -8535,7 +8548,7 @@
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
 			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"binary-extensions": "^2.0.0"
 			},
@@ -8705,7 +8718,7 @@
 			"version": "2.1.1",
 			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 			"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=0.10.0"
 			}
@@ -8723,7 +8736,7 @@
 			"version": "4.0.1",
 			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
 			"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"is-extglob": "^2.1.1"
 			},
@@ -9916,7 +9929,7 @@
 			"version": "3.0.0",
 			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
 			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=0.10.0"
 			}
@@ -10483,7 +10496,7 @@
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
 			"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-			"dev": true
+			"devOptional": true
 		},
 		"node_modules/path-exists": {
 			"version": "4.0.0",
@@ -10575,7 +10588,7 @@
 			"version": "2.3.0",
 			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
 			"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=8.6"
 			},
@@ -11727,7 +11740,7 @@
 			"version": "3.6.0",
 			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
 			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-			"dev": true,
+			"devOptional": true,
 			"dependencies": {
 				"picomatch": "^2.2.1"
 			},
@@ -11860,7 +11873,7 @@
 			"version": "1.1.0",
 			"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
 			"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-			"dev": true
+			"devOptional": true
 		},
 		"node_modules/renderkid": {
 			"version": "2.0.7",
@@ -14024,7 +14037,7 @@
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
 			"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
-			"dev": true,
+			"devOptional": true,
 			"engines": {
 				"node": ">=4",
 				"yarn": "*"
@@ -14453,7 +14466,6 @@
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
 			"integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"chokidar": "^2.1.8"
@@ -14463,7 +14475,6 @@
 			"version": "2.0.0",
 			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
 			"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"micromatch": "^3.1.4",
@@ -14474,7 +14485,6 @@
 			"version": "2.1.1",
 			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
 			"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"remove-trailing-separator": "^1.0.1"
@@ -14487,7 +14497,6 @@
 			"version": "1.13.1",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
 			"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-			"dev": true,
 			"optional": true,
 			"engines": {
 				"node": ">=0.10.0"
@@ -14498,7 +14507,6 @@
 			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
 			"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
 			"deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"anymatch": "^2.0.0",
@@ -14522,7 +14530,6 @@
 			"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
 			"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
 			"deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
-			"dev": true,
 			"hasInstallScript": true,
 			"optional": true,
 			"os": [
@@ -14540,7 +14547,6 @@
 			"version": "3.1.0",
 			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
 			"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"is-glob": "^3.1.0",
@@ -14551,7 +14557,6 @@
 			"version": "3.1.0",
 			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
 			"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"is-extglob": "^2.1.0"
@@ -14564,7 +14569,6 @@
 			"version": "1.0.1",
 			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
 			"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"binary-extensions": "^1.0.0"
@@ -14577,7 +14581,6 @@
 			"version": "2.2.1",
 			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
 			"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
-			"dev": true,
 			"optional": true,
 			"dependencies": {
 				"graceful-fs": "^4.1.11",
@@ -17699,6 +17702,15 @@
 			"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
 			"dev": true
 		},
+		"@vueup/vue-quill": {
+			"version": "1.0.0-alpha.40",
+			"resolved": "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.0.0-alpha.40.tgz",
+			"integrity": "sha512-Cn6li+LxOiPQSvmZE+F9jKGsemksQq2VDfTBdgFIlNfNv3+BcBN+DblQstj9NDV5BBF97BHmM2dk7E84yYJI+Q==",
+			"requires": {
+				"quill": "^1.3.7",
+				"quill-delta": "^3.6.3"
+			}
+		},
 		"@webassemblyjs/ast": {
 			"version": "1.9.0",
 			"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
@@ -17983,7 +17995,7 @@
 			"version": "3.1.2",
 			"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
 			"integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"normalize-path": "^3.0.0",
 				"picomatch": "^2.0.4"
@@ -18131,7 +18143,7 @@
 			"version": "1.0.3",
 			"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
 			"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
-			"dev": true
+			"devOptional": true
 		},
 		"async-limiter": {
 			"version": "1.0.1",
@@ -18361,7 +18373,7 @@
 			"version": "2.2.0",
 			"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
 			"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-			"dev": true
+			"devOptional": true
 		},
 		"bluebird": {
 			"version": "3.7.2",
@@ -18784,7 +18796,7 @@
 			"version": "3.5.2",
 			"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
 			"integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"anymatch": "~3.1.2",
 				"braces": "~3.0.2",
@@ -18800,7 +18812,7 @@
 					"version": "3.0.2",
 					"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
 					"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-					"dev": true,
+					"devOptional": true,
 					"requires": {
 						"fill-range": "^7.0.1"
 					}
@@ -18809,7 +18821,7 @@
 					"version": "7.0.1",
 					"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
 					"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-					"dev": true,
+					"devOptional": true,
 					"requires": {
 						"to-regex-range": "^5.0.1"
 					}
@@ -18818,13 +18830,13 @@
 					"version": "7.0.0",
 					"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
 					"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-					"dev": true
+					"devOptional": true
 				},
 				"to-regex-range": {
 					"version": "5.0.1",
 					"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
 					"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-					"dev": true,
+					"devOptional": true,
 					"requires": {
 						"is-number": "^7.0.0"
 					}
@@ -21338,7 +21350,7 @@
 			"version": "5.1.2",
 			"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
 			"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"is-glob": "^4.0.1"
 			}
@@ -22184,7 +22196,7 @@
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
 			"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"binary-extensions": "^2.0.0"
 			}
@@ -22304,7 +22316,7 @@
 			"version": "2.1.1",
 			"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 			"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-			"dev": true
+			"devOptional": true
 		},
 		"is-fullwidth-code-point": {
 			"version": "3.0.0",
@@ -22316,7 +22328,7 @@
 			"version": "4.0.1",
 			"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
 			"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"is-extglob": "^2.1.1"
 			}
@@ -23289,7 +23301,7 @@
 			"version": "3.0.0",
 			"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
 			"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-			"dev": true
+			"devOptional": true
 		},
 		"normalize-range": {
 			"version": "0.1.2",
@@ -23729,7 +23741,7 @@
 			"version": "1.0.2",
 			"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
 			"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-			"dev": true
+			"devOptional": true
 		},
 		"path-exists": {
 			"version": "4.0.0",
@@ -23805,7 +23817,7 @@
 			"version": "2.3.0",
 			"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
 			"integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
-			"dev": true
+			"devOptional": true
 		},
 		"pify": {
 			"version": "4.0.1",
@@ -24774,7 +24786,7 @@
 			"version": "3.6.0",
 			"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
 			"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
-			"dev": true,
+			"devOptional": true,
 			"requires": {
 				"picomatch": "^2.2.1"
 			}
@@ -24879,7 +24891,7 @@
 			"version": "1.1.0",
 			"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
 			"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-			"dev": true
+			"devOptional": true
 		},
 		"renderkid": {
 			"version": "2.0.7",
@@ -26614,7 +26626,7 @@
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
 			"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
-			"dev": true
+			"devOptional": true
 		},
 		"upper-case": {
 			"version": "1.1.3",
@@ -26957,7 +26969,6 @@
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
 			"integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
-			"dev": true,
 			"optional": true,
 			"requires": {
 				"chokidar": "^2.1.8"
@@ -26967,7 +26978,6 @@
 					"version": "2.0.0",
 					"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
 					"integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"micromatch": "^3.1.4",
@@ -26978,7 +26988,6 @@
 							"version": "2.1.1",
 							"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
 							"integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
-							"dev": true,
 							"optional": true,
 							"requires": {
 								"remove-trailing-separator": "^1.0.1"
@@ -26990,14 +26999,12 @@
 					"version": "1.13.1",
 					"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
 					"integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-					"dev": true,
 					"optional": true
 				},
 				"chokidar": {
 					"version": "2.1.8",
 					"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
 					"integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"anymatch": "^2.0.0",
@@ -27018,7 +27025,6 @@
 					"version": "1.2.13",
 					"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
 					"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"bindings": "^1.5.0",
@@ -27029,7 +27035,6 @@
 					"version": "3.1.0",
 					"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
 					"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"is-glob": "^3.1.0",
@@ -27040,7 +27045,6 @@
 							"version": "3.1.0",
 							"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
 							"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-							"dev": true,
 							"optional": true,
 							"requires": {
 								"is-extglob": "^2.1.0"
@@ -27052,7 +27056,6 @@
 					"version": "1.0.1",
 					"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
 					"integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"binary-extensions": "^1.0.0"
@@ -27062,7 +27065,6 @@
 					"version": "2.2.1",
 					"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
 					"integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
-					"dev": true,
 					"optional": true,
 					"requires": {
 						"graceful-fs": "^4.1.11",

+ 1 - 1
web/package.json

@@ -9,6 +9,7 @@
 		"preview": "vite preview"
 	},
 	"dependencies": {
+		"@vueup/vue-quill": "^1.0.0-alpha.40",
 		"axios": "^0.19.2",
 		"core-js": "^3.6.5",
 		"echarts": "4.9.0",
@@ -18,7 +19,6 @@
 		"mitt": "^3.0.0",
 		"path": "^0.12.7",
 		"qs": "^6.8.0",
-		"quill": "^1.3.7",
 		"screenfull": "^5.0.2",
 		"script-ext-html-webpack-plugin": "^2.1.4",
 		"spark-md5": "^3.0.1",

+ 97 - 0
web/src/api/problemReply.js

@@ -0,0 +1,97 @@
+import service from '@/utils/request'
+
+// @Tags ProblemReply
+// @Summary 创建ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body model.ProblemReply true "创建ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /problemReply/createProblemReply [post]
+export const createProblemReply = (data) => {
+  return service({
+    url: '/problemReply/createProblemReply',
+    method: 'post',
+    data
+  })
+}
+
+// @Tags ProblemReply
+// @Summary 删除ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body model.ProblemReply true "删除ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
+// @Router /problemReply/deleteProblemReply [delete]
+export const deleteProblemReply = (data) => {
+  return service({
+    url: '/problemReply/deleteProblemReply',
+    method: 'delete',
+    data
+  })
+}
+
+// @Tags ProblemReply
+// @Summary 删除ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.IdsReq true "批量删除ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
+// @Router /problemReply/deleteProblemReply [delete]
+export const deleteProblemReplyByIds = (data) => {
+  return service({
+    url: '/problemReply/deleteProblemReplyByIds',
+    method: 'delete',
+    data
+  })
+}
+
+// @Tags ProblemReply
+// @Summary 更新ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body model.ProblemReply true "更新ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
+// @Router /problemReply/updateProblemReply [put]
+export const updateProblemReply = (data) => {
+  return service({
+    url: '/problemReply/updateProblemReply',
+    method: 'put',
+    data
+  })
+}
+
+// @Tags ProblemReply
+// @Summary 用id查询ProblemReply
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query model.ProblemReply true "用id查询ProblemReply"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
+// @Router /problemReply/findProblemReply [get]
+export const findProblemReply = (params) => {
+  return service({
+    url: '/problemReply/findProblemReply',
+    method: 'get',
+    params
+  })
+}
+
+// @Tags ProblemReply
+// @Summary 分页获取ProblemReply列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query request.PageInfo true "分页获取ProblemReply列表"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
+// @Router /problemReply/getProblemReplyList [get]
+export const getProblemReplyList = (params) => {
+  return service({
+    url: '/problemReply/getProblemReplyList',
+    method: 'get',
+    params
+  })
+}

+ 5 - 0
web/src/main.js

@@ -15,6 +15,10 @@ import DictTag from '@/components/DictTag/index.vue'
 import { auth } from '@/directive/auth'
 import App from './App.vue'
 
+import { QuillEditor } from '@vueup/vue-quill'
+import '@vueup/vue-quill/dist/vue-quill.snow.css';
+
+
 const app = createApp(App)
 import { findSysDictionary as getDicts } from '@/api/sysDictionary'
 import DataDict from '@/utils/dict'
@@ -29,6 +33,7 @@ auth(app)
 app.config.productionTip = false
 app.config.globalProperties.$getDicts = getDicts
 app.component('DictTag', DictTag)
+app.component('QuillEditor', QuillEditor)
 app.use(DataDict, {
   metas: {
     '*': {

+ 11 - 2
web/src/view/content/norm.vue

@@ -33,7 +33,7 @@
       </el-table-column>
       <el-table-column label="操作人" prop="oper" width="120" />
       <el-table-column label="标题" prop="title" width="120" />
-      <el-table-column label="内容" prop="text" width="120" />
+      <el-table-column label="内容" :show-overflow-tooltip="true" prop="text" width="120" />
       <el-table-column label="图片" prop="imgs" width="165"  >
         <template #default="scope">
           <img :src="scope.row.imgs.split('|')[0] " width="140"/>
@@ -65,8 +65,13 @@
           <el-input v-model="formData.title" clearable placeholder="请输入" />
         </el-form-item>
         <el-form-item label="内容:">
-          <el-input type="textarea" :autosize="{ minRows: 4, maxRows: 10 }" v-model="formData.text" clearable placeholder="请输入" />
+          <QuillEditor theme="snow"
+              v-model:content="formData.text"
+              contentType="html"
+          ></QuillEditor>
         </el-form-item>
+<!--          <el-input type="textarea" :autosize="{ minRows: 4, maxRows: 10 }" v-model="formData.text" clearable placeholder="请输入" />-->
+<!--        </el-form-item>-->
         <el-form-item label="图片:">
           <el-upload
               class="upload-demo"
@@ -138,6 +143,10 @@ export default {
   },
   methods: {
   // 条件搜索前端看此方法
+    onEditorChange({editor, html, text}) {
+      // 此处三项可以自己在控制台打印
+      this.formData.text = html
+    },
     onSubmit() {
       this.page = 1
       this.pageSize = 10

+ 1 - 1
web/src/view/person/person.vue

@@ -67,7 +67,7 @@
                   <p class="title">修改密码</p>
                   <p class="desc">
                     修改个人密码
-                    <a href="#" @click="showPassword=true">修改密码</a>
+                    <a href="javascript:void(0);" @click="showPassword=true">修改密码</a>
                   </p>
                 </li>
               </ul>

+ 45 - 0
web/src/view/problemInfo/problemInfo.vue

@@ -149,6 +149,28 @@
             <el-descriptions-item span="2" label="问题备注">{{ formData.remark }}</el-descriptions-item>
           </el-descriptions>
         </el-card>
+        <el-card class="box-card" v-show="replyList.length>0">
+          <div class="el-descriptions__header el-descriptions__title">问题批示</div>
+            <el-table border stripe :data="replyList" style="width: 100%">
+              <el-table-column label="头像" prop="headerImg" >
+                <template #default="scope">
+                  <div :style="{'textAlign':'center'}">
+                    <CustomPic :pic-src="scope.row.headerImg" />
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="昵称" prop="nickName" />
+              <el-table-column label="批示内容" prop="replyMsg" />
+              <el-table-column label="批示时间" prop="CreatedAt" >
+                <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
+              </el-table-column>
+              <el-table-column>
+                <template #default="scope">
+                  <el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteReplyRow(scope.row)">删除</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+        </el-card>
         <el-card class="box-card">
           <el-descriptions title="问题列表" :column="1" direction="vertical">
             <el-descriptions-item v-for="item in formData.matter.split('|')">
@@ -208,6 +230,11 @@ import {
   getProblemInfoList,
   exportExcel, sendUserProblemInfo, getReadCountList
 } from '@/api/problemInfo' //  此处请自行替换地址
+import {
+  deleteProblemReply,
+  getProblemReplyList
+} from '@/api/problemReply' //  此处请自行替换地址
+import CustomPic from '@/components/customPic/index.vue'
 import infoList from '@/mixins/infoList'
 import { getDict } from '@/utils/dictionary'
 import { getUserPList } from '@/api/user'
@@ -215,6 +242,7 @@ import { getUserPList } from '@/api/user'
 export default {
   name: 'ProblemInfo',
   mixins: [infoList],
+  components: { CustomPic },
   dicts: ['site_type'],
   data() {
     return {
@@ -233,6 +261,7 @@ export default {
       deleteVisible: false,
       multipleSelection: [],
       matterList: [],
+      replyList: [],
       formData: {
         oper: 0,
         handler: 0,
@@ -278,6 +307,18 @@ export default {
         this.deleteProblemInfo(row)
       })
     },
+    deleteReplyRow(row){
+      this.$confirm('确定要删除吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        deleteProblemReply({ID:row.ID}).then(res=>{
+          this.closeDialog()
+          this.updateProblemInfo({ID:row.problemId})
+        })
+      })
+    },
     async handlePdf(row) {
       const href = window.location.origin
       const params = `/#/proInfo?Id=${row.ID}`
@@ -347,6 +388,10 @@ export default {
       const res = await findProblemInfo({ ID: row.ID })
       this.type = 'update'
       if (res.code === 0) {
+        await getProblemReplyList({page: 1, pageSize: 9999, problemId: row.ID }).then(res=>{
+          this.replyList=res.data.list
+          console.log(this.replyList)
+        })
         this.formData = res.data.reproblemInfo
         if (this.formData.imgs.length > 0) {
           this.formData.imgList = this.formData.imgs.split('|')

+ 223 - 0
web/src/view/problemReply/problemReply.vue

@@ -0,0 +1,223 @@
+<template>
+  <div>
+    <div class="search-term">
+      <el-form :inline="true" :model="searchInfo" class="demo-form-inline">
+        <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: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>
+            <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>
+    </div>
+    <el-table
+      ref="multipleTable"
+      border
+      stripe
+      style="width: 100%"
+      tooltip-effect="dark"
+      :data="tableData"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column label="日期" width="180">
+        <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
+      </el-table-column>
+      <el-table-column label="用户UUID" prop="uuid" />
+      <el-table-column label="用户昵称" prop="nickName" />
+      <el-table-column label="用户头像" prop="headerImg" />
+      <el-table-column label="用户回复" prop="replyMsg" />
+      <el-table-column label="问题 id" prop="problemId" />
+      <el-table-column label="按钮组">
+        <template #default="scope">
+          <el-button size="small" type="primary" icon="el-icon-edit" class="table-button" @click="updateProblemReply(scope.row)">变更</el-button>
+          <el-button type="danger" icon="el-icon-delete" size="mini" @click="deleteRow(scope.row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      layout="total, sizes, prev, pager, next, jumper"
+      :current-page="page"
+      :page-size="pageSize"
+      :page-sizes="[10, 30, 50, 100]"
+      :style="{float:'right',padding:'20px'}"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作">
+      <el-form :model="formData" label-position="right" label-width="80px">
+        <el-form-item label="用户UUID:">
+          <el-input v-model="formData.uuid" clearable placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="用户昵称:">
+          <el-input v-model="formData.nickName" clearable placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="用户头像:">
+          <el-input v-model="formData.headerImg" clearable placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="用户回复:">
+          <el-input v-model="formData.replyMsg" clearable placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="问题 id:">
+          <el-input v-model.number="formData.problemId" clearable placeholder="请输入" />
+        </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>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  createProblemReply,
+  deleteProblemReply,
+  deleteProblemReplyByIds,
+  updateProblemReply,
+  findProblemReply,
+  getProblemReplyList
+} from '@/api/problemReply' //  此处请自行替换地址
+import infoList from '@/mixins/infoList'
+export default {
+  name: 'ProblemReply',
+  mixins: [infoList],
+  data() {
+    return {
+      listApi: getProblemReplyList,
+      dialogFormVisible: false,
+      type: '',
+      deleteVisible: false,
+      multipleSelection: [],
+      formData: {
+        uuid: '',
+        nickName: '',
+        headerImg: '',
+        replyMsg: '',
+        problemId: 0,
+      }
+    }
+  },
+  async created() {
+    await this.getTableData()
+  },
+  methods: {
+  // 条件搜索前端看此方法
+    onSubmit() {
+      this.page = 1
+      this.pageSize = 10
+      this.getTableData()
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val
+    },
+    deleteRow(row) {
+      this.$confirm('确定要删除吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.deleteProblemReply(row)
+      })
+    },
+    async onDelete() {
+      const ids = []
+      if (this.multipleSelection.length === 0) {
+        this.$message({
+          type: 'warning',
+          message: '请选择要删除的数据'
+        })
+        return
+      }
+      this.multipleSelection &&
+        this.multipleSelection.map(item => {
+          ids.push(item.ID)
+        })
+      const res = await deleteProblemReplyByIds({ ids })
+      if (res.code === 0) {
+        this.$message({
+          type: 'success',
+          message: '删除成功'
+        })
+        if (this.tableData.length === ids.length && this.page > 1) {
+          this.page--
+        }
+        this.deleteVisible = false
+        this.getTableData()
+      }
+    },
+    async updateProblemReply(row) {
+      const res = await findProblemReply({ ID: row.ID })
+      this.type = 'update'
+      if (res.code === 0) {
+        this.formData = res.data.reproblemReply
+        this.dialogFormVisible = true
+      }
+    },
+    closeDialog() {
+      this.dialogFormVisible = false
+      this.formData = {
+        uuid: '',
+        nickName: '',
+        headerImg: '',
+        replyMsg: '',
+        problemId: 0,
+      }
+    },
+    async deleteProblemReply(row) {
+      const res = await deleteProblemReply({ ID: row.ID })
+      if (res.code === 0) {
+        this.$message({
+          type: 'success',
+          message: '删除成功'
+        })
+        if (this.tableData.length === 1 && this.page > 1) {
+          this.page--
+        }
+        this.getTableData()
+      }
+    },
+    async enterDialog() {
+      let res
+      switch (this.type) {
+        case 'create':
+          res = await createProblemReply(this.formData)
+          break
+        case 'update':
+          res = await updateProblemReply(this.formData)
+          break
+        default:
+          res = await createProblemReply(this.formData)
+          break
+      }
+      if (res.code === 0) {
+        this.$message({
+          type: 'success',
+          message: '创建/更改成功'
+        })
+        this.closeDialog()
+        this.getTableData()
+      }
+    },
+    openDialog() {
+      this.type = 'create'
+      this.dialogFormVisible = true
+    }
+  },
+}
+</script>
+
+<style>
+</style>

+ 90 - 0
web/src/view/problemReply/problemReplyForm.vue

@@ -0,0 +1,90 @@
+<template>
+  <div>
+    <el-form :model="formData" label-position="right" label-width="80px">
+      <el-form-item label="用户UUID:">
+        <el-input v-model="formData.uuid" clearable placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="用户昵称:">
+        <el-input v-model="formData.nickName" clearable placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="用户头像:">
+        <el-input v-model="formData.headerImg" clearable placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="用户回复:">
+        <el-input v-model="formData.replyMsg" clearable placeholder="请输入" />
+      </el-form-item>
+      <el-form-item label="问题 id:">
+        <el-input v-model.number="formData.problemId" clearable placeholder="请输入" />
+      </el-form-item>
+      <el-form-item>
+        <el-button size="mini" type="primary" @click="save">保存</el-button>
+        <el-button size="mini" type="primary" @click="back">返回</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import {
+  createProblemReply,
+  updateProblemReply,
+  findProblemReply
+} from '@/api/problemReply' //  此处请自行替换地址
+import infoList from '@/mixins/infoList'
+export default {
+  name: 'ProblemReply',
+  mixins: [infoList],
+  data() {
+    return {
+      type: '',
+      formData: {
+        uuid: '',
+        nickName: '',
+        headerImg: '',
+        replyMsg: '',
+        problemId: 0,
+      }
+    }
+  },
+  async created() {
+    // 建议通过url传参获取目标数据ID 调用 find方法进行查询数据操作 从而决定本页面是create还是update 以下为id作为url参数示例
+    if (this.$route.query.id) {
+      const res = await findProblemReply({ ID: this.$route.query.id })
+      if (res.code === 0) {
+        this.formData = res.data.reproblemReply
+        this.type = 'update'
+      }
+    } else {
+      this.type = 'create'
+    }
+  },
+  methods: {
+    async save() {
+      let res
+      switch (this.type) {
+        case 'create':
+          res = await createProblemReply(this.formData)
+          break
+        case 'update':
+          res = await updateProblemReply(this.formData)
+          break
+        default:
+          res = await createProblemReply(this.formData)
+          break
+      }
+      if (res.code === 0) {
+        this.$message({
+          type: 'success',
+          message: '创建/更改成功'
+        })
+      }
+    },
+    back() {
+      this.$router.go(-1)
+    }
+  }
+}
+</script>
+
+<style>
+</style>

+ 10 - 2
web/yarn.lock

@@ -1541,6 +1541,14 @@
   "resolved" "https://registry.npmjs.org/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz"
   "version" "1.3.0"
 
+"@vueup/vue-quill@^1.0.0-alpha.40":
+  "integrity" "sha512-Cn6li+LxOiPQSvmZE+F9jKGsemksQq2VDfTBdgFIlNfNv3+BcBN+DblQstj9NDV5BBF97BHmM2dk7E84yYJI+Q=="
+  "resolved" "https://registry.npmjs.org/@vueup/vue-quill/-/vue-quill-1.0.0-alpha.40.tgz"
+  "version" "1.0.0-alpha.40"
+  dependencies:
+    "quill" "^1.3.7"
+    "quill-delta" "^3.6.3"
+
 "@webassemblyjs/[email protected]":
   "integrity" "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA=="
   "resolved" "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz"
@@ -7158,7 +7166,7 @@
   "resolved" "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz"
   "version" "2.2.0"
 
-"quill-delta@^3.6.2":
+"quill-delta@^3.6.2", "quill-delta@^3.6.3":
   "integrity" "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg=="
   "resolved" "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz"
   "version" "3.6.3"
@@ -8818,7 +8826,7 @@
   "resolved" "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz"
   "version" "1.9.1"
 
-"vue@^2 || ^3.0.0-0", "vue@^3.0.0", "vue@^3.0.2", "vue@^3.2.0":
+"vue@^2 || ^3.0.0-0", "vue@^3.0.0", "vue@^3.0.11", "vue@^3.0.2", "vue@^3.2.0":
   "integrity" "sha512-x7lwdnOSkceHQUXRVVHBaZzcp6v7M2CYtSZH75zZaT1mTjB4plC4KZHKP/5jAvdqOLBHZGwDSMkWXm3YbAufrA=="
   "resolved" "https://registry.npmjs.org/vue/-/vue-3.2.8.tgz"
   "version" "3.2.8"