From 705ccb4c35d1974f428bb722502e85a68ccfe0d5 Mon Sep 17 00:00:00 2001 From: ch3nnn Date: Sat, 1 Feb 2025 00:17:56 +0800 Subject: [PATCH] feat(site): add sort functionality to sites - Add sort field to Site struct and StSite model - Implement sorting logic in site list and export functions- Update site creation and update functions to handle sort value - Modify database queries to support sorting by sort field - Update UI to display and allow editing of site sort order --- api/v1/site.go | 22 +++++++++-------- internal/dal/model/st_site.gen.go | 3 ++- internal/dal/query/st_site.gen.go | 6 ++++- internal/dal/repository/st_site.gen.go | 7 ++++++ internal/dal/repository/st_site.go | 6 ++--- internal/service/index/Index.go | 4 ++++ internal/service/site/batchcreate.go | 1 + internal/service/site/export.go | 7 +++++- internal/service/site/list.go | 8 ++++++- internal/service/site/update.go | 3 +++ web/templates/site/site_list.html | 33 ++++++++++++++------------ 11 files changed, 68 insertions(+), 32 deletions(-) diff --git a/api/v1/site.go b/api/v1/site.go index cee8d87..6742a53 100644 --- a/api/v1/site.go +++ b/api/v1/site.go @@ -12,16 +12,17 @@ import ( ) type Site struct { - Id int `json:"id"` // ID - Thumb string `json:"thumb"` // 网站 logo - Title string `json:"title"` // 名称简介 - Url string `json:"url"` // 链接 - Category string `json:"category"` // 分类 - CategoryId int `json:"category_id"` // 分类id - Description string `json:"description"` // 描述 - IsUsed bool `json:"is_used"` // 是否启用 - CreatedAt string `json:"created_at"` // 创建时间 - UpdatedAt string `json:"updated_at"` // 更新时间 + Id int `json:"id"` // ID + Thumb string `json:"thumb"` // 网站 logo + Title string `json:"title"` // 名称简介 + Url string `json:"url"` // 链接 + Category string `json:"category"` // 分类 + CategoryId int `json:"category_id"` // 分类id + Description string `json:"description"` // 描述 + IsUsed bool `json:"is_used"` // 是否启用 + Sort int `json:"sort" form:"sort"` // 排序 + CreatedAt string `json:"created_at"` // 创建时间 + UpdatedAt string `json:"updated_at"` // 更新时间 } type ( @@ -78,6 +79,7 @@ type ( Description string `json:"description" form:"description"` // 描述 IsUsed *bool `json:"is_used" form:"is_used"` // 是否启用 File *multipart.FileHeader `json:"file"` // 上传 logo 图片 + Sort int `json:"sort" form:"sort"` // 排序 } SiteUpdateResp struct { diff --git a/internal/dal/model/st_site.gen.go b/internal/dal/model/st_site.gen.go index e811186..46d29ab 100644 --- a/internal/dal/model/st_site.gen.go +++ b/internal/dal/model/st_site.gen.go @@ -12,7 +12,7 @@ const TableNameStSite = "st_site" // StSite mapped from table type StSite struct { - ID int `gorm:"column:id;type:INTEGER" json:"id"` + ID int `gorm:"column:id;type:INTEGER;primaryKey" json:"id"` CategoryID int `gorm:"column:category_id;type:int(11)" json:"category_id"` Title string `gorm:"column:title;type:varchar(50)" json:"title"` Icon string `gorm:"column:icon;type:text" json:"icon"` @@ -22,6 +22,7 @@ type StSite struct { CreatedAt *time.Time `gorm:"column:created_at;type:datetime;not null;default:CURRENT_TIMESTAMP not null" json:"created_at"` UpdatedAt *time.Time `gorm:"column:updated_at;type:datetime;not null;default:CURRENT_TIMESTAMP not null" json:"updated_at"` DeletedAt *time.Time `gorm:"column:deleted_at;type:datetime" json:"deleted_at"` + Sort int `gorm:"column:sort;type:int(11)" json:"sort"` } // TableName StSite's table name diff --git a/internal/dal/query/st_site.gen.go b/internal/dal/query/st_site.gen.go index 50f172f..ae3c0ae 100644 --- a/internal/dal/query/st_site.gen.go +++ b/internal/dal/query/st_site.gen.go @@ -37,6 +37,7 @@ func newStSite(db *gorm.DB, opts ...gen.DOOption) stSite { _stSite.CreatedAt = field.NewTime(tableName, "created_at") _stSite.UpdatedAt = field.NewTime(tableName, "updated_at") _stSite.DeletedAt = field.NewTime(tableName, "deleted_at") + _stSite.Sort = field.NewInt(tableName, "sort") _stSite.fillFieldMap() @@ -57,6 +58,7 @@ type stSite struct { CreatedAt field.Time UpdatedAt field.Time DeletedAt field.Time + Sort field.Int fieldMap map[string]field.Expr } @@ -83,6 +85,7 @@ func (s *stSite) updateTableName(table string) *stSite { s.CreatedAt = field.NewTime(table, "created_at") s.UpdatedAt = field.NewTime(table, "updated_at") s.DeletedAt = field.NewTime(table, "deleted_at") + s.Sort = field.NewInt(table, "sort") s.fillFieldMap() @@ -107,7 +110,7 @@ func (s *stSite) GetFieldByName(fieldName string) (field.OrderExpr, bool) { } func (s *stSite) fillFieldMap() { - s.fieldMap = make(map[string]field.Expr, 10) + s.fieldMap = make(map[string]field.Expr, 11) s.fieldMap["id"] = s.ID s.fieldMap["category_id"] = s.CategoryID s.fieldMap["title"] = s.Title @@ -118,6 +121,7 @@ func (s *stSite) fillFieldMap() { s.fieldMap["created_at"] = s.CreatedAt s.fieldMap["updated_at"] = s.UpdatedAt s.fieldMap["deleted_at"] = s.DeletedAt + s.fieldMap["sort"] = s.Sort } func (s stSite) clone(db *gorm.DB) stSite { diff --git a/internal/dal/repository/st_site.gen.go b/internal/dal/repository/st_site.gen.go index 329cd9f..be5cb0b 100644 --- a/internal/dal/repository/st_site.gen.go +++ b/internal/dal/repository/st_site.gen.go @@ -30,6 +30,7 @@ type iWhereStSiteFunc interface { WhereByCreatedAt(createdAt time.Time) func(dao gen.Dao) gen.Dao WhereByUpdatedAt(updatedAt time.Time) func(dao gen.Dao) gen.Dao WhereByDeletedAt(deletedAt time.Time) func(dao gen.Dao) gen.Dao + WhereBySort(sort int) func(dao gen.Dao) gen.Dao } // ------------------------------------ @@ -117,6 +118,12 @@ func (s *stSiteDao) WhereByDeletedAt(deletedAt time.Time) func(dao gen.Dao) gen. } } +func (s *stSiteDao) WhereBySort(sort int) func(dao gen.Dao) gen.Dao { + return func(dao gen.Dao) gen.Dao { + return dao.Where(query.StSite.Sort.Eq(sort)) + } +} + func (s *stSiteDao) Create(m *model.StSite) (*model.StSite, error) { if err := s.stSiteDo.Create(m); err != nil { return nil, err diff --git a/internal/dal/repository/st_site.go b/internal/dal/repository/st_site.go index d92ad71..7247d5f 100644 --- a/internal/dal/repository/st_site.go +++ b/internal/dal/repository/st_site.go @@ -31,7 +31,7 @@ type ( // TODO Custom DaoFunc .... // ... - FindSiteCategoryWithPage(page, pageSize int, result any, whereFunc ...func(dao gen.Dao) gen.Dao) (count int64, err error) + FindSiteCategoryWithPage(page, pageSize int, result any, orderColumns []field.Expr, whereFunc ...func(dao gen.Dao) gen.Dao) (count int64, err error) } // not edit interface name @@ -77,7 +77,7 @@ func (d *customStSiteDao) LikeInByTitleOrDescOrURL(search string) func(dao gen.D } } -func (d *customStSiteDao) FindSiteCategoryWithPage(page, pageSize int, result any, whereFunc ...func(dao gen.Dao) gen.Dao) (count int64, err error) { +func (d *customStSiteDao) FindSiteCategoryWithPage(page, pageSize int, result any, orderColumns []field.Expr, whereFunc ...func(dao gen.Dao) gen.Dao) (count int64, err error) { return d.stSiteDo. Select( field.NewAsterisk(query.StSite.TableName()), @@ -88,7 +88,7 @@ func (d *customStSiteDao) FindSiteCategoryWithPage(page, pageSize int, result an query.StCategory.ID.EqCol(query.StSite.CategoryID), ). Order( - query.StSite.CreatedAt.Desc(), + orderColumns..., ). Scopes(whereFunc...). ScanByPage(result, (page-1)*pageSize, pageSize) diff --git a/internal/service/index/Index.go b/internal/service/index/Index.go index 986a594..73c013b 100644 --- a/internal/service/index/Index.go +++ b/internal/service/index/Index.go @@ -55,6 +55,10 @@ func categorySites(sites []*model.StSite, treeNodes []*v1.TreeNode) (data []*v1. categorySite.SiteList = append(categorySite.SiteList, *site) } } + // Sort 字段进行升序排序 + sort.Slice(categorySite.SiteList, func(i, j int) bool { + return categorySite.SiteList[i].Sort < categorySite.SiteList[j].Sort + }) if len(categorySite.SiteList) > 0 { data = append(data, categorySite) diff --git a/internal/service/site/batchcreate.go b/internal/service/site/batchcreate.go index 92a0b94..38382f6 100644 --- a/internal/service/site/batchcreate.go +++ b/internal/service/site/batchcreate.go @@ -64,6 +64,7 @@ func (s *service) BatchCreate(ctx context.Context, req *v1.SiteCreateReq) (*v1.S URL: url, CategoryID: req.CategoryID, IsUsed: req.IsUsed, + Sort: 0, }) if err != nil { failURLs = append(failURLs, url) diff --git a/internal/service/site/export.go b/internal/service/site/export.go index 200a48d..aa8b62d 100644 --- a/internal/service/site/export.go +++ b/internal/service/site/export.go @@ -8,9 +8,11 @@ package site import ( "strconv" + "github.com/ch3nnn/webstack-go/internal/dal/query" "github.com/gin-gonic/gin" excelize "github.com/xuri/excelize/v2" "gorm.io/gen" + "gorm.io/gen/field" v1 "github.com/ch3nnn/webstack-go/api/v1" "github.com/ch3nnn/webstack-go/internal/dal/repository" @@ -22,6 +24,8 @@ var ( ) func (s *service) Export(ctx *gin.Context, req *v1.SiteExportReq) (resp *v1.SiteExportResp, err error) { + var orderColumns []field.Expr + orderColumns = append(orderColumns, query.StSite.CreatedAt.Desc()) var whereFunc []func(dao gen.Dao) gen.Dao if req.Search != "" { @@ -29,10 +33,11 @@ func (s *service) Export(ctx *gin.Context, req *v1.SiteExportReq) (resp *v1.Site } if req.CategoryID != 0 { whereFunc = append(whereFunc, s.siteRepository.WhereByCategoryID(req.CategoryID)) + orderColumns = []field.Expr{query.StSite.Sort.Asc()} } var siteCategories []repository.SiteCategory - _, err = s.siteRepository.WithContext(ctx).FindSiteCategoryWithPage(1, 10000, &siteCategories, whereFunc...) + _, err = s.siteRepository.WithContext(ctx).FindSiteCategoryWithPage(1, 10000, &siteCategories, orderColumns, whereFunc...) if err != nil { return nil, err } diff --git a/internal/service/site/list.go b/internal/service/site/list.go index 434dc5e..18dac7a 100644 --- a/internal/service/site/list.go +++ b/internal/service/site/list.go @@ -10,11 +10,15 @@ import ( "time" v1 "github.com/ch3nnn/webstack-go/api/v1" + "github.com/ch3nnn/webstack-go/internal/dal/query" "github.com/ch3nnn/webstack-go/internal/dal/repository" "gorm.io/gen" + "gorm.io/gen/field" ) func (s *service) List(ctx context.Context, req *v1.SiteListReq) (resp *v1.SiteListResp, err error) { + var orderColumns []field.Expr + orderColumns = append(orderColumns, query.StSite.CreatedAt.Desc()) var whereFunc []func(dao gen.Dao) gen.Dao if req.Search != "" { @@ -22,10 +26,11 @@ func (s *service) List(ctx context.Context, req *v1.SiteListReq) (resp *v1.SiteL } if req.CategoryID != 0 { whereFunc = append(whereFunc, s.siteRepository.WhereByCategoryID(req.CategoryID)) + orderColumns = []field.Expr{query.StSite.Sort.Asc()} // 同分类网址按排序升序 } var siteCategories []repository.SiteCategory - count, err := s.siteRepository.WithContext(ctx).FindSiteCategoryWithPage(req.Page, req.PageSize, &siteCategories, whereFunc...) + count, err := s.siteRepository.WithContext(ctx).FindSiteCategoryWithPage(req.Page, req.PageSize, &siteCategories, orderColumns, whereFunc...) if err != nil { return nil, err } @@ -41,6 +46,7 @@ func (s *service) List(ctx context.Context, req *v1.SiteListReq) (resp *v1.SiteL CategoryId: siteCategory.StSite.CategoryID, Description: siteCategory.StSite.Description, IsUsed: siteCategory.StSite.IsUsed, + Sort: siteCategory.StSite.Sort, CreatedAt: siteCategory.StSite.CreatedAt.Format(time.DateTime), UpdatedAt: siteCategory.StSite.UpdatedAt.Format(time.DateTime), } diff --git a/internal/service/site/update.go b/internal/service/site/update.go index 69b55c3..080681a 100644 --- a/internal/service/site/update.go +++ b/internal/service/site/update.go @@ -76,6 +76,9 @@ func (s *service) Update(ctx *gin.Context, req *v1.SiteUpdateReq) (resp *v1.Site if req.IsUsed != nil { update["IsUsed"] = req.IsUsed } + if req.Sort >= 0 { + update["Sort"] = req.Sort + } _, err = s.siteRepository.WithContext(ctx).Update(update, s.siteRepository.WhereByID(req.Id)) if err != nil { diff --git a/web/templates/site/site_list.html b/web/templates/site/site_list.html index 9261e21..6136512 100755 --- a/web/templates/site/site_list.html +++ b/web/templates/site/site_list.html @@ -49,6 +49,7 @@ 创建日期 更新日期 状态 + 排序 操作 @@ -166,8 +167,9 @@ '' + value.category + '' + '' + value.created_at + '' + '' + value.updated_at + '' + - '' + showUsedBadge + '' + - '' + + '' + showUsedBadge + '' + + '' + value.sort + '' + + '' + '' + '' + @@ -241,7 +244,7 @@ text: '关闭', action: function () { const currentPage = document.querySelector('.page-link.pageActive').textContent; - getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value); + getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value, $("#category-filter").val()); } } } @@ -263,9 +266,7 @@ // 详情 $(document).on('click', '.btn-detail', function () { const business_title = $(this).attr('data-title'); - const business_thumb = $(this).attr('data-thumb'); const business_description = $(this).attr('data-description'); - $.alert({ title: '详情', content: '名称简介 :' + business_title + '
描述:' + business_description, @@ -321,7 +322,7 @@ text: '关闭', action: function () { const currentPage = document.querySelector('.page-link.pageActive').textContent; - getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value); + getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value, $("#category-filter").val()); } } } @@ -372,7 +373,7 @@ text: '关闭', action: function () { const currentPage = document.querySelector('.page-link.pageActive').textContent; - getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value); + getPageListData(parseInt(currentPage), 10, document.getElementById('search-keyword').value, $("#category-filter").val()); } } } @@ -399,6 +400,8 @@ const url = $(this).attr('data-url'); const description = $(this).attr('data-description'); const category_id = $(this).attr('data-category_id'); + const sort = $(this).attr('data-sort_order'); // 获取排序数据 + $.confirm({ title: '编辑网站', //content: 'url:form.html', // 也可以采用url形式 @@ -428,6 +431,11 @@ '
' + ' ' + ' ' + + '
' + + '
' + + ' ' + + ' (相同分类下网站排序)'+ + ' ' + '
', onOpen: function () { // bootstrap-fileinput 初始化引导文件输入插件 @@ -502,18 +510,15 @@ formData.append('description', description); formData.append('file', $("#file")[0].files[0]); formData.append('category_id', $("#category_id").val()); + formData.append('sort', $("#sort_order").val()); // 添加排序值 + AjaxMultipartForm( "PUT", "/api/admin/site/" + id, formData, function () { - $(this).hide(); - $("#btnLoading").show(); }, function (data) { - $("#btnLoading").hide(); - $("#btnOk").show(); - $.alert({ title: '操作成功', icon: 'mdi mdi-check-decagram', @@ -524,15 +529,13 @@ text: '关闭', action: function () { const currentPage = $(".page-link.pageActive").text(); // 获取当前页码 - getPageListData(currentPage, 10, $("#search-keyword").val()); // 重新加载当前页数据 + getPageListData(currentPage, 10, $("#search-keyword").val(), $("#category-filter").val()); // 重新加载当前页数据 } } } }); }, function (response) { - $("#btnLoading").hide(); - $("#btnOk").show(); AjaxError(response); } );