restore new ui files

This commit is contained in:
RockYang 2024-03-15 11:13:02 +08:00
commit 72c6bd3f77
344 changed files with 56769 additions and 887 deletions

View File

@ -10,10 +10,6 @@ WeChatBot = false
SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80" # 注意:这个是 JWT Token 授权密钥,生产环境请务必更换
MaxAge = 86400
[Manager]
Username = "admin"
Password = "admin123" # 如果是生产环境的话,这里管理员的密码记得修改
[Redis] # redis 配置信息
Host = "localhost"
Port = 6379

View File

@ -162,8 +162,7 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/payment/") ||
strings.HasPrefix(c.Request.URL.Path, "/static/") ||
c.Request.URL.Path == "/api/admin/config/get" {
strings.HasPrefix(c.Request.URL.Path, "/static/") {
c.Next()
return
}

View File

@ -30,6 +30,7 @@ const (
Success = BizCode(0)
Failed = BizCode(1)
NotAuthorized = BizCode(400) // 未授权
NotPermission = BizCode(403) // 没有权限
OkMsg = "Success"
ErrorMsg = "系统开小差了"

View File

@ -95,7 +95,31 @@ func (h *ManagerHandler) Login(c *gin.Context) {
manager.LastLoginAt = time.Now().Unix()
h.db.Model(&manager).Updates(manager)
resp.SUCCESS(c, tokenString)
permissions := h.GetAdminSlugs(manager.Id)
var result = struct {
IsSuperAdmin bool `json:"is_super_admin"`
Token string `json:"token"`
Permissions []string `json:"permissions"`
}{
IsSuperAdmin: manager.Id == 1,
Token: tokenString,
Permissions: permissions,
}
resp.SUCCESS(c, result)
}
func (h *ManagerHandler) GetAdminSlugs(userId uint) []string {
var permissions []string
err := h.db.Raw("SELECT distinct p.slug "+
"FROM chatgpt_admin_user_roles as ur "+
"LEFT JOIN chatgpt_admin_role_permissions as rp ON ur.role_id = rp.role_id "+
"LEFT JOIN chatgpt_admin_permissions as p ON rp.permission_id = p.id "+
"WHERE ur.admin_id = ?", userId).Scan(&permissions)
if err.Error == nil {
return []string{}
}
return permissions
}
// Logout 注销

View File

@ -0,0 +1,132 @@
package admin
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/handler"
"chatplus/store/model"
"chatplus/store/vo"
"chatplus/utils"
"chatplus/utils/resp"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type SysPermissionHandler struct {
handler.BaseHandler
db *gorm.DB
}
func NewSysPermissionHandler(app *core.AppServer, db *gorm.DB) *SysPermissionHandler {
h := SysPermissionHandler{db: db}
h.App = app
return &h
}
func (h *SysPermissionHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var items []model.AdminPermission
var data = make([]vo.AdminPermission, 0)
res := h.db.Find(&items)
if res.Error != nil {
resp.ERROR(c, "暂无数据")
return
}
for _, item := range items {
adminPermissionVo := vo.AdminPermission{}
_ = utils.CopyObject(item, &adminPermissionVo)
data = append(data, adminPermissionVo)
}
data = ArrayToTree(data)
resp.SUCCESS(c, data)
}
func ArrayToTree(dates []vo.AdminPermission) []vo.AdminPermission {
group := make(map[int][]vo.AdminPermission, 0)
for _, node := range dates {
group[node.Pid] = append(group[node.Pid], node)
}
// 初始化递归,从根节点开始构建树
result := FindSiblings(group[0], group)
return result
}
func FindSiblings(siblings []vo.AdminPermission, group map[int][]vo.AdminPermission) []vo.AdminPermission {
result := make([]vo.AdminPermission, 0)
for _, sibling := range siblings {
children, ok := group[sibling.Id]
if ok {
sibling.Children = FindSiblings(children, group)
}
result = append(result, sibling)
}
return result
}
func (h *SysPermissionHandler) Save(c *gin.Context) {
var data struct {
Id int `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Sort int `json:"sort"`
Pid int `json:"pid"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
var permission = model.AdminPermission{}
var res *gorm.DB
if data.Id > 0 { // 更新
permission.Id = data.Id
// 此处需要用 map 更新,用结构体无法更新 0 值
res = h.db.Model(&permission).Updates(map[string]interface{}{
"name": data.Name,
"slug": data.Slug,
"sort": data.Sort,
"pid": data.Pid,
})
} else {
p := model.AdminPermission{
Name: data.Name,
Slug: data.Slug,
Sort: data.Sort,
Pid: data.Pid,
}
res = h.db.Create(&p)
}
if res.Error != nil {
fmt.Println(res.Error)
resp.ERROR(c, "更新数据库失败")
return
}
resp.SUCCESS(c)
}
func (h *SysPermissionHandler) Remove(c *gin.Context) {
var data struct {
Id int
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
res := h.db.Where("id = ?", data.Id).Delete(&model.AdminPermission{})
if res.Error != nil {
resp.ERROR(c, "删除失败")
return
}
}
resp.SUCCESS(c)
}

View File

@ -0,0 +1,166 @@
package admin
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/handler"
"chatplus/store/model"
"chatplus/store/vo"
"chatplus/utils"
"chatplus/utils/resp"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type SysRoleHandler struct {
handler.BaseHandler
db *gorm.DB
}
func NewSysRoleHandler(app *core.AppServer, db *gorm.DB) *SysRoleHandler {
h := SysRoleHandler{db: db}
h.App = app
return &h
}
type permission struct {
Id int `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
}
func (h *SysRoleHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
page := h.GetInt(c, "page", 1)
pageSize := h.GetInt(c, "page_size", 20)
name := h.GetTrim(c, "name")
offset := (page - 1) * pageSize
var items []model.AdminRole
var data = make([]vo.AdminRole, 0)
var total int64
session := h.db.Session(&gorm.Session{})
if name != "" {
session = session.Where("name LIKE ?", "%"+name+"%")
}
session.Model(&model.AdminRole{}).Count(&total)
res := session.Offset(offset).Limit(pageSize).Find(&items)
if res.Error != nil {
resp.ERROR(c, "暂无数据")
return
}
for _, item := range items {
adminRoleVo := vo.AdminRole{}
err := utils.CopyObject(item, &adminRoleVo)
if err == nil {
var permissions []permission
h.db.Raw("SELECT p.id,p.name,p.slug "+
"FROM chatgpt_admin_role_permissions as rp "+
"LEFT JOIN chatgpt_admin_permissions as p ON rp.permission_id = p.id "+
"WHERE rp.role_id = ?", item.Id).Scan(&permissions)
adminRoleVo.Permissions = permissions
adminRoleVo.CreatedAt = item.CreatedAt.Format("2006-01-02 15:04:05")
data = append(data, adminRoleVo)
}
}
pageVo := vo.NewPage(total, page, pageSize, data)
resp.SUCCESS(c, pageVo)
}
func (h *SysRoleHandler) Save(c *gin.Context) {
var data struct {
Id int
Name string
Description string
Permissions []int
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
var role = model.AdminRole{}
var res *gorm.DB
tx := h.db.Begin()
if data.Id > 0 { // 更新
role.Id = data.Id
//删除角色对应的权限
err := tx.Where("role_id = ?", role.Id).Delete(model.AdminRolePermission{})
if err.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
//更新角色名
res = tx.Model(&role).Updates(map[string]interface{}{
"name": data.Name,
"description": data.Description,
})
} else {
//新建角色
role.Name = data.Name
role.Description = data.Description
res = tx.Create(&role)
}
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
rp := make([]model.AdminRolePermission, 0)
if len(data.Permissions) > 0 {
for _, per := range data.Permissions {
rp = append(rp, model.AdminRolePermission{
RoleId: role.Id,
PermissionId: per,
})
}
res2 := tx.CreateInBatches(rp, len(rp))
if res2.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
}
tx.Commit()
resp.SUCCESS(c)
}
func (h *SysRoleHandler) Remove(c *gin.Context) {
var data struct {
Id int
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
tx := h.db.Begin()
res := tx.Where("id = ?", data.Id).Delete(&model.AdminRole{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
res = tx.Where("role_id = ?", data.Id).Delete(&model.AdminRolePermission{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
tx.Commit()
}
resp.SUCCESS(c)
}

View File

@ -23,8 +23,18 @@ func NewSysUserHandler(app *core.AppServer, db *gorm.DB) *SysUserHandler {
return &h
}
type role struct {
Id int `json:"id"`
Name string `json:"name"`
}
// List 用户列表
func (h *SysUserHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
page := h.GetInt(c, "page", 1)
pageSize := h.GetInt(c, "page_size", 20)
username := h.GetTrim(c, "username")
@ -48,9 +58,16 @@ func (h *SysUserHandler) List(c *gin.Context) {
var userVo vo.AdminUser
err := utils.CopyObject(item, &userVo)
if err == nil {
var roles []role
h.db.Raw("SELECT r.id,r.name "+
"FROM chatgpt_admin_user_roles as ur "+
"LEFT JOIN chatgpt_admin_roles as r ON ur.role_id = r.id "+
"WHERE ur.admin_id = ?", item.Id).Scan(&roles)
userVo.Id = item.Id
userVo.CreatedAt = item.CreatedAt.Unix()
userVo.UpdatedAt = item.UpdatedAt.Unix()
userVo.RoleIds = roles
users = append(users, userVo)
} else {
logger.Error(err)
@ -68,6 +85,7 @@ func (h *SysUserHandler) Save(c *gin.Context) {
Password string `json:"password"`
Username string `json:"username"`
Status bool `json:"status"`
RoleIds []int `json:"role_ids"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
@ -83,31 +101,58 @@ func (h *SysUserHandler) Save(c *gin.Context) {
var user = model.AdminUser{}
var res *gorm.DB
var userVo vo.AdminUser
tx := h.db.Begin()
if data.Id > 0 { // 更新
user.Id = data.Id
err := tx.Where("admin_id = ?", user.Id).Delete(&model.AdminUserRole{})
if err.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
// 此处需要用 map 更新,用结构体无法更新 0 值
res = h.db.Model(&user).Updates(map[string]interface{}{
res = tx.Model(&user).Updates(map[string]interface{}{
"username": data.Username,
"status": data.Status,
})
} else {
salt := utils.RandString(8)
u := model.AdminUser{
Username: data.Username,
Password: utils.GenPassword(data.Password, salt),
Salt: salt,
Status: true,
}
res = h.db.Create(&u)
_ = utils.CopyObject(u, &userVo)
userVo.Id = u.Id
userVo.CreatedAt = u.CreatedAt.Unix()
userVo.UpdatedAt = u.UpdatedAt.Unix()
user.Username = data.Username
user.Password = utils.GenPassword(data.Password, salt)
user.Salt = salt
user.Status = true
res = tx.Create(&user)
_ = utils.CopyObject(user, &userVo)
userVo.Id = user.Id
userVo.CreatedAt = user.CreatedAt.Unix()
userVo.UpdatedAt = user.UpdatedAt.Unix()
}
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
// 添加角色
userRole := make([]model.AdminUserRole, 0)
if len(data.RoleIds) > 0 {
for _, roleId := range data.RoleIds {
userRole = append(userRole, model.AdminUserRole{
AdminId: user.Id,
RoleId: roleId,
})
}
err := tx.CreateInBatches(userRole, len(userRole))
if err.Error != nil {
tx.Rollback()
resp.ERROR(c, "更新数据库失败")
return
}
}
tx.Commit()
resp.SUCCESS(c, userVo)
}
@ -155,11 +200,20 @@ func (h *SysUserHandler) Remove(c *gin.Context) {
return
}
if data.Id > 0 {
res := h.db.Where("id = ?", data.Id).Delete(&model.AdminUser{})
tx := h.db.Begin()
res := tx.Where("id = ?", data.Id).Delete(&model.AdminUser{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
res2 := tx.Where("admin_id = ?", data.Id).Delete(&model.AdminUserRole{})
if res2.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
tx.Commit()
}
resp.SUCCESS(c)
}

View File

@ -68,6 +68,11 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
}
func (h *ApiKeyHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var items []model.ApiKey
var keys = make([]vo.ApiKey, 0)
res := h.db.Find(&items)
@ -109,10 +114,15 @@ func (h *ApiKeyHandler) Set(c *gin.Context) {
}
func (h *ApiKeyHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id > 0 {
res := h.db.Where("id = ?", id).Delete(&model.ApiKey{})
var data struct {
Id uint
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
res := h.db.Where("id = ?", data.Id).Delete(&model.ApiKey{})
if res.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return

View File

@ -35,6 +35,11 @@ type chatItemVo struct {
}
func (h *ChatHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var data struct {
Title string `json:"title"`
UserId uint `json:"user_id"`
@ -212,9 +217,14 @@ func (h *ChatHandler) History(c *gin.Context) {
// RemoveChat 删除对话
func (h *ChatHandler) RemoveChat(c *gin.Context) {
chatId := h.GetTrim(c, "chat_id")
if chatId == "" {
resp.ERROR(c, "请传入 ChatId")
return
}
tx := h.db.Begin()
// 删除聊天记录
res := tx.Unscoped().Where("chat_id = ?", chatId).Delete(&model.ChatMessage{})
res := tx.Unscoped().Debug().Where("chat_id = ?", chatId).Delete(&model.ChatMessage{})
if res.Error != nil {
resp.ERROR(c, "failed to remove chat message")
return
@ -235,7 +245,7 @@ func (h *ChatHandler) RemoveChat(c *gin.Context) {
// RemoveMessage 删除聊天记录
func (h *ChatHandler) RemoveMessage(c *gin.Context) {
id := h.GetInt(c, "id", 0)
tx := h.db.Unscoped().Delete(&model.ChatMessage{}, id)
tx := h.db.Unscoped().Where("id = ?", id).Delete(&model.ChatMessage{})
if tx.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return

View File

@ -72,6 +72,11 @@ func (h *ChatModelHandler) Save(c *gin.Context) {
// List 模型列表
func (h *ChatModelHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
session := h.db.Session(&gorm.Session{})
enable := h.GetBool(c, "enable")
if enable {
@ -140,10 +145,15 @@ func (h *ChatModelHandler) Sort(c *gin.Context) {
}
func (h *ChatModelHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id > 0 {
res := h.db.Where("id = ?", id).Delete(&model.ChatModel{})
var data struct {
Id uint
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
res := h.db.Where("id = ?", data.Id).Delete(&model.ChatModel{})
if res.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return

View File

@ -53,6 +53,11 @@ func (h *ChatRoleHandler) Save(c *gin.Context) {
}
func (h *ChatRoleHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var items []model.ChatRole
var roles = make([]vo.ChatRole, 0)
res := h.db.Order("sort_num ASC").Find(&items)
@ -119,13 +124,18 @@ func (h *ChatRoleHandler) Set(c *gin.Context) {
}
func (h *ChatRoleHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id <= 0 {
var data struct {
Id uint
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
res := h.db.Where("id = ?", id).Delete(&model.ChatRole{})
if data.Id <= 0 {
resp.ERROR(c, types.InvalidArgs)
return
}
res := h.db.Where("id = ?", data.Id).Delete(&model.ChatRole{})
if res.Error != nil {
resp.ERROR(c, "删除失败!")
return

View File

@ -71,6 +71,11 @@ func (h *ConfigHandler) Update(c *gin.Context) {
// Get 获取指定的系统配置
func (h *ConfigHandler) Get(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
key := c.Query("key")
var config model.Config
res := h.db.Where("marker", key).First(&config)

View File

@ -74,6 +74,11 @@ func (h *FunctionHandler) Set(c *gin.Context) {
}
func (h *FunctionHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var items []model.Function
res := h.db.Find(&items)
if res.Error != nil {

View File

@ -25,6 +25,11 @@ func NewOrderHandler(app *core.AppServer, db *gorm.DB) *OrderHandler {
}
func (h *OrderHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var data struct {
OrderNo string `json:"order_no"`
Status int `json:"status"`

View File

@ -70,6 +70,11 @@ func (h *ProductHandler) Save(c *gin.Context) {
// List 模型列表
func (h *ProductHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
session := h.db.Session(&gorm.Session{})
enable := h.GetBool(c, "enable")
if enable {

View File

@ -2,6 +2,7 @@ package admin
import (
"chatplus/core"
"chatplus/core/types"
"chatplus/handler"
"chatplus/store/model"
"chatplus/store/vo"
@ -23,6 +24,11 @@ func NewRewardHandler(app *core.AppServer, db *gorm.DB) *RewardHandler {
}
func (h *RewardHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
var items []model.Reward
res := h.db.Order("id DESC").Find(&items)
var rewards = make([]vo.Reward, 0)
@ -57,10 +63,15 @@ func (h *RewardHandler) List(c *gin.Context) {
}
func (h *RewardHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id > 0 {
res := h.db.Where("id = ?", id).Delete(&model.Reward{})
var data struct {
Id uint
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
res := h.db.Where("id = ?", data.Id).Delete(&model.Reward{})
if res.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return

View File

@ -27,6 +27,11 @@ func NewUserHandler(app *core.AppServer, db *gorm.DB) *UserHandler {
// List 用户列表
func (h *UserHandler) List(c *gin.Context) {
if err := utils.CheckPermission(c, h.db); err != nil {
resp.NotPermission(c)
return
}
page := h.GetInt(c, "page", 1)
pageSize := h.GetInt(c, "page_size", 20)
username := h.GetTrim(c, "username")
@ -154,30 +159,36 @@ func (h *UserHandler) ResetPass(c *gin.Context) {
}
func (h *UserHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id > 0 {
var data struct {
Id uint
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
if data.Id > 0 {
tx := h.db.Begin()
res := h.db.Where("id = ?", id).Delete(&model.User{})
res := h.db.Where("id = ?", data.Id).Delete(&model.User{})
if res.Error != nil {
resp.ERROR(c, "删除失败")
return
}
// 删除聊天记录
res = h.db.Where("user_id = ?", id).Delete(&model.ChatItem{})
res = h.db.Where("user_id = ?", data.Id).Delete(&model.ChatItem{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
// 删除聊天历史记录
res = h.db.Where("user_id = ?", id).Delete(&model.ChatMessage{})
res = h.db.Where("user_id = ?", data.Id).Delete(&model.ChatMessage{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")
return
}
// 删除登录日志
res = h.db.Where("user_id = ?", id).Delete(&model.UserLoginLog{})
res = h.db.Where("user_id = ?", data.Id).Delete(&model.UserLoginLog{})
if res.Error != nil {
tx.Rollback()
resp.ERROR(c, "删除失败")

View File

@ -0,0 +1,42 @@
package handler
import (
"chatplus/core"
"chatplus/store/model"
"chatplus/utils"
"chatplus/utils/resp"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
type ConfigHandler struct {
BaseHandler
db *gorm.DB
}
func NewConfigHandler(app *core.AppServer, db *gorm.DB) *ConfigHandler {
h := ConfigHandler{db: db}
h.App = app
return &h
}
// Get 获取指定的系统配置
func (h *ConfigHandler) Get(c *gin.Context) {
key := c.Query("key")
var config model.Config
res := h.db.Where("marker", key).First(&config)
if res.Error != nil {
resp.ERROR(c, res.Error.Error())
return
}
var value map[string]interface{}
err := utils.JsonDecode(config.Config, &value)
if err != nil {
resp.ERROR(c, err.Error())
return
}
resp.SUCCESS(c, value)
}

View File

@ -125,6 +125,7 @@ func main() {
fx.Provide(handler.NewPaymentHandler),
fx.Provide(handler.NewOrderHandler),
fx.Provide(handler.NewProductHandler),
fx.Provide(handler.NewConfigHandler),
fx.Provide(admin.NewConfigHandler),
fx.Provide(admin.NewAdminHandler),
@ -255,6 +256,10 @@ func main() {
group.POST("remove", h.Remove)
group.POST("publish", h.Publish)
}),
fx.Invoke(func(s *core.AppServer, h *handler.ConfigHandler) {
group := s.Engine.Group("/api/config/")
group.GET("get", h.Get)
}),
// 管理后台控制器
fx.Invoke(func(s *core.AppServer, h *admin.ConfigHandler) {
@ -273,13 +278,13 @@ func main() {
group.POST("save", h.Save)
group.GET("list", h.List)
group.POST("set", h.Set)
group.GET("remove", h.Remove)
group.POST("remove", h.Remove)
}),
fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) {
group := s.Engine.Group("/api/admin/user/")
group.GET("list", h.List)
group.POST("save", h.Save)
group.GET("remove", h.Remove)
group.POST("remove", h.Remove)
group.GET("loginLog", h.LoginLog)
group.POST("resetPass", h.ResetPass)
}),
@ -289,12 +294,12 @@ func main() {
group.POST("save", h.Save)
group.POST("sort", h.Sort)
group.POST("set", h.Set)
group.GET("remove", h.Remove)
group.POST("remove", h.Remove)
}),
fx.Invoke(func(s *core.AppServer, h *admin.RewardHandler) {
group := s.Engine.Group("/api/admin/reward/")
group.GET("list", h.List)
group.GET("remove", h.Remove)
group.POST("remove", h.Remove)
}),
fx.Invoke(func(s *core.AppServer, h *admin.DashboardHandler) {
group := s.Engine.Group("/api/admin/dashboard/")
@ -310,7 +315,7 @@ func main() {
group.GET("list", h.List)
group.POST("set", h.Set)
group.POST("sort", h.Sort)
group.GET("remove", h.Remove)
group.POST("remove", h.Remove)
}),
fx.Invoke(func(s *core.AppServer, h *handler.PaymentHandler) {
group := s.Engine.Group("/api/payment/")
@ -392,6 +397,22 @@ func main() {
group.POST("remove", h.Remove)
group.POST("resetPass", h.ResetPass)
}),
// 权限
fx.Provide(admin.NewSysPermissionHandler),
fx.Invoke(func(s *core.AppServer, h *admin.SysPermissionHandler) {
group := s.Engine.Group("/api/admin/sysPermission/")
group.GET("list", h.List)
group.POST("save", h.Save)
group.POST("remove", h.Remove)
}),
// 角色
fx.Provide(admin.NewSysRoleHandler),
fx.Invoke(func(s *core.AppServer, h *admin.SysRoleHandler) {
group := s.Engine.Group("/api/admin/sysRole/")
group.GET("list", h.List)
group.POST("save", h.Save)
group.POST("remove", h.Remove)
}),
fx.Provide(handler.NewFunctionHandler),
fx.Invoke(func(s *core.AppServer, h *handler.FunctionHandler) {

View File

@ -0,0 +1,13 @@
package model
import "time"
type AdminPermission struct {
Id int `gorm:"primarykey;column:id"`
Name string
Slug string
Sort int
Pid int
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -0,0 +1,11 @@
package model
import "time"
type AdminRole struct {
Id int `gorm:"primarykey;column:id"`
Name string
Description string
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -0,0 +1,11 @@
package model
import "time"
type AdminRolePermission struct {
Id int `gorm:"primarykey;column:id"`
RoleId int
PermissionId int
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -0,0 +1,11 @@
package model
import "time"
type AdminUserRole struct {
Id int `gorm:"primarykey;column:id"`
AdminId uint
RoleId int
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -0,0 +1,10 @@
package vo
type AdminPermission struct {
Id int `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
Sort int `json:"sort"`
Pid int `json:"pid"`
Children []AdminPermission `json:"children"`
}

View File

@ -0,0 +1,9 @@
package vo
type AdminRole struct {
Id int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Permissions interface{} `json:"permissions"`
CreatedAt string `json:"created_at"`
}

View File

@ -2,8 +2,9 @@ package vo
type AdminUser struct {
BaseVo
Username string `json:"username"`
Status bool `json:"status"` // 当前状态
LastLoginAt int64 `json:"last_login_at"` // 最后登录时间
LastLoginIp string `json:"last_login_ip"` // 最后登录 IP
Username string `json:"username"`
Status bool `json:"status"` // 当前状态
LastLoginAt int64 `json:"last_login_at"` // 最后登录时间
LastLoginIp string `json:"last_login_ip"` // 最后登录 IP
RoleIds interface{} `json:"role_ids"` //角色ids
}

36
api/utils/permission.go Normal file
View File

@ -0,0 +1,36 @@
package utils
import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// CheckPermission Todo: 放在缓存
// CheckPermission 检查权限
func CheckPermission(c *gin.Context, db *gorm.DB) error {
return nil
//u, err := url.Parse(c.Request.RequestURI)
//if err != nil {
// panic(err)
//}
//slug := strings.Replace(u.Path, "/", "_", -1)[1:]
//
//// 用户名
//userName, _ := c.Get(types.LoginUserID)
//
//var manager model.AdminUser
//db.Table("chatgpt_admin_users").Select("chatgpt_admin_users.id").Where("username = ?", userName).First(&manager)
//
//// 超级管理员不判断
//if manager.Id == 1 {
// return nil
//}
//var roleIds []int
//var count int64
//db.Raw("SELECT `chatgpt_admin_user_roles`.role_id FROM `chatgpt_admin_users` LEFT JOIN `chatgpt_admin_user_roles` ON ( `chatgpt_admin_users`.id = `chatgpt_admin_user_roles`.admin_id ) WHERE `chatgpt_admin_users`.id = ?", manager.Id).Find(&roleIds)
//db.Raw("SELECT `chatgpt_admin_permissions`.slug FROM `chatgpt_admin_permissions` LEFT JOIN `chatgpt_admin_role_permissions` ON (`chatgpt_admin_permissions`.id = `chatgpt_admin_role_permissions`.permission_id) WHERE `chatgpt_admin_role_permissions`.role_id IN ? and `chatgpt_admin_permissions`.slug = ? ", roleIds, slug).Count(&count)
//if count > 0 {
// return nil
//}
//return fmt.Errorf("没有权限")
}

View File

@ -34,3 +34,11 @@ func NotAuth(c *gin.Context, messages ...string) {
c.JSON(http.StatusOK, types.BizVo{Code: types.NotAuthorized, Message: "Not Authorized"})
}
}
func NotPermission(c *gin.Context, messages ...string) {
if messages != nil {
c.JSON(http.StatusOK, types.BizVo{Code: types.NotPermission, Message: messages[0]})
} else {
c.JSON(http.StatusOK, types.BizVo{Code: types.NotPermission, Message: "Not Permission"})
}
}

View File

@ -60,3 +60,73 @@ CREATE TABLE `chatgpt_admin_users` (
) ENGINE = InnoDB AUTO_INCREMENT = 108 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '系统用户' ROW_FORMAT = Dynamic;
INSERT INTO `chatgpt_admin_users` VALUES (1, 'admin', '6d17e80c87d209efb84ca4b2e0824f549d09fac8b2e1cc698de5bb5e1d75dfd0', 'mmrql75o', 1, 1710238055, '172.22.11.29', '2024-03-11 16:30:20', '2024-03-12 18:07:35');
CREATE TABLE `chatgpt_admin_user_roles` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`admin_id` int NOT NULL COMMENT '用户ID',
`role_id` int NOT NULL COMMENT '角色ID',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_admin_id` (`admin_id`),
KEY `idx_role_id` (`role_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色表';
CREATE TABLE `chatgpt_admin_roles` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '角色名称',
`description` varchar(255) DEFAULT '' COMMENT '描述',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色表';
CREATE TABLE `chatgpt_admin_role_permissions` (
`id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
`role_id` int NOT NULL COMMENT '角色ID',
`permission_id` int NOT NULL COMMENT '权限ID',
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_role_id` (`role_id`),
KEY `idx_permission_id` (`permission_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户角色表';
CREATE TABLE `chatgpt_admin_permissions` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '权限名称(模块@菜单名称@方法)',
`slug` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '前端权限模块',
`sort` int NOT NULL DEFAULT '1' COMMENT '排序(越大排越靠前)',
`pid` int NOT NULL DEFAULT '0' COMMENT '父节点',
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_slug` (`slug`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='权限表';
INSERT INTO `chatgpt_admin_permissions` VALUES (6, '用户管理', '', 2, 0, '2024-03-14 10:47:41', '2024-03-14 10:48:41');
INSERT INTO `chatgpt_admin_permissions` VALUES (7, '列表', 'api_admin_user_list', 1, 6, '2024-03-14 10:53:36', '2024-03-14 10:53:36');
INSERT INTO `chatgpt_admin_permissions` VALUES (10, '角色模型', '', 3, 0, '2024-03-14 11:02:45', '2024-03-14 11:02:45');
INSERT INTO `chatgpt_admin_permissions` VALUES (11, '列表', 'api_admin_role_list', 1, 10, '2024-03-14 11:03:43', '2024-03-14 11:03:45');
INSERT INTO `chatgpt_admin_permissions` VALUES (12, '语言模型', '', 4, 0, '2024-03-14 11:05:00', '2024-03-14 11:05:00');
INSERT INTO `chatgpt_admin_permissions` VALUES (13, '列表', 'api_admin_model_list', 1, 12, '2024-03-14 11:05:36', '2024-03-14 11:05:36');
INSERT INTO `chatgpt_admin_permissions` VALUES (14, '充值产品', '', 5, 0, '2024-03-14 11:16:37', '2024-03-14 11:16:37');
INSERT INTO `chatgpt_admin_permissions` VALUES (15, '列表', 'api_admin_product_list', 1, 14, '2024-03-14 11:17:05', '2024-03-14 11:17:05');
INSERT INTO `chatgpt_admin_permissions` VALUES (16, 'APIKEY', '', 6, 0, '2024-03-14 11:17:16', '2024-03-14 11:17:16');
INSERT INTO `chatgpt_admin_permissions` VALUES (17, '列表', 'api_admin_apikey_list', 1, 16, '2024-03-14 11:17:53', '2024-03-14 11:18:02');
INSERT INTO `chatgpt_admin_permissions` VALUES (18, '充值订单', '', 7, 0, '2024-03-14 11:18:20', '2024-03-14 11:18:20');
INSERT INTO `chatgpt_admin_permissions` VALUES (19, '列表', 'api_admin_order_list', 1, 18, '2024-03-14 11:18:44', '2024-03-14 11:18:44');
INSERT INTO `chatgpt_admin_permissions` VALUES (20, '众筹管理', '', 8, 0, '2024-03-14 11:18:59', '2024-03-14 11:18:59');
INSERT INTO `chatgpt_admin_permissions` VALUES (21, '列表', 'api_admin_reward_list', 1, 20, '2024-03-14 11:19:28', '2024-03-14 11:19:28');
INSERT INTO `chatgpt_admin_permissions` VALUES (22, '函数管理', '', 9, 0, '2024-03-14 11:19:42', '2024-03-14 11:19:42');
INSERT INTO `chatgpt_admin_permissions` VALUES (23, '列表', 'api_admin_function_list', 1, 22, '2024-03-14 11:20:07', '2024-03-14 11:20:07');
INSERT INTO `chatgpt_admin_permissions` VALUES (24, '对话管理', '', 10, 0, '2024-03-14 11:20:30', '2024-03-14 11:20:30');
INSERT INTO `chatgpt_admin_permissions` VALUES (25, '列表', 'api_admin_chat_list', 1, 24, '2024-03-14 11:20:51', '2024-03-14 11:20:51');
INSERT INTO `chatgpt_admin_permissions` VALUES (26, '网站设置', '', 11, 0, '2024-03-14 11:21:05', '2024-03-14 11:21:05');
INSERT INTO `chatgpt_admin_permissions` VALUES (27, '配置列表', 'api_admin_config_get', 1, 26, '2024-03-14 11:21:53', '2024-03-14 11:21:53');
INSERT INTO `chatgpt_admin_permissions` VALUES (28, '系统配置', '', 12, 0, '2024-03-14 11:22:07', '2024-03-14 15:28:22');
INSERT INTO `chatgpt_admin_permissions` VALUES (29, '列表', 'api_admin_sysUser_list', 1, 30, '2024-03-14 11:22:36', '2024-03-14 15:28:52');
INSERT INTO `chatgpt_admin_permissions` VALUES (30, '系统管理员', '', 1, 28, '2024-03-14 15:28:43', '2024-03-14 15:28:43');
INSERT INTO `chatgpt_admin_permissions` VALUES (31, '权限配置', '', 2, 28, '2024-03-14 15:29:05', '2024-03-14 15:29:05');
INSERT INTO `chatgpt_admin_permissions` VALUES (32, '角色配置', '', 3, 28, '2024-03-14 15:29:15', '2024-03-14 15:29:15');
INSERT INTO `chatgpt_admin_permissions` VALUES (33, '列表', 'api_admin_sysPermission_list', 1, 31, '2024-03-14 15:29:52', '2024-03-14 15:29:52');
INSERT INTO `chatgpt_admin_permissions` VALUES (34, '列表', 'api_admin_sysRole_list', 1, 32, '2024-03-14 15:30:21', '2024-03-14 15:30:21');

View File

@ -10,10 +10,6 @@ WeChatBot = false
SecretKey = "azyehq3ivunjhbntz78isj00i4hz2mt9xtddysfucxakadq4qbfrt0b7q3lnvg80" # 注意:这个是 JWT Token 授权密钥,生产环境请务必更换
MaxAge = 86400
[Manager]
Username = "admin"
Password = "admin123" # 如果是生产环境的话,这里管理员的密码记得修改
[Redis] # redis 配置信息
Host = "localhost"
Port = 6379

View File

@ -1,12 +1,12 @@
{
"name": "gpt-vue",
"name": "chatgpt-plus",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"clear": "rimraf node_modules projects/vue-admin/node_modules",
"dev": "pnpm --filter=@gpt-vue-projects/* run dev",
"build": "pnpm --filter=@gpt-vue-projects/* run build"
"clear": "rimraf node_modules rimraf -g */node_modules rimraf -g */*/node_modules",
"dev": "pnpm --filter=@chatgpt-plus-projects/* run dev",
"build": "pnpm --filter=@chatgpt-plus-projects/* run build"
},
"keywords": [],
"author": "",

View File

@ -1,5 +1,5 @@
{
"name": "@gpt-vue/packages",
"name": "@chatgpt-plus/packages",
"version": "1.0.0",
"description": "",
"main": "index.js",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">

View File

@ -1,5 +1,5 @@
{
"name": "@gpt-vue-projects/vue-admin",
"name": "@chatgpt-plus-projects/admin",
"version": "0.0.0",
"private": true,
"type": "module",
@ -13,7 +13,7 @@
},
"dependencies": {
"@arco-design/web-vue": "^2.54.6",
"@gpt-vue/packages": "workspace:^1.0.0",
"@chatgpt-plus/packages": "workspace:^1.0.0",
"echarts": "^5.5.0",
"md-editor-v3": "^2.2.1",
"pinia": "^2.1.7",

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 276 B

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { computed } from "vue";
import { Message, type SwitchInstance } from "@arco-design/web-vue";
import type { BaseResponse } from "@gpt-vue/packages/type";
import type { BaseResponse } from "@chatgpt-plus/packages/type";
type OriginProps = SwitchInstance["$props"];

View File

@ -0,0 +1,10 @@
<script lang="ts" setup>
import { hasPermission } from "@/directives/permission";
defineProps<{
permission: string | string[] | true;
}>();
</script>
<template>
<slot v-if="hasPermission(permission)" />
<slot v-else name="none" />
</template>

View File

@ -38,30 +38,32 @@ onActivated(handleSearch);
<template>
<div class="search-table">
<div class="search-table-header">
<div>
<slot name="header-title">{{ props.headerTitle }}</slot>
</div>
<div class="header-option">
<slot name="header-option" :formData="formData" :reload="handleSearch" />
<div class="search-table-header-option">
<div>
<slot name="header-title">{{ props.headerTitle }}</slot>
</div>
<div class="header-option">
<slot name="header-option" :formData="formData" :reload="handleSearch" />
</div>
</div>
<FormSection
v-model="formData"
:columns="columns"
:submitting="tableConfig.loading as boolean"
@request="handleSearch"
>
<template v-for="slot in Object.keys($slots)" #[slot]="config">
<slot :name="slot" v-bind="{ ...config, reload: handleSearch }" />
</template>
</FormSection>
</div>
<FormSection
v-model="formData"
:columns="columns"
:submitting="tableConfig.loading as boolean"
@request="handleSearch"
>
<template v-for="slot in Object.keys($slots)" #[slot]="config">
<slot :name="slot" v-bind="{ ...config, reload: handleSearch }" />
</template>
</FormSection>
<div ref="tableContainerRef" class="search-table-container">
<ATable
v-bind="{
...$attrs,
...tableConfig,
...props,
scroll: useTableScroll(_columns, tableContainerRef as HTMLElement),
scroll: useTableScroll(_columns),
columns: _columns,
}"
>
@ -74,14 +76,18 @@ onActivated(handleSearch);
</template>
<style scoped>
.search-table {
display: flex;
flex-direction: column;
position: relative;
height: 100%;
}
.search-table-container {
flex: 1;
position: relative;
z-index: 1;
}
.search-table-header {
background: #fff;
z-index: 2;
}
.search-table-header-option {
display: flex;
align-items: center;
justify-content: space-between;

View File

@ -1,6 +1,6 @@
import { computed, onMounted, reactive, unref, type Ref } from "vue";
import type { TableInstance } from "@arco-design/web-vue";
import type { BaseResponse, ListResponse } from "@gpt-vue/packages/type";
import type { BaseResponse, ListResponse } from "@chatgpt-plus/packages/type";
export type TableOriginalProps = TableInstance["$props"];
export type TableRequest<T extends Record<string, unknown>> = (params?: any) => Promise<BaseResponse<ListResponse<T>>>

View File

@ -1,20 +1,11 @@
import type { TableColumnData } from "@arco-design/web-vue";
import type { SearchTableColumns, SearchColumns } from "./type";
export function useTableXScroll(columns: TableColumnData[]) {
return columns.reduce((prev, curr) => {
const width = curr.width ?? 150;
return prev + width;
}, 0);
}
export function useTableScroll(columns: SearchTableColumns[], container?: HTMLElement) {
export function useTableScroll(columns: SearchTableColumns[]) {
const x = columns.reduce((prev, curr) => {
const width = curr.hideInTable ? 0 : curr.width ?? 150;
return prev + width;
}, 0);
const y = container?.clientHeight ?? undefined;
return { x, y };
return { x };
}
export function getDefaultFormData(columns: SearchTableColumns[]) {

View File

@ -1,6 +1,6 @@
import { computed, onMounted, reactive, unref } from "vue";
import type { TableInstance } from "@arco-design/web-vue";
import type { BaseResponse } from "@gpt-vue/packages/type";
import type { BaseResponse } from "@chatgpt-plus/packages/type";
export type TableOriginalProps = TableInstance["$props"];
export type TableRequest<T extends Record<string, unknown>> = (

View File

@ -0,0 +1,64 @@
<script lang="ts">
import { defineComponent, h } from "vue";
import type { Component, PropType } from "vue";
import type { RouteRecordRaw } from "vue-router";
import { SubMenu, MenuItem } from "@arco-design/web-vue";
const CustomMenuItem: Component = defineComponent({
props: {
tree: {
type: Array as PropType<RouteRecordRaw[]>,
default: () => [],
},
},
setup: (props) => {
return () =>
props.tree?.map((item) => {
const _icon = item.meta?.icon ? h(item.meta.icon) : undefined;
const hasChildren = Array.isArray(item.children) && item.children.length;
if (hasChildren) {
return h(
SubMenu,
{ title: item.meta.title, key: item.name },
{
default: () => h(CustomMenuItem, { tree: item.children }),
icon: () => _icon,
}
);
}
return h(
MenuItem,
{ key: item.name },
{ default: () => item.meta.title, icon: () => _icon }
);
});
},
});
</script>
<script lang="ts" setup>
import { computed } from "vue";
import { useRoute } from "vue-router";
import router from "@/router";
import menu from "@/router/menu";
import { hasPermission } from "@/directives/permission";
defineProps({
width: {
type: [Number, String],
default: 200,
},
});
const route = useRoute();
const goto = (name: string) => router.push({ name });
const selectedKeys = computed(() => [route.name]);
const showMenu = computed(() => menu.filter((item: any) => hasPermission(item.meta?.permission)));
</script>
<template>
<ALayoutSider :style="{ width, height: '100%' }">
<AMenu :selectedKeys="selectedKeys" auto-open-selected @menu-item-click="goto">
<CustomMenuItem :tree="showMenu" />
</AMenu>
</ALayoutSider>
</template>

View File

@ -1,7 +1,7 @@
import usePopup, { type Config } from "./usePopup";
import { Message } from "@arco-design/web-vue";
import type { Component } from "vue";
import type { BaseResponse } from "@gpt-vue/packages/type";
import type { BaseResponse } from "@chatgpt-plus/packages/type";
interface Arg {
reload?: () => void;
record?: Record<string, any>;

View File

@ -1,6 +1,6 @@
import { ref } from "vue";
import type { Ref } from "vue";
import type { BaseResponse } from "@gpt-vue/packages/type";
import type { BaseResponse } from "@chatgpt-plus/packages/type";
type Request<T> = (params?: any) => Promise<BaseResponse<T>>
function useRequest<T>(request: Request<T>) {

View File

@ -1,6 +1,5 @@
import { ref, reactive, unref } from "vue";
import { Message } from "@arco-design/web-vue";
import type { BaseResponse } from "@gpt-vue/packages/type";
import type { BaseResponse } from "@chatgpt-plus/packages/type";
function useSubmit<T extends Record<string, any> = Record<string, any>, R = any>(defaultData?: T) {
const formRef = ref();
const formData = reactive<T | Record<string, any>>({ ...defaultData ?? {} });
@ -10,14 +9,18 @@ function useSubmit<T extends Record<string, any> = Record<string, any>, R = any>
submitting.value = true;
try {
const hasError = await formRef.value?.validate();
if (!hasError) {
const { data, message } = await api({ ...formData ?? {}, ...unref(params) });
message && Message.success(message);
return Promise.resolve({ formData, data });
if (hasError) {
return Promise.reject({ validateErrors: hasError });
}
return Promise.reject(false);
const { data, code, message } = await api({ ...formData ?? {}, ...unref(params) });
if (code) {
return Promise.reject({ requestErrors: message })
}
return Promise.resolve({ formData, data });
} catch (err) {
return Promise.reject(err);
return Promise.reject({ errors: err });
} finally {
submitting.value = false;
}

View File

@ -0,0 +1,32 @@
import { useAuthStore } from "@/stores/auth";
// 判断操作权限
export function hasPermission(permissionTag: string | string[] | boolean) {
const authStore = useAuthStore();
const { is_super_admin, permissions = [] } = authStore;
if (is_super_admin) {
return true;
}
if (Array.isArray(permissionTag)) {
return permissionTag.every((tag) => permissions.includes(tag));
}
if (typeof permissionTag === "string") {
return permissions.includes(permissionTag);
}
return permissionTag;
}
function checkPermission(el, binding) {
if (!hasPermission(binding.value)) {
el.parentNode && el.parentNode.removeChild(el);
}
}
export const permission = {
mounted(el, binding) {
checkPermission(el, binding);
},
updated(el, binding) {
checkPermission(el, binding);
},
};

View File

@ -1,25 +1,33 @@
import router from "@/router";
import { Notification } from "@arco-design/web-vue";
import createInstance from "@gpt-vue/packages/request"
import type { BaseResponse } from "@gpt-vue/packages/type";
import createInstance from "@chatgpt-plus/packages/request"
import type { BaseResponse } from "@chatgpt-plus/packages/type";
export const uploadUrl = import.meta.env.VITE_PROXY_BASE_URL + "/api/admin/upload";
export const instance = createInstance(import.meta.env.VITE_PROXY_BASE_URL)
instance.interceptors.request.use((config) => {
config.headers[__AUTH_KEY] = localStorage.getItem(__AUTH_KEY);
config.headers["Authorization"] = localStorage.getItem(__AUTH_KEY);
const TOKEN = JSON.parse(localStorage.getItem(__AUTH_KEY))?.token
config.headers[__AUTH_KEY] = TOKEN;
config.headers["Authorization"] = TOKEN;
return config;
});
instance.interceptors.response.use(
(response) => {
const { data }: { data: BaseResponse<unknown> } = response
if (data && typeof data === "object" && data.code !== 0) {
if (data.code === 400) {
localStorage.removeItem(__AUTH_KEY);
router.push({ name: "Login" })
if (data && typeof data === "object" && data.code > 0) {
switch (data.code) {
case 400: {
localStorage.removeItem(__AUTH_KEY);
router.push({ name: "Login" })
break;
}
case 403: {
router.replace({ name: "403" })
break;
}
}
Notification.error(data.message ?? '未知错误')
}

View File

@ -1,9 +1,6 @@
import http from "@/http/config";
export const userLogin = (data: {
username: string;
password: string;
}) => {
export const userLogin = (data) => {
return http({
url: "/api/admin/login",
method: "post",
@ -26,7 +23,7 @@ export const getSession = () => {
};
export const loginLog = (params?: Record<string, unknown>) => {
export const loginLog = (params) => {
return http({
url: "/api/admin/user/loginLog",
method: "get",

View File

@ -3,6 +3,8 @@ import { createPinia } from "pinia";
import ArcoVue from "@arco-design/web-vue";
import ArcoVueIcon from "@arco-design/web-vue/es/icon";
import "@arco-design/web-vue/dist/arco.css";
import PermissionRender from "@/components/PermissionRender.vue";
import { permission } from "@/directives/permission";
import App from "./App.vue";
import router from "./router";
@ -14,6 +16,9 @@ app.use(router);
app.use(ArcoVue);
app.use(ArcoVueIcon);
app.component("PermissionRender", PermissionRender);
app.directive("permission", permission);
app.mount("#app");
app.config.warnHandler = (msg, vm, trace) => {
if (msg.includes('Invalid prop name: "key" is a reserved property.')) {

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