Merge pull request #273 from alimy/pr-topic-follow

add topic follow feature support
pull/274/head
北野 - Michael Li 2 years ago committed by GitHub
commit 5b08ad51e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -29,6 +29,7 @@ All notable changes to paopao-ce are documented in this file.
Default: ["Base", "Postgres", "Zinc", "LocalOSS", "LoggerZinc", "BigCacheIndex", "Friendship", "Service", "Web:DisallowUserRegister"] Default: ["Base", "Postgres", "Zinc", "LocalOSS", "LoggerZinc", "BigCacheIndex", "Friendship", "Service", "Web:DisallowUserRegister"]
... ...
``` ```
- add topic follow feature support [#273](https://github.com/rocboss/paopao-ce/pull/273)
### Fixed ### Fixed

@ -16,6 +16,7 @@ type Loose interface {
// Chain provide handlers chain for gin // Chain provide handlers chain for gin
Chain() gin.HandlersChain Chain() gin.HandlersChain
TopicList(*web.TopicListReq) (*web.TopicListResp, mir.Error)
GetUserProfile(*web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error) GetUserProfile(*web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error)
GetUserTweets(*web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error) GetUserTweets(*web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error)
Timeline(*web.TimelineReq) (*web.TimelineResp, mir.Error) Timeline(*web.TimelineReq) (*web.TimelineResp, mir.Error)
@ -24,6 +25,7 @@ type Loose interface {
} }
type LooseBinding interface { type LooseBinding interface {
BindTopicList(*gin.Context) (*web.TopicListReq, mir.Error)
BindGetUserProfile(*gin.Context) (*web.GetUserProfileReq, mir.Error) BindGetUserProfile(*gin.Context) (*web.GetUserProfileReq, mir.Error)
BindGetUserTweets(*gin.Context) (*web.GetUserTweetsReq, mir.Error) BindGetUserTweets(*gin.Context) (*web.GetUserTweetsReq, mir.Error)
BindTimeline(*gin.Context) (*web.TimelineReq, mir.Error) BindTimeline(*gin.Context) (*web.TimelineReq, mir.Error)
@ -32,6 +34,7 @@ type LooseBinding interface {
} }
type LooseRender interface { type LooseRender interface {
RenderTopicList(*gin.Context, *web.TopicListResp, mir.Error)
RenderGetUserProfile(*gin.Context, *web.GetUserProfileResp, mir.Error) RenderGetUserProfile(*gin.Context, *web.GetUserProfileResp, mir.Error)
RenderGetUserTweets(*gin.Context, *web.GetUserTweetsResp, mir.Error) RenderGetUserTweets(*gin.Context, *web.GetUserTweetsResp, mir.Error)
RenderTimeline(*gin.Context, *web.TimelineResp, mir.Error) RenderTimeline(*gin.Context, *web.TimelineResp, mir.Error)
@ -47,6 +50,22 @@ func RegisterLooseServant(e *gin.Engine, s Loose, b LooseBinding, r LooseRender)
router.Use(middlewares...) router.Use(middlewares...)
// register routes info to router // register routes info to router
router.Handle("GET", "/tags", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req, err := b.BindTopicList(c)
if err != nil {
r.RenderTopicList(c, nil, err)
return
}
resp, err := s.TopicList(req)
r.RenderTopicList(c, resp, err)
})
router.Handle("GET", "/user/profile", func(c *gin.Context) { router.Handle("GET", "/user/profile", func(c *gin.Context) {
select { select {
case <-c.Request.Context().Done(): case <-c.Request.Context().Done():
@ -105,6 +124,10 @@ func (UnimplementedLooseServant) Chain() gin.HandlersChain {
return nil return nil
} }
func (UnimplementedLooseServant) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error) { func (UnimplementedLooseServant) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
} }
@ -124,6 +147,10 @@ type UnimplementedLooseRender struct {
RenderAny func(*gin.Context, any, mir.Error) RenderAny func(*gin.Context, any, mir.Error)
} }
func (r *UnimplementedLooseRender) RenderTopicList(c *gin.Context, data *web.TopicListResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedLooseRender) RenderGetUserProfile(c *gin.Context, data *web.GetUserProfileResp, err mir.Error) { func (r *UnimplementedLooseRender) RenderGetUserProfile(c *gin.Context, data *web.GetUserProfileResp, err mir.Error) {
r.RenderAny(c, data, err) r.RenderAny(c, data, err)
} }
@ -143,6 +170,12 @@ type UnimplementedLooseBinding struct {
BindAny func(*gin.Context, any) mir.Error BindAny func(*gin.Context, any) mir.Error
} }
func (b *UnimplementedLooseBinding) BindTopicList(c *gin.Context) (*web.TopicListReq, mir.Error) {
obj := new(web.TopicListReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedLooseBinding) BindGetUserProfile(c *gin.Context) (*web.GetUserProfileReq, mir.Error) { func (b *UnimplementedLooseBinding) BindGetUserProfile(c *gin.Context) (*web.GetUserProfileReq, mir.Error) {
obj := new(web.GetUserProfileReq) obj := new(web.GetUserProfileReq)
err := b.BindAny(c, obj) err := b.BindAny(c, obj)

@ -16,6 +16,9 @@ type Priv interface {
// Chain provide handlers chain for gin // Chain provide handlers chain for gin
Chain() gin.HandlersChain Chain() gin.HandlersChain
UnfollowTopic(*web.UnfollowTopicReq) mir.Error
FollowTopic(*web.FollowTopicReq) mir.Error
StickTopic(*web.StickTopicReq) (*web.StickTopicResp, mir.Error)
DeleteCommentReply(*web.DeleteCommentReplyReq) mir.Error DeleteCommentReply(*web.DeleteCommentReplyReq) mir.Error
CreateCommentReply(*web.CreateCommentReplyReq) (*web.CreateCommentReplyResp, mir.Error) CreateCommentReply(*web.CreateCommentReplyReq) (*web.CreateCommentReplyResp, mir.Error)
DeleteComment(*web.DeleteCommentReq) mir.Error DeleteComment(*web.DeleteCommentReq) mir.Error
@ -35,6 +38,9 @@ type Priv interface {
} }
type PrivBinding interface { type PrivBinding interface {
BindUnfollowTopic(*gin.Context) (*web.UnfollowTopicReq, mir.Error)
BindFollowTopic(*gin.Context) (*web.FollowTopicReq, mir.Error)
BindStickTopic(*gin.Context) (*web.StickTopicReq, mir.Error)
BindDeleteCommentReply(*gin.Context) (*web.DeleteCommentReplyReq, mir.Error) BindDeleteCommentReply(*gin.Context) (*web.DeleteCommentReplyReq, mir.Error)
BindCreateCommentReply(*gin.Context) (*web.CreateCommentReplyReq, mir.Error) BindCreateCommentReply(*gin.Context) (*web.CreateCommentReplyReq, mir.Error)
BindDeleteComment(*gin.Context) (*web.DeleteCommentReq, mir.Error) BindDeleteComment(*gin.Context) (*web.DeleteCommentReq, mir.Error)
@ -54,6 +60,9 @@ type PrivBinding interface {
} }
type PrivRender interface { type PrivRender interface {
RenderUnfollowTopic(*gin.Context, mir.Error)
RenderFollowTopic(*gin.Context, mir.Error)
RenderStickTopic(*gin.Context, *web.StickTopicResp, mir.Error)
RenderDeleteCommentReply(*gin.Context, mir.Error) RenderDeleteCommentReply(*gin.Context, mir.Error)
RenderCreateCommentReply(*gin.Context, *web.CreateCommentReplyResp, mir.Error) RenderCreateCommentReply(*gin.Context, *web.CreateCommentReplyResp, mir.Error)
RenderDeleteComment(*gin.Context, mir.Error) RenderDeleteComment(*gin.Context, mir.Error)
@ -80,6 +89,52 @@ func RegisterPrivServant(e *gin.Engine, s Priv, b PrivBinding, r PrivRender) {
router.Use(middlewares...) router.Use(middlewares...)
// register routes info to router // register routes info to router
router.Handle("POST", "/topic/unfollow", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req, err := b.BindUnfollowTopic(c)
if err != nil {
r.RenderUnfollowTopic(c, err)
return
}
r.RenderUnfollowTopic(c, s.UnfollowTopic(req))
})
router.Handle("POST", "/topic/follow", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req, err := b.BindFollowTopic(c)
if err != nil {
r.RenderFollowTopic(c, err)
return
}
r.RenderFollowTopic(c, s.FollowTopic(req))
})
router.Handle("POST", "/topic/stick", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req, err := b.BindStickTopic(c)
if err != nil {
r.RenderStickTopic(c, nil, err)
return
}
resp, err := s.StickTopic(req)
r.RenderStickTopic(c, resp, err)
})
router.Handle("DELETE", "/post/comment/reply", func(c *gin.Context) { router.Handle("DELETE", "/post/comment/reply", func(c *gin.Context) {
select { select {
case <-c.Request.Context().Done(): case <-c.Request.Context().Done():
@ -311,6 +366,18 @@ func (UnimplementedPrivServant) Chain() gin.HandlersChain {
return nil return nil
} }
func (UnimplementedPrivServant) UnfollowTopic(req *web.UnfollowTopicReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) FollowTopic(req *web.FollowTopicReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) StickTopic(req *web.StickTopicReq) (*web.StickTopicResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteCommentReply(req *web.DeleteCommentReplyReq) mir.Error { func (UnimplementedPrivServant) DeleteCommentReply(req *web.DeleteCommentReplyReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
} }
@ -374,6 +441,18 @@ type UnimplementedPrivRender struct {
RenderAny func(*gin.Context, any, mir.Error) RenderAny func(*gin.Context, any, mir.Error)
} }
func (r *UnimplementedPrivRender) RenderUnfollowTopic(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
}
func (r *UnimplementedPrivRender) RenderFollowTopic(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
}
func (r *UnimplementedPrivRender) RenderStickTopic(c *gin.Context, data *web.StickTopicResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPrivRender) RenderDeleteCommentReply(c *gin.Context, err mir.Error) { func (r *UnimplementedPrivRender) RenderDeleteCommentReply(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err) r.RenderAny(c, nil, err)
} }
@ -437,6 +516,24 @@ type UnimplementedPrivBinding struct {
BindAny func(*gin.Context, any) mir.Error BindAny func(*gin.Context, any) mir.Error
} }
func (b *UnimplementedPrivBinding) BindUnfollowTopic(c *gin.Context) (*web.UnfollowTopicReq, mir.Error) {
obj := new(web.UnfollowTopicReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPrivBinding) BindFollowTopic(c *gin.Context) (*web.FollowTopicReq, mir.Error) {
obj := new(web.FollowTopicReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPrivBinding) BindStickTopic(c *gin.Context) (*web.StickTopicReq, mir.Error) {
obj := new(web.StickTopicReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPrivBinding) BindDeleteCommentReply(c *gin.Context) (*web.DeleteCommentReplyReq, mir.Error) { func (b *UnimplementedPrivBinding) BindDeleteCommentReply(c *gin.Context) (*web.DeleteCommentReplyReq, mir.Error) {
obj := new(web.DeleteCommentReplyReq) obj := new(web.DeleteCommentReplyReq)
err := b.BindAny(c, obj) err := b.BindAny(c, obj)

@ -13,7 +13,6 @@ import (
) )
type Pub interface { type Pub interface {
TopicList(*web.TopicListReq) (*web.TopicListResp, mir.Error)
TweetComments(*web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) TweetComments(*web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error)
TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, mir.Error)
SendCaptcha(*web.SendCaptchaReq) mir.Error SendCaptcha(*web.SendCaptchaReq) mir.Error
@ -26,7 +25,6 @@ type Pub interface {
} }
type PubBinding interface { type PubBinding interface {
BindTopicList(*gin.Context) (*web.TopicListReq, mir.Error)
BindTweetComments(*gin.Context) (*web.TweetCommentsReq, mir.Error) BindTweetComments(*gin.Context) (*web.TweetCommentsReq, mir.Error)
BindTweetDetail(*gin.Context) (*web.TweetDetailReq, mir.Error) BindTweetDetail(*gin.Context) (*web.TweetDetailReq, mir.Error)
BindSendCaptcha(*gin.Context) (*web.SendCaptchaReq, mir.Error) BindSendCaptcha(*gin.Context) (*web.SendCaptchaReq, mir.Error)
@ -37,7 +35,6 @@ type PubBinding interface {
} }
type PubRender interface { type PubRender interface {
RenderTopicList(*gin.Context, *web.TopicListResp, mir.Error)
RenderTweetComments(*gin.Context, *web.TweetCommentsResp, mir.Error) RenderTweetComments(*gin.Context, *web.TweetCommentsResp, mir.Error)
RenderTweetDetail(*gin.Context, *web.TweetDetailResp, mir.Error) RenderTweetDetail(*gin.Context, *web.TweetDetailResp, mir.Error)
RenderSendCaptcha(*gin.Context, mir.Error) RenderSendCaptcha(*gin.Context, mir.Error)
@ -54,22 +51,6 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
router := e.Group("v1") router := e.Group("v1")
// register routes info to router // register routes info to router
router.Handle("GET", "/tags", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req, err := b.BindTopicList(c)
if err != nil {
r.RenderTopicList(c, nil, err)
return
}
resp, err := s.TopicList(req)
r.RenderTopicList(c, resp, err)
})
router.Handle("GET", "/post/comments", func(c *gin.Context) { router.Handle("GET", "/post/comments", func(c *gin.Context) {
select { select {
case <-c.Request.Context().Done(): case <-c.Request.Context().Done():
@ -177,10 +158,6 @@ func RegisterPubServant(e *gin.Engine, s Pub, b PubBinding, r PubRender) {
type UnimplementedPubServant struct { type UnimplementedPubServant struct {
} }
func (UnimplementedPubServant) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) { func (UnimplementedPubServant) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented)) return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
} }
@ -216,10 +193,6 @@ type UnimplementedPubRender struct {
RenderAny func(*gin.Context, any, mir.Error) RenderAny func(*gin.Context, any, mir.Error)
} }
func (r *UnimplementedPubRender) RenderTopicList(c *gin.Context, data *web.TopicListResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedPubRender) RenderTweetComments(c *gin.Context, data *web.TweetCommentsResp, err mir.Error) { func (r *UnimplementedPubRender) RenderTweetComments(c *gin.Context, data *web.TweetCommentsResp, err mir.Error) {
r.RenderAny(c, data, err) r.RenderAny(c, data, err)
} }
@ -255,12 +228,6 @@ type UnimplementedPubBinding struct {
BindAny func(*gin.Context, any) mir.Error BindAny func(*gin.Context, any) mir.Error
} }
func (b *UnimplementedPubBinding) BindTopicList(c *gin.Context) (*web.TopicListReq, mir.Error) {
obj := new(web.TopicListReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedPubBinding) BindTweetComments(c *gin.Context) (*web.TweetCommentsReq, mir.Error) { func (b *UnimplementedPubBinding) BindTweetComments(c *gin.Context) (*web.TweetCommentsReq, mir.Error) {
obj := new(web.TweetCommentsReq) obj := new(web.TweetCommentsReq)
err := b.BindAny(c, obj) err := b.BindAny(c, obj)

@ -18,5 +18,11 @@ type TopicService interface {
CreateTag(tag *Tag) (*Tag, error) CreateTag(tag *Tag) (*Tag, error)
DeleteTag(tag *Tag) error DeleteTag(tag *Tag) error
GetTags(conditions *ConditionsT, offset, limit int) ([]*Tag, error) GetTags(conditions *ConditionsT, offset, limit int) ([]*Tag, error)
GetHotTags(userId int64, limit int, offset int) ([]*TagFormated, error)
GetNewestTags(userId int64, limit int, offset int) ([]*TagFormated, error)
GetFollowTags(userId int64, limit int, offset int) ([]*TagFormated, error)
GetTagsByKeyword(keyword string) ([]*Tag, error) GetTagsByKeyword(keyword string) ([]*Tag, error)
FollowTopic(userId int64, topicId int64) error
UnfollowTopic(userId int64, topicId int64) error
StickTopic(userId int64, topicId int64) (int8, error)
} }

@ -16,12 +16,27 @@ type Tag struct {
Tag string `json:"tag"` Tag string `json:"tag"`
QuoteNum int64 `json:"quote_num"` QuoteNum int64 `json:"quote_num"`
} }
type TopicUser struct {
*Model
UserID int64 `json:"user_id"`
TopicID int64 `json:"topic_id"`
AliasName string `json:"-"`
Remark string `json:"-"`
QuoteNum int64 `json:"quote_num"`
IsTop int8 `json:"is_top"`
ReserveA string `json:"-"`
ReserveB string `json:"-"`
}
type TagFormated struct { type TagFormated struct {
ID int64 `json:"id"` ID int64 `json:"id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
User *UserFormated `json:"user"` User *UserFormated `json:"user"`
Tag string `json:"tag"` Tag string `json:"tag"`
QuoteNum int64 `json:"quote_num"` QuoteNum int64 `json:"quote_num"`
IsFollowing int8 `json:"is_following"`
IsTop int8 `json:"is_top"`
} }
func (t *Tag) Format() *TagFormated { func (t *Tag) Format() *TagFormated {
@ -30,11 +45,13 @@ func (t *Tag) Format() *TagFormated {
} }
return &TagFormated{ return &TagFormated{
ID: t.ID, ID: t.ID,
UserID: t.UserID, UserID: t.UserID,
User: &UserFormated{}, User: &UserFormated{},
Tag: t.Tag, Tag: t.Tag,
QuoteNum: t.QuoteNum, QuoteNum: t.QuoteNum,
IsFollowing: 0,
IsTop: 0,
} }
} }

@ -49,6 +49,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
pvs := security.NewPhoneVerifyService() pvs := security.NewPhoneVerifyService()
ams := NewAuthorizationManageService() ams := NewAuthorizationManageService()
ths := newTweetHelpService(db) ths := newTweetHelpService(db)
ums := newUserManageService(db)
// initialize core.IndexPostsService // initialize core.IndexPostsService
if cfg.If("Friendship") { if cfg.If("Friendship") {
@ -86,7 +87,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
IndexPostsService: cis, IndexPostsService: cis,
WalletService: newWalletService(db), WalletService: newWalletService(db),
MessageService: newMessageService(db), MessageService: newMessageService(db),
TopicService: newTopicService(db), TopicService: newTopicService(db, ums),
TweetService: newTweetService(db), TweetService: newTweetService(db),
TweetManageService: newTweetManageService(db, cis), TweetManageService: newTweetManageService(db, cis),
TweetHelpService: newTweetHelpService(db), TweetHelpService: newTweetHelpService(db),

@ -5,6 +5,7 @@
package jinzhu package jinzhu
import ( import (
"errors"
"strings" "strings"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
@ -17,12 +18,23 @@ var (
) )
type topicServant struct { type topicServant struct {
db *gorm.DB db *gorm.DB
ums core.UserManageService
tnTopicUser string
tnDotTopicUser string
} }
func newTopicService(db *gorm.DB) core.TopicService { type topicInfo struct {
TopicId int64
IsTop int8
}
func newTopicService(db *gorm.DB, ums core.UserManageService) core.TopicService {
return &topicServant{ return &topicServant{
db: db, db: db,
ums: ums,
tnTopicUser: db.NamingStrategy.TableName("TopicUser"),
tnDotTopicUser: db.NamingStrategy.TableName("TopicUser") + ".",
} }
} }
@ -38,6 +50,105 @@ func (s *topicServant) GetTags(conditions *core.ConditionsT, offset, limit int)
return (&dbr.Tag{}).List(s.db, conditions, offset, limit) return (&dbr.Tag{}).List(s.db, conditions, offset, limit)
} }
func (s *topicServant) GetHotTags(userId int64, limit int, offset int) ([]*core.TagFormated, error) {
tags, err := (&dbr.Tag{}).List(s.db, &core.ConditionsT{
"ORDER": "quote_num DESC",
}, offset, limit)
if err != nil {
return nil, err
}
return s.tagsFormat(userId, nil, tags)
}
func (s *topicServant) GetNewestTags(userId int64, limit int, offset int) ([]*core.TagFormated, error) {
tags, err := (&dbr.Tag{}).List(s.db, &core.ConditionsT{
"ORDER": "id DESC",
}, offset, limit)
if err != nil {
return nil, err
}
return s.tagsFormat(userId, nil, tags)
}
func (s *topicServant) GetFollowTags(userId int64, limit int, offset int) ([]*core.TagFormated, error) {
if userId < 0 {
return nil, nil
}
userTopics := []*topicInfo{}
err := s.db.Model(&dbr.TopicUser{}).Where("user_id=?", userId).Order("is_top DESC").Find(&userTopics).Error
if err != nil {
return nil, err
}
userTopicsMap := make(map[int64]*topicInfo, len(userTopics))
topicIds := make([]int64, 0, len(userTopics))
topicIdsMap := make(map[int64]int, len(userTopics))
for idx, info := range userTopics {
userTopicsMap[info.TopicId] = info
topicIds = append(topicIds, info.TopicId)
topicIdsMap[info.TopicId] = idx
}
var tags []*core.Tag
err = s.db.Model(&dbr.Tag{}).Where("quote_num > 0 and id in ?", topicIds).Order("quote_num DESC").Find(&tags).Error
if err != nil {
return nil, err
}
formtedTags, err := s.tagsFormat(-1, userTopicsMap, tags)
if err != nil {
return nil, err
}
// 置顶排序后处理
// TODO: 垃圾办法最好是topic_user join tag 一次查询但是gorm的join真他喵的别扭F*K
res := make([]*core.TagFormated, len(topicIds), len(topicIds))
for _, tag := range formtedTags {
res[topicIdsMap[tag.ID]] = tag
}
return res, nil
}
func (s *topicServant) tagsFormat(userId int64, userTopicsMap map[int64]*topicInfo, tags []*core.Tag) ([]*core.TagFormated, error) {
// 获取创建者User IDs
userIds := []int64{}
tagIds := []int64{}
for _, tag := range tags {
userIds = append(userIds, tag.UserID)
tagIds = append(tagIds, tag.ID)
}
users, err := s.ums.GetUsersByIDs(userIds)
if err != nil {
return nil, err
}
tagsFormated := []*core.TagFormated{}
for _, tag := range tags {
tagFormated := tag.Format()
for _, user := range users {
if user.ID == tagFormated.UserID {
tagFormated.User = user.Format()
}
}
tagsFormated = append(tagsFormated, tagFormated)
}
// 填充话题follow信息
if userId > -1 && len(userTopicsMap) <= 0 {
userTopics := []*topicInfo{}
err = s.db.Model(&dbr.TopicUser{}).Where("is_del=0 and user_id=? and topic_id in ?", userId, tagIds).Find(&userTopics).Error
if err != nil {
return nil, err
}
userTopicsMap = make(map[int64]*topicInfo, len(userTopics))
for _, info := range userTopics {
userTopicsMap[info.TopicId] = info
}
}
if len(userTopicsMap) > 0 {
for _, tag := range tagsFormated {
if info, exist := userTopicsMap[tag.ID]; exist {
tag.IsFollowing, tag.IsTop = 1, info.IsTop
}
}
}
return tagsFormated, nil
}
func (s *topicServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) { func (s *topicServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) {
tag := &dbr.Tag{} tag := &dbr.Tag{}
@ -53,3 +164,39 @@ func (s *topicServant) GetTagsByKeyword(keyword string) ([]*core.Tag, error) {
}, 0, 6) }, 0, 6)
} }
} }
func (s *topicServant) FollowTopic(userId int64, topicId int64) (err error) {
return s.db.Create(&dbr.TopicUser{
UserID: userId,
TopicID: topicId,
IsTop: 0,
}).Error
}
func (s *topicServant) UnfollowTopic(userId int64, topicId int64) error {
return s.db.Exec("DELETE FROM "+s.tnTopicUser+" WHERE user_id=? AND topic_id=?", userId, topicId).Error
}
func (s *topicServant) StickTopic(userId int64, topicId int64) (status int8, err error) {
db := s.db.Begin()
defer db.Rollback()
m := &dbr.TopicUser{}
err = db.Model(m).
Where("user_id=? and topic_id=?", userId, topicId).
UpdateColumn("is_top", gorm.Expr("1-is_top")).Error
if err != nil {
return
}
status = -1
err = db.Model(m).Where("user_id=? and topic_id=?", userId, topicId).Select("is_top").Scan(&status).Error
if err != nil {
return
}
if status < 0 {
return -1, errors.New("topic not exist")
}
db.Commit()
return
}

@ -9,6 +9,15 @@ import (
"github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/internal/servants/base"
) )
const (
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
TagTypeFollow TagType = "follow"
TagTypeHotExtral TagType = "hot_extral"
)
type TagType string
type TimelineReq struct { type TimelineReq struct {
BaseInfo `form:"-" binding:"-"` BaseInfo `form:"-" binding:"-"`
Query string `form:"query"` Query string `form:"query"`
@ -44,6 +53,20 @@ type GetUserProfileResp struct {
IsFriend bool `json:"is_friend"` IsFriend bool `json:"is_friend"`
} }
type TopicListReq struct {
SimpleInfo `form:"-" binding:"-"`
Type TagType `json:"type" form:"type" binding:"required"`
Num int `json:"num" form:"num" binding:"required"`
ExtralNum int `json:"extral_num" form:"extral_num"`
}
// TopicListResp 主题返回值
// TODO: 优化内容定义
type TopicListResp struct {
Topics []*core.TagFormated `json:"topics"`
ExtralTopics []*core.TagFormated `json:"extral_topics,omitempty"`
}
func (r *GetUserTweetsReq) SetPageInfo(page int, pageSize int) { func (r *GetUserTweetsReq) SetPageInfo(page int, pageSize int) {
r.Page, r.PageSize = page, pageSize r.Page, r.PageSize = page, pageSize
} }

@ -146,6 +146,25 @@ type DownloadAttachmentResp struct {
SignedURL string `json:"signed_url"` SignedURL string `json:"signed_url"`
} }
type StickTopicReq struct {
SimpleInfo `json:"-" binding:"-"`
TopicId int64 `json:"topic_id" binding:"required"`
}
type StickTopicResp struct {
StickStatus int8 `json:"top_status"`
}
type FollowTopicReq struct {
SimpleInfo `json:"-" binding:"-"`
TopicId int64 `json:"topic_id" binding:"required"`
}
type UnfollowTopicReq struct {
SimpleInfo `json:"-" binding:"-"`
TopicId int64 `json:"topic_id" binding:"required"`
}
// Check 检查PostContentItem属性 // Check 检查PostContentItem属性
func (p *PostContentItem) Check(acs core.AttachmentCheckService) error { func (p *PostContentItem) Check(acs core.AttachmentCheckService) error {
// 检查附件是否是本站资源 // 检查附件是否是本站资源

@ -10,13 +10,6 @@ import (
"github.com/rocboss/paopao-ce/pkg/version" "github.com/rocboss/paopao-ce/pkg/version"
) )
const (
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
)
type TagType string
type TweetDetailReq struct { type TweetDetailReq struct {
TweetId int64 `form:"id"` TweetId int64 `form:"id"`
} }
@ -32,17 +25,6 @@ type TweetCommentsReq struct {
type TweetCommentsResp base.PageResp type TweetCommentsResp base.PageResp
type TopicListReq struct {
Type TagType `json:"type" form:"type" binding:"required"`
Num int `json:"num" form:"num" binding:"required"`
}
// TopicListResp 主题返回值
// TODO: 优化内容定义
type TopicListResp struct {
Topics []*core.TagFormated `json:"topics"`
}
type GetCaptchaResp struct { type GetCaptchaResp struct {
Id string `json:"id"` Id string `json:"id"`
Content string `json:"b64s"` Content string `json:"b64s"`

@ -149,6 +149,41 @@ func (s *looseSrv) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfi
}, nil }, nil
} }
func (s *looseSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
var (
tags, extralTags []*core.TagFormated
err error
)
num := req.Num
switch req.Type {
case web.TagTypeHot:
tags, err = s.Ds.GetHotTags(req.Uid, num, 0)
case web.TagTypeNew:
tags, err = s.Ds.GetNewestTags(req.Uid, num, 0)
case web.TagTypeFollow:
tags, err = s.Ds.GetFollowTags(req.Uid, num, 0)
case web.TagTypeHotExtral:
extralNum := req.ExtralNum
if extralNum == 0 {
extralNum = num
}
tags, err = s.Ds.GetHotTags(req.Uid, num, 0)
if err == nil {
extralTags, err = s.Ds.GetFollowTags(req.Uid, num, 0)
}
default:
// TODO: return good error
err = _errGetPostTagsFailed
}
if err != nil {
return nil, _errGetPostTagsFailed
}
return &web.TopicListResp{
Topics: tags,
ExtralTopics: extralTags,
}, nil
}
func newLooseSrv(s *base.DaoServant) api.Loose { func newLooseSrv(s *base.DaoServant) api.Loose {
return &looseSrv{ return &looseSrv{
DaoServant: s, DaoServant: s,

@ -143,6 +143,33 @@ func (s *privSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT(), chain.Priv()} return gin.HandlersChain{chain.JWT(), chain.Priv()}
} }
func (s *privSrv) UnfollowTopic(req *web.UnfollowTopicReq) mir.Error {
if err := s.Ds.UnfollowTopic(req.Uid, req.TopicId); err != nil {
logrus.Errorf("user(%d) unfollow topic(%d) failed: %s", req.Uid, req.TopicId, err)
return _errUnfollowTopicFailed
}
return nil
}
func (s *privSrv) FollowTopic(req *web.FollowTopicReq) mir.Error {
if err := s.Ds.FollowTopic(req.Uid, req.TopicId); err != nil {
logrus.Errorf("user(%d) follow topic(%d) failed: %s", req.Uid, req.TopicId, err)
return _errFollowTopicFailed
}
return nil
}
func (s *privSrv) StickTopic(req *web.StickTopicReq) (*web.StickTopicResp, mir.Error) {
status, err := s.Ds.StickTopic(req.Uid, req.TopicId)
if err != nil {
logrus.Errorf("user(%d) stick topic(%d) failed: %s", req.Uid, req.TopicId, err)
return nil, _errStickTopicFailed
}
return &web.StickTopicResp{
StickStatus: status,
}, nil
}
func (s *privSrv) UploadAttachment(req *web.UploadAttachmentReq) (*web.UploadAttachmentResp, mir.Error) { func (s *privSrv) UploadAttachment(req *web.UploadAttachmentReq) (*web.UploadAttachmentResp, mir.Error) {
defer req.File.Close() defer req.File.Close()

@ -18,7 +18,6 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
api "github.com/rocboss/paopao-ce/auto/api/v1" api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model/web" "github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/internal/servants/base"
@ -66,50 +65,6 @@ func (b *pubBinding) BindTweetComments(c *gin.Context) (*web.TweetCommentsReq, m
}, nil }, nil
} }
func (s *pubSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Error) {
// tags, err := broker.GetPostTags(&param)
num := req.Num
if num > conf.AppSetting.MaxPageSize {
num = conf.AppSetting.MaxPageSize
}
conditions := &core.ConditionsT{}
if req.Type == web.TagTypeHot {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "quote_num DESC",
}
} else if req.Type == web.TagTypeNew {
// 热门标签
conditions = &core.ConditionsT{
"ORDER": "id DESC",
}
}
tags, err := s.Ds.GetTags(conditions, 0, num)
if err != nil {
return nil, _errGetPostTagsFailed
}
// 获取创建者User IDs
userIds := []int64{}
for _, tag := range tags {
userIds = append(userIds, tag.UserID)
}
users, _ := s.Ds.GetUsersByIDs(userIds)
tagsFormated := []*core.TagFormated{}
for _, tag := range tags {
tagFormated := tag.Format()
for _, user := range users {
if user.ID == tagFormated.UserID {
tagFormated.User = user.Format()
}
}
tagsFormated = append(tagsFormated, tagFormated)
}
return &web.TopicListResp{
Topics: tagsFormated,
}, nil
}
func (s *pubSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) { func (s *pubSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
sort := "id ASC" sort := "id ASC"
if req.SortStrategy == "newest" { if req.SortStrategy == "newest" {

@ -78,6 +78,10 @@ var (
_errGetContactsFailed = xerror.NewError(80007, "获取联系人列表失败") _errGetContactsFailed = xerror.NewError(80007, "获取联系人列表失败")
_errNoActionToSelf = xerror.NewError(80008, "不允许对自己操作") _errNoActionToSelf = xerror.NewError(80008, "不允许对自己操作")
_errFollowTopicFailed = xerror.NewError(90001, "关注话题失败")
_errUnfollowTopicFailed = xerror.NewError(90002, "取消关注话题失败")
_errStickTopicFailed = xerror.NewError(90003, "更行话题置顶状态失败")
_errFileUploadFailed = xerror.NewError(10200, "文件上传失败") _errFileUploadFailed = xerror.NewError(10200, "文件上传失败")
_errFileInvalidExt = xerror.NewError(10201, "文件类型不合法") _errFileInvalidExt = xerror.NewError(10201, "文件类型不合法")
_errFileInvalidSize = xerror.NewError(10202, "文件大小超限") _errFileInvalidSize = xerror.NewError(10202, "文件大小超限")

@ -23,4 +23,7 @@ type Loose struct {
// GetUserProfile 获取用户基本信息 // GetUserProfile 获取用户基本信息
GetUserProfile func(Get, web.GetUserProfileReq) web.GetUserProfileResp `mir:"/user/profile"` GetUserProfile func(Get, web.GetUserProfileReq) web.GetUserProfileResp `mir:"/user/profile"`
// TopicList 获取话题列表
TopicList func(Get, web.TopicListReq) web.TopicListResp `mir:"/tags"`
} }

@ -56,4 +56,13 @@ type Priv struct {
// DeleteCommentReply 删除评论回复 // DeleteCommentReply 删除评论回复
DeleteCommentReply func(Delete, web.DeleteCommentReplyReq) `mir:"/post/comment/reply"` DeleteCommentReply func(Delete, web.DeleteCommentReplyReq) `mir:"/post/comment/reply"`
// StickTopic 置顶动态
StickTopic func(Post, web.StickTopicReq) web.StickTopicResp `mir:"/topic/stick"`
// FollowTopic 关注话题
FollowTopic func(Post, web.FollowTopicReq) `mir:"/topic/follow"`
// UnfollowTopic 取消关注话题
UnfollowTopic func(Post, web.UnfollowTopicReq) `mir:"/topic/unfollow"`
} }

@ -34,7 +34,4 @@ type Pub struct {
// TweetComments 获取动态评论 // TweetComments 获取动态评论
TweetComments func(Get, web.TweetCommentsReq) web.TweetCommentsResp `mir:"/post/comments"` TweetComments func(Get, web.TweetCommentsReq) web.TweetCommentsResp `mir:"/post/comments"`
// TopicList 获取话题列表
TopicList func(Get, web.TopicListReq) web.TopicListResp `mir:"/tags"`
} }

@ -0,0 +1 @@
DROP TABLE IF EXISTS `p_topic_user`;

@ -0,0 +1,17 @@
CREATE TABLE `p_topic_user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`topic_id` BIGINT UNSIGNED NOT NULL COMMENT '标签ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '创建者ID',
`alias_name` VARCHAR ( 255 ) COMMENT '别名',
`remark` VARCHAR ( 512 ) COMMENT '备注',
`quote_num` BIGINT UNSIGNED COMMENT '引用数',
`is_top` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否置顶 0 为未置顶、1 为已置顶',
`created_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
`reserve_a` VARCHAR ( 255 ) COMMENT '保留字段a',
`reserve_b` VARCHAR ( 255 ) COMMENT '保留字段b',
PRIMARY KEY ( `id` ) USING BTREE,
UNIQUE KEY `idx_topic_user_uid_tid` ( `topic_id`, `user_id` ) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户话题';

@ -0,0 +1 @@
DROP TABLE IF EXISTS `p_topic_user`;

@ -0,0 +1,16 @@
CREATE TABLE p_topic_user (
ID BIGSERIAL PRIMARY KEY,
topic_id BIGINT NOT NULL,-- 标签ID
user_id BIGINT NOT NULL,-- 创建者ID
alias_name VARCHAR ( 255 ),-- 别名
remark VARCHAR ( 512 ),-- 备注
quote_num BIGINT,-- 引用数
is_top SMALLINT NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
created_on BIGINT NOT NULL DEFAULT 0,-- 创建时间
modified_on BIGINT NOT NULL DEFAULT 0,-- 修改时间
deleted_on BIGINT NOT NULL DEFAULT 0,-- 删除时间
is_del SMALLINT NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
reserve_a VARCHAR ( 255 ),-- 保留字段a
reserve_b VARCHAR ( 255 ) -- 保留字段b
);
CREATE UNIQUE INDEX idx_topic_user_uid_tid ON p_topic_user USING btree ( topic_id, user_id );

@ -0,0 +1 @@
DROP TABLE IF EXISTS `p_topic_user`;

@ -0,0 +1,17 @@
CREATE TABLE "p_topic_user" (
"id" integer,
"topic_id" integer NOT NULL,-- 标签ID
"user_id" integer NOT NULL,-- 创建者ID
"alias_name" text ( 255 ),-- 别名
"remark" text ( 512 ),-- 备注
"quote_num" integer,-- 引用数
"is_top" integer NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
"created_on" integer NOT NULL DEFAULT 0,-- 创建时间
"modified_on" integer NOT NULL DEFAULT 0,-- 修改时间
"deleted_on" integer NOT NULL DEFAULT 0,-- 删除时间
"is_del" integer NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
"reserve_a" text,-- 保留字段a
"reserve_b" text,-- 保留字段b
PRIMARY KEY ( "id" )
);
CREATE UNIQUE INDEX "idx_topic_user_uid_tid" ON "p_topic_user" ( "topic_id", "user_id" );

@ -247,6 +247,28 @@ CREATE TABLE `p_tag` (
KEY `idx_tag_quote_num` (`quote_num`) USING BTREE KEY `idx_tag_quote_num` (`quote_num`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9000065 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='标签'; ) ENGINE=InnoDB AUTO_INCREMENT=9000065 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='标签';
-- ----------------------------
-- Table structure for p_topic_user
-- ----------------------------
DROP TABLE IF EXISTS `p_topic_user`;
CREATE TABLE `p_topic_user` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`topic_id` BIGINT UNSIGNED NOT NULL COMMENT '标签ID',
`user_id` BIGINT UNSIGNED NOT NULL COMMENT '创建者ID',
`alias_name` VARCHAR ( 255 ) COMMENT '别名',
`remark` VARCHAR ( 512 ) COMMENT '备注',
`quote_num` BIGINT UNSIGNED COMMENT '引用数',
`is_top` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否置顶 0 为未置顶、1 为已置顶',
`created_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
`reserve_a` VARCHAR ( 255 ) COMMENT '保留字段a',
`reserve_b` VARCHAR ( 255 ) COMMENT '保留字段b',
PRIMARY KEY ( `id` ) USING BTREE,
UNIQUE KEY `idx_topic_user_uid_tid` ( `topic_id`, `user_id` ) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户话题';
-- ---------------------------- -- ----------------------------
-- Table structure for p_user -- Table structure for p_user
-- ---------------------------- -- ----------------------------

@ -204,9 +204,28 @@ CREATE UNIQUE INDEX idx_tag_tag ON p_tag USING btree (tag);
CREATE INDEX idx_tag_user_id ON p_tag USING btree (user_id); CREATE INDEX idx_tag_user_id ON p_tag USING btree (user_id);
CREATE INDEX idx_tag_quote_num ON p_tag USING btree (quote_num); CREATE INDEX idx_tag_quote_num ON p_tag USING btree (quote_num);
DROP TABLE IF EXISTS p_topic_user;
CREATE TABLE p_topic_user (
ID BIGSERIAL PRIMARY KEY,
topic_id BIGINT NOT NULL,-- 标签ID
user_id BIGINT NOT NULL,-- 创建者ID
alias_name VARCHAR ( 255 ),-- 别名
remark VARCHAR ( 512 ),-- 备注
quote_num BIGINT,-- 引用数
is_top SMALLINT NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
created_on BIGINT NOT NULL DEFAULT 0,-- 创建时间
modified_on BIGINT NOT NULL DEFAULT 0,-- 修改时间
deleted_on BIGINT NOT NULL DEFAULT 0,-- 删除时间
is_del SMALLINT NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
reserve_a VARCHAR ( 255 ),-- 保留字段a
reserve_b VARCHAR ( 255 ) -- 保留字段b
);
CREATE UNIQUE INDEX idx_topic_user_uid_tid ON p_topic_user USING btree ( topic_id, user_id );
CREATE SEQUENCE IF NOT EXISTS user_id_seq AS BIGINT MINVALUE 100058 NO MAXVALUE;
DROP TABLE IF EXISTS p_user; DROP TABLE IF EXISTS p_user;
CREATE TABLE p_user ( CREATE TABLE p_user (
id BIGSERIAL PRIMARY KEY, id BIGINT NOT NULL DEFAULT nextval('user_id_seq'::regclass),
nickname VARCHAR(32) NOT NULL DEFAULT '', nickname VARCHAR(32) NOT NULL DEFAULT '',
username VARCHAR(32) NOT NULL DEFAULT '', username VARCHAR(32) NOT NULL DEFAULT '',
phone VARCHAR(16) NOT NULL DEFAULT '', -- 手机号 phone VARCHAR(16) NOT NULL DEFAULT '', -- 手机号
@ -219,7 +238,8 @@ CREATE TABLE p_user (
created_on BIGINT NOT NULL DEFAULT 0, created_on BIGINT NOT NULL DEFAULT 0,
modified_on BIGINT NOT NULL DEFAULT 0, modified_on BIGINT NOT NULL DEFAULT 0,
deleted_on BIGINT NOT NULL DEFAULT 0, deleted_on BIGINT NOT NULL DEFAULT 0,
is_del SMALLINT NOT NULL DEFAULT 0 is_del SMALLINT NOT NULL DEFAULT 0,
PRIMARY KEY (id)
); );
CREATE UNIQUE INDEX idx_user_username ON p_user USING btree (username); CREATE UNIQUE INDEX idx_user_username ON p_user USING btree (username);
CREATE INDEX idx_user_phone ON p_user USING btree (phone); CREATE INDEX idx_user_phone ON p_user USING btree (phone);

@ -255,6 +255,27 @@ CREATE TABLE "p_tag" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
); );
-- ----------------------------
-- Table structure for p_topic_user
-- ----------------------------
DROP TABLE IF EXISTS "p_topic_user";
CREATE TABLE "p_topic_user" (
"id" integer,
"topic_id" integer NOT NULL,-- 标签ID
"user_id" integer NOT NULL,-- 创建者ID
"alias_name" text ( 255 ),-- 别名
"remark" text ( 512 ),-- 备注
"quote_num" integer,-- 引用数
"is_top" integer NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
"created_on" integer NOT NULL DEFAULT 0,-- 创建时间
"modified_on" integer NOT NULL DEFAULT 0,-- 修改时间
"deleted_on" integer NOT NULL DEFAULT 0,-- 删除时间
"is_del" integer NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
"reserve_a" text,-- 保留字段a
"reserve_b" text,-- 保留字段b
PRIMARY KEY ( "id" )
);
-- ---------------------------- -- ----------------------------
-- Table structure for p_user -- Table structure for p_user
-- ---------------------------- -- ----------------------------
@ -483,6 +504,15 @@ ON "p_tag" (
"user_id" ASC "user_id" ASC
); );
-- ----------------------------
-- Indexes structure for table p_topic_user
-- ----------------------------
CREATE UNIQUE INDEX "idx_topic_user_uid_tid"
ON "p_topic_user" (
"topic_id",
"user_id"
);
-- ---------------------------- -- ----------------------------
-- Indexes structure for table p_user -- Indexes structure for table p_user
-- ---------------------------- -- ----------------------------

@ -1 +1 @@
import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{u as a}from"./vue-router-29025daf.js";import{F as i,e as c,a2 as u}from"./naive-ui-ddb574dd.js";import{d as l,c as d,L as t,Y as o,o as f,e as x}from"./@vue-f70ab1bd.js";import{_ as g}from"./index-ce5b62d8.js";import"./vuex-cc1858c6.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./@vicons-fc06a0bb.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const K=g(v,[["__scopeId","data-v-e62daa85"]]);export{K as default}; import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as a}from"./vue-router-29025daf.js";import{F as i,e as c,a2 as u}from"./naive-ui-ddb574dd.js";import{d as l,c as d,L as t,Y as o,o as f,e as x}from"./@vue-f70ab1bd.js";import{_ as g}from"./index-f6017bc3.js";import"./vuex-cc1858c6.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./@vicons-2f3cb6b9.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const K=g(v,[["__scopeId","data-v-e62daa85"]]);export{K as default};

File diff suppressed because one or more lines are too long

@ -1 +1 @@
import{_ as F}from"./post-skeleton-a5bf805a.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{u as z}from"./vuex-cc1858c6.js";import{b as A}from"./vue-router-29025daf.js";import{a as R}from"./formatTime-936c40eb.js";import{d as S,r as n,j as V,c as o,L as a,Y as p,o as e,U as u,O as l,F as I,$ as L,K as M,a as s,M as _,a1 as O}from"./@vue-f70ab1bd.js";import{F as P,G as U,I as $,H as j}from"./naive-ui-ddb574dd.js";import{_ as q}from"./index-ce5b62d8.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./@vicons-fc06a0bb.js";import"./moment-b7869f98.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";/* empty css */const D={key:0,class:"pagination-wrap"},E={key:0,class:"skeleton-wrap"},G={key:1},H={key:0,class:"empty-wrap"},K={class:"bill-line"},T=S({__name:"Anouncement",setup(Y){const d=z(),g=A(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{i.value=m};return V(()=>{}),(m,J)=>{const y=N,k=U,x=F,w=$,B=j,C=P;return e(),o("div",null,[a(y,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(e(),o("div",D,[a(k,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":c.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(e(),o("div",E,[a(x,{num:f.value},null,8,["num"])])):(e(),o("div",G,[r.value.length===0?(e(),o("div",H,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(e(!0),o(I,null,L(r.value,t=>(e(),M(B,{key:t.id},{default:p(()=>[s("div",K,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:O({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(R)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const kt=q(T,[["__scopeId","data-v-d4d04859"]]);export{kt as default}; import{_ as F}from"./post-skeleton-fdf95824.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as z}from"./vuex-cc1858c6.js";import{b as A}from"./vue-router-29025daf.js";import{a as R}from"./formatTime-936c40eb.js";import{d as S,r as n,j as V,c as o,L as a,Y as p,o as e,U as u,O as l,F as I,$ as L,K as M,a as s,M as _,a1 as O}from"./@vue-f70ab1bd.js";import{F as P,G as U,I as $,H as j}from"./naive-ui-ddb574dd.js";import{_ as q}from"./index-f6017bc3.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./@vicons-2f3cb6b9.js";import"./moment-b7869f98.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";/* empty css */const D={key:0,class:"pagination-wrap"},E={key:0,class:"skeleton-wrap"},G={key:1},H={key:0,class:"empty-wrap"},K={class:"bill-line"},T=S({__name:"Anouncement",setup(Y){const d=z(),g=A(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{i.value=m};return V(()=>{}),(m,J)=>{const y=N,k=U,x=F,w=$,B=j,C=P;return e(),o("div",null,[a(y,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(e(),o("div",D,[a(k,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":c.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(e(),o("div",E,[a(x,{num:f.value},null,8,["num"])])):(e(),o("div",G,[r.value.length===0?(e(),o("div",H,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(e(!0),o(I,null,L(r.value,t=>(e(),M(B,{key:t.id},{default:p(()=>[s("div",K,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:O({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(R)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const kt=q(T,[["__scopeId","data-v-d4d04859"]]);export{kt as default};

@ -0,0 +1 @@
import{_ as z}from"./post-item.vue_vue_type_style_index_0_lang-c48fe5cf.js";import{_ as B}from"./post-skeleton-fdf95824.js";import{_ as F}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as P}from"./vuex-cc1858c6.js";import{b as R,u as $}from"./vue-router-29025daf.js";import{G as b,_ as G}from"./index-f6017bc3.js";import{d as I,r as s,j as L,c as e,L as n,Y as m,U as M,O as u,o as t,F as N,$ as S,K as U}from"./@vue-f70ab1bd.js";import{F as V,G as j,I as q,H as E}from"./naive-ui-ddb574dd.js";import"./content-0b348d1e.js";import"./@vicons-2f3cb6b9.js";import"./nonesir-video-29a967e9.js";import"./formatTime-936c40eb.js";import"./moment-b7869f98.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const H={key:0,class:"skeleton-wrap"},K={key:1},O={key:0,class:"empty-wrap"},T={key:0,class:"pagination-wrap"},Y=I({__name:"Collection",setup(A){const d=P(),g=R();$();const a=s(!1),_=s([]),p=s(+g.query.p||1),i=s(20),r=s(0),l=()=>{a.value=!0,b({page:p.value,page_size:i.value}).then(o=>{a.value=!1,_.value=o.list,r.value=Math.ceil(o.pager.total_rows/i.value),window.scrollTo(0,0)}).catch(o=>{a.value=!1})},v=o=>{p.value=o,l()};return L(()=>{l()}),(o,D)=>{const f=F,h=B,k=q,y=z,w=E,C=V,x=j;return t(),e("div",null,[n(f,{title:"收藏"}),n(C,{class:"main-content-wrap",bordered:""},{default:m(()=>[a.value?(t(),e("div",H,[n(h,{num:i.value},null,8,["num"])])):(t(),e("div",K,[_.value.length===0?(t(),e("div",O,[n(k,{size:"large",description:"暂无数据"})])):u("",!0),(t(!0),e(N,null,S(_.value,c=>(t(),U(w,{key:c.id},{default:m(()=>[n(y,{post:c},null,8,["post"])]),_:2},1024))),128))]))]),_:1}),r.value>0?(t(),e("div",T,[n(x,{page:p.value,"onUpdate:page":v,"page-slot":M(d).state.collapsedRight?5:8,"page-count":r.value},null,8,["page","page-slot","page-count"])])):u("",!0)])}}});const xt=G(Y,[["__scopeId","data-v-1e709369"]]);export{xt as default};

@ -1 +0,0 @@
import{_ as z}from"./post-item.vue_vue_type_style_index_0_lang-02b1501b.js";import{_ as B}from"./post-skeleton-a5bf805a.js";import{_ as F}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{u as P}from"./vuex-cc1858c6.js";import{b as R,u as $}from"./vue-router-29025daf.js";import{D as b,_ as I}from"./index-ce5b62d8.js";import{d as L,r as s,j as M,c as e,L as n,Y as m,U as N,O as u,o as t,F as S,$ as U,K as V}from"./@vue-f70ab1bd.js";import{F as j,G as q,I as D,H as E}from"./naive-ui-ddb574dd.js";import"./content-16569a30.js";import"./@vicons-fc06a0bb.js";import"./nonesir-video-29a967e9.js";import"./formatTime-936c40eb.js";import"./moment-b7869f98.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const G={key:0,class:"skeleton-wrap"},H={key:1},K={key:0,class:"empty-wrap"},O={key:0,class:"pagination-wrap"},T=L({__name:"Collection",setup(Y){const d=P(),g=R();$();const a=s(!1),_=s([]),p=s(+g.query.p||1),i=s(20),r=s(0),l=()=>{a.value=!0,b({page:p.value,page_size:i.value}).then(o=>{a.value=!1,_.value=o.list,r.value=Math.ceil(o.pager.total_rows/i.value),window.scrollTo(0,0)}).catch(o=>{a.value=!1})},v=o=>{p.value=o,l()};return M(()=>{l()}),(o,A)=>{const f=F,h=B,k=D,y=z,w=E,C=j,x=q;return t(),e("div",null,[n(f,{title:"收藏"}),n(C,{class:"main-content-wrap",bordered:""},{default:m(()=>[a.value?(t(),e("div",G,[n(h,{num:i.value},null,8,["num"])])):(t(),e("div",H,[_.value.length===0?(t(),e("div",K,[n(k,{size:"large",description:"暂无数据"})])):u("",!0),(t(!0),e(S,null,U(_.value,c=>(t(),V(w,{key:c.id},{default:m(()=>[n(y,{post:c},null,8,["post"])]),_:2},1024))),128))]))]),_:1}),r.value>0?(t(),e("div",O,[n(x,{page:p.value,"onUpdate:page":v,"page-slot":N(d).state.collapsedRight?5:8,"page-count":r.value},null,8,["page","page-slot","page-count"])])):u("",!0)])}}});const xt=I(T,[["__scopeId","data-v-1e709369"]]);export{xt as default};

@ -0,0 +1 @@
import{u as M,b as N}from"./vue-router-29025daf.js";import{d as b,o as t,c as n,a as s,L as a,M as v,r as i,j as P,Y as h,U as R,O as y,F as k,$ as S,K as V}from"./@vue-f70ab1bd.js";import{o as q,F as D,G as L,I as T,H as j}from"./naive-ui-ddb574dd.js";import{_ as C,J as E}from"./index-f6017bc3.js";import{_ as G}from"./post-skeleton-fdf95824.js";import{_ as H}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as J}from"./vuex-cc1858c6.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./evtd-b614532e.js";import"./@css-render-66126308.js";import"./vooks-dfdd6eef.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";import"./@vicons-2f3cb6b9.js";/* empty css */const K={class:"avatar"},O={class:"base-info"},Y={class:"username"},A={class:"uid"},Q=b({__name:"contact-item",props:{contact:null},setup(c){const p=M(),m=e=>{p.push({name:"user",query:{username:e}})};return(e,o)=>{const _=q;return t(),n("div",{class:"contact-item",onClick:o[0]||(o[0]=l=>m(c.contact.username))},[s("div",K,[a(_,{size:"large",src:c.contact.avatar},null,8,["src"])]),s("div",O,[s("div",Y,[s("strong",null,v(c.contact.nickname),1),s("span",null," @"+v(c.contact.username),1)]),s("div",A,"UID. "+v(c.contact.user_id),1)])])}}});const W=C(Q,[["__scopeId","data-v-08ee9b2e"]]),X={key:0,class:"skeleton-wrap"},Z={key:1},tt={key:0,class:"empty-wrap"},et={key:0,class:"pagination-wrap"},ot=b({__name:"Contacts",setup(c){const p=J(),m=N(),e=i(!1),o=i([]),_=i(+m.query.p||1),l=i(20),d=i(0),$=r=>{_.value=r,g()};P(()=>{g()});const g=(r=!1)=>{o.value.length===0&&(e.value=!0),E({page:_.value,page_size:l.value}).then(u=>{e.value=!1,o.value=u.list,d.value=Math.ceil(u.pager.total_rows/l.value),r&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(u=>{e.value=!1})};return(r,u)=>{const w=H,x=G,I=T,z=W,B=j,U=D,F=L;return t(),n(k,null,[s("div",null,[a(w,{title:"好友"}),a(U,{class:"main-content-wrap",bordered:""},{default:h(()=>[e.value?(t(),n("div",X,[a(x,{num:l.value},null,8,["num"])])):(t(),n("div",Z,[o.value.length===0?(t(),n("div",tt,[a(I,{size:"large",description:"暂无数据"})])):y("",!0),(t(!0),n(k,null,S(o.value,f=>(t(),V(B,{key:f.user_id},{default:h(()=>[a(z,{contact:f},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})]),d.value>0?(t(),n("div",et,[a(F,{page:_.value,"onUpdate:page":$,"page-slot":R(p).state.collapsedRight?5:8,"page-count":d.value},null,8,["page","page-slot","page-count"])])):y("",!0)],64)}}});const It=C(ot,[["__scopeId","data-v-3b2bf978"]]);export{It as default};

@ -1 +0,0 @@
import{u as M,b as N}from"./vue-router-29025daf.js";import{d as b,o as t,c as n,a as s,L as a,M as v,r as i,j as P,Y as h,U as R,O as y,F as k,$ as S,K as V}from"./@vue-f70ab1bd.js";import{o as q,F as D,G,I as L,H as T}from"./naive-ui-ddb574dd.js";import{_ as C,G as j}from"./index-ce5b62d8.js";import{_ as E}from"./post-skeleton-a5bf805a.js";import{_ as H}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{u as K}from"./vuex-cc1858c6.js";import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./evtd-b614532e.js";import"./@css-render-66126308.js";import"./vooks-dfdd6eef.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-707ed124.js";import"./@vicons-fc06a0bb.js";/* empty css */const O={class:"avatar"},Y={class:"base-info"},A={class:"username"},J={class:"uid"},Q=b({__name:"contact-item",props:{contact:null},setup(c){const p=M(),m=e=>{p.push({name:"user",query:{username:e}})};return(e,o)=>{const _=q;return t(),n("div",{class:"contact-item",onClick:o[0]||(o[0]=l=>m(c.contact.username))},[s("div",O,[a(_,{size:"large",src:c.contact.avatar},null,8,["src"])]),s("div",Y,[s("div",A,[s("strong",null,v(c.contact.nickname),1),s("span",null," @"+v(c.contact.username),1)]),s("div",J,"UID. "+v(c.contact.user_id),1)])])}}});const W=C(Q,[["__scopeId","data-v-08ee9b2e"]]),X={key:0,class:"skeleton-wrap"},Z={key:1},tt={key:0,class:"empty-wrap"},et={key:0,class:"pagination-wrap"},ot=b({__name:"Contacts",setup(c){const p=K(),m=N(),e=i(!1),o=i([]),_=i(+m.query.p||1),l=i(20),d=i(0),$=r=>{_.value=r,g()};P(()=>{g()});const g=(r=!1)=>{o.value.length===0&&(e.value=!0),j({page:_.value,page_size:l.value}).then(u=>{e.value=!1,o.value=u.list,d.value=Math.ceil(u.pager.total_rows/l.value),r&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(u=>{e.value=!1})};return(r,u)=>{const w=H,x=E,I=L,z=W,B=T,U=D,F=G;return t(),n(k,null,[s("div",null,[a(w,{title:"好友"}),a(U,{class:"main-content-wrap",bordered:""},{default:h(()=>[e.value?(t(),n("div",X,[a(x,{num:l.value},null,8,["num"])])):(t(),n("div",Z,[o.value.length===0?(t(),n("div",tt,[a(I,{size:"large",description:"暂无数据"})])):y("",!0),(t(!0),n(k,null,S(o.value,f=>(t(),V(B,{key:f.user_id},{default:h(()=>[a(z,{contact:f},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})]),d.value>0?(t(),n("div",et,[a(F,{page:_.value,"onUpdate:page":$,"page-slot":R(p).state.collapsedRight?5:8,"page-count":d.value},null,8,["page","page-slot","page-count"])])):y("",!0)],64)}}});const It=C(ot,[["__scopeId","data-v-3b2bf978"]]);export{It as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
import{_ as F}from"./post-item.vue_vue_type_style_index_0_lang-02b1501b.js";import{_ as M}from"./post-skeleton-a5bf805a.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{u as S}from"./vuex-cc1858c6.js";import{b as V}from"./vue-router-29025daf.js";import{r as D,_ as L}from"./index-ce5b62d8.js";import{d as R,r,j,c as a,L as e,U as _,K as h,Y as m,O as d,o as t,a as s,M as f,F as q,$ as E}from"./@vue-f70ab1bd.js";import{F as G,G as H,o as K,f as O,g as T,I as Y,H as A}from"./naive-ui-ddb574dd.js";import"./content-16569a30.js";import"./@vicons-fc06a0bb.js";import"./nonesir-video-29a967e9.js";import"./formatTime-936c40eb.js";import"./moment-b7869f98.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const J={class:"profile-baseinfo"},Q={class:"avatar"},W={class:"base-info"},X={class:"username"},Z={class:"uid"},ee={key:0,class:"skeleton-wrap"},te={key:1},oe={key:0,class:"empty-wrap"},se={key:1,class:"pagination-wrap"},ne=R({__name:"Profile",setup(ae){const o=S(),k=V(),i=r(!1),p=r([]),l=r(+k.query.p||1),c=r(20),u=r(0),g=()=>{i.value=!0,D({username:o.state.userInfo.username,page:l.value,page_size:c.value}).then(n=>{i.value=!1,p.value=n.list,u.value=Math.ceil(n.pager.total_rows/c.value),window.scrollTo(0,0)}).catch(n=>{i.value=!1})},y=n=>{l.value=n,g()};return j(()=>{g()}),(n,_e)=>{const w=N,I=K,b=O,P=T,x=M,z=Y,B=F,U=A,$=G,C=H;return t(),a("div",null,[e(w,{title:"主页"}),_(o).state.userInfo.id>0?(t(),h($,{key:0,class:"main-content-wrap profile-wrap",bordered:""},{default:m(()=>[s("div",J,[s("div",Q,[e(I,{size:"large",src:_(o).state.userInfo.avatar},null,8,["src"])]),s("div",W,[s("div",X,[s("strong",null,f(_(o).state.userInfo.nickname),1),s("span",null," @"+f(_(o).state.userInfo.username),1)]),s("div",Z,"UID. "+f(_(o).state.userInfo.id),1)])]),e(P,{class:"profile-tabs-wrap",animated:""},{default:m(()=>[e(b,{name:"post",tab:"泡泡"})]),_:1}),i.value?(t(),a("div",ee,[e(x,{num:c.value},null,8,["num"])])):(t(),a("div",te,[p.value.length===0?(t(),a("div",oe,[e(z,{size:"large",description:"暂无数据"})])):d("",!0),(t(!0),a(q,null,E(p.value,v=>(t(),h(U,{key:v.id},{default:m(()=>[e(B,{post:v},null,8,["post"])]),_:2},1024))),128))]))]),_:1})):d("",!0),u.value>0?(t(),a("div",se,[e(C,{page:l.value,"onUpdate:page":y,"page-slot":_(o).state.collapsedRight?5:8,"page-count":u.value},null,8,["page","page-slot","page-count"])])):d("",!0)])}}});const Ve=L(ne,[["__scopeId","data-v-1d87d974"]]);export{Ve as default}; import{_ as F}from"./post-item.vue_vue_type_style_index_0_lang-c48fe5cf.js";import{_ as M}from"./post-skeleton-fdf95824.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as S}from"./vuex-cc1858c6.js";import{b as V}from"./vue-router-29025daf.js";import{w as D,_ as L}from"./index-f6017bc3.js";import{d as R,r,j,c as a,L as e,U as _,K as h,Y as m,O as d,o as t,a as s,M as f,F as q,$ as E}from"./@vue-f70ab1bd.js";import{F as G,G as H,o as K,f as O,g as T,I as Y,H as A}from"./naive-ui-ddb574dd.js";import"./content-0b348d1e.js";import"./@vicons-2f3cb6b9.js";import"./nonesir-video-29a967e9.js";import"./formatTime-936c40eb.js";import"./moment-b7869f98.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const J={class:"profile-baseinfo"},Q={class:"avatar"},W={class:"base-info"},X={class:"username"},Z={class:"uid"},ee={key:0,class:"skeleton-wrap"},te={key:1},oe={key:0,class:"empty-wrap"},se={key:1,class:"pagination-wrap"},ne=R({__name:"Profile",setup(ae){const o=S(),k=V(),i=r(!1),p=r([]),l=r(+k.query.p||1),c=r(20),u=r(0),g=()=>{i.value=!0,D({username:o.state.userInfo.username,page:l.value,page_size:c.value}).then(n=>{i.value=!1,p.value=n.list,u.value=Math.ceil(n.pager.total_rows/c.value),window.scrollTo(0,0)}).catch(n=>{i.value=!1})},y=n=>{l.value=n,g()};return j(()=>{g()}),(n,_e)=>{const w=N,I=K,b=O,P=T,x=M,z=Y,B=F,U=A,$=G,C=H;return t(),a("div",null,[e(w,{title:"主页"}),_(o).state.userInfo.id>0?(t(),h($,{key:0,class:"main-content-wrap profile-wrap",bordered:""},{default:m(()=>[s("div",J,[s("div",Q,[e(I,{size:"large",src:_(o).state.userInfo.avatar},null,8,["src"])]),s("div",W,[s("div",X,[s("strong",null,f(_(o).state.userInfo.nickname),1),s("span",null," @"+f(_(o).state.userInfo.username),1)]),s("div",Z,"UID. "+f(_(o).state.userInfo.id),1)])]),e(P,{class:"profile-tabs-wrap",animated:""},{default:m(()=>[e(b,{name:"post",tab:"泡泡"})]),_:1}),i.value?(t(),a("div",ee,[e(x,{num:c.value},null,8,["num"])])):(t(),a("div",te,[p.value.length===0?(t(),a("div",oe,[e(z,{size:"large",description:"暂无数据"})])):d("",!0),(t(!0),a(q,null,E(p.value,v=>(t(),h(U,{key:v.id},{default:m(()=>[e(B,{post:v},null,8,["post"])]),_:2},1024))),128))]))]),_:1})):d("",!0),u.value>0?(t(),a("div",se,[e(C,{page:l.value,"onUpdate:page":y,"page-slot":_(o).state.collapsedRight?5:8,"page-count":u.value},null,8,["page","page-slot","page-count"])])):d("",!0)])}}});const Ve=L(ne,[["__scopeId","data-v-1d87d974"]]);export{Ve as default};

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
.tags-wrap[data-v-c1908b4e]{padding:20px}.tags-wrap .tag-item .tag-hot[data-v-c1908b4e]{margin-left:12px;font-size:12px;opacity:.75}.dark .tags-wrap[data-v-c1908b4e]{background-color:#101014bf}

@ -0,0 +1 @@
import{q as x,u as S,r as I,t as U,_ as j}from"./index-f6017bc3.js";import{l as z}from"./@vicons-2f3cb6b9.js";import{d as F,r as _,n as $,j as q,_ as E,o as l,c as u,L as n,Y as a,K as T,e as A,M as w,O as m,U as r,w as D,a3 as K,F as Y,$ as G}from"./@vue-f70ab1bd.js";import{o as H,M as L,j as J,e as P,O as Q,L as R,F as W,f as X,g as Z,a as tt,k as et}from"./naive-ui-ddb574dd.js";import{_ as ot}from"./main-nav.vue_vue_type_style_index_0_lang-5f0e81a4.js";import{u as nt}from"./vuex-cc1858c6.js";import"./vue-router-29025daf.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./evtd-b614532e.js";import"./@css-render-66126308.js";import"./vooks-dfdd6eef.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const st={key:0,class:"tag-item"},at={key:0,class:"tag-quote"},ct={key:1,class:"tag-quote tag-follow"},lt={key:0,class:"options"},it=F({__name:"tag-item",props:{tag:null,showAction:{type:Boolean},checkFollowing:{type:Boolean}},setup(s){const e=s,g=_(!1),d=$(()=>{let o=[];return e.tag.is_following===0?o.push({label:"关注",key:"follow"}):(e.tag.is_top===0?o.push({label:"置顶",key:"stick"}):o.push({label:"取消置顶",key:"unstick"}),o.push({label:"取消关注",key:"unfollow"})),o}),i=o=>{switch(o){case"follow":I({topic_id:e.tag.id}).then(t=>{e.tag.is_following=1,window.$message.success("关注成功")}).catch(t=>{console.log(t)});break;case"unfollow":S({topic_id:e.tag.id}).then(t=>{e.tag.is_following=0,window.$message.success("取消关注")}).catch(t=>{console.log(t)});break;case"stick":x({topic_id:e.tag.id}).then(t=>{e.tag.is_top=t.top_status,window.$message.success("置顶成功")}).catch(t=>{console.log(t)});break;case"unstick":x({topic_id:e.tag.id}).then(t=>{e.tag.is_top=t.top_status,window.$message.success("取消置顶")}).catch(t=>{console.log(t)});break}};return q(()=>{g.value=!1}),(o,t)=>{const k=E("router-link"),f=H,v=L,c=J,h=P,y=Q,p=R;return!s.checkFollowing||s.checkFollowing&&s.tag.is_following===1?(l(),u("div",st,[n(p,null,{header:a(()=>[(l(),T(v,{type:"success",size:"large",round:"",key:s.tag.id},{avatar:a(()=>[n(f,{src:s.tag.user.avatar},null,8,["src"])]),default:a(()=>[n(k,{class:"hash-link",to:{name:"home",query:{q:s.tag.tag,t:"tag"}}},{default:a(()=>[A(" #"+w(s.tag.tag),1)]),_:1},8,["to"]),s.showAction?m("",!0):(l(),u("span",at,"("+w(s.tag.quote_num)+")",1)),s.showAction?(l(),u("span",ct,"("+w(s.tag.quote_num)+")",1)):m("",!0)]),_:1}))]),"header-extra":a(()=>[s.showAction?(l(),u("div",lt,[n(y,{placement:"bottom-end",trigger:"click",size:"small",options:r(d),onSelect:i},{default:a(()=>[n(h,{type:"success",quaternary:"",circle:"",block:""},{icon:a(()=>[n(c,null,{default:a(()=>[n(r(z))]),_:1})]),_:1})]),_:1},8,["options"])])):m("",!0)]),_:1})])):m("",!0)}}});const _t=F({__name:"Topic",setup(s){const e=nt(),g=_([]),d=_("hot"),i=_(!1),o=_(!1),t=_(!1);D(o,()=>{o.value||(window.$message.success("保存成功"),e.commit("refreshTopicFollow"))});const k=$({get:()=>{let c="编辑";return o.value&&(c="保存"),c},set:c=>{}}),f=()=>{i.value=!0,U({type:d.value,num:50}).then(c=>{g.value=c.topics,i.value=!1}).catch(c=>{console.log(c),i.value=!1})},v=c=>{d.value=c,c=="follow"?t.value=!0:t.value=!1,f()};return q(()=>{f()}),(c,h)=>{const y=ot,p=X,B=L,C=Z,V=it,M=tt,N=et,O=W;return l(),u("div",null,[n(y,{title:"话题"}),n(O,{class:"main-content-wrap tags-wrap",bordered:""},{default:a(()=>[n(C,{type:"line",animated:"","onUpdate:value":v},K({default:a(()=>[n(p,{name:"hot",tab:"热门"}),n(p,{name:"new",tab:"最新"}),r(e).state.userLogined?(l(),T(p,{key:0,name:"follow",tab:"关注"})):m("",!0)]),_:2},[r(e).state.userLogined?{name:"suffix",fn:a(()=>[n(B,{checked:o.value,"onUpdate:checked":h[0]||(h[0]=b=>o.value=b),checkable:""},{default:a(()=>[A(w(r(k)),1)]),_:1},8,["checked"])]),key:"0"}:void 0]),1024),n(N,{show:i.value},{default:a(()=>[n(M,null,{default:a(()=>[(l(!0),u(Y,null,G(g.value,b=>(l(),T(V,{tag:b,showAction:r(e).state.userLogined&&o.value,checkFollowing:t.value},null,8,["tag","showAction","checkFollowing"]))),256))]),_:1})]),_:1},8,["show"])]),_:1})])}}});const Vt=j(_t,[["__scopeId","data-v-15794a53"]]);export{Vt as default};

@ -0,0 +1 @@
.tag-item .tag-quote{margin-left:12px;font-size:14px;opacity:.75}.tag-item .tag-follow{margin-right:22px}.tag-item .options{margin-left:-32px;margin-bottom:4px;opacity:.55}.tag-item .n-thing .n-thing-header{margin-bottom:0}.tag-item .n-thing .n-thing-avatar-header-wrapper{align-items:center}.tags-wrap[data-v-15794a53]{padding:20px}.dark .tags-wrap[data-v-15794a53]{background-color:#101014bf}

@ -1 +0,0 @@
import{_ as T}from"./main-nav.vue_vue_type_style_index_0_lang-d6d2ed7f.js";import{q as w,_ as x}from"./index-ce5b62d8.js";import{d as q,r as s,j as B,c as i,L as t,Y as o,o as _,F as C,$ as F,_ as M,K as N,e as V,M as l,a as L}from"./@vue-f70ab1bd.js";import{F as $,f as j,g as D,a as E,k as I,o as K,M as S}from"./naive-ui-ddb574dd.js";import"./vuex-cc1858c6.js";import"./vue-router-29025daf.js";import"./vooks-dfdd6eef.js";import"./evtd-b614532e.js";import"./@vicons-fc06a0bb.js";import"./axios-707ed124.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-804c4158.js";import"./@css-render-66126308.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const U={class:"tag-hot"},Y=q({__name:"Topic",setup(z){const p=s([]),c=s("hot"),a=s(!1),r=()=>{a.value=!0,w({type:c.value,num:50}).then(n=>{p.value=n.topics,a.value=!1}).catch(n=>{a.value=!1})},u=n=>{c.value=n,r()};return B(()=>{r()}),(n,A)=>{const d=T,m=j,g=D,f=M("router-link"),v=K,h=S,y=E,b=I,k=$;return _(),i("div",null,[t(d,{title:"话题"}),t(k,{class:"main-content-wrap tags-wrap",bordered:""},{default:o(()=>[t(g,{type:"line",animated:"","onUpdate:value":u},{default:o(()=>[t(m,{name:"hot",tab:"热门"}),t(m,{name:"new",tab:"最新"})]),_:1}),t(b,{show:a.value},{default:o(()=>[t(y,null,{default:o(()=>[(_(!0),i(C,null,F(p.value,e=>(_(),N(h,{class:"tag-item",type:"success",round:"",key:e.id},{avatar:o(()=>[t(v,{src:e.user.avatar},null,8,["src"])]),default:o(()=>[t(f,{class:"hash-link",to:{name:"home",query:{q:e.tag,t:"tag"}}},{default:o(()=>[V(" #"+l(e.tag),1)]),_:2},1032,["to"]),L("span",U,"("+l(e.quote_num)+")",1)]),_:2},1024))),128))]),_:1})]),_:1},8,["show"])]),_:1})])}}});const lt=x(Y,[["__scopeId","data-v-c1908b4e"]]);export{lt as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.auth-wrap[data-v-52205ad0]{margin-top:-30px}.dark .auth-wrap[data-v-52205ad0]{background-color:#101014bf}.rightbar-wrap[data-v-200967dd]{width:240px;position:fixed;left:calc(50% + var(--content-main) / 2 + 10px)}.rightbar-wrap .search-wrap[data-v-200967dd]{margin:12px 0}.rightbar-wrap .hot-tag-item[data-v-200967dd]{line-height:2;position:relative}.rightbar-wrap .hot-tag-item .hash-link[data-v-200967dd]{width:calc(100% - 60px);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block}.rightbar-wrap .hot-tag-item .post-num[data-v-200967dd]{position:absolute;right:0;top:0;width:60px;text-align:right;line-height:2;opacity:.5}.rightbar-wrap .hottopic-wrap[data-v-200967dd]{margin-bottom:10px}.rightbar-wrap .copyright-wrap .copyright[data-v-200967dd]{font-size:12px;opacity:.75}.rightbar-wrap .copyright-wrap .hash-link[data-v-200967dd]{font-size:12px}.dark .hottopic-wrap[data-v-200967dd],.dark .copyright-wrap[data-v-200967dd]{background-color:#18181c}.sidebar-wrap{z-index:99;width:200px;height:100vh;position:fixed;right:calc(50% + var(--content-main) / 2 + 10px);padding:12px 0;box-sizing:border-box}.sidebar-wrap .n-menu .n-menu-item-content:before{border-radius:21px}.logo-wrap{display:flex;justify-content:flex-start;margin-bottom:12px}.logo-wrap .logo-img{margin-left:24px}.logo-wrap .logo-img:hover{cursor:pointer}.user-wrap{display:flex;align-items:center;position:absolute;bottom:12px;left:12px;right:12px}.user-wrap .user-mini-wrap{display:none}.user-wrap .user-avatar{margin-right:8px}.user-wrap .user-info{display:flex;flex-direction:column}.user-wrap .user-info .nickname{font-size:16px;font-weight:700;line-height:16px;height:16px;margin-bottom:2px;display:flex;align-items:center}.user-wrap .user-info .nickname .nickname-txt{max-width:90px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.user-wrap .user-info .nickname .logout{margin-left:6px}.user-wrap .user-info .username{font-size:14px;line-height:16px;height:16px;width:120px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;opacity:.75}.user-wrap .login-only-wrap{display:flex;justify-content:center;width:100%}.user-wrap .login-only-wrap button{margin:0 4px;width:80%}.user-wrap .login-wrap{display:flex;justify-content:center;width:100%}.user-wrap .login-wrap button{margin:0 4px}.auth-card .n-card-header{z-index:999}@media screen and (max-width: 821px){.sidebar-wrap{width:200px;right:calc(100% - 200px)}.logo-wrap .logo-img{margin-left:12px!important}.user-wrap .user-avatar,.user-wrap .user-info,.user-wrap .login-only-wrap,.user-wrap .login-wrap{margin-bottom:32px}}:root{--content-main: 544px}.app-container{margin:0}.app-container .app-wrap{width:100%;margin:0 auto}.main-wrap{min-height:100vh;display:flex;flex-direction:row;justify-content:center}.main-wrap .content-wrap{width:100%;max-width:var(--content-main);position:relative}.main-wrap .main-content-wrap{margin:0;border-top:none;border-radius:0}.main-wrap .main-content-wrap .n-list-item{padding:0}.empty-wrap{min-height:300px;display:flex;align-items:center;justify-content:center}.hash-link,.user-link{color:#18a058;text-decoration:none;cursor:pointer}.hash-link:hover,.user-link:hover{opacity:.8}.beian-link{color:#333;text-decoration:none}.beian-link:hover{opacity:.75}.username-link{color:#000;color:none;text-decoration:none;cursor:pointer}.username-link:hover{text-decoration:underline}.dark .hash-link,.dark .user-link{color:#63e2b7}.dark .username-link{color:#eee}.dark .beian-link{color:#ddd}@media screen and (max-width: 821px){.content-wrap{top:0;position:absolute!important}}

@ -1 +0,0 @@
.auth-wrap[data-v-52205ad0]{margin-top:-30px}.dark .auth-wrap[data-v-52205ad0]{background-color:#101014bf}.rightbar-wrap[data-v-9c65d923]{width:240px;position:fixed;left:calc(50% + var(--content-main) / 2 + 10px)}.rightbar-wrap .search-wrap[data-v-9c65d923]{margin:12px 0}.rightbar-wrap .hot-tag-item[data-v-9c65d923]{line-height:2;position:relative}.rightbar-wrap .hot-tag-item .hash-link[data-v-9c65d923]{width:calc(100% - 60px);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block}.rightbar-wrap .hot-tag-item .post-num[data-v-9c65d923]{position:absolute;right:0;top:0;width:60px;text-align:right;line-height:2;opacity:.5}.rightbar-wrap .copyright-wrap[data-v-9c65d923]{margin-top:10px}.rightbar-wrap .copyright-wrap .copyright[data-v-9c65d923]{font-size:12px;opacity:.75}.rightbar-wrap .copyright-wrap .hash-link[data-v-9c65d923]{font-size:12px}.dark .hottopic-wrap[data-v-9c65d923],.dark .copyright-wrap[data-v-9c65d923]{background-color:#18181c}.sidebar-wrap{z-index:99;width:200px;height:100vh;position:fixed;right:calc(50% + var(--content-main) / 2 + 10px);padding:12px 0;box-sizing:border-box}.sidebar-wrap .n-menu .n-menu-item-content:before{border-radius:21px}.logo-wrap{display:flex;justify-content:flex-start;margin-bottom:12px}.logo-wrap .logo-img{margin-left:24px}.logo-wrap .logo-img:hover{cursor:pointer}.user-wrap{display:flex;align-items:center;position:absolute;bottom:12px;left:12px;right:12px}.user-wrap .user-mini-wrap{display:none}.user-wrap .user-avatar{margin-right:8px}.user-wrap .user-info{display:flex;flex-direction:column}.user-wrap .user-info .nickname{font-size:16px;font-weight:700;line-height:16px;height:16px;margin-bottom:2px;display:flex;align-items:center}.user-wrap .user-info .nickname .nickname-txt{max-width:90px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.user-wrap .user-info .nickname .logout{margin-left:6px}.user-wrap .user-info .username{font-size:14px;line-height:16px;height:16px;width:120px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;opacity:.75}.user-wrap .login-only-wrap{display:flex;justify-content:center;width:100%}.user-wrap .login-only-wrap button{margin:0 4px;width:80%}.user-wrap .login-wrap{display:flex;justify-content:center;width:100%}.user-wrap .login-wrap button{margin:0 4px}.auth-card .n-card-header{z-index:999}@media screen and (max-width: 821px){.sidebar-wrap{width:200px;right:calc(100% - 200px)}.logo-wrap .logo-img{margin-left:12px!important}.user-wrap .user-avatar,.user-wrap .user-info,.user-wrap .login-only-wrap,.user-wrap .login-wrap{margin-bottom:32px}}:root{--content-main: 544px}.app-container{margin:0}.app-container .app-wrap{width:100%;margin:0 auto}.main-wrap{min-height:100vh;display:flex;flex-direction:row;justify-content:center}.main-wrap .content-wrap{width:100%;max-width:var(--content-main);position:relative}.main-wrap .main-content-wrap{margin:0;border-top:none;border-radius:0}.main-wrap .main-content-wrap .n-list-item{padding:0}.empty-wrap{min-height:300px;display:flex;align-items:center;justify-content:center}.hash-link,.user-link{color:#18a058;text-decoration:none;cursor:pointer}.hash-link:hover,.user-link:hover{opacity:.8}.beian-link{color:#333;text-decoration:none}.beian-link:hover{opacity:.75}.username-link{color:#000;color:none;text-decoration:none;cursor:pointer}.username-link:hover{text-decoration:underline}.dark .hash-link,.dark .user-link{color:#63e2b7}.dark .username-link{color:#eee}.dark .beian-link{color:#ddd}@media screen and (max-width: 821px){.content-wrap{top:0;position:absolute!important}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
import{S as B}from"./index-ce5b62d8.js";import{u as E}from"./vuex-cc1858c6.js";import{u as z}from"./vue-router-29025daf.js";import{j as A}from"./vooks-dfdd6eef.js";import{D as C,t as D,u as N,v as P}from"./@vicons-fc06a0bb.js";import{a3 as x,a4 as R,j as I,e as V,a5 as j,h as H}from"./naive-ui-ddb574dd.js";import{d as L,r as f,j as U,o as a,c as g,U as o,L as e,Y as t,O as c,a as q,K as _,e as F,M as $,F as K}from"./@vue-f70ab1bd.js";const Y={key:0},G={class:"navbar"},oe=L({__name:"main-nav",props:{title:{default:""},back:{type:Boolean,default:!1},theme:{type:Boolean,default:!0}},setup(r){const i=r,n=E(),m=z(),l=f(!1),k=f("left"),u=d=>{d?(localStorage.setItem("PAOPAO_THEME","dark"),n.commit("triggerTheme","dark")):(localStorage.setItem("PAOPAO_THEME","light"),n.commit("triggerTheme","light"))},w=()=>{window.history.length<=1?m.push({path:"/"}):m.go(-1)},v=()=>{l.value=!0};return U(()=>{localStorage.getItem("PAOPAO_THEME")||u(A()==="dark")}),(d,p)=>{const y=B,b=x,O=R,s=I,h=V,M=j,S=H;return a(),g(K,null,[o(n).state.drawerModelShow?(a(),g("div",Y,[e(O,{show:l.value,"onUpdate:show":p[0]||(p[0]=T=>l.value=T),width:212,placement:k.value,resizable:""},{default:t(()=>[e(b,null,{default:t(()=>[e(y)]),_:1})]),_:1},8,["show","placement"])])):c("",!0),e(S,{size:"small",bordered:!0,class:"nav-title-card"},{header:t(()=>[q("div",G,[o(n).state.drawerModelShow&&!r.back?(a(),_(h,{key:0,class:"drawer-btn",onClick:v,quaternary:"",circle:"",size:"medium"},{icon:t(()=>[e(s,null,{default:t(()=>[e(o(C))]),_:1})]),_:1})):c("",!0),r.back?(a(),_(h,{key:1,class:"back-btn",onClick:w,quaternary:"",circle:"",size:"small"},{icon:t(()=>[e(s,null,{default:t(()=>[e(o(D))]),_:1})]),_:1})):c("",!0),F(" "+$(i.title)+" ",1),i.theme?(a(),_(M,{key:2,value:o(n).state.theme==="dark","onUpdate:value":u,size:"small",class:"theme-switch-wrap"},{"checked-icon":t(()=>[e(s,{component:o(N)},null,8,["component"])]),"unchecked-icon":t(()=>[e(s,{component:o(P)},null,8,["component"])]),_:1},8,["value"])):c("",!0)])]),_:1})],64)}}});export{oe as _}; import{V as E}from"./index-f6017bc3.js";import{u as S}from"./vuex-cc1858c6.js";import{u as z}from"./vue-router-29025daf.js";import{j as A}from"./vooks-dfdd6eef.js";import{D as C,u as D,v as N,w as P}from"./@vicons-2f3cb6b9.js";import{a3 as x,a4 as R,j as V,e as I,a5 as j,h as H}from"./naive-ui-ddb574dd.js";import{d as L,r as f,j as U,o as a,c as g,U as o,L as e,Y as t,O as c,a as q,K as _,e as F,M as $,F as K}from"./@vue-f70ab1bd.js";const Y={key:0},G={class:"navbar"},oe=L({__name:"main-nav",props:{title:{default:""},back:{type:Boolean,default:!1},theme:{type:Boolean,default:!0}},setup(r){const i=r,n=S(),m=z(),l=f(!1),k=f("left"),u=d=>{d?(localStorage.setItem("PAOPAO_THEME","dark"),n.commit("triggerTheme","dark")):(localStorage.setItem("PAOPAO_THEME","light"),n.commit("triggerTheme","light"))},w=()=>{window.history.length<=1?m.push({path:"/"}):m.go(-1)},v=()=>{l.value=!0};return U(()=>{localStorage.getItem("PAOPAO_THEME")||u(A()==="dark")}),(d,p)=>{const y=E,b=x,O=R,s=V,h=I,M=j,T=H;return a(),g(K,null,[o(n).state.drawerModelShow?(a(),g("div",Y,[e(O,{show:l.value,"onUpdate:show":p[0]||(p[0]=B=>l.value=B),width:212,placement:k.value,resizable:""},{default:t(()=>[e(b,null,{default:t(()=>[e(y)]),_:1})]),_:1},8,["show","placement"])])):c("",!0),e(T,{size:"small",bordered:!0,class:"nav-title-card"},{header:t(()=>[q("div",G,[o(n).state.drawerModelShow&&!r.back?(a(),_(h,{key:0,class:"drawer-btn",onClick:v,quaternary:"",circle:"",size:"medium"},{icon:t(()=>[e(s,null,{default:t(()=>[e(o(C))]),_:1})]),_:1})):c("",!0),r.back?(a(),_(h,{key:1,class:"back-btn",onClick:w,quaternary:"",circle:"",size:"small"},{icon:t(()=>[e(s,null,{default:t(()=>[e(o(D))]),_:1})]),_:1})):c("",!0),F(" "+$(i.title)+" ",1),i.theme?(a(),_(M,{key:2,value:o(n).state.theme==="dark","onUpdate:value":u,size:"small",class:"theme-switch-wrap"},{"checked-icon":t(()=>[e(s,{component:o(N)},null,8,["component"])]),"unchecked-icon":t(()=>[e(s,{component:o(P)},null,8,["component"])]),_:1},8,["value"])):c("",!0)])]),_:1})],64)}}});export{oe as _};

@ -1 +1 @@
import{p as N,a as S,_ as $,b as V,c as j}from"./content-16569a30.js";import{d as H,n as R,_ as D,o as i,c as f,L as a,a3 as F,U as t,Y as n,F as I,$ as P,Z as v,a as l,e as r,M as c,K as p,O as _}from"./@vue-f70ab1bd.js";import{u as E}from"./vuex-cc1858c6.js";import{b as K,u as U}from"./vue-router-29025daf.js";import{a as Y}from"./formatTime-936c40eb.js";import{f as Z,h as A,i as G,k as J}from"./@vicons-fc06a0bb.js";import{o as Q,M as W,j as X,a as tt,L as et}from"./naive-ui-ddb574dd.js";const st={class:"nickname-wrap"},nt={class:"username-wrap"},ot={class:"timestamp"},at=["innerHTML"],it={class:"opt-item"},rt={class:"opt-item"},ct={class:"opt-item"},pt={class:"opt-item"},ft=H({__name:"post-item",props:{post:null},setup(x){const C=x;K();const d=U(),z=E(),e=R(()=>{let o=Object.assign({texts:[],imgs:[],videos:[],links:[],attachments:[],charge_attachments:[]},C.post);return o.contents.map(s=>{(+s.type==1||+s.type==2)&&o.texts.push(s),+s.type==3&&o.imgs.push(s),+s.type==4&&o.videos.push(s),+s.type==6&&o.links.push(s),+s.type==7&&o.attachments.push(s),+s.type==8&&o.charge_attachments.push(s)}),o}),k=o=>{d.push({name:"post",query:{id:o}})},b=(o,s)=>{if(o.target.dataset.detail){const m=o.target.dataset.detail.split(":");if(m.length===2){z.commit("refresh"),m[0]==="tag"?d.push({name:"home",query:{q:m[1],t:"tag"}}):d.push({name:"user",query:{username:m[1]}});return}}k(s)};return(o,s)=>{const m=Q,w=D("router-link"),h=W,y=S,O=$,T=V,q=j,u=X,B=tt,L=et;return i(),f("div",{class:"post-item",onClick:s[2]||(s[2]=g=>k(t(e).id))},[a(L,{"content-indented":""},F({avatar:n(()=>[a(m,{round:"",size:30,src:t(e).user.avatar},null,8,["src"])]),header:n(()=>[l("span",st,[a(w,{onClick:s[0]||(s[0]=v(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{username:t(e).user.username}}},{default:n(()=>[r(c(t(e).user.nickname),1)]),_:1},8,["to"])]),l("span",nt," @"+c(t(e).user.username),1),t(e).is_top?(i(),p(h,{key:0,class:"top-tag",type:"warning",size:"small",round:""},{default:n(()=>[r(" 置顶 ")]),_:1})):_("",!0),t(e).visibility==1?(i(),p(h,{key:1,class:"top-tag",type:"error",size:"small",round:""},{default:n(()=>[r(" 私密 ")]),_:1})):_("",!0),t(e).visibility==2?(i(),p(h,{key:2,class:"top-tag",type:"info",size:"small",round:""},{default:n(()=>[r(" 好友可见 ")]),_:1})):_("",!0)]),"header-extra":n(()=>[l("span",ot,c(t(e).ip_loc?t(e).ip_loc+" · ":t(e).ip_loc)+" "+c(t(Y)(t(e).created_on)),1)]),footer:n(()=>[t(e).attachments.length>0?(i(),p(y,{key:0,attachments:t(e).attachments},null,8,["attachments"])):_("",!0),t(e).charge_attachments.length>0?(i(),p(y,{key:1,attachments:t(e).charge_attachments,price:t(e).attachment_price},null,8,["attachments","price"])):_("",!0),t(e).imgs.length>0?(i(),p(O,{key:2,imgs:t(e).imgs},null,8,["imgs"])):_("",!0),t(e).videos.length>0?(i(),p(T,{key:3,videos:t(e).videos},null,8,["videos"])):_("",!0),t(e).links.length>0?(i(),p(q,{key:4,links:t(e).links},null,8,["links"])):_("",!0)]),action:n(()=>[a(B,{justify:"space-between"},{default:n(()=>[l("div",it,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(Z))]),_:1}),r(" "+c(t(e).upvote_count),1)]),l("div",rt,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(A))]),_:1}),r(" "+c(t(e).comment_count),1)]),l("div",ct,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(G))]),_:1}),r(" "+c(t(e).collection_count),1)]),l("div",pt,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(J))]),_:1}),r(" "+c(t(e).share_count),1)])]),_:1})]),_:2},[t(e).texts.length>0?{name:"description",fn:n(()=>[(i(!0),f(I,null,P(t(e).texts,g=>(i(),f("span",{key:g.id,class:"post-text",onClick:s[1]||(s[1]=v(M=>b(M,t(e).id),["stop"])),innerHTML:t(N)(g.content).content},null,8,at))),128))]),key:"0"}:void 0]),1024)])}}});export{ft as _}; import{p as N,a as S,_ as $,b as V,c as j}from"./content-0b348d1e.js";import{d as H,n as R,_ as D,o as i,c as f,L as a,a3 as F,U as t,Y as n,F as I,$ as P,Z as v,a as l,e as r,M as c,K as p,O as _}from"./@vue-f70ab1bd.js";import{u as E}from"./vuex-cc1858c6.js";import{b as K,u as U}from"./vue-router-29025daf.js";import{a as Y}from"./formatTime-936c40eb.js";import{f as Z,h as A,i as G,k as J}from"./@vicons-2f3cb6b9.js";import{o as Q,M as W,j as X,a as tt,L as et}from"./naive-ui-ddb574dd.js";const st={class:"nickname-wrap"},nt={class:"username-wrap"},ot={class:"timestamp"},at=["innerHTML"],it={class:"opt-item"},rt={class:"opt-item"},ct={class:"opt-item"},pt={class:"opt-item"},ft=H({__name:"post-item",props:{post:null},setup(x){const C=x;K();const d=U(),z=E(),e=R(()=>{let o=Object.assign({texts:[],imgs:[],videos:[],links:[],attachments:[],charge_attachments:[]},C.post);return o.contents.map(s=>{(+s.type==1||+s.type==2)&&o.texts.push(s),+s.type==3&&o.imgs.push(s),+s.type==4&&o.videos.push(s),+s.type==6&&o.links.push(s),+s.type==7&&o.attachments.push(s),+s.type==8&&o.charge_attachments.push(s)}),o}),k=o=>{d.push({name:"post",query:{id:o}})},b=(o,s)=>{if(o.target.dataset.detail){const m=o.target.dataset.detail.split(":");if(m.length===2){z.commit("refresh"),m[0]==="tag"?d.push({name:"home",query:{q:m[1],t:"tag"}}):d.push({name:"user",query:{username:m[1]}});return}}k(s)};return(o,s)=>{const m=Q,w=D("router-link"),h=W,y=S,O=$,T=V,q=j,u=X,B=tt,L=et;return i(),f("div",{class:"post-item",onClick:s[2]||(s[2]=g=>k(t(e).id))},[a(L,{"content-indented":""},F({avatar:n(()=>[a(m,{round:"",size:30,src:t(e).user.avatar},null,8,["src"])]),header:n(()=>[l("span",st,[a(w,{onClick:s[0]||(s[0]=v(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{username:t(e).user.username}}},{default:n(()=>[r(c(t(e).user.nickname),1)]),_:1},8,["to"])]),l("span",nt," @"+c(t(e).user.username),1),t(e).is_top?(i(),p(h,{key:0,class:"top-tag",type:"warning",size:"small",round:""},{default:n(()=>[r(" 置顶 ")]),_:1})):_("",!0),t(e).visibility==1?(i(),p(h,{key:1,class:"top-tag",type:"error",size:"small",round:""},{default:n(()=>[r(" 私密 ")]),_:1})):_("",!0),t(e).visibility==2?(i(),p(h,{key:2,class:"top-tag",type:"info",size:"small",round:""},{default:n(()=>[r(" 好友可见 ")]),_:1})):_("",!0)]),"header-extra":n(()=>[l("span",ot,c(t(e).ip_loc?t(e).ip_loc+" · ":t(e).ip_loc)+" "+c(t(Y)(t(e).created_on)),1)]),footer:n(()=>[t(e).attachments.length>0?(i(),p(y,{key:0,attachments:t(e).attachments},null,8,["attachments"])):_("",!0),t(e).charge_attachments.length>0?(i(),p(y,{key:1,attachments:t(e).charge_attachments,price:t(e).attachment_price},null,8,["attachments","price"])):_("",!0),t(e).imgs.length>0?(i(),p(O,{key:2,imgs:t(e).imgs},null,8,["imgs"])):_("",!0),t(e).videos.length>0?(i(),p(T,{key:3,videos:t(e).videos},null,8,["videos"])):_("",!0),t(e).links.length>0?(i(),p(q,{key:4,links:t(e).links},null,8,["links"])):_("",!0)]),action:n(()=>[a(B,{justify:"space-between"},{default:n(()=>[l("div",it,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(Z))]),_:1}),r(" "+c(t(e).upvote_count),1)]),l("div",rt,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(A))]),_:1}),r(" "+c(t(e).comment_count),1)]),l("div",ct,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(G))]),_:1}),r(" "+c(t(e).collection_count),1)]),l("div",pt,[a(u,{size:"18",class:"opt-item-icon"},{default:n(()=>[a(t(J))]),_:1}),r(" "+c(t(e).share_count),1)])]),_:1})]),_:2},[t(e).texts.length>0?{name:"description",fn:n(()=>[(i(!0),f(I,null,P(t(e).texts,g=>(i(),f("span",{key:g.id,class:"post-text",onClick:s[1]||(s[1]=v(M=>b(M,t(e).id),["stop"])),innerHTML:t(N)(g.content).content},null,8,at))),128))]),key:"0"}:void 0]),1024)])}}});export{ft as _};

@ -1 +1 @@
import{U as c}from"./naive-ui-ddb574dd.js";import{d as r,o as s,c as n,$ as l,a as o,L as t,F as p}from"./@vue-f70ab1bd.js";import{_ as i}from"./index-ce5b62d8.js";const m={class:"user"},d={class:"content"},u=r({__name:"post-skeleton",props:{num:{default:1}},setup(_){return(f,k)=>{const e=c;return s(!0),n(p,null,l(new Array(_.num),a=>(s(),n("div",{class:"skeleton-item",key:a},[o("div",m,[t(e,{circle:"",size:"small"})]),o("div",d,[t(e,{text:"",repeat:3}),t(e,{text:"",style:{width:"60%"}})])]))),128)}}});const b=i(u,[["__scopeId","data-v-ab0015b4"]]);export{b as _}; import{U as c}from"./naive-ui-ddb574dd.js";import{d as r,o as s,c as n,$ as l,a as o,L as t,F as p}from"./@vue-f70ab1bd.js";import{_ as i}from"./index-f6017bc3.js";const m={class:"user"},d={class:"content"},u=r({__name:"post-skeleton",props:{num:{default:1}},setup(_){return(f,k)=>{const e=c;return s(!0),n(p,null,l(new Array(_.num),a=>(s(),n("div",{class:"skeleton-item",key:a},[o("div",m,[t(e,{circle:"",size:"small"})]),o("div",d,[t(e,{text:"",repeat:3}),t(e,{text:"",style:{width:"60%"}})])]))),128)}}});const b=i(u,[["__scopeId","data-v-ab0015b4"]]);export{b as _};

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<title></title> <title></title>
<script type="module" crossorigin src="/assets/index-ce5b62d8.js"></script> <script type="module" crossorigin src="/assets/index-f6017bc3.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue-f70ab1bd.js"> <link rel="modulepreload" crossorigin href="/assets/@vue-f70ab1bd.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router-29025daf.js"> <link rel="modulepreload" crossorigin href="/assets/vue-router-29025daf.js">
<link rel="modulepreload" crossorigin href="/assets/vuex-cc1858c6.js"> <link rel="modulepreload" crossorigin href="/assets/vuex-cc1858c6.js">
@ -27,8 +27,8 @@
<link rel="modulepreload" crossorigin href="/assets/async-validator-dee29e8b.js"> <link rel="modulepreload" crossorigin href="/assets/async-validator-dee29e8b.js">
<link rel="modulepreload" crossorigin href="/assets/date-fns-975a2d8f.js"> <link rel="modulepreload" crossorigin href="/assets/date-fns-975a2d8f.js">
<link rel="modulepreload" crossorigin href="/assets/naive-ui-ddb574dd.js"> <link rel="modulepreload" crossorigin href="/assets/naive-ui-ddb574dd.js">
<link rel="modulepreload" crossorigin href="/assets/@vicons-fc06a0bb.js"> <link rel="modulepreload" crossorigin href="/assets/@vicons-2f3cb6b9.js">
<link rel="stylesheet" href="/assets/index-bea67790.css"> <link rel="stylesheet" href="/assets/index-6c46cd46.css">
<link rel="stylesheet" href="/assets/vfonts-7afd136d.css"> <link rel="stylesheet" href="/assets/vfonts-7afd136d.css">
</head> </head>

@ -1,163 +1,232 @@
import { request } from '@/utils/request'; import { request } from "@/utils/request";
/** 获取动态列表 */ /** 获取动态列表 */
export const getPosts = (params: NetParams.PostGetPosts): Promise<NetReq.PostGetPosts> => { export const getPosts = (
return request({ params: NetParams.PostGetPosts
method: 'get', ): Promise<NetReq.PostGetPosts> => {
url: '/v1/posts', return request({
params method: "get",
}); url: "/v1/posts",
params,
});
}; };
/** 获取标签列表 */ /** 获取标签列表 */
export const getTags = (params: NetParams.PostGetTags): Promise<NetReq.PostGetTags> => { export const getTags = (
return request({ params: NetParams.PostGetTags
method: 'get', ): Promise<NetReq.PostGetTags> => {
url: '/v1/tags', return request({
params method: "get",
}); url: "/v1/tags",
params,
});
}; };
/** 获取动态详情 */ /** 获取动态详情 */
export const getPost = (params: NetParams.PostGetPost): Promise<NetReq.PostGetPost> => { export const getPost = (
return request({ params: NetParams.PostGetPost
method: 'get', ): Promise<NetReq.PostGetPost> => {
url: '/v1/post', return request({
params method: "get",
}); url: "/v1/post",
params,
});
}; };
/** 获取动态点赞状态 */ /** 获取动态点赞状态 */
export const getPostStar = (params: NetParams.PostPostStar): Promise<NetReq.PostGetPostStar> => { export const getPostStar = (
return request({ params: NetParams.PostPostStar
method: 'get', ): Promise<NetReq.PostGetPostStar> => {
url: '/v1/post/star', return request({
params method: "get",
}); url: "/v1/post/star",
params,
});
}; };
/** 动态点赞 */ /** 动态点赞 */
export const postStar = (data: NetParams.PostPostStar): Promise<NetReq.PostPostStar> => { export const postStar = (
return request({ data: NetParams.PostPostStar
method: 'post', ): Promise<NetReq.PostPostStar> => {
url: '/v1/post/star', return request({
data method: "post",
}); url: "/v1/post/star",
data,
});
}; };
/** 获取动态收藏状态 */ /** 获取动态收藏状态 */
export const getPostCollection = (params: NetParams.PostGetPostCollection): Promise<NetReq.PostGetPostCollection> => { export const getPostCollection = (
return request({ params: NetParams.PostGetPostCollection
method: 'get', ): Promise<NetReq.PostGetPostCollection> => {
url: '/v1/post/collection', return request({
params method: "get",
}); url: "/v1/post/collection",
params,
});
}; };
/** 动态收藏 */ /** 动态收藏 */
export const postCollection = (data: NetParams.PostPostCollection): Promise<NetReq.PostPostCollection> => { export const postCollection = (
return request({ data: NetParams.PostPostCollection
method: 'post', ): Promise<NetReq.PostPostCollection> => {
url: '/v1/post/collection', return request({
data method: "post",
}); url: "/v1/post/collection",
data,
});
}; };
/** 获取动态评论列表 */ /** 获取动态评论列表 */
export const getPostComments = (params: NetParams.PostGetPostComments): Promise<NetReq.PostGetPostComments> => { export const getPostComments = (
return request({ params: NetParams.PostGetPostComments
method: 'get', ): Promise<NetReq.PostGetPostComments> => {
url: '/v1/post/comments', return request({
params method: "get",
}); url: "/v1/post/comments",
params,
});
}; };
/** 获取联系人列表 */ /** 获取联系人列表 */
export const getContacts = (params: NetParams.GetContacts): Promise<NetReq.GetContacts> => { export const getContacts = (
return request({ params: NetParams.GetContacts
method: 'get', ): Promise<NetReq.GetContacts> => {
url: '/v1/user/contacts', return request({
params method: "get",
}); url: "/v1/user/contacts",
params,
});
}; };
/** 发布动态 */ /** 发布动态 */
export const createPost = (data: NetParams.PostCreatePost): Promise<NetReq.PostCreatePost> => { export const createPost = (
return request({ data: NetParams.PostCreatePost
method: 'post', ): Promise<NetReq.PostCreatePost> => {
url: '/v1/post', return request({
data method: "post",
}); url: "/v1/post",
data,
});
}; };
/** 删除动态 */ /** 删除动态 */
export const deletePost = (data: NetParams.PostDeletePost): Promise<NetReq.PostDeletePost> => { export const deletePost = (
return request({ data: NetParams.PostDeletePost
method: 'delete', ): Promise<NetReq.PostDeletePost> => {
url: '/v1/post', return request({
data method: "delete",
}); url: "/v1/post",
data,
});
}; };
/** 锁定/解锁动态 */ /** 锁定/解锁动态 */
export const lockPost = (data: NetParams.PostLockPost): Promise<NetReq.PostLockPost> => { export const lockPost = (
return request({ data: NetParams.PostLockPost
method: 'post', ): Promise<NetReq.PostLockPost> => {
url: '/v1/post/lock', return request({
data method: "post",
}); url: "/v1/post/lock",
data,
});
}; };
/** 置顶/取消置顶动态 */ /** 置顶/取消置顶动态 */
export const stickPost = (data: NetParams.PostStickPost): Promise<NetReq.PostStickPost> => { export const stickPost = (
return request({ data: NetParams.PostStickPost
method: 'post', ): Promise<NetReq.PostStickPost> => {
url: '/v1/post/stick', return request({
data method: "post",
}); url: "/v1/post/stick",
data,
});
}; };
/** 置顶/取消置顶动态 */ /** 置顶/取消置顶动态 */
export const visibilityPost = (data: NetParams.PostVisibilityPost): Promise<NetReq.PostVisibilityPost> => { export const visibilityPost = (
return request({ data: NetParams.PostVisibilityPost
method: 'post', ): Promise<NetReq.PostVisibilityPost> => {
url: '/v1/post/visibility', return request({
data method: "post",
}); url: "/v1/post/visibility",
data,
});
}; };
/** 发布动态评论 */ /** 发布动态评论 */
export const createComment = (data: NetParams.PostCreateComment): Promise<NetReq.PostCreateComment> => { export const createComment = (
return request({ data: NetParams.PostCreateComment
method: 'post', ): Promise<NetReq.PostCreateComment> => {
url: '/v1/post/comment', return request({
data method: "post",
}); url: "/v1/post/comment",
data,
});
}; };
/** 删除评论 */ /** 删除评论 */
export const deleteComment = (data: NetParams.PostDeleteComment): Promise<NetReq.PostDeleteComment> => { export const deleteComment = (
return request({ data: NetParams.PostDeleteComment
method: 'delete', ): Promise<NetReq.PostDeleteComment> => {
url: '/v1/post/comment', return request({
data method: "delete",
}); url: "/v1/post/comment",
data,
});
}; };
/** 发布评论回复 */ /** 发布评论回复 */
export const createCommentReply = (data: NetParams.PostCreateCommentReply): Promise<NetReq.PostCreateCommentReply> => { export const createCommentReply = (
return request({ data: NetParams.PostCreateCommentReply
method: 'post', ): Promise<NetReq.PostCreateCommentReply> => {
url: '/v1/post/comment/reply', return request({
data method: "post",
}); url: "/v1/post/comment/reply",
data,
});
}; };
/** 删除评论回复 */ /** 删除评论回复 */
export const deleteCommentReply = (data: NetParams.PostDeleteCommentReply): Promise<NetReq.PostDeleteCommentReply> => { export const deleteCommentReply = (
return request({ data: NetParams.PostDeleteCommentReply
method: 'delete', ): Promise<NetReq.PostDeleteCommentReply> => {
url: '/v1/post/comment/reply', return request({
data method: "delete",
}); url: "/v1/post/comment/reply",
data,
});
};
/** 置顶/取消置顶话题 */
export const stickTopic = (
data: NetParams.PostStickTopic
): Promise<NetReq.PostStickTopic> => {
return request({
method: "post",
url: "/v1/topic/stick",
data,
});
};
/** 关注话题 */
export const followTopic = (
data: NetParams.PostFollowTopic
): Promise<NetReq.PostFollowTopic> => {
return request({
method: "post",
url: "/v1/topic/follow",
data,
});
};
/** 取消关注话题 */
export const unfollowTopic = (
data: NetParams.PostUnfollowTopic
): Promise<NetReq.PostUnfollowTopic> => {
return request({
method: "post",
url: "/v1/topic/unfollow",
data,
});
}; };

@ -13,9 +13,31 @@
</template> </template>
</n-input> </n-input>
</div> </div>
<n-card v-if="showFollowTopics" class="hottopic-wrap" title="关注话题" embedded :bordered="false" size="small">
<n-spin :show="loading">
<div class="hot-tag-item" v-for="tag in followTags" :key="tag.id">
<router-link
class="hash-link"
:to="{
name: 'home',
query: {
q: tag.tag,
t: 'tag',
},
}"
>
#{{ tag.tag }}
</router-link>
<div class="post-num">
{{ formatQuoteNum(tag.quote_num) }}
</div>
</div>
</n-spin>
</n-card>
<n-card class="hottopic-wrap" title="热门话题" embedded :bordered="false" size="small"> <n-card class="hottopic-wrap" title="热门话题" embedded :bordered="false" size="small">
<n-spin :show="loading"> <n-spin :show="loading">
<div class="hot-tag-item" v-for="tag in tags" :key="tag.id"> <div class="hot-tag-item" v-for="tag in hotTags" :key="tag.id">
<router-link <router-link
class="hash-link" class="hash-link"
:to="{ :to="{
@ -60,13 +82,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed, watch } from 'vue';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { getTags } from '@/api/post'; import { getTags } from '@/api/post';
import { Search } from '@vicons/ionicons5'; import { Search } from '@vicons/ionicons5';
const tags = ref<Item.TagProps[]>([]); const hotTags = ref<Item.TagProps[]>([]);
const followTags = ref<Item.TagProps[]>([]);
const loading = ref(false); const loading = ref(false);
const keyword = ref(''); const keyword = ref('');
const store = useStore(); const store = useStore();
@ -77,14 +100,17 @@ const copyrightLeftLink = import.meta.env.VITE_COPYRIGHT_LEFT_LINK
const copyrightRight = import.meta.env.VITE_COPYRIGHT_RIGHT const copyrightRight = import.meta.env.VITE_COPYRIGHT_RIGHT
const copyrightRightLink = import.meta.env.VITE_COPYRIGHT_RIGHT_LINK const copyrightRightLink = import.meta.env.VITE_COPYRIGHT_RIGHT_LINK
const loadTags = () => { const loadHotTags = () => {
loading.value = true; loading.value = true;
getTags({ getTags({
type: 'hot', type: 'hot_extral',
num: 12, num: 12,
extral_num: 8,
}) })
.then((res) => { .then((res) => {
tags.value = res.topics; hotTags.value = res.topics;
followTags.value = res.extral_topics??[];
showFollowTopics.value = true
loading.value = false; loading.value = false;
}) })
.catch((err) => { .catch((err) => {
@ -106,8 +132,26 @@ const handleSearch = () => {
}, },
}); });
}; };
const showFollowTopics = computed({
get: () => {
return store.state.userLogined && followTags.value.length !==0;
},
set: (newVal) => {
// do nothing
},
});
watch(
() => ({
refreshTopicFollow: store.state.refreshTopicFollow,
}),
(to, from) => {
if (to.refreshTopicFollow !== from.refreshTopicFollow) {
loadHotTags();
}
}
);
onMounted(() => { onMounted(() => {
loadTags(); loadHotTags();
}); });
</script> </script>
@ -143,9 +187,11 @@ onMounted(() => {
} }
} }
.copyright-wrap { .hottopic-wrap {
margin-top: 10px; margin-bottom: 10px;
}
.copyright-wrap {
.copyright { .copyright {
font-size: 12px; font-size: 12px;
opacity: 0.75; opacity: 0.75;

@ -239,7 +239,6 @@ const goHome = () => {
if (route.path === '/') { if (route.path === '/') {
store.commit('refresh'); store.commit('refresh');
} }
goRouter('home'); goRouter('home');
}; };
const triggerAuth = (key: string) => { const triggerAuth = (key: string) => {
@ -248,6 +247,7 @@ const triggerAuth = (key: string) => {
}; };
const handleLogout = () => { const handleLogout = () => {
store.commit('userLogout'); store.commit('userLogout');
goHome()
}; };
window.$store = store; window.$store = store;
window.$message = useMessage(); window.$message = useMessage();

@ -0,0 +1,184 @@
<template>
<div v-if="!checkFollowing || (checkFollowing && tag.is_following === 1)" class="tag-item">
<n-thing>
<template #header>
<n-tag
type="success"
size="large"
round
:key="tag.id"
>
<router-link
class="hash-link"
:to="{
name: 'home',
query: {
q: tag.tag,
t: 'tag',
},
}"
>
#{{ tag.tag }}
</router-link>
<span v-if="!showAction" class="tag-quote">({{ tag.quote_num }})</span>
<span v-if="showAction" class="tag-quote tag-follow">({{ tag.quote_num }})</span>
<template #avatar>
<n-avatar :src="tag.user.avatar" />
</template>
</n-tag>
</template>
<template #header-extra>
<div
v-if="showAction"
class="options">
<n-dropdown
placement="bottom-end"
trigger="click"
size="small"
:options="tagOptions"
@select="handleTagAction"
>
<n-button type="success" quaternary circle block>
<template #icon>
<n-icon>
<more-vert-outlined />
</n-icon>
</template>
</n-button>
</n-dropdown>
</div>
</template>
</n-thing>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { MoreVertOutlined } from '@vicons/material';
import type { DropdownOption } from 'naive-ui';
import { stickTopic, followTopic, unfollowTopic } from '@/api/post';
const hasFollowing= ref(false);
const props = withDefaults(
defineProps<{
tag: Item.TagProps;
showAction: boolean;
checkFollowing: boolean;
}>(),
{}
);
const tagOptions = computed(() => {
let options: DropdownOption[] = [];
if (props.tag.is_following === 0) {
options.push({
label: '',
key: 'follow',
});
} else {
if (props.tag.is_top === 0) {
options.push({
label: '',
key: 'stick',
});
} else {
options.push({
label: '',
key: 'unstick',
});
}
options.push({
label: '',
key: 'unfollow',
});
}
return options;
});
const handleTagAction = (
item: 'follow' | 'unfollow' | 'stick' | 'unstick'
) => {
switch (item) {
case 'follow':
followTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_following = 1
window.$message.success(`关注成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unfollow':
unfollowTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_following = 0
window.$message.success(`取消关注`);
})
.catch((err) => {
console.log(err);
});
break;
case 'stick':
stickTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_top = res.top_status
window.$message.success(`置顶成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unstick':
stickTopic({
topic_id: props.tag.id
})
.then((res) => {
props.tag.is_top = res.top_status
window.$message.success(`取消置顶`);
})
.catch((err) => {
console.log(err);
});
break;
default:
break;
}
};
onMounted(() => {
hasFollowing.value = false
});
</script>
<style lang="less">
.tag-item {
.tag-quote {
margin-left: 12px;
font-size: 14px;
opacity: 0.75;
}
.tag-follow {
margin-right: 22px;
}
.options {
margin-left: -32px;
margin-bottom: 4px;
opacity: 0.55;
}
.n-thing {
.n-thing-header {
margin-bottom: 0px;
}
.n-thing-avatar-header-wrapper {
align-items: center;
}
}
}
</style>

@ -4,6 +4,7 @@ import { createStore } from "vuex";
export default createStore({ export default createStore({
state: { state: {
refresh: Date.now(), refresh: Date.now(),
refreshTopicFollow: Date.now(),
theme: localStorage.getItem("PAOPAO_THEME"), theme: localStorage.getItem("PAOPAO_THEME"),
collapsedLeft: document.body.clientWidth <= 821, collapsedLeft: document.body.clientWidth <= 821,
collapsedRight: document.body.clientWidth <= 821, collapsedRight: document.body.clientWidth <= 821,
@ -11,6 +12,7 @@ export default createStore({
desktopModelShow: document.body.clientWidth > 821, desktopModelShow: document.body.clientWidth > 821,
authModalShow: false, authModalShow: false,
authModelTab: "signin", authModelTab: "signin",
userLogined: false,
userInfo: { userInfo: {
id: 0, id: 0,
username: "", username: "",
@ -21,6 +23,9 @@ export default createStore({
refresh(state, refresh) { refresh(state, refresh) {
state.refresh = refresh || Date.now(); state.refresh = refresh || Date.now();
}, },
refreshTopicFollow(state) {
state.refreshTopicFollow = Date.now();
},
triggerTheme(state, theme) { triggerTheme(state, theme) {
state.theme = theme; state.theme = theme;
}, },
@ -40,10 +45,14 @@ export default createStore({
}, },
updateUserinfo(state, data) { updateUserinfo(state, data) {
state.userInfo = data; state.userInfo = data;
if (state.userInfo.id > 0) {
state.userLogined = true;
}
}, },
userLogout(state) { userLogout(state) {
localStorage.removeItem("PAOPAO_TOKEN"); localStorage.removeItem("PAOPAO_TOKEN");
state.userInfo = { id: 0, nickname: "", username: "" }; state.userInfo = { id: 0, nickname: "", username: "" };
state.userLogined = false;
}, },
}, },
actions: {}, actions: {},

@ -290,6 +290,10 @@ declare module Item {
modified_on?: number; modified_on?: number;
/** 删除时间 */ /** 删除时间 */
deleted_on?: number; deleted_on?: number;
/** 是否关注0为未关注1为已关注 */
is_following?: 0 | 1;
/** 是否置顶0为未置顶1为已置顶 */
is_top?: 0 | 1;
/** 是否删除0为未删除1为已删除 */ /** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1; is_del?: 0 | 1;
} }

@ -165,8 +165,9 @@ declare module NetParams {
} }
interface PostGetTags { interface PostGetTags {
type: "hot" | "new"; type: "hot" | "new" | "follow" | "hot_extral";
num: number; num: number;
extral_num?: number;
} }
interface PostGetPostComments { interface PostGetPostComments {
@ -218,4 +219,16 @@ declare module NetParams {
interface PostDeleteCommentReply { interface PostDeleteCommentReply {
id: number; id: number;
} }
interface PostStickTopic {
topic_id: number;
}
interface PostFollowTopic {
topic_id: number;
}
interface PostUnfollowTopic {
topic_id: number;
}
} }

@ -1,197 +1,181 @@
declare module NetReq { declare module NetReq {
interface AuthUserLogin {
token: string;
}
interface AuthUserLogin { interface AuthUserRegister {
token: string /** 用户UID */
} id: number;
/** 用户名 */
username: string;
}
interface AuthUserRegister { type AuthUserInfo = Item.UserInfo;
/** 用户UID */
id: number,
/** 用户名 */
username: string
}
type AuthUserInfo = Item.UserInfo interface AuthUpdateUserPassword {}
interface AuthUpdateUserPassword { interface UserGetCollections {
/** 帖子列表 */
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
} interface UserGetSuggestUsers {
suggest: string[];
}
interface UserGetCollections { interface UserGetSuggestTags {
/** 帖子列表 */ suggest: string[];
list: Item.PostProps[], }
/** 页码信息 */
pager: Item.PagerProps
}
interface UserGetSuggestUsers { interface UserPrecheckAttachment {
suggest: string[] paid: number;
} }
interface UserGetSuggestTags { interface UserGetAttachment {
suggest: string[] signed_url: string;
} }
interface UserPrecheckAttachment { interface UserGetUnreadMsgCount {
paid: number count: number;
} }
interface UserGetAttachment { interface UserGetMessages {
signed_url: string /** 消息列表 */
} list: Item.MessageProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
interface UserGetUserPosts {
/** 帖子列表 */
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
interface UserGetUnreadMsgCount { type UserGetUserProfile = Item.UserInfo;
count: number
}
interface UserGetMessages { interface UserGetBills {
/** 消息列表 */ list: Item.BillProps[];
list: Item.MessageProps[], /** 页码信息 */
/** 页码信息 */ pager: Item.PagerProps;
pager: Item.PagerProps }
}
interface UserGetUserPosts { interface UserReqRecharge {
/** 帖子列表 */ id: number;
list: Item.PostProps[], pay: string;
/** 页码信息 */ }
pager: Item.PagerProps
}
type UserGetUserProfile = Item.UserInfo interface UserGetRecharge {
status: string;
}
interface UserGetBills { interface UserBindUserPhone {}
list: Item.BillProps[],
/** 页码信息 */
pager: Item.PagerProps
}
interface UserReqRecharge { interface UserGetCaptcha {
id: number, id: string;
pay: string /** 头像图片 base64 */
} b64s: string;
}
interface UserGetRecharge { interface UserChangeNickname {}
status: string
}
interface UserBindUserPhone { interface UserChangePassword {}
} interface UserChangeStatus {}
interface UserGetCaptcha { interface AddFriend {}
id: string,
/** 头像图片 base64 */
b64s: string
}
interface UserChangeNickname { interface DeleteFriend {}
} interface GetContacts {
contacts: Item.ContactsItemProps;
total: number;
}
interface UserChangePassword { interface RejectFriend {}
} interface RequestingFriend {}
interface UserChangeStatus { type PostGetPost = Item.PostProps;
} interface PostGetPosts {
/** 帖子列表 */
list: Item.PostProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
interface AddFriend { interface PostLockPost {
/** 锁定状态0为未锁定1为锁定 */
lock_status: 0 | 1;
}
} interface PostStickPost {
/** 置顶状态0为未置顶1为置顶 */
top_status: 0 | 1;
}
interface DeleteFriend { interface PostVisibilityPost {
/** 可见性0为公开1为私密2为好友可见 */
visibility_status: import("@/utils/IEnum").VisibilityEnum;
}
} interface PostGetPostStar {
status: boolean;
}
interface GetContacts { interface PostPostStar {
contacts: Item.ContactsItemProps, status: boolean;
total: number }
}
interface RejectFriend { interface PostGetPostCollection {
status: boolean;
}
} interface PostPostCollection {
status: boolean;
}
interface RequestingFriend { interface PostGetTags {
topics: Item.TagProps[];
extral_topics?: Item.TagProps[];
}
} interface PostGetPostComments {
/** 评论列表 */
list: Item.CommentProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
type PostGetPost = Item.PostProps type PostCreatePost = Item.PostProps;
interface PostGetPosts { interface PostDeletePost {}
/** 帖子列表 */
list: Item.PostProps[],
/** 页码信息 */
pager: Item.PagerProps
}
interface PostLockPost { type PostCreateComment = Item.CommentProps;
/** 锁定状态0为未锁定1为锁定 */
lock_status: 0 | 1
}
interface PostStickPost { interface PostDeleteComment {}
/** 置顶状态0为未置顶1为置顶 */
top_status: 0 | 1
}
interface PostVisibilityPost { type PostCreateCommentReply = Item.ReplyProps;
/** 可见性0为公开1为私密2为好友可见 */
visibility_status: import('@/utils/IEnum').VisibilityEnum
}
interface PostGetPostStar { interface PostDeleteCommentReply {}
status: boolean
}
interface PostPostStar { interface GetContacts {
status: boolean /** 评论列表 */
} list: Item.ContactItemProps[];
/** 页码信息 */
pager: Item.PagerProps;
}
interface PostGetPostCollection { interface PostStickTopic {
status: boolean /** 置顶状态0为未置顶1为置顶 */
} top_status: 0 | 1;
}
interface PostPostCollection { interface PostFollowTopic {}
status: boolean
}
interface PostGetTags {
topics: Item.TagProps[]
}
interface PostGetPostComments {
/** 评论列表 */
list: Item.CommentProps[],
/** 页码信息 */
pager: Item.PagerProps
}
type PostCreatePost = Item.PostProps
interface PostDeletePost {
}
type PostCreateComment = Item.CommentProps
interface PostDeleteComment {
}
type PostCreateCommentReply = Item.ReplyProps
interface PostDeleteCommentReply {
}
interface GetContacts {
/** 评论列表 */
list: Item.ContactItemProps[],
/** 页码信息 */
pager: Item.PagerProps
}
interface PostUnfollowTopic {}
} }

@ -14,17 +14,13 @@
</n-spin> </n-spin>
</n-list-item> </n-list-item>
<div class="comment-opts-wrap" v-if="post.id > 0"> <div class="comment-opts-wrap" v-if="post.id > 0">
<n-space justify="space-between"> <n-tabs type="bar" justify-content="end" size="small" animated @update:value="commentTab">
<div class="comment-title-item"> <template #prefix>
<span comment-title-item></span> <span class="comment-title-item"></span>
</div> </template>
<div class="comment-opt-item "> <n-tab-pane name="default" tab="默认" />
<n-tabs type="bar" size="small" animated @update:value="commentTab"> <n-tab-pane name="newest" tab="最新" />
<n-tab-pane name="default" tab="默认" /> </n-tabs>
<n-tab-pane name="newest" tab="最新" />
</n-tabs>
</div>
</n-space>
</div> </div>
<n-list-item v-if="post.id > 0"> <n-list-item v-if="post.id > 0">
<compose-comment <compose-comment
@ -131,22 +127,15 @@ watch(postId, () => {
.detail-wrap { .detail-wrap {
min-height: 100px; min-height: 100px;
} }
.comment-opts-wrap { .comment-opts-wrap {
margin-top: 6px; padding-top: 6px;
.comment-opt-item { padding-left: 16px;
display: flex; padding-right: 16px;
padding-left: 16px; opacity: 0.75;
padding-right: 16px;
align-items: center;
opacity: 0.75;
}
.comment-title-item { .comment-title-item {
padding-left: 16px;
padding-top: 4px; padding-top: 4px;
font-size: 16px; font-size: 16px;
text-align: center; text-align: center;
opacity: 0.75;
} }
} }
.dark { .dark {

@ -6,33 +6,23 @@
<n-tabs type="line" animated @update:value="changeTab"> <n-tabs type="line" animated @update:value="changeTab">
<n-tab-pane name="hot" tab="热门" /> <n-tab-pane name="hot" tab="热门" />
<n-tab-pane name="new" tab="最新" /> <n-tab-pane name="new" tab="最新" />
<n-tab-pane v-if="store.state.userLogined"
name="follow" tab="关注" />
<template v-if="store.state.userLogined" #suffix>
<n-tag v-model:checked="tagsChecked" checkable>
{{tagsEditText}}
</n-tag>
</template>
</n-tabs> </n-tabs>
<n-spin :show="loading"> <n-spin :show="loading">
<n-space> <n-space>
<n-tag <tag-item
class="tag-item"
type="success"
round
v-for="tag in tags" v-for="tag in tags"
:key="tag.id" :tag="tag"
:showAction="store.state.userLogined && tagsChecked"
:checkFollowing="inFollwTab"
> >
<router-link </tag-item>
class="hash-link"
:to="{
name: 'home',
query: {
q: tag.tag,
t: 'tag',
},
}"
>
#{{ tag.tag }}
</router-link>
<span class="tag-hot">({{ tag.quote_num }})</span>
<template #avatar>
<n-avatar :src="tag.user.avatar" />
</template>
</n-tag>
</n-space> </n-space>
</n-spin> </n-spin>
</n-list> </n-list>
@ -40,13 +30,35 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed, watch} from 'vue';
import { getTags } from '@/api/post'; import { getTags } from '@/api/post';
import { useStore } from 'vuex';
const store = useStore();
const tags = ref<Item.TagProps[]>([]); const tags = ref<Item.TagProps[]>([]);
const tagType = ref<"hot" | "new">('hot'); const tagType = ref<"hot" | "new" | "follow">('hot');
const loading = ref(false); const loading = ref(false);
const tagsChecked = ref(false)
const inFollwTab = ref(false)
watch(tagsChecked, () => {
if (!tagsChecked.value) {
window.$message.success("保存成功");
store.commit("refreshTopicFollow")
}
});
const tagsEditText = computed({
get: () => {
let text = "编辑";
if (tagsChecked.value) {
text = "保存";
}
return text;
},
set: (newVal) => {
// do nothing
},
});
const loadTags = () => { const loadTags = () => {
loading.value = true; loading.value = true;
getTags({ getTags({
@ -58,11 +70,17 @@ const loadTags = () => {
loading.value = false; loading.value = false;
}) })
.catch((err) => { .catch((err) => {
console.log(err);
loading.value = false; loading.value = false;
}); });
}; };
const changeTab = (tab: "hot" | "new") => { const changeTab = (tab: "hot" | "new" | "follow") => {
tagType.value = tab; tagType.value = tab;
if (tab == "follow") {
inFollwTab.value = true
} else {
inFollwTab.value = false
}
loadTags(); loadTags();
}; };
onMounted(() => { onMounted(() => {
@ -73,13 +91,6 @@ onMounted(() => {
<style lang="less" scoped> <style lang="less" scoped>
.tags-wrap { .tags-wrap {
padding: 20px; padding: 20px;
.tag-item {
.tag-hot {
margin-left: 12px;
font-size: 12px;
opacity: 0.75;
}
}
} }
.dark { .dark {
.tags-wrap { .tags-wrap {

Loading…
Cancel
Save