Merge branch 'x/gorm' into x/sqlc

r/paopao-ce-pro
Michael Li 2 years ago
commit a1557403fb
No known key found for this signature in database

@ -147,6 +147,7 @@ All notable changes to paopao-ce are documented in this file.
WHERE is_del=0
GROUP BY user_id;
```
- add message filter support for message page.
## 0.4.2
### Fixed

@ -220,13 +220,17 @@ func RegisterCoreServant(e *gin.Engine, s Core) {
default:
}
req := new(web.GetMessagesReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetMessages(req)
s.Render(c, resp, err)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "/user/info", func(c *gin.Context) {
select {

@ -0,0 +1,66 @@
// Code generated by go-mir. DO NOT EDIT.
// versions:
// - mir v4.0.0
package v1
import (
"net/http"
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
)
type Trends interface {
_default_
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
GetIndexTrends(*web.GetIndexTrendsReq) (*web.GetIndexTrendsResp, mir.Error)
mustEmbedUnimplementedTrendsServant()
}
// RegisterTrendsServant register Trends servant to gin
func RegisterTrendsServant(e *gin.Engine, s Trends) {
router := e.Group("v1")
// use chain for router
middlewares := s.Chain()
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "/trends/index", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetIndexTrendsReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetIndexTrends(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
}
// UnimplementedTrendsServant can be embedded to have forward compatible implementations.
type UnimplementedTrendsServant struct{}
func (UnimplementedTrendsServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedTrendsServant) GetIndexTrends(req *web.GetIndexTrendsReq) (*web.GetIndexTrendsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedTrendsServant) mustEmbedUnimplementedTrendsServant() {}

@ -29,6 +29,8 @@ const (
PrefixIdxTweetsNewest = "paopao:index:tweets:newest:"
PrefixIdxTweetsHots = "paopao:index:tweets:hots:"
PrefixIdxTweetsFollowing = "paopao:index:tweets:following:"
PrefixIdxTrends = "paopao:index:trends:"
PrefixMessages = "paopao:messages:"
PrefixUserInfo = "paopao:userinfo:"
PrefixUserInfoById = "paopao:userinfo:id:"
PrefixUserInfoByName = "paopao:userinfo:name:"

@ -12,9 +12,11 @@ Cache:
UserTweetsExpire: 60 # 获取用户推文列表过期时间,单位秒, 默认60s
IndexTweetsExpire: 120 # 获取广场推文列表过期时间,单位秒, 默认120s
TweetCommentsExpire: 180 # 获取推文评论过期时间,单位秒, 默认180s
IndexTrendsExpire: 300 # 获取广场动态信息过期时间,单位秒, 默认300s
OnlineUserExpire: 300 # 标记在线用户 过期时间,单位秒, 默认300s
UserInfoExpire: 300 # 获取用户信息过期时间,单位秒, 默认300s
UserRelationExpire: 600 # 用户关系信息过期时间,单位秒, 默认600s
MessagesExpire: 60 # 消息列表过期时间,单位秒, 默认60s
EventManager: # 事件管理器的配置参数
MinWorker: 64 # 最小后台工作者, 设置范围[5, ++], 默认64
MaxEventBuf: 128 # 最大log缓存条数, 设置范围[10, ++], 默认128

@ -102,6 +102,8 @@ type cacheConf struct {
UnreadMsgExpire int64
UserTweetsExpire int64
IndexTweetsExpire int64
MessagesExpire int64
IndexTrendsExpire int64
TweetCommentsExpire int64
OnlineUserExpire int64
UserInfoExpire int64

@ -21,9 +21,13 @@ type DataService interface {
TweetHelpService
// 推文指标服务
UserMetricServantA
TweetMetricServantA
CommentMetricServantA
// 动态信息相关服务
TrendsManageServantA
// 评论服务
CommentService
CommentManageService

@ -0,0 +1,16 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package cs
const (
// 消息列表样式
StyleMsgAll MessageStyle = "all"
StyleMsgSystem MessageStyle = "system"
StyleMsgWhisper MessageStyle = "whisper"
StyleMsgRequesting MessageStyle = "requesting"
StyleMsgUnread MessageStyle = "unread"
)
type MessageStyle string

@ -2,11 +2,13 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// Package cs contain core data service interface type
// model define
package cs
const (
MetricActionCreateTweet uint8 = iota
MetricActionDeleteTweet
)
type TweetMetric struct {
PostId int64
CommentCount int64

@ -0,0 +1,12 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package cs
type TrendsItem struct {
Username string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
IsFresh bool `json:"is_fresh" gorm:"-"`
}

@ -5,6 +5,7 @@
package core
import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
)
@ -14,6 +15,5 @@ type MessageService interface {
GetUnreadCount(userID int64) (int64, error)
GetMessageByID(id int64) (*ms.Message, error)
ReadMessage(message *ms.Message) error
GetMessages(userId int64, offset, limit int) ([]*ms.MessageFormated, error)
GetMessageCount(userId int64) (int64, error)
GetMessages(userId int64, style cs.MessageStyle, limit, offset int) ([]*ms.MessageFormated, int64, error)
}

@ -0,0 +1,14 @@
// Copyright 2022 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package core
import (
"github.com/rocboss/paopao-ce/internal/core/cs"
)
// TrendsManageServantA 动态信息管理服务
type TrendsManageServantA interface {
GetIndexTrends(userId int64, limit int, offset int) ([]*cs.TrendsItem, int64, error)
}

@ -4,7 +4,9 @@
package core
import "github.com/rocboss/paopao-ce/internal/core/ms"
import (
"github.com/rocboss/paopao-ce/internal/core/ms"
)
// UserManageService 用户管理服务
type UserManageService interface {

@ -39,7 +39,9 @@ type dataSrv struct {
core.CommentService
core.CommentManageService
core.CommentMetricServantA
core.TrendsManageServantA
core.UserManageService
core.UserMetricServantA
core.ContactManageService
core.FollowingManageService
core.UserRelationService
@ -59,11 +61,13 @@ func NewDataService() (core.DataService, core.VersionInfo) {
db := conf.MustGormDB()
pvs := security.NewPhoneVerifyService()
tms := newTweetMetricServentA(db)
ums := newUserMetricServentA(db)
cms := newCommentMetricServentA(db)
cis := cache.NewEventCacheIndexSrv(tms)
ds := &dataSrv{
TweetMetricServantA: tms,
CommentMetricServantA: cms,
UserMetricServantA: ums,
WalletService: newWalletService(db),
MessageService: newMessageService(db),
TopicService: newTopicService(db),
@ -72,7 +76,8 @@ func NewDataService() (core.DataService, core.VersionInfo) {
TweetHelpService: newTweetHelpService(db),
CommentService: newCommentService(db),
CommentManageService: newCommentManageService(db),
UserManageService: newUserManageService(db),
TrendsManageServantA: newTrendsManageServentA(db),
UserManageService: newUserManageService(db, ums),
ContactManageService: newContactManageService(db),
FollowingManageService: newFollowingManageService(db),
UserRelationService: newUserRelationService(db),

@ -6,6 +6,7 @@ package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"gorm.io/gorm"
@ -46,19 +47,35 @@ func (s *messageSrv) ReadMessage(message *ms.Message) error {
return message.Update(s.db)
}
func (s *messageSrv) GetMessages(userId int64, offset, limit int) ([]*ms.MessageFormated, error) {
messages, err := (&dbr.Message{}).List(s.db, userId, offset, limit)
if err != nil {
return nil, err
func (s *messageSrv) GetMessages(userId int64, style cs.MessageStyle, limit int, offset int) (res []*ms.MessageFormated, total int64, err error) {
var messages []*dbr.Message
db := s.db.Table(_message_)
// 1动态2评论3回复4私信5好友申请99系统通知'
switch style {
case cs.StyleMsgSystem:
db = db.Where("receiver_user_id=? AND type IN (1, 2, 3, 99)", userId)
case cs.StyleMsgWhisper:
db = db.Where("(receiver_user_id=? OR sender_user_id=?) AND type=4", userId, userId)
case cs.StyleMsgRequesting:
db = db.Where("receiver_user_id=? AND type=5", userId)
case cs.StyleMsgUnread:
db = db.Where("receiver_user_id=? AND is_read=0", userId)
case cs.StyleMsgAll:
fallthrough
default:
db = db.Where("receiver_user_id=? OR (sender_user_id=? AND type=4)", userId, userId)
}
if err = db.Count(&total).Error; err != nil || total == 0 {
return
}
if offset >= 0 && limit > 0 {
db = db.Limit(limit).Offset(offset)
}
if err = db.Order("id DESC").Find(&messages).Error; err != nil {
return
}
mfs := []*dbr.MessageFormated{}
for _, message := range messages {
mf := message.Format()
mfs = append(mfs, mf)
res = append(res, message.Format())
}
return mfs, nil
}
func (s *messageSrv) GetMessageCount(userId int64) (int64, error) {
return (&dbr.Message{}).Count(s.db, userId)
return
}

@ -63,9 +63,23 @@ func (s *commentMetricSrvA) DeleteCommentMetric(commentId int64) (err error) {
return (&dbr.CommentMetric{CommentId: commentId}).Delete(s.db)
}
func (s *userMetricSrvA) UpdateUserMetric(userId int64, action uint8) error {
// TODO
return cs.ErrNotImplemented
func (s *userMetricSrvA) UpdateUserMetric(userId int64, action uint8) (err error) {
metric := &dbr.UserMetric{}
db := s.db.Model(metric)
if err = db.Where("user_id=?", userId).First(metric).Error; err != nil {
metric = &dbr.UserMetric{
UserId: userId,
}
}
switch action {
case cs.MetricActionCreateTweet:
metric.TweetsCount++
case cs.MetricActionDeleteTweet:
if metric.TweetsCount > 0 {
metric.TweetsCount--
}
}
return db.Save(metric).Error
}
func (s *userMetricSrvA) AddUserMetric(userId int64) (err error) {

@ -0,0 +1,38 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"fmt"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs"
"gorm.io/gorm"
)
type trendsSrvA struct {
db *gorm.DB
}
func (s *trendsSrvA) GetIndexTrends(userId int64, limit int, offset int) (res []*cs.TrendsItem, total int64, err error) {
db := s.db.Table(_user_).
Joins(fmt.Sprintf("JOIN %s c ON c.friend_id=%s.id AND c.user_id=? AND c.is_del=0", _contact_, _user_), userId).
Joins(fmt.Sprintf("JOIN %s m ON c.friend_id=m.user_id AND m.tweets_count>0 AND m.is_del=0", _userMetric_)).
Where(fmt.Sprintf("%s.is_del=0", _user_))
if err = db.Count(&total).Error; err != nil || total == 0 {
return
}
if offset >= 0 && limit > 0 {
db = db.Limit(limit).Offset(offset)
}
err = db.Find(&res).Error
return
}
func newTrendsManageServentA(db *gorm.DB) core.TrendsManageServantA {
return &trendsSrvA{
db: db,
}
}

@ -508,7 +508,7 @@ func (s *tweetSrv) getUserRelation(userId int64) (beFriendIds []int64, beFollowI
// 找到item即删数据库已经保证唯一性
if beFollowIds[i] == id {
lastIdx := len(beFollowIds) - 1
beFriendIds[i] = beFriendIds[lastIdx]
beFollowIds[i] = beFollowIds[lastIdx]
beFollowIds = beFollowIds[:lastIdx]
break
}

@ -19,15 +19,17 @@ var (
type userManageSrv struct {
db *gorm.DB
ums core.UserMetricServantA
}
type userRelationSrv struct {
db *gorm.DB
}
func newUserManageService(db *gorm.DB) core.UserManageService {
func newUserManageService(db *gorm.DB, ums core.UserMetricServantA) core.UserManageService {
return &userManageSrv{
db: db,
ums: ums,
}
}
@ -81,8 +83,12 @@ func (s *userManageSrv) GetUsersByKeyword(keyword string) ([]*ms.User, error) {
}
}
func (s *userManageSrv) CreateUser(user *dbr.User) (*ms.User, error) {
return user.Create(s.db)
func (s *userManageSrv) CreateUser(user *dbr.User) (res *ms.User, err error) {
if res, err = user.Create(s.db); err == nil {
// 宽松处理错误
s.ums.AddUserMetric(res.ID)
}
return
}
func (s *userManageSrv) UpdateUser(user *ms.User) error {

@ -7,11 +7,15 @@ package web
import (
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/xerror"
)
type MessageStyle = cs.MessageStyle
type ChangeAvatarReq struct {
BaseInfo `json:"-" binding:"-"`
Avatar string `json:"avatar" form:"avatar" binding:"required"`
@ -40,9 +44,15 @@ type UserInfoResp struct {
Followings int64 `json:"followings"`
}
type GetMessagesReq BasePageReq
type GetMessagesReq struct {
SimpleInfo `json:"-" binding:"-"`
joint.BasePageInfo
Style MessageStyle `form:"style" binding:"required"`
}
type GetMessagesResp base.PageResp
type GetMessagesResp struct {
joint.CachePageResp
}
type ReadMessageReq struct {
SimpleInfo `json:"-" binding:"-"`
@ -120,10 +130,6 @@ func (r *UserInfoReq) Bind(c *gin.Context) mir.Error {
return nil
}
func (r *GetMessagesReq) Bind(c *gin.Context) mir.Error {
return (*BasePageReq)(r).Bind(c)
}
func (r *GetCollectionsReq) Bind(c *gin.Context) mir.Error {
return (*BasePageReq)(r).Bind(c)
}

@ -4,7 +4,9 @@
package web
import "github.com/rocboss/paopao-ce/internal/servants/base"
import (
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type RequestingFriendReq struct {
BaseInfo `json:"-" binding:"-"`

@ -0,0 +1,18 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/rocboss/paopao-ce/internal/model/joint"
)
type GetIndexTrendsReq struct {
SimpleInfo `json:"-" binding:"-"`
joint.BasePageInfo
}
type GetIndexTrendsResp struct {
joint.CachePageResp
}

@ -90,6 +90,8 @@ var (
ErrNotAllowFollowSelf = xerror.NewError(80105, "不能关注自己")
ErrNotAllowUnfollowSelf = xerror.NewError(80106, "不能取消关注自己")
ErrGetIndexTrendsFailed = xerror.NewError(802001, "获取动态条栏信息失败")
ErrFollowTopicFailed = xerror.NewError(90001, "关注话题失败")
ErrUnfollowTopicFailed = xerror.NewError(90002, "取消关注话题失败")
ErrStickTopicFailed = xerror.NewError(90003, "更行话题置顶状态失败")

@ -6,6 +6,7 @@ package web
import (
"context"
"fmt"
"time"
"unicode/utf8"
@ -13,8 +14,10 @@ import (
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
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/ms"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
@ -35,9 +38,10 @@ var (
type coreSrv struct {
api.UnimplementedCoreServant
*base.DaoServant
oss core.ObjectStorageService
wc core.WebCache
messagesExpire int64
prefixMessages string
}
func (s *coreSrv) Chain() gin.HandlersChain {
@ -83,8 +87,19 @@ func (s *coreSrv) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Erro
return resp, nil
}
func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mir.Error) {
messages, err := s.Ds.GetMessages(req.UserId, (req.Page-1)*req.PageSize, req.PageSize)
func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (res *web.GetMessagesResp, _ mir.Error) {
limit, offset := req.PageSize, (req.Page-1)*req.PageSize
// 尝试直接从缓存中获取数据
key, ok := "", false
if res, key, ok = s.messagesFromCache(req, limit, offset); ok {
// logrus.Debugf("coreSrv.GetMessages from cache key:%s", key)
return
}
messages, totalRows, err := s.Ds.GetMessages(req.Uid, req.Style, limit, offset)
if err != nil {
logrus.Errorf("Ds.GetMessages err[1]: %s", err)
return nil, web.ErrGetMessagesFailed
}
for _, mf := range messages {
// TODO: 优化处理这里的user获取逻辑以及错误处理
if mf.SenderUserID > 0 {
@ -92,7 +107,7 @@ func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mi
mf.SenderUser = user.Format()
}
}
if mf.Type == ms.MsgTypeWhisper && mf.ReceiverUserID != req.UserId {
if mf.Type == ms.MsgTypeWhisper && mf.ReceiverUserID != req.Uid {
if user, err := s.Ds.GetUserByID(mf.ReceiverUserID); err == nil {
mf.ReceiverUser = user.Format()
}
@ -121,16 +136,21 @@ func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mi
}
}
if err != nil {
logrus.Errorf("Ds.GetMessages err: %v\n", err)
logrus.Errorf("Ds.GetMessages err[2]: %s", err)
return nil, web.ErrGetMessagesFailed
}
if err = s.PrepareMessages(req.UserId, messages); err != nil {
logrus.Errorf("get messages err[2]: %v\n", err)
if err = s.PrepareMessages(req.Uid, messages); err != nil {
logrus.Errorf("get messages err[3]: %s", err)
return nil, web.ErrGetMessagesFailed
}
totalRows, _ := s.Ds.GetMessageCount(req.UserId)
resp := base.PageRespFrom(messages, req.Page, req.PageSize, totalRows)
return (*web.GetMessagesResp)(resp), nil
resp := joint.PageRespFrom(messages, req.Page, req.PageSize, totalRows)
// 缓存处理
base.OnCacheRespEvent(s.wc, key, resp, s.messagesExpire)
return &web.GetMessagesResp{
CachePageResp: joint.CachePageResp{
Data: resp,
},
}, nil
}
func (s *coreSrv) ReadMessage(req *web.ReadMessageReq) mir.Error {
@ -145,8 +165,8 @@ func (s *coreSrv) ReadMessage(req *web.ReadMessageReq) mir.Error {
logrus.Errorf("Ds.ReadMessage err: %s", err)
return web.ErrReadMessageFailed
}
// 清除未读消息数缓存,不需要处理错误
s.wc.DelUnreadMsgCountResp(req.Uid)
// 缓存处理
onMessageActionEvent(_messageActionRead, req.Uid)
return nil
}
@ -377,10 +397,25 @@ func (s *coreSrv) TweetStarStatus(req *web.TweetStarStatusReq) (*web.TweetStarSt
return resp, nil
}
func (s *coreSrv) messagesFromCache(req *web.GetMessagesReq, limit int, offset int) (res *web.GetMessagesResp, key string, ok bool) {
key = fmt.Sprintf("%s%d:%s:%d:%d", s.prefixMessages, req.Uid, req.Style, limit, offset)
if data, err := s.wc.Get(key); err == nil {
ok, res = true, &web.GetMessagesResp{
CachePageResp: joint.CachePageResp{
JsonResp: data,
},
}
}
return
}
func newCoreSrv(s *base.DaoServant, oss core.ObjectStorageService, wc core.WebCache) api.Core {
cs := conf.CacheSetting
return &coreSrv{
DaoServant: s,
oss: oss,
wc: wc,
messagesExpire: cs.MessagesExpire,
prefixMessages: conf.PrefixMessages,
}
}

@ -16,6 +16,7 @@ import (
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/sirupsen/logrus"
)
const (
@ -27,6 +28,18 @@ const (
_commentActionReplyDelete
_commentActionReplyThumbsUp
_commentActionReplyThumbsDown
_commentActionHighlight
)
const (
_messageActionCreate uint8 = iota
_messageActionRead
_messageActionFollow
)
const (
_trendsActionCreateTweet uint8 = iota
_trendsActionDeleteTweet
)
type cacheUnreadMsgEvent struct {
@ -52,6 +65,38 @@ type commentActionEvent struct {
action uint8
}
type messageActionEvent struct {
event.UnimplementedEvent
wc core.WebCache
action uint8
userId []int64
}
type trendsActionEvent struct {
event.UnimplementedEvent
ac core.AppCache
ds core.DataService
action uint8
userId int64
}
func onTrendsActionEvent(action uint8, userId int64) {
events.OnEvent(&trendsActionEvent{
ac: _ac,
ds: _ds,
action: action,
userId: userId,
})
}
func onMessageActionEvent(action uint8, userIds ...int64) {
events.OnEvent(&messageActionEvent{
wc: _wc,
action: action,
userId: userIds,
})
}
func onCommentActionEvent(tweetId int64, commentId int64, action uint8) {
events.OnEvent(&commentActionEvent{
ds: _ds,
@ -138,6 +183,8 @@ func (e *commentActionEvent) Action() (err error) {
case _commentActionThumbsUp, _commentActionThumbsDown:
err = e.updateCommentMetric()
e.expireHotsComments()
case _commentActionHighlight:
e.expireAllStyleComments()
default:
// nothing
}
@ -166,3 +213,54 @@ func (e *commentActionEvent) updateCommentMetric() error {
})
return nil
}
func (e *messageActionEvent) Name() string {
return "expireMessagesEvent"
}
func (e *messageActionEvent) Action() (err error) {
for _, userId := range e.userId {
switch e.action {
case _messageActionRead:
// 清除未读消息数缓存,不需要处理错误
e.wc.DelUnreadMsgCountResp(userId)
case _messageActionCreate,
_messageActionFollow:
fallthrough
default:
// TODO
}
//清除该用户所有消息缓存
err = e.wc.DelAny(fmt.Sprintf("%s%d:*", conf.PrefixMessages, userId))
}
return
}
func (e *trendsActionEvent) Name() string {
return "trendsActionEvent"
}
func (e *trendsActionEvent) Action() (err error) {
switch e.action {
case _trendsActionCreateTweet:
logrus.Debug("trigger trendsActionEvent by create tweet ")
e.ds.UpdateUserMetric(e.userId, cs.MetricActionCreateTweet)
goto ExpireTrends
case _trendsActionDeleteTweet:
logrus.Debug("trigger trendsActionEvent by delete tweet ")
e.ds.UpdateUserMetric(e.userId, cs.MetricActionDeleteTweet)
goto ExpireTrends
default:
// nothing
goto JustReturn
}
ExpireTrends:
if friendIds, err := e.ds.MyFriendIds(e.userId); err == nil {
for _, id := range friendIds {
e.ac.DelAny(fmt.Sprintf("%s%d:*", conf.PrefixIdxTrends, id))
}
return err
}
JustReturn:
return
}

@ -86,8 +86,10 @@ func (s *followshipSrv) UnfollowUser(r *web.UnfollowUserReq) mir.Error {
return web.ErrUnfollowUserFailed
}
// 触发缓存更新事件
// TODO: 合并成一个事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionFollow, r.User.ID)
return nil
}
@ -102,8 +104,10 @@ func (s *followshipSrv) FollowUser(r *web.FollowUserReq) mir.Error {
return web.ErrUnfollowUserFailed
}
// 触发缓存更新事件
// TODO: 合并成一个事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionFollow, r.User.ID)
return nil
}

@ -313,6 +313,8 @@ func (s *privSrv) CreateTweet(req *web.CreateTweetReq) (_ *web.CreateTweetResp,
logrus.Infof("Ds.RevampPosts err: %s", err)
return nil, web.ErrCreatePostFailed
}
// 缓存处理
onTrendsActionEvent(_trendsActionCreateTweet, req.User.ID)
return (*web.CreateTweetResp)(formatedPosts[0]), nil
}
@ -341,6 +343,8 @@ func (s *privSrv) DeleteTweet(req *web.DeleteTweetReq) mir.Error {
logrus.Errorf("s.DeleteSearchPost failed: %s", err)
return web.ErrDeletePostFailed
}
// 缓存处理
onTrendsActionEvent(_trendsActionDeleteTweet, req.User.ID)
return nil
}
@ -482,6 +486,10 @@ func (s *privSrv) HighlightComment(req *web.HighlightCommentReq) (*web.Highlight
} else if err != nil {
return nil, web.ErrHighlightCommentFailed
}
// 缓存处理, 宽松处理错误
if comment, err := s.Ds.GetCommentByID(req.CommentId); err == nil {
onCommentActionEvent(comment.PostID, comment.ID, _commentActionHighlight)
}
return &web.HighlightCommentResp{
HighlightStatus: status,
}, nil

@ -0,0 +1,81 @@
// Copyright 2022 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"fmt"
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
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/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/sirupsen/logrus"
)
var (
_ api.Trends = (*trendsSrv)(nil)
)
type trendsSrv struct {
api.UnimplementedTrendsServant
*base.DaoServant
ac core.AppCache
indexTrendsExpire int64
prefixTrends string
}
func (s *trendsSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
func (s *trendsSrv) GetIndexTrends(req *web.GetIndexTrendsReq) (res *web.GetIndexTrendsResp, _ mir.Error) {
limit, offset := req.PageSize, (req.Page-1)*req.PageSize
// 尝试直接从缓存中获取数据
key, ok := "", false
if res, key, ok = s.trendsFromCache(req, limit, offset); ok {
// logrus.Debugf("trendsSrv.GetIndexTrends from cache key:%s", key)
return
}
trends, totalRows, err := s.Ds.GetIndexTrends(req.Uid, limit, offset)
if err != nil {
logrus.Errorf("Ds.GetIndexTrends err[1]: %s", err)
return nil, web.ErrGetIndexTrendsFailed
}
resp := joint.PageRespFrom(trends, req.Page, req.PageSize, totalRows)
// 缓存处理
base.OnCacheRespEvent(s.ac, key, resp, s.indexTrendsExpire)
return &web.GetIndexTrendsResp{
CachePageResp: joint.CachePageResp{
Data: resp,
},
}, nil
}
func (s *trendsSrv) trendsFromCache(req *web.GetIndexTrendsReq, limit int, offset int) (res *web.GetIndexTrendsResp, key string, ok bool) {
key = fmt.Sprintf("%s%d:%d:%d", s.prefixTrends, req.Uid, limit, offset)
if data, err := s.ac.Get(key); err == nil {
ok, res = true, &web.GetIndexTrendsResp{
CachePageResp: joint.CachePageResp{
JsonResp: data,
},
}
}
return
}
func newTrendsSrv(s *base.DaoServant) api.Trends {
cs := conf.CacheSetting
return &trendsSrv{
DaoServant: s,
ac: _ac,
indexTrendsExpire: cs.IndexTrendsExpire,
prefixTrends: conf.PrefixIdxTrends,
}
}

@ -38,6 +38,7 @@ func RouteWeb(e *gin.Engine) {
api.RegisterLooseServant(e, newLooseSrv(ds, _ac))
api.RegisterPrivServant(e, newPrivSrv(ds, _oss), newPrivChain())
api.RegisterPubServant(e, newPubSrv(ds))
api.RegisterTrendsServant(e, newTrendsSrv(ds))
api.RegisterFollowshipServant(e, newFollowshipSrv(ds))
api.RegisterFriendshipServant(e, newFriendshipSrv(ds))
// regster servants if needed by configure

@ -0,0 +1,20 @@
package v1
import (
. "github.com/alimy/mir/v4"
. "github.com/alimy/mir/v4/engine"
"github.com/rocboss/paopao-ce/internal/model/web"
)
func init() {
Entry[Trends]()
}
// Trends 动态相关 服务
type Trends struct {
Chain `mir:"-"`
Group `mir:"v1"`
// GetIndexTrends 获取广场页面动态条栏的索引item
GetIndexTrends func(Get, web.GetIndexTrendsReq) web.GetIndexTrendsResp `mir:"/trends/index"`
}

@ -1 +1 @@
import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-93352cc4.js";import{u as i}from"./vue-router-e5a2430e.js";import{G as a,e as c,a2 as u}from"./naive-ui-defd0b2d.js";import{d as l,f as d,k as t,w as o,e as f,A as x}from"./@vue-a481fc63.js";import{_ as g}from"./index-daff1b26.js";import"./vuex-44de225f.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./@vicons-c265fba6.js";import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./@css-render-7124a1a5.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-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */const v=l({__name:"404",setup(h){const e=i(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=a;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 O=g(v,[["__scopeId","data-v-e62daa85"]]);export{O as default};
import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-65b365f0.js";import{u as i}from"./vue-router-e5a2430e.js";import{G as a,e as c,a2 as u}from"./naive-ui-eecf2ec3.js";import{d as l,f as d,k as t,w as o,e as f,A as x}from"./@vue-a481fc63.js";import{_ as g}from"./index-83d7d999.js";import"./vuex-44de225f.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./@vicons-f0266f88.js";import"./seemly-76b7b838.js";import"./vueuc-7c8d4b48.js";import"./@css-render-7124a1a5.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-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */const v=l({__name:"404",setup(h){const e=i(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=a;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 O=g(v,[["__scopeId","data-v-e62daa85"]]);export{O as default};

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 N}from"./post-skeleton-8434d30b.js";import{_ as R}from"./main-nav.vue_vue_type_style_index_0_lang-93352cc4.js";import{u as z}from"./vuex-44de225f.js";import{b as A}from"./vue-router-e5a2430e.js";import{I as F,_ as S}from"./index-daff1b26.js";import{G as V,R as q,J as H,H as I}from"./naive-ui-defd0b2d.js";import{d as P,H as n,b as j,f as o,k as a,w as p,e as t,bf as u,Y as l,F as D,u as E,q as G,j as s,x as _,l as J}from"./@vue-a481fc63.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./@vicons-c265fba6.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./@css-render-7124a1a5.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 L={key:0,class:"pagination-wrap"},M={key:0,class:"skeleton-wrap"},O={key:1},T={key:0,class:"empty-wrap"},U={class:"bill-line"},Y=P({__name:"Anouncement",setup($){const d=z(),g=A(),v=n(!1),i=n([]),r=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{r.value=m};return j(()=>{}),(m,K)=>{const k=R,y=q,x=N,w=H,B=I,C=V;return t(),o("div",null,[a(k,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(t(),o("div",L,[a(y,{page:r.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?(t(),o("div",M,[a(x,{num:f.value},null,8,["num"])])):(t(),o("div",O,[i.value.length===0?(t(),o("div",T,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(t(!0),o(D,null,E(i.value,e=>(t(),G(B,{key:e.id},{default:p(()=>[s("div",U,[s("div",null,"NO."+_(e.id),1),s("div",null,_(e.reason),1),s("div",{class:J({income:e.change_amount>=0,out:e.change_amount<0})},_((e.change_amount>0?"+":"")+(e.change_amount/100).toFixed(2)),3),s("div",null,_(u(F)(e.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const ke=S(Y,[["__scopeId","data-v-d4d04859"]]);export{ke as default};
import{_ as N}from"./post-skeleton-63a5a4f0.js";import{_ as R}from"./main-nav.vue_vue_type_style_index_0_lang-65b365f0.js";import{u as z}from"./vuex-44de225f.js";import{b as A}from"./vue-router-e5a2430e.js";import{J as F,_ as S}from"./index-83d7d999.js";import{G as V,R as q,J as H,H as J}from"./naive-ui-eecf2ec3.js";import{d as P,H as n,b as j,f as o,k as a,w as p,e as t,bf as u,Y as l,F as D,u as E,q as G,j as s,x as _,l as I}from"./@vue-a481fc63.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./@vicons-f0266f88.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-7c8d4b48.js";import"./@css-render-7124a1a5.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 L={key:0,class:"pagination-wrap"},M={key:0,class:"skeleton-wrap"},O={key:1},T={key:0,class:"empty-wrap"},U={class:"bill-line"},Y=P({__name:"Anouncement",setup($){const d=z(),g=A(),v=n(!1),i=n([]),r=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{r.value=m};return j(()=>{}),(m,K)=>{const k=R,y=q,x=N,w=H,B=J,C=V;return t(),o("div",null,[a(k,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(t(),o("div",L,[a(y,{page:r.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?(t(),o("div",M,[a(x,{num:f.value},null,8,["num"])])):(t(),o("div",O,[i.value.length===0?(t(),o("div",T,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(t(!0),o(D,null,E(i.value,e=>(t(),G(B,{key:e.id},{default:p(()=>[s("div",U,[s("div",null,"NO."+_(e.id),1),s("div",null,_(e.reason),1),s("div",{class:I({income:e.change_amount>=0,out:e.change_amount<0})},_((e.change_amount>0?"+":"")+(e.change_amount/100).toFixed(2)),3),s("div",null,_(u(F)(e.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const ke=S(Y,[["__scopeId","data-v-d4d04859"]]);export{ke as default};

@ -1 +0,0 @@
import{_ as T}from"./whisper-9b4eeceb.js";import{_ as U,a as q}from"./post-item.vue_vue_type_style_index_0_lang-c2092e3d.js";import{_ as N}from"./post-skeleton-8434d30b.js";import{_ as V}from"./main-nav.vue_vue_type_style_index_0_lang-93352cc4.js";import{u as W}from"./vuex-44de225f.js";import{b as D}from"./vue-router-e5a2430e.js";import{R as E,u as G,f as J,_ as L}from"./index-daff1b26.js";import{d as Y,H as a,b as j,f as t,k as s,w as f,bf as c,Y as y,e as o,F as C,u as x,q as F}from"./@vue-a481fc63.js";import{F as K,G as Q,R as X,J as Z,H as ee}from"./naive-ui-defd0b2d.js";import"./content-64a02a2f.js";import"./@vicons-c265fba6.js";import"./paopao-video-player-2fe58954.js";import"./copy-to-clipboard-4ef7d3eb.js";import"./@babel-725317a4.js";import"./toggle-selection-93f4ad84.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./@css-render-7124a1a5.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 oe={key:0,class:"skeleton-wrap"},te={key:1},se={key:0,class:"empty-wrap"},ne={key:1},ae={key:2},ie={key:0,class:"pagination-wrap"},le=Y({__name:"Collection",setup(re){const i=W(),S=D(),$=K(),l=a(!1),r=a([]),u=a(+S.query.p||1),p=a(20),m=a(0),d=a(!1),g=a({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),v=e=>{g.value=e,d.value=!0},b=()=>{d.value=!1},w=e=>{$.success({title:"提示",content:"确定"+(e.user.is_following?"取消关注":"关注")+"该用户吗?",positiveText:"确定",negativeText:"取消",onPositiveClick:()=>{e.user.is_following?G({user_id:e.user.id}).then(_=>{window.$message.success("操作成功"),e.user.is_following=!1}).catch(_=>{}):J({user_id:e.user.id}).then(_=>{window.$message.success("关注成功"),e.user.is_following=!0}).catch(_=>{})}})},h=()=>{l.value=!0,E({page:u.value,page_size:p.value}).then(e=>{l.value=!1,r.value=e.list,m.value=Math.ceil(e.pager.total_rows/p.value),window.scrollTo(0,0)}).catch(e=>{l.value=!1})},R=e=>{u.value=e,h()};return j(()=>{h()}),(e,_)=>{const O=V,P=N,z=Z,A=U,k=ee,B=q,H=T,I=Q,M=X;return o(),t("div",null,[s(O,{title:"收藏"}),s(I,{class:"main-content-wrap",bordered:""},{default:f(()=>[l.value?(o(),t("div",oe,[s(P,{num:p.value},null,8,["num"])])):(o(),t("div",te,[r.value.length===0?(o(),t("div",se,[s(z,{size:"large",description:"暂无数据"})])):y("",!0),c(i).state.desktopModelShow?(o(),t("div",ne,[(o(!0),t(C,null,x(r.value,n=>(o(),F(k,{key:n.id},{default:f(()=>[s(A,{post:n,isOwner:c(i).state.userInfo.id==n.user_id,addFollowAction:!0,onSendWhisper:v,onHandleFollowAction:w},null,8,["post","isOwner"])]),_:2},1024))),128))])):(o(),t("div",ae,[(o(!0),t(C,null,x(r.value,n=>(o(),F(k,{key:n.id},{default:f(()=>[s(B,{post:n,isOwner:c(i).state.userInfo.id==n.user_id,addFollowAction:!0,onSendWhisper:v,onHandleFollowAction:w},null,8,["post","isOwner"])]),_:2},1024))),128))]))])),s(H,{show:d.value,user:g.value,onSuccess:b},null,8,["show","user"])]),_:1}),m.value>0?(o(),t("div",ie,[s(M,{page:u.value,"onUpdate:page":R,"page-slot":c(i).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):y("",!0)])}}});const Ne=L(le,[["__scopeId","data-v-c8f8eee7"]]);export{Ne as default};

@ -1 +0,0 @@
.pagination-wrap[data-v-c8f8eee7]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-c8f8eee7],.dark .empty-wrap[data-v-c8f8eee7],.dark .skeleton-wrap[data-v-c8f8eee7]{background-color:#101014bf}

@ -0,0 +1 @@
import{_ as j}from"./whisper-7277e566.js";import{_ as q,a as D}from"./post-item.vue_vue_type_style_index_0_lang-32efb38a.js";import{_ as R}from"./post-skeleton-63a5a4f0.js";import{_ as U}from"./main-nav.vue_vue_type_style_index_0_lang-65b365f0.js";import{u as E}from"./vuex-44de225f.js";import{b as G}from"./vue-router-e5a2430e.js";import{W as J}from"./v3-infinite-loading-2c58ec2f.js";import{S as L,u as Y,f as K,_ as Q}from"./index-83d7d999.js";import{d as X,H as s,b as Z,f as t,k as n,w as _,q as m,Y as g,e as o,bf as d,F as S,u as F,j as $,x as ee}from"./@vue-a481fc63.js";import{F as oe,G as se,a as te,J as ne,k as ae,H as ie}from"./naive-ui-eecf2ec3.js";import"./content-d0e46162.js";import"./@vicons-f0266f88.js";import"./paopao-video-player-2fe58954.js";import"./copy-to-clipboard-4ef7d3eb.js";import"./@babel-725317a4.js";import"./toggle-selection-93f4ad84.js";import"./vooks-6d99783e.js";import"./evtd-b614532e.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-7c8d4b48.js";import"./@css-render-7124a1a5.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 le={key:0,class:"skeleton-wrap"},re={key:1},_e={key:0,class:"empty-wrap"},ue={key:1},ce={key:2},pe={class:"load-more-wrap"},me={class:"load-more-spinner"},de=X({__name:"Collection",setup(fe){const f=E(),b=G(),z=oe(),u=s(!1),r=s(!1),a=s([]),l=s(+b.query.p||1),v=s(20),c=s(0),w=s(!1),h=s({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),k=e=>{h.value=e,w.value=!0},B=()=>{w.value=!1},y=e=>{z.success({title:"提示",content:"确定"+(e.user.is_following?"取消关注":"关注")+"该用户吗?",positiveText:"确定",negativeText:"取消",onPositiveClick:()=>{e.user.is_following?Y({user_id:e.user.id}).then(p=>{window.$message.success("操作成功"),e.user.is_following=!1}).catch(p=>{}):K({user_id:e.user.id}).then(p=>{window.$message.success("关注成功"),e.user.is_following=!0}).catch(p=>{})}})},x=()=>{u.value=!0,L({page:l.value,page_size:v.value}).then(e=>{u.value=!1,e.list.length===0&&(r.value=!0),l.value>1?a.value=a.value.concat(e.list):(a.value=e.list,window.scrollTo(0,0)),c.value=Math.ceil(e.pager.total_rows/v.value)}).catch(e=>{u.value=!1,l.value>1&&l.value--})},I=()=>{l.value<c.value||c.value==0?(r.value=!1,l.value++,x()):r.value=!0};return Z(()=>{x()}),(e,p)=>{const M=U,O=R,P=ne,A=q,C=ie,H=D,N=j,T=se,V=ae,W=te;return o(),t("div",null,[n(M,{title:"收藏"}),n(T,{class:"main-content-wrap",bordered:""},{default:_(()=>[u.value&&a.value.length===0?(o(),t("div",le,[n(O,{num:v.value},null,8,["num"])])):(o(),t("div",re,[a.value.length===0?(o(),t("div",_e,[n(P,{size:"large",description:"暂无数据"})])):g("",!0),d(f).state.desktopModelShow?(o(),t("div",ue,[(o(!0),t(S,null,F(a.value,i=>(o(),m(C,{key:i.id},{default:_(()=>[n(A,{post:i,isOwner:d(f).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:k,onHandleFollowAction:y},null,8,["post","isOwner"])]),_:2},1024))),128))])):(o(),t("div",ce,[(o(!0),t(S,null,F(a.value,i=>(o(),m(C,{key:i.id},{default:_(()=>[n(H,{post:i,isOwner:d(f).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:k,onHandleFollowAction:y},null,8,["post","isOwner"])]),_:2},1024))),128))]))])),n(N,{show:w.value,user:h.value,onSuccess:B},null,8,["show","user"])]),_:1}),c.value>0?(o(),m(W,{key:0,justify:"center"},{default:_(()=>[n(d(J),{class:"load-more",slots:{complete:"没有更多收藏了",error:"加载出错"},onInfinite:I},{spinner:_(()=>[$("div",pe,[r.value?g("",!0):(o(),m(V,{key:0,size:14})),$("span",me,ee(r.value?"没有更多收藏了":"加载更多"),1)])]),_:1})]),_:1})):g("",!0)])}}});const Le=Q(de,[["__scopeId","data-v-02747f0a"]]);export{Le as default};

@ -0,0 +1 @@
.load-more[data-v-02747f0a]{margin:20px}.load-more .load-more-wrap[data-v-02747f0a]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-02747f0a]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-02747f0a],.dark .empty-wrap[data-v-02747f0a],.dark .skeleton-wrap[data-v-02747f0a]{background-color:#101014bf}

@ -0,0 +1 @@
import{_ as T}from"./whisper-7277e566.js";import{d as N,c as A,r as R,e as c,f as p,k as t,w as n,j as _,y as E,A as G,x as d,bf as h,h as x,H as l,b as J,q as $,Y as C,F as S,u as K}from"./@vue-a481fc63.js";import{K as L,_ as P,W as U}from"./index-83d7d999.js";import{k as Y,r as Q}from"./@vicons-f0266f88.js";import{j as M,o as X,e as Z,P as ee,O as te,G as ne,a as oe,J as se,k as ae,H as ce}from"./naive-ui-eecf2ec3.js";import{_ as _e}from"./post-skeleton-63a5a4f0.js";import{_ as ie}from"./main-nav.vue_vue_type_style_index_0_lang-65b365f0.js";import{W as le}from"./v3-infinite-loading-2c58ec2f.js";import{b as re}from"./vue-router-e5a2430e.js";import"./vuex-44de225f.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-7c8d4b48.js";import"./evtd-b614532e.js";import"./@css-render-7124a1a5.js";import"./vooks-6d99783e.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 ue={class:"contact-item"},pe={class:"nickname-wrap"},me={class:"username-wrap"},de={class:"user-info"},fe={class:"info-item"},ve={class:"info-item"},he={class:"item-header-extra"},ge=N({__name:"contact-item",props:{contact:{}},emits:["send-whisper"],setup(b,{emit:g}){const o=b,r=e=>()=>x(M,null,{default:()=>x(e)}),s=A(()=>[{label:"私信",key:"whisper",icon:r(Q)}]),i=e=>{switch(e){case"whisper":const a={id:o.contact.user_id,avatar:o.contact.avatar,username:o.contact.username,nickname:o.contact.nickname,is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1};g("send-whisper",a);break}};return(e,a)=>{const m=X,f=R("router-link"),w=Z,k=ee,y=te;return c(),p("div",ue,[t(y,{"content-indented":""},{avatar:n(()=>[t(m,{size:54,src:e.contact.avatar},null,8,["src"])]),header:n(()=>[_("span",pe,[t(f,{onClick:a[0]||(a[0]=E(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{s:e.contact.username}}},{default:n(()=>[G(d(e.contact.nickname),1)]),_:1},8,["to"])]),_("span",me," @"+d(e.contact.username),1),_("div",de,[_("span",fe," UID. "+d(e.contact.user_id),1),_("span",ve,d(h(L)(e.contact.created_on))+" 加入 ",1)])]),"header-extra":n(()=>[_("div",he,[t(k,{placement:"bottom-end",trigger:"click",size:"small",options:s.value,onSelect:i},{default:n(()=>[t(w,{quaternary:"",circle:""},{icon:n(()=>[t(h(M),null,{default:n(()=>[t(h(Y))]),_:1})]),_:1})]),_:1},8,["options"])])]),_:1})])}}});const we=P(ge,[["__scopeId","data-v-d62f19da"]]),ke={key:0,class:"skeleton-wrap"},ye={key:1},$e={key:0,class:"empty-wrap"},Ce={class:"load-more-wrap"},be={class:"load-more-spinner"},ze=N({__name:"Contacts",setup(b){const g=re(),o=l(!1),r=l(!1),s=l([]),i=l(+g.query.p||1),e=l(20),a=l(0),m=l(!1),f=l({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),w=v=>{f.value=v,m.value=!0},k=()=>{m.value=!1},y=()=>{i.value<a.value||a.value==0?(r.value=!1,i.value++,z()):r.value=!0};J(()=>{z()});const z=(v=!1)=>{s.value.length===0&&(o.value=!0),U({page:i.value,page_size:e.value}).then(u=>{o.value=!1,u.list.length===0&&(r.value=!0),i.value>1?s.value=s.value.concat(u.list):(s.value=u.list,v&&setTimeout(()=>{window.scrollTo(0,99999)},50)),a.value=Math.ceil(u.pager.total_rows/e.value)}).catch(u=>{o.value=!1,i.value>1&&i.value--})};return(v,u)=>{const q=ie,B=_e,V=se,W=we,j=ce,D=T,F=ne,H=ae,O=oe;return c(),p(S,null,[_("div",null,[t(q,{title:"好友"}),t(F,{class:"main-content-wrap",bordered:""},{default:n(()=>[o.value&&s.value.length===0?(c(),p("div",ke,[t(B,{num:e.value},null,8,["num"])])):(c(),p("div",ye,[s.value.length===0?(c(),p("div",$e,[t(V,{size:"large",description:"暂无数据"})])):C("",!0),(c(!0),p(S,null,K(s.value,I=>(c(),$(j,{class:"list-item",key:I.user_id},{default:n(()=>[t(W,{contact:I,onSendWhisper:w},null,8,["contact"])]),_:2},1024))),128))])),t(D,{show:m.value,user:f.value,onSuccess:k},null,8,["show","user"])]),_:1})]),a.value>0?(c(),$(O,{key:0,justify:"center"},{default:n(()=>[t(h(le),{class:"load-more",slots:{complete:"没有更多好友了",error:"加载出错"},onInfinite:y},{spinner:n(()=>[_("div",Ce,[r.value?C("",!0):(c(),$(H,{key:0,size:14})),_("span",be,d(r.value?"没有更多好友了":"加载更多"),1)])]),_:1})]),_:1})):C("",!0)],64)}}});const Xe=P(ze,[["__scopeId","data-v-69277f0c"]]);export{Xe as default};

@ -1 +0,0 @@
import{_ as O}from"./whisper-9b4eeceb.js";import{d as N,c as T,r as j,e as s,f as c,k as t,w as n,j as _,y as A,A as J,x as v,bf as g,h as S,H as a,b as U,Y as z,F as I,u as W,q as E}from"./@vue-a481fc63.js";import{J as G,_ as P,b as L}from"./index-daff1b26.js";import{k as Y,r as K}from"./@vicons-c265fba6.js";import{j as x,o as Q,e as X,P as Z,O as ee,G as te,R as ne,J as oe,H as se}from"./naive-ui-defd0b2d.js";import{_ as ae}from"./post-skeleton-8434d30b.js";import{_ as ce}from"./main-nav.vue_vue_type_style_index_0_lang-93352cc4.js";import{u as _e}from"./vuex-44de225f.js";import{b as ie}from"./vue-router-e5a2430e.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./evtd-b614532e.js";import"./@css-render-7124a1a5.js";import"./vooks-6d99783e.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 re={class:"contact-item"},le={class:"nickname-wrap"},pe={class:"username-wrap"},ue={class:"user-info"},me={class:"info-item"},de={class:"info-item"},fe={class:"item-header-extra"},ve=N({__name:"contact-item",props:{contact:{}},emits:["send-whisper"],setup(C,{emit:h}){const i=C,r=e=>()=>S(x,null,{default:()=>S(e)}),l=T(()=>[{label:"私信",key:"whisper",icon:r(K)}]),u=e=>{switch(e){case"whisper":const o={id:i.contact.user_id,avatar:i.contact.avatar,username:i.contact.username,nickname:i.contact.nickname,is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1};h("send-whisper",o);break}};return(e,o)=>{const m=Q,d=j("router-link"),w=X,k=Z,y=ee;return s(),c("div",re,[t(y,{"content-indented":""},{avatar:n(()=>[t(m,{size:54,src:e.contact.avatar},null,8,["src"])]),header:n(()=>[_("span",le,[t(d,{onClick:o[0]||(o[0]=A(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{s:e.contact.username}}},{default:n(()=>[J(v(e.contact.nickname),1)]),_:1},8,["to"])]),_("span",pe," @"+v(e.contact.username),1),_("div",ue,[_("span",me," UID. "+v(e.contact.user_id),1),_("span",de,v(g(G)(e.contact.created_on))+" 加入 ",1)])]),"header-extra":n(()=>[_("div",fe,[t(k,{placement:"bottom-end",trigger:"click",size:"small",options:l.value,onSelect:u},{default:n(()=>[t(w,{quaternary:"",circle:""},{icon:n(()=>[t(g(x),null,{default:n(()=>[t(g(Y))]),_:1})]),_:1})]),_:1},8,["options"])])]),_:1})])}}});const ge=P(ve,[["__scopeId","data-v-d62f19da"]]),he={key:0,class:"skeleton-wrap"},we={key:1},ke={key:0,class:"empty-wrap"},ye={key:0,class:"pagination-wrap"},Ce=N({__name:"Contacts",setup(C){const h=_e(),i=ie(),r=a(!1),l=a([]),u=a(+i.query.p||1),e=a(20),o=a(0),m=a(!1),d=a({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),w=p=>{d.value=p,m.value=!0},k=()=>{m.value=!1},y=p=>{u.value=p,$()};U(()=>{$()});const $=(p=!1)=>{l.value.length===0&&(r.value=!0),L({page:u.value,page_size:e.value}).then(f=>{r.value=!1,l.value=f.list,o.value=Math.ceil(f.pager.total_rows/e.value),p&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(f=>{r.value=!1})};return(p,f)=>{const q=ce,B=ae,M=oe,R=ge,V=se,D=O,F=te,H=ne;return s(),c(I,null,[_("div",null,[t(q,{title:"好友"}),t(F,{class:"main-content-wrap",bordered:""},{default:n(()=>[r.value?(s(),c("div",he,[t(B,{num:e.value},null,8,["num"])])):(s(),c("div",we,[l.value.length===0?(s(),c("div",ke,[t(M,{size:"large",description:"暂无数据"})])):z("",!0),(s(!0),c(I,null,W(l.value,b=>(s(),E(V,{class:"list-item",key:b.user_id},{default:n(()=>[t(R,{contact:b,onSendWhisper:w},null,8,["contact"])]),_:2},1024))),128))])),t(D,{show:m.value,user:d.value,onSuccess:k},null,8,["show","user"])]),_:1})]),o.value>0?(s(),c("div",ye,[t(H,{page:u.value,"onUpdate:page":y,"page-slot":g(h).state.collapsedRight?5:8,"page-count":o.value},null,8,["page","page-slot","page-count"])])):z("",!0)],64)}}});const Le=P(Ce,[["__scopeId","data-v-e20fef94"]]);export{Le as default};

@ -1 +1 @@
.contact-item[data-v-d62f19da]{width:100%;box-sizing:border-box;padding:12px 16px}.contact-item[data-v-d62f19da]:hover{background:#f7f9f9}.contact-item .nickname-wrap[data-v-d62f19da],.contact-item .username-wrap[data-v-d62f19da]{line-height:16px;font-size:16px}.contact-item .top-tag[data-v-d62f19da]{transform:scale(.75)}.contact-item .user-info .info-item[data-v-d62f19da]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.contact-item .item-header-extra[data-v-d62f19da]{display:flex;align-items:center;opacity:.75}.dark .contact-item[data-v-d62f19da]{background-color:#101014bf}.dark .contact-item[data-v-d62f19da]:hover{background:#18181c}.pagination-wrap[data-v-e20fef94]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-e20fef94],.dark .empty-wrap[data-v-e20fef94],.dark .skeleton-wrap[data-v-e20fef94]{background-color:#101014bf}
.contact-item[data-v-d62f19da]{width:100%;box-sizing:border-box;padding:12px 16px}.contact-item[data-v-d62f19da]:hover{background:#f7f9f9}.contact-item .nickname-wrap[data-v-d62f19da],.contact-item .username-wrap[data-v-d62f19da]{line-height:16px;font-size:16px}.contact-item .top-tag[data-v-d62f19da]{transform:scale(.75)}.contact-item .user-info .info-item[data-v-d62f19da]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.contact-item .item-header-extra[data-v-d62f19da]{display:flex;align-items:center;opacity:.75}.dark .contact-item[data-v-d62f19da]{background-color:#101014bf}.dark .contact-item[data-v-d62f19da]:hover{background:#18181c}.load-more[data-v-69277f0c]{margin:20px}.load-more .load-more-wrap[data-v-69277f0c]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-69277f0c]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-69277f0c],.dark .empty-wrap[data-v-69277f0c],.dark .skeleton-wrap[data-v-69277f0c]{background-color:#101014bf}

File diff suppressed because one or more lines are too long

@ -1 +1 @@
.follow-item[data-v-1fb7364a]{display:border-box;width:100%;padding:12px 16px}.follow-item[data-v-1fb7364a]:hover{background:#f7f9f9}.follow-item .nickname-wrap[data-v-1fb7364a],.follow-item .username-wrap[data-v-1fb7364a]{line-height:16px;font-size:16px}.follow-item .top-tag[data-v-1fb7364a]{transform:scale(.75)}.follow-item .user-info .info-item[data-v-1fb7364a]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.follow-item .item-header-extra[data-v-1fb7364a]{display:flex;align-items:center;opacity:.75}.dark .follow-item[data-v-1fb7364a]{background-color:#101014bf}.dark .follow-item[data-v-1fb7364a]:hover{background:#18181c}.main-content-wrap[data-v-0a10234f]{padding:20px}.pagination-wrap[data-v-0a10234f]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-0a10234f],.dark .empty-wrap[data-v-0a10234f],.dark .skeleton-wrap[data-v-0a10234f]{background-color:#101014bf}
.follow-item[data-v-1fb7364a]{display:border-box;width:100%;padding:12px 16px}.follow-item[data-v-1fb7364a]:hover{background:#f7f9f9}.follow-item .nickname-wrap[data-v-1fb7364a],.follow-item .username-wrap[data-v-1fb7364a]{line-height:16px;font-size:16px}.follow-item .top-tag[data-v-1fb7364a]{transform:scale(.75)}.follow-item .user-info .info-item[data-v-1fb7364a]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.follow-item .item-header-extra[data-v-1fb7364a]{display:flex;align-items:center;opacity:.75}.dark .follow-item[data-v-1fb7364a]{background-color:#101014bf}.dark .follow-item[data-v-1fb7364a]:hover{background:#18181c}.main-content-wrap[data-v-598cf32e]{padding:20px}.load-more[data-v-598cf32e]{margin:20px}.load-more .load-more-wrap[data-v-598cf32e]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-598cf32e]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-598cf32e],.dark .empty-wrap[data-v-598cf32e],.dark .skeleton-wrap[data-v-598cf32e]{background-color:#101014bf}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
.compose-wrap{width:100%;padding:16px;box-sizing:border-box}.compose-wrap .compose-line{display:flex;flex-direction:row}.compose-wrap .compose-line .compose-user{width:42px;height:42px;display:flex;align-items:center}.compose-wrap .compose-line.compose-options{margin-top:6px;padding-left:42px;display:flex;justify-content:space-between}.compose-wrap .compose-line.compose-options .submit-wrap{display:flex;align-items:center}.compose-wrap .compose-line.compose-options .submit-wrap .text-statistic{margin-right:8px;width:20px;height:20px;transform:rotate(180deg)}.compose-wrap .link-wrap{margin-left:42px;margin-right:42px}.compose-wrap .eye-wrap{margin-left:64px}.compose-wrap .login-only-wrap{display:flex;justify-content:center;width:100%}.compose-wrap .login-only-wrap button{margin:0 4px;width:50%}.compose-wrap .login-wrap{display:flex;justify-content:center;width:100%}.compose-wrap .login-wrap .login-banner{margin-bottom:12px;opacity:.8}.compose-wrap .login-wrap button{margin:0 4px}.attachment-list-wrap{margin-top:12px;margin-left:42px}.attachment-list-wrap .n-upload-file-info__thumbnail{overflow:hidden}.dark .compose-wrap{background-color:#101014bf}.tiny-slide-bar .tiny-slide-bar__list>div.tiny-slide-bar__select .slide-bar-item .slide-bar-item-title[data-v-c53a3615]{color:#18a058;opacity:.8}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item[data-v-c53a3615]{cursor:pointer}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-avatar[data-v-c53a3615]{color:#18a058;opacity:.8}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-title[data-v-c53a3615]{color:#18a058;opacity:.8}.tiny-slide-bar[data-v-c53a3615]{margin-top:-30px;margin-bottom:-30px}.tiny-slide-bar .slide-bar-item[data-v-c53a3615]{min-height:170px;width:64px;display:flex;flex-direction:column;justify-content:center;align-items:center;margin-top:8px}.tiny-slide-bar .slide-bar-item .slide-bar-item-title[data-v-c53a3615]{justify-content:center;font-size:12px;margin-top:4px;height:40px}.load-more[data-v-c53a3615]{margin:20px}.load-more .load-more-wrap[data-v-c53a3615]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-c53a3615]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-c53a3615],.dark .pagination-wrap[data-v-c53a3615],.dark .empty-wrap[data-v-c53a3615],.dark .skeleton-wrap[data-v-c53a3615]{background-color:#101014bf}.dark .tiny-slide-bar .tiny-slide-bar__list>div.tiny-slide-bar__select .slide-bar-item .slide-bar-item-title[data-v-c53a3615]{color:#63e2b7;opacity:.8}.dark .tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-title[data-v-c53a3615]{color:#63e2b7;opacity:.8}
.compose-wrap{width:100%;padding:16px;box-sizing:border-box}.compose-wrap .compose-line{display:flex;flex-direction:row}.compose-wrap .compose-line .compose-user{width:42px;height:42px;display:flex;align-items:center}.compose-wrap .compose-line.compose-options{margin-top:6px;padding-left:42px;display:flex;justify-content:space-between}.compose-wrap .compose-line.compose-options .submit-wrap{display:flex;align-items:center}.compose-wrap .compose-line.compose-options .submit-wrap .text-statistic{margin-right:8px;width:20px;height:20px;transform:rotate(180deg)}.compose-wrap .link-wrap{margin-left:42px;margin-right:42px}.compose-wrap .eye-wrap{margin-left:64px}.compose-wrap .login-only-wrap{display:flex;justify-content:center;width:100%}.compose-wrap .login-only-wrap button{margin:0 4px;width:50%}.compose-wrap .login-wrap{display:flex;justify-content:center;width:100%}.compose-wrap .login-wrap .login-banner{margin-bottom:12px;opacity:.8}.compose-wrap .login-wrap button{margin:0 4px}.attachment-list-wrap{margin-top:12px;margin-left:42px}.attachment-list-wrap .n-upload-file-info__thumbnail{overflow:hidden}.dark .compose-wrap{background-color:#101014bf}.tiny-slide-bar .tiny-slide-bar__list>div.tiny-slide-bar__select .slide-bar-item .slide-bar-item-title[data-v-8d9bf027]{color:#18a058;opacity:.8}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item[data-v-8d9bf027]{cursor:pointer}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-avatar[data-v-8d9bf027]{color:#18a058;opacity:.8}.tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-title[data-v-8d9bf027]{color:#18a058;opacity:.8}.tiny-slide-bar[data-v-8d9bf027]{margin-top:-30px;margin-bottom:-30px}.tiny-slide-bar .slide-bar-item[data-v-8d9bf027]{min-height:170px;width:64px;display:flex;flex-direction:column;justify-content:center;align-items:center;margin-top:8px}.tiny-slide-bar .slide-bar-item .slide-bar-item-title[data-v-8d9bf027]{justify-content:center;font-size:12px;margin-top:4px;height:40px}.load-more[data-v-8d9bf027]{margin:20px}.load-more .load-more-wrap[data-v-8d9bf027]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-8d9bf027]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-8d9bf027],.dark .pagination-wrap[data-v-8d9bf027],.dark .empty-wrap[data-v-8d9bf027],.dark .skeleton-wrap[data-v-8d9bf027]{background-color:#101014bf}.dark .tiny-slide-bar .tiny-slide-bar__list>div.tiny-slide-bar__select .slide-bar-item .slide-bar-item-title[data-v-8d9bf027]{color:#63e2b7;opacity:.8}.dark .tiny-slide-bar .tiny-slide-bar__list>div:hover .slide-bar-item .slide-bar-item-title[data-v-8d9bf027]{color:#63e2b7;opacity:.8}

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 @@
.message-item[data-v-2507cd8e]{padding:16px}.message-item.unread[data-v-2507cd8e]{background:#fcfffc}.message-item .sender-wrap[data-v-2507cd8e]{display:flex;align-items:center}.message-item .sender-wrap .top-tag[data-v-2507cd8e]{transform:scale(.75)}.message-item .sender-wrap .username[data-v-2507cd8e]{opacity:.75;font-size:14px}.message-item .timestamp[data-v-2507cd8e]{opacity:.75;font-size:12px;display:flex;align-items:center}.message-item .timestamp .timestamp-txt[data-v-2507cd8e]{margin-left:6px}.message-item .brief-wrap[data-v-2507cd8e]{margin-top:10px}.message-item .brief-wrap .brief-content[data-v-2507cd8e],.message-item .brief-wrap .whisper-content-wrap[data-v-2507cd8e],.message-item .brief-wrap .requesting-friend-wrap[data-v-2507cd8e]{display:flex;width:100%}.message-item .view-link[data-v-2507cd8e]{margin-left:8px;display:flex;align-items:center}.message-item .status-info[data-v-2507cd8e]{margin-left:8px;align-items:center}.dark .message-item[data-v-2507cd8e]{background-color:#101014bf}.dark .message-item.unread[data-v-2507cd8e]{background:#0f180b}.dark .message-item .brief-wrap[data-v-2507cd8e]{background-color:#18181c}.skeleton-item[data-v-01d2e871]{padding:12px;display:flex}.skeleton-item .content[data-v-01d2e871]{width:100%}.dark .skeleton-item[data-v-01d2e871]{background-color:#101014bf}.load-more[data-v-7b84a068]{margin:20px}.load-more .load-more-wrap[data-v-7b84a068]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-7b84a068]{font-size:14px;opacity:.65}.title[data-v-7b84a068]{padding-top:4px;opacity:.9}.title-action[data-v-7b84a068]{display:flex;align-items:center;margin-left:20px}.title-filter[data-v-7b84a068]{margin-right:20px}.dark .empty-wrap[data-v-7b84a068],.dark .messages-wrap[data-v-7b84a068],.dark .pagination-wrap[data-v-7b84a068]{background-color:#101014bf}

@ -1 +0,0 @@
.message-item[data-v-282eff6a]{padding:16px}.message-item.unread[data-v-282eff6a]{background:#fcfffc}.message-item .sender-wrap[data-v-282eff6a]{display:flex;align-items:center}.message-item .sender-wrap .top-tag[data-v-282eff6a]{transform:scale(.75)}.message-item .sender-wrap .username[data-v-282eff6a]{opacity:.75;font-size:14px}.message-item .timestamp[data-v-282eff6a]{opacity:.75;font-size:12px;display:flex;align-items:center}.message-item .timestamp .timestamp-txt[data-v-282eff6a]{margin-left:6px}.message-item .brief-wrap[data-v-282eff6a]{margin-top:10px}.message-item .brief-wrap .brief-content[data-v-282eff6a],.message-item .brief-wrap .whisper-content-wrap[data-v-282eff6a],.message-item .brief-wrap .requesting-friend-wrap[data-v-282eff6a]{display:flex;width:100%}.message-item .view-link[data-v-282eff6a]{margin-left:8px;display:flex;align-items:center}.message-item .status-info[data-v-282eff6a]{margin-left:8px;align-items:center}.dark .message-item[data-v-282eff6a]{background-color:#101014bf}.dark .message-item.unread[data-v-282eff6a]{background:#0f180b}.dark .message-item .brief-wrap[data-v-282eff6a]{background-color:#18181c}.skeleton-item[data-v-01d2e871]{padding:12px;display:flex}.skeleton-item .content[data-v-01d2e871]{width:100%}.dark .skeleton-item[data-v-01d2e871]{background-color:#101014bf}.pagination-wrap[data-v-eb622a78]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .empty-wrap[data-v-eb622a78],.dark .messages-wrap[data-v-eb622a78],.dark .pagination-wrap[data-v-eb622a78]{background-color:#101014bf}

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

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 @@
import{E as U,F as A,G as M,H as O,I as x,_ as z}from"./index-83d7d999.js";import{D}from"./@vicons-f0266f88.js";import{d as q,H as _,c as T,b as B,r as G,e as c,f as u,k as n,w as s,q as $,A as C,x as h,Y as r,bf as w,E as H,al as j,F as P,u as Y}from"./@vue-a481fc63.js";import{o as J,M as V,j as K,e as Q,P as R,O as W,G as X,f as Z,g as ee,a as oe,k as te}from"./naive-ui-eecf2ec3.js";import{_ as ne}from"./main-nav.vue_vue_type_style_index_0_lang-65b365f0.js";import{u as se}from"./vuex-44de225f.js";import"./vue-router-e5a2430e.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-7c8d4b48.js";import"./evtd-b614532e.js";import"./@css-render-7124a1a5.js";import"./vooks-6d99783e.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 ae={key:0,class:"tag-item"},ce={key:0,class:"tag-quote"},le={key:1,class:"tag-quote tag-follow"},ie={key:0,class:"options"},_e=q({__name:"tag-item",props:{tag:{},showAction:{type:Boolean},checkFollowing:{type:Boolean}},setup(F){const o=F,m=_(!1),g=T(()=>o.tag.user?o.tag.user.avatar:U),i=T(()=>{let e=[];return o.tag.is_following===0?e.push({label:"关注",key:"follow"}):(o.tag.is_top===0?e.push({label:"置顶",key:"stick"}):e.push({label:"取消置顶",key:"unstick"}),e.push({label:"取消关注",key:"unfollow"})),e}),l=e=>{switch(e){case"follow":O({topic_id:o.tag.id}).then(t=>{o.tag.is_following=1,window.$message.success("关注成功")}).catch(t=>{console.log(t)});break;case"unfollow":M({topic_id:o.tag.id}).then(t=>{o.tag.is_following=0,window.$message.success("取消关注")}).catch(t=>{console.log(t)});break;case"stick":A({topic_id:o.tag.id}).then(t=>{o.tag.is_top=t.top_status,window.$message.success("置顶成功")}).catch(t=>{console.log(t)});break;case"unstick":A({topic_id:o.tag.id}).then(t=>{o.tag.is_top=t.top_status,window.$message.success("取消置顶")}).catch(t=>{console.log(t)});break}};return B(()=>{m.value=!1}),(e,t)=>{const d=G("router-link"),k=J,a=V,f=K,v=Q,p=R,y=W;return!e.checkFollowing||e.checkFollowing&&e.tag.is_following===1?(c(),u("div",ae,[n(y,null,{header:s(()=>[(c(),$(a,{type:"success",size:"large",round:"",key:e.tag.id},{avatar:s(()=>[n(k,{src:g.value},null,8,["src"])]),default:s(()=>[n(d,{class:"hash-link",to:{name:"home",query:{q:e.tag.tag,t:"tag"}}},{default:s(()=>[C(" #"+h(e.tag.tag),1)]),_:1},8,["to"]),e.showAction?r("",!0):(c(),u("span",ce,"("+h(e.tag.quote_num)+")",1)),e.showAction?(c(),u("span",le,"("+h(e.tag.quote_num)+")",1)):r("",!0)]),_:1}))]),"header-extra":s(()=>[e.showAction?(c(),u("div",ie,[n(p,{placement:"bottom-end",trigger:"click",size:"small",options:i.value,onSelect:l},{default:s(()=>[n(v,{type:"success",quaternary:"",circle:"",block:""},{icon:s(()=>[n(f,null,{default:s(()=>[n(w(D))]),_:1})]),_:1})]),_:1},8,["options"])])):r("",!0)]),_:1})])):r("",!0)}}});const ue=q({__name:"Topic",setup(F){const o=se(),m=_([]),g=_("hot"),i=_(!1),l=_(!1),e=_(!1);H(l,()=>{l.value||(window.$message.success("保存成功"),o.commit("refreshTopicFollow"))});const t=T({get:()=>{let a="编辑";return l.value&&(a="保存"),a},set:a=>{}}),d=()=>{i.value=!0,x({type:g.value,num:50}).then(a=>{m.value=a.topics,i.value=!1}).catch(a=>{console.log(a),i.value=!1})},k=a=>{g.value=a,a=="follow"?e.value=!0:e.value=!1,d()};return B(()=>{d()}),(a,f)=>{const v=ne,p=Z,y=V,E=ee,I=_e,L=oe,N=te,S=X;return c(),u("div",null,[n(v,{title:"话题"}),n(S,{class:"main-content-wrap tags-wrap",bordered:""},{default:s(()=>[n(E,{type:"line",animated:"","onUpdate:value":k},j({default:s(()=>[n(p,{name:"hot",tab:"热门"}),n(p,{name:"new",tab:"最新"}),w(o).state.userLogined?(c(),$(p,{key:0,name:"follow",tab:"关注"})):r("",!0)]),_:2},[w(o).state.userLogined?{name:"suffix",fn:s(()=>[n(y,{checked:l.value,"onUpdate:checked":f[0]||(f[0]=b=>l.value=b),checkable:""},{default:s(()=>[C(h(t.value),1)]),_:1},8,["checked"])]),key:"0"}:void 0]),1024),n(N,{show:i.value},{default:s(()=>[n(L,null,{default:s(()=>[(c(!0),u(P,null,Y(m.value,b=>(c(),$(I,{tag:b,showAction:w(o).state.userLogined&&l.value,checkFollowing:e.value},null,8,["tag","showAction","checkFollowing"]))),256))]),_:1})]),_:1},8,["show"])]),_:1})])}}});const Ne=z(ue,[["__scopeId","data-v-1fb31ecf"]]);export{Ne as default};

@ -1 +0,0 @@
import{E as $,F as M,G as O,H as z,_ as D}from"./index-daff1b26.js";import{D as G}from"./@vicons-c265fba6.js";import{d as F,H as i,c as q,b as A,r as H,e as c,f as _,k as n,w as s,q as b,A as B,x as f,Y as u,bf as h,E as U,al as j,F as x,u as P}from"./@vue-a481fc63.js";import{o as Y,M as C,j as J,e as K,P as Q,O as R,G as W,f as X,g as Z,a as ee,k as oe}from"./naive-ui-defd0b2d.js";import{_ as te}from"./main-nav.vue_vue_type_style_index_0_lang-93352cc4.js";import{u as ne}from"./vuex-44de225f.js";import"./vue-router-e5a2430e.js";import"./axios-4a70c6fc.js";import"./moment-2ab8298d.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-39372edb.js";import"./evtd-b614532e.js";import"./@css-render-7124a1a5.js";import"./vooks-6d99783e.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 se={key:0,class:"tag-item"},ae={key:0,class:"tag-quote"},ce={key:1,class:"tag-quote tag-follow"},le={key:0,class:"options"},ie=F({__name:"tag-item",props:{tag:{},showAction:{type:Boolean},checkFollowing:{type:Boolean}},setup(T){const t=T,r=i(!1),m=q(()=>{let e=[];return t.tag.is_following===0?e.push({label:"关注",key:"follow"}):(t.tag.is_top===0?e.push({label:"置顶",key:"stick"}):e.push({label:"取消置顶",key:"unstick"}),e.push({label:"取消关注",key:"unfollow"})),e}),l=e=>{switch(e){case"follow":O({topic_id:t.tag.id}).then(o=>{t.tag.is_following=1,window.$message.success("关注成功")}).catch(o=>{console.log(o)});break;case"unfollow":M({topic_id:t.tag.id}).then(o=>{t.tag.is_following=0,window.$message.success("取消关注")}).catch(o=>{console.log(o)});break;case"stick":$({topic_id:t.tag.id}).then(o=>{t.tag.is_top=o.top_status,window.$message.success("置顶成功")}).catch(o=>{console.log(o)});break;case"unstick":$({topic_id:t.tag.id}).then(o=>{t.tag.is_top=o.top_status,window.$message.success("取消置顶")}).catch(o=>{console.log(o)});break}};return A(()=>{r.value=!1}),(e,o)=>{const w=H("router-link"),g=Y,k=C,a=J,d=K,v=Q,p=R;return!e.checkFollowing||e.checkFollowing&&e.tag.is_following===1?(c(),_("div",se,[n(p,null,{header:s(()=>[(c(),b(k,{type:"success",size:"large",round:"",key:e.tag.id},{avatar:s(()=>[n(g,{src:e.tag.user.avatar},null,8,["src"])]),default:s(()=>[n(w,{class:"hash-link",to:{name:"home",query:{q:e.tag.tag,t:"tag"}}},{default:s(()=>[B(" #"+f(e.tag.tag),1)]),_:1},8,["to"]),e.showAction?u("",!0):(c(),_("span",ae,"("+f(e.tag.quote_num)+")",1)),e.showAction?(c(),_("span",ce,"("+f(e.tag.quote_num)+")",1)):u("",!0)]),_:1}))]),"header-extra":s(()=>[e.showAction?(c(),_("div",le,[n(v,{placement:"bottom-end",trigger:"click",size:"small",options:m.value,onSelect:l},{default:s(()=>[n(d,{type:"success",quaternary:"",circle:"",block:""},{icon:s(()=>[n(a,null,{default:s(()=>[n(h(G))]),_:1})]),_:1})]),_:1},8,["options"])])):u("",!0)]),_:1})])):u("",!0)}}});const _e=F({__name:"Topic",setup(T){const t=ne(),r=i([]),m=i("hot"),l=i(!1),e=i(!1),o=i(!1);U(e,()=>{e.value||(window.$message.success("保存成功"),t.commit("refreshTopicFollow"))});const w=q({get:()=>{let a="编辑";return e.value&&(a="保存"),a},set:a=>{}}),g=()=>{l.value=!0,z({type:m.value,num:50}).then(a=>{r.value=a.topics,l.value=!1}).catch(a=>{console.log(a),l.value=!1})},k=a=>{m.value=a,a=="follow"?o.value=!0:o.value=!1,g()};return A(()=>{g()}),(a,d)=>{const v=te,p=X,V=C,E=Z,L=ie,N=ee,S=oe,I=W;return c(),_("div",null,[n(v,{title:"话题"}),n(I,{class:"main-content-wrap tags-wrap",bordered:""},{default:s(()=>[n(E,{type:"line",animated:"","onUpdate:value":k},j({default:s(()=>[n(p,{name:"hot",tab:"热门"}),n(p,{name:"new",tab:"最新"}),h(t).state.userLogined?(c(),b(p,{key:0,name:"follow",tab:"关注"})):u("",!0)]),_:2},[h(t).state.userLogined?{name:"suffix",fn:s(()=>[n(V,{checked:e.value,"onUpdate:checked":d[0]||(d[0]=y=>e.value=y),checkable:""},{default:s(()=>[B(f(w.value),1)]),_:1},8,["checked"])]),key:"0"}:void 0]),1024),n(S,{show:l.value},{default:s(()=>[n(N,null,{default:s(()=>[(c(!0),_(x,null,P(r.value,y=>(c(),b(L,{tag:y,showAction:h(t).state.userLogined&&e.value,checkFollowing:o.value},null,8,["tag","showAction","checkFollowing"]))),256))]),_:1})]),_:1},8,["show"])]),_:1})])}}});const Ne=D(_e,[["__scopeId","data-v-1fb31ecf"]]);export{Ne 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

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

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

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

@ -1 +1 @@
import{X as b,_ as k}from"./index-daff1b26.js";import{d as B,H as p,e as C,q as N,w as s,j as a,k as n,A as _,x as i}from"./@vue-a481fc63.js";import{S as U,I as V,T as z,b as I,e as R,i as S}from"./naive-ui-defd0b2d.js";const T={class:"whisper-wrap"},W={class:"whisper-line"},$={class:"whisper-line send-wrap"},j=B({__name:"whisper",props:{show:{type:Boolean,default:!1},user:{}},emits:["success"],setup(r,{emit:u}){const d=r,o=p(""),t=p(!1),c=()=>{u("success")},m=()=>{t.value=!0,b({user_id:d.user.id,content:o.value}).then(e=>{window.$message.success("发送成功"),t.value=!1,o.value="",c()}).catch(e=>{t.value=!1})};return(e,l)=>{const h=U,w=V,f=z,v=I,g=R,y=S;return C(),N(y,{show:e.show,"onUpdate:show":c,class:"whisper-card",preset:"card",size:"small",title:"私信","mask-closable":!1,bordered:!1,style:{width:"360px"}},{default:s(()=>[a("div",T,[n(f,{"show-icon":!1},{default:s(()=>[_(" 即将发送私信给: "),n(w,{style:{"max-width":"100%"}},{default:s(()=>[n(h,{type:"success"},{default:s(()=>[_(i(e.user.nickname)+"@"+i(e.user.username),1)]),_:1})]),_:1})]),_:1}),a("div",W,[n(v,{type:"textarea",placeholder:"请输入私信内容(请勿发送不和谐内容,否则将会被封号)",autosize:{minRows:5,maxRows:10},value:o.value,"onUpdate:value":l[0]||(l[0]=x=>o.value=x),maxlength:"200","show-count":""},null,8,["value"])]),a("div",$,[n(g,{strong:"",secondary:"",type:"primary",loading:t.value,onClick:m},{default:s(()=>[_(" 发送 ")]),_:1},8,["loading"])])])]),_:1},8,["show"])}}});const H=k(j,[["__scopeId","data-v-0cbfe47c"]]);export{H as _};
import{Z as b,_ as k}from"./index-83d7d999.js";import{d as B,H as p,e as C,q as N,w as s,j as a,k as n,A as _,x as i}from"./@vue-a481fc63.js";import{S as U,I as V,T as z,b as I,e as R,i as S}from"./naive-ui-eecf2ec3.js";const T={class:"whisper-wrap"},W={class:"whisper-line"},$={class:"whisper-line send-wrap"},j=B({__name:"whisper",props:{show:{type:Boolean,default:!1},user:{}},emits:["success"],setup(r,{emit:u}){const d=r,o=p(""),t=p(!1),c=()=>{u("success")},m=()=>{t.value=!0,b({user_id:d.user.id,content:o.value}).then(e=>{window.$message.success("发送成功"),t.value=!1,o.value="",c()}).catch(e=>{t.value=!1})};return(e,l)=>{const h=U,w=V,f=z,v=I,g=R,y=S;return C(),N(y,{show:e.show,"onUpdate:show":c,class:"whisper-card",preset:"card",size:"small",title:"私信","mask-closable":!1,bordered:!1,style:{width:"360px"}},{default:s(()=>[a("div",T,[n(f,{"show-icon":!1},{default:s(()=>[_(" 即将发送私信给: "),n(w,{style:{"max-width":"100%"}},{default:s(()=>[n(h,{type:"success"},{default:s(()=>[_(i(e.user.nickname)+"@"+i(e.user.username),1)]),_:1})]),_:1})]),_:1}),a("div",W,[n(v,{type:"textarea",placeholder:"请输入私信内容(请勿发送不和谐内容,否则将会被封号)",autosize:{minRows:5,maxRows:10},value:o.value,"onUpdate:value":l[0]||(l[0]=x=>o.value=x),maxlength:"200","show-count":""},null,8,["value"])]),a("div",$,[n(g,{strong:"",secondary:"",type:"primary",loading:t.value,onClick:m},{default:s(()=>[_(" 发送 ")]),_:1},8,["loading"])])])]),_:1},8,["show"])}}});const H=k(j,[["__scopeId","data-v-0cbfe47c"]]);export{H as _};

@ -1 +1 @@
import{M as b,_ as k}from"./index-daff1b26.js";import{S as B,I as A,T as C,b as F,e as N,i as V}from"./naive-ui-defd0b2d.js";import{d as W,H as i,e as q,q as z,w as s,j as a,k as n,A as _,x as r}from"./@vue-a481fc63.js";const I={class:"whisper-wrap"},M={class:"whisper-line"},R={class:"whisper-line send-wrap"},S=W({__name:"whisper-add-friend",props:{show:{type:Boolean,default:!1},user:{}},emits:["success"],setup(p,{emit:d}){const u=p,o=i(""),t=i(!1),l=()=>{d("success")},m=()=>{t.value=!0,b({user_id:u.user.id,greetings:o.value}).then(e=>{window.$message.success("发送成功"),t.value=!1,o.value="",l()}).catch(e=>{t.value=!1})};return(e,c)=>{const h=B,w=A,f=C,g=F,v=N,y=V;return q(),z(y,{show:e.show,"onUpdate:show":l,class:"whisper-card",preset:"card",size:"small",title:"申请添加朋友","mask-closable":!1,bordered:!1,style:{width:"360px"}},{default:s(()=>[a("div",I,[n(f,{"show-icon":!1},{default:s(()=>[_(" 发送添加朋友申请给: "),n(w,{style:{"max-width":"100%"}},{default:s(()=>[n(h,{type:"success"},{default:s(()=>[_(r(e.user.nickname)+"@"+r(e.user.username),1)]),_:1})]),_:1})]),_:1}),a("div",M,[n(g,{type:"textarea",placeholder:"请输入真挚的问候语",autosize:{minRows:5,maxRows:10},value:o.value,"onUpdate:value":c[0]||(c[0]=x=>o.value=x),maxlength:"120","show-count":""},null,8,["value"])]),a("div",R,[n(v,{strong:"",secondary:"",type:"primary",loading:t.value,onClick:m},{default:s(()=>[_(" 发送 ")]),_:1},8,["loading"])])])]),_:1},8,["show"])}}});const D=k(S,[["__scopeId","data-v-60be56a2"]]);export{D as W};
import{N as b,_ as k}from"./index-83d7d999.js";import{S as B,I as N,T as A,b as C,e as F,i as V}from"./naive-ui-eecf2ec3.js";import{d as W,H as i,e as q,q as z,w as s,j as a,k as n,A as _,x as r}from"./@vue-a481fc63.js";const I={class:"whisper-wrap"},R={class:"whisper-line"},S={class:"whisper-line send-wrap"},T=W({__name:"whisper-add-friend",props:{show:{type:Boolean,default:!1},user:{}},emits:["success"],setup(p,{emit:d}){const u=p,o=i(""),t=i(!1),l=()=>{d("success")},m=()=>{t.value=!0,b({user_id:u.user.id,greetings:o.value}).then(e=>{window.$message.success("发送成功"),t.value=!1,o.value="",l()}).catch(e=>{t.value=!1})};return(e,c)=>{const h=B,w=N,f=A,g=C,v=F,y=V;return q(),z(y,{show:e.show,"onUpdate:show":l,class:"whisper-card",preset:"card",size:"small",title:"申请添加朋友","mask-closable":!1,bordered:!1,style:{width:"360px"}},{default:s(()=>[a("div",I,[n(f,{"show-icon":!1},{default:s(()=>[_(" 发送添加朋友申请给: "),n(w,{style:{"max-width":"100%"}},{default:s(()=>[n(h,{type:"success"},{default:s(()=>[_(r(e.user.nickname)+"@"+r(e.user.username),1)]),_:1})]),_:1})]),_:1}),a("div",R,[n(g,{type:"textarea",placeholder:"请输入真挚的问候语",autosize:{minRows:5,maxRows:10},value:o.value,"onUpdate:value":c[0]||(c[0]=x=>o.value=x),maxlength:"120","show-count":""},null,8,["value"])]),a("div",S,[n(v,{strong:"",secondary:"",type:"primary",loading:t.value,onClick:m},{default:s(()=>[_(" 发送 ")]),_:1},8,["loading"])])])]),_:1},8,["show"])}}});const H=k(T,[["__scopeId","data-v-60be56a2"]]);export{H as W};

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0" />
<link rel="manifest" href="/manifest.json" />
<title></title>
<script type="module" crossorigin src="/assets/index-daff1b26.js"></script>
<script type="module" crossorigin src="/assets/index-83d7d999.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue-a481fc63.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router-e5a2430e.js">
<link rel="modulepreload" crossorigin href="/assets/vuex-44de225f.js">
@ -21,14 +21,14 @@
<link rel="modulepreload" crossorigin href="/assets/@juggle-41516555.js">
<link rel="modulepreload" crossorigin href="/assets/@emotion-8a8e73f6.js">
<link rel="modulepreload" crossorigin href="/assets/css-render-6a5c5852.js">
<link rel="modulepreload" crossorigin href="/assets/vueuc-39372edb.js">
<link rel="modulepreload" crossorigin href="/assets/vueuc-7c8d4b48.js">
<link rel="modulepreload" crossorigin href="/assets/lodash-es-8412e618.js">
<link rel="modulepreload" crossorigin href="/assets/treemate-25c27bff.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/naive-ui-defd0b2d.js">
<link rel="modulepreload" crossorigin href="/assets/naive-ui-eecf2ec3.js">
<link rel="modulepreload" crossorigin href="/assets/moment-2ab8298d.js">
<link rel="modulepreload" crossorigin href="/assets/@vicons-c265fba6.js">
<link rel="modulepreload" crossorigin href="/assets/@vicons-f0266f88.js">
<link rel="stylesheet" href="/assets/index-c337d1db.css">
<link rel="stylesheet" href="/assets/vfonts-7afd136d.css">
</head>

@ -99,6 +99,17 @@ export const getContacts = (
});
};
/** 获取联系人列表 */
export const getIndexTrends = (
params: NetParams.IndexTrendsReq
): Promise<NetReq.IndexTrendsResp> => {
return request({
method: "get",
url: "/v1/trends/index",
params,
});
};
/** 发布动态 */
export const createPost = (
data: NetParams.PostCreatePost

@ -22,7 +22,7 @@
}">
{{ message.sender_user.nickname }}
</router-link>
<span class="username">
<span v-if="store.state.desktopModelShow" class="username">
@{{ message.sender_user.username }}
</span>
</span>
@ -35,29 +35,11 @@
}">
{{ message.receiver_user.nickname }}
</router-link>
<span class="username">
<span v-if="store.state.desktopModelShow" class="username">
@{{ message.receiver_user.username }}
</span>
</span>
<span class="nickname" v-else> </span>
<n-tag
v-if="message.type == 4"
class="top-tag"
type="success"
size="small"
round
>
</n-tag>
<!-- <n-tag
v-if="message.type != 4"
class="top-tag"
type="info"
size="small"
round
>
</n-tag> -->
<n-tag
v-if="isWhisperSender"
class="top-tag"
@ -65,7 +47,7 @@
size="small"
round
>
<template #icon>
<n-icon :component="CheckmarkCircle" />
</template>
@ -77,7 +59,7 @@
size="small"
round
>
<template #icon>
<n-icon :component="CheckmarkCircle" />
</template>

@ -303,7 +303,7 @@ const whisperSuccess = () => {
};
const emit = defineEmits<{
(e: 'reload'): void;
(e: 'reload', post_id: number): void;
}>();
const post = computed({
@ -606,7 +606,7 @@ const execLockAction = () => {
id: post.value.id,
})
.then((res) => {
emit('reload');
emit('reload', post.value.id);
if (res.lock_status === 1) {
window.$message.success('');
} else {
@ -622,7 +622,7 @@ const execStickAction = () => {
id: post.value.id,
})
.then((res) => {
emit('reload');
emit('reload', post.value.id);
if (res.top_status === 1) {
window.$message.success('');
} else {
@ -658,7 +658,7 @@ const execVisibilityAction = () => {
visibility: tempVisibility.value
})
.then((_res) => {
emit('reload');
emit('reload', post.value.id);
window.$message.success('');
})
.catch((_err) => {

@ -23,7 +23,7 @@
<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" />
<n-avatar :src="tagUserAvatar" />
</template>
</n-tag>
</template>
@ -57,6 +57,7 @@ import { ref, onMounted, computed } from 'vue';
import { MoreVertOutlined } from '@vicons/material';
import type { DropdownOption } from 'naive-ui';
import { stickTopic, followTopic, unfollowTopic } from '@/api/post';
import defaultUserAvatar from '@/assets/img/logo.png';
const hasFollowing= ref(false);
const props = withDefaults(
@ -68,6 +69,15 @@ const props = withDefaults(
{}
);
const tagUserAvatar = computed(() => {
if (props.tag.user) {
return props.tag.user.avatar
} else {
return defaultUserAvatar
}
})
const tagOptions = computed(() => {
let options: DropdownOption[] = [];
if (props.tag.is_following === 0) {

@ -142,6 +142,13 @@ declare module Item {
created_on: number;
}
interface IndexTrendsItem {
nickname: string;
username: string;
avatar: string;
is_fresh: boolean;
}
/** slide bar item */
interface SlideBarItem {
title: string;

@ -38,6 +38,7 @@ declare module NetParams {
interface UserGetUnreadMsgCount {}
interface UserGetMessages {
style: "all" | "system" | "whisper" | "requesting" | "unread";
page: number;
page_size: number;
}
@ -121,6 +122,11 @@ declare module NetParams {
page_size: number;
}
interface IndexTrendsReq {
page: number;
page_size: number;
}
interface GetUserFollows {
username: string;
page: number;

@ -189,6 +189,11 @@ declare module NetReq {
pager: Item.PagerProps;
}
interface IndexTrendsResp {
list: Item.IndexTrendsItem[];
pager: Item.PagerProps;
}
interface PostStickTopic {
/** 置顶状态0为未置顶1为置顶 */
top_status: 0 | 1;

@ -3,7 +3,7 @@
<main-nav title="收藏" />
<n-list class="main-content-wrap" bordered>
<div v-if="loading" class="skeleton-wrap">
<div v-if="loading && list.length === 0" class="skeleton-wrap">
<post-skeleton :num="pageSize" />
</div>
<div v-else>
@ -33,14 +33,16 @@
<!-- -->
<whisper :show="showWhisper" :user="whisperReceiver" @success="whisperSuccess" />
</n-list>
<div class="pagination-wrap" v-if="totalPage > 0">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage" />
<n-space v-if="totalPage > 0" justify="center">
<InfiniteLoading class="load-more" :slots="{ complete: '没有更多收藏了', error: '加载出错' }" @infinite="nextPage">
<template #spinner>
<div class="load-more-wrap">
<n-spin :size="14" v-if="!noMore" />
<span class="load-more-spinner">{{ noMore ? '' : '' }}</span>
</div>
</template>
</InfiniteLoading>
</n-space>
</div>
</template>
@ -49,6 +51,7 @@ import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import { useDialog } from 'naive-ui';
import InfiniteLoading from "v3-infinite-loading";
import { getCollections, followUser, unfollowUser } from '@/api/user';
const store = useStore();
@ -56,6 +59,7 @@ const route = useRoute();
const dialog = useDialog();
const loading = ref(false);
const noMore = ref(false);
const list = ref<any[]>([]);
const page = ref(+(route.query.p as any) || 1);
const pageSize = ref(20);
@ -118,21 +122,34 @@ const loadPosts = () => {
getCollections({
page: page.value,
page_size: pageSize.value,
})
.then((rsp) => {
}).then((res) => {
loading.value = false;
list.value = rsp.list;
totalPage.value = Math.ceil(rsp.pager.total_rows / pageSize.value);
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((err) => {
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--
}
});
};
const updatePage = (p: number) => {
page.value = p;
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPosts();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadPosts();
@ -140,11 +157,21 @@ onMounted(() => {
</script>
<style lang="less" scoped>
.pagination-wrap {
padding: 10px;
.load-more {
margin: 20px;
.load-more-wrap {
display: flex;
flex-direction: row;
justify-content: center;
overflow: hidden;
align-items: center;
gap: 14px;
.load-more-spinner {
font-size: 14px;
opacity: 0.65;
}
}
}
.dark {
.main-content-wrap, .empty-wrap, .skeleton-wrap {

@ -3,7 +3,7 @@
<main-nav title="好友" />
<n-list class="main-content-wrap" bordered>
<div v-if="loading" class="skeleton-wrap">
<div v-if="loading && list.length === 0" class="skeleton-wrap">
<post-skeleton :num="pageSize" />
</div>
<div v-else>
@ -19,26 +19,27 @@
<whisper :show="showWhisper" :user="whisperReceiver" @success="whisperSuccess" />
</n-list>
</div>
<div class="pagination-wrap" v-if="totalPage > 0">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage" />
<n-space v-if="totalPage > 0" justify="center">
<InfiniteLoading class="load-more" :slots="{ complete: '没有更多好友了', error: '加载出错' }" @infinite="nextPage">
<template #spinner>
<div class="load-more-wrap">
<n-spin :size="14" v-if="!noMore" />
<span class="load-more-spinner">{{ noMore ? '' : '' }}</span>
</div>
</template>
</InfiniteLoading>
</n-space>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { getContacts } from '@/api/post';
import { useStore } from 'vuex';
import InfiniteLoading from "v3-infinite-loading";
import { useRoute } from 'vue-router';
const store = useStore();
const route = useRoute();
const loading = ref(false);
const noMore = ref(false);
const list = ref<Item.ContactItemProps[]>([]);
const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(20);
@ -67,9 +68,14 @@ const whisperSuccess = () => {
showWhisper.value = false;
};
const updatePage = (p: number) => {
page.value = p;
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadContacts();
} else {
noMore.value = true;
}
};
onMounted(() => {
@ -83,30 +89,48 @@ const loadContacts = (scrollToBottom: boolean = false) => {
getContacts({
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((err) => {
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
}
</script>
<style lang="less" scoped>
.pagination-wrap {
padding: 10px;
.load-more {
margin: 20px;
.load-more-wrap {
display: flex;
flex-direction: row;
justify-content: center;
overflow: hidden;
align-items: center;
gap: 14px;
.load-more-spinner {
font-size: 14px;
opacity: 0.65;
}
}
}
.dark {
.main-content-wrap, .empty-wrap, .skeleton-wrap {

@ -7,7 +7,7 @@
<n-tab-pane name="follows" tab="正在关注" />
<n-tab-pane name="followings" tab="我的粉丝" />
</n-tabs>
<div v-if="loading" class="skeleton-wrap">
<div v-if="loading && list.length === 0" class="skeleton-wrap">
<post-skeleton :num="pageSize" />
</div>
<div v-else>
@ -23,26 +23,27 @@
<whisper :show="showWhisper" :user="whisperReceiver" @success="whisperSuccess" />
</n-list>
</div>
<div class="pagination-wrap" v-if="totalPage > 0">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage" />
<n-space v-if="totalPage > 0" justify="center">
<InfiniteLoading class="load-more" :slots="{ complete: completeStr, error: '加载出错' }" @infinite="nextPage">
<template #spinner>
<div class="load-more-wrap">
<n-spin :size="14" v-if="!noMore" />
<span class="load-more-spinner">{{ noMore ? completeStr : '' }}</span>
</div>
</template>
</InfiniteLoading>
</n-space>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref, onMounted, computed } from 'vue';
import { getUserFollows, getUserFollowings } from '@/api/user';
import { useStore } from 'vuex';
import InfiniteLoading from "v3-infinite-loading";
import { useRoute } from 'vue-router';
const store = useStore();
const route = useRoute();
const loading = ref(false);
const noMore = ref(false);
const list = ref<Item.ContactItemProps[]>([]);
const nickname= route.query.n as string || "粉丝详情";
const username = route.query.s as string || "";
@ -65,6 +66,14 @@ const whisperReceiver = ref<Item.UserInfo>({
status: 1,
});
const completeStr = computed(() => {
if (tabler.value == "follows") {
return ''
} else {
return ''
}
});
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
@ -74,9 +83,14 @@ const whisperSuccess = () => {
showWhisper.value = false;
};
const updatePage = (p: number) => {
page.value = p;
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadPage();
} else {
noMore.value = true;
}
};
const changeTab = (tab: "follows" | "followings") => {
@ -100,20 +114,28 @@ const loadFollows = (username: string, scrollToBottom: boolean = false) => {
username: username,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
}).then((res) => {
loading.value = false;
list.value = res.list || [];
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((err) => {
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
@ -125,20 +147,28 @@ const loadFollowings = (username: string, scrollToBottom: boolean = false) => {
username: username,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
}).then((res) => {
loading.value = false;
list.value = res.list || [];
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
if (scrollToBottom) {
setTimeout(() => {
window.scrollTo(0, 99999);
}, 50);
}
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((err) => {
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--;
}
});
};
@ -153,11 +183,21 @@ onMounted(() => {
padding: 20px;
}
.pagination-wrap {
padding: 10px;
.load-more {
margin: 20px;
.load-more-wrap {
display: flex;
flex-direction: row;
justify-content: center;
overflow: hidden;
align-items: center;
gap: 14px;
.load-more-spinner {
font-size: 14px;
opacity: 0.65;
}
}
}
.dark {

@ -84,7 +84,7 @@ import { useStore } from 'vuex';
import { useRoute, useRouter } from 'vue-router';
import { useDialog } from 'naive-ui';
import InfiniteLoading from "v3-infinite-loading";
import { getPosts, getContacts } from '@/api/post';
import { getPosts, getIndexTrends } from '@/api/post';
import { getUserPosts, deleteFriend, followUser, unfollowUser } from '@/api/user';
import SlideBar from '@opentiny/vue-slide-bar';
import allTweets from '@/assets/img/fresh-tweets.png';
@ -289,7 +289,7 @@ const loadContacts = () => {
if (!useFriendship || !enableFriendsBar || store.state.userInfo.id === 0) {
return
}
getContacts({
getIndexTrends({
page: 1,
page_size: 50,
}).then((res) => {
@ -297,13 +297,13 @@ const loadContacts = () => {
const list = res.list || []
let barItems: Item.SlideBarItem[] = []
for (; i < list.length; i++) {
let item: Item.ContactItemProps = list[i];
let item: Item.IndexTrendsItem = list[i];
barItems.push({
title: item.nickname,
style: 21,
username: item.username,
avatar: item.avatar,
show: false
show: item.is_fresh,
});
}
if (barItems.length > 0) {

@ -5,42 +5,89 @@
<n-list class="main-content-wrap messages-wrap" bordered>
<!-- -->
<whisper :show="showWhisper" :user="whisperReceiver" @success="whisperSuccess" />
<div v-if="loading" class="skeleton-wrap">
<n-space justify="space-between">
<div class="title title-action">
<n-button text size="small" @click="handleUnreadMessage">
<template #icon>
<n-icon>
<UnreadIcon />
</n-icon>
</template>
0
</n-button>
<n-divider vertical />
<n-button text size="small" @click="handleReadAll"></n-button>
</div>
<div class="title title-filter">
<n-dropdown
placement="bottom-end"
trigger="click"
size="small"
:options="options"
@select="handleAction">
<n-button text>
<template #icon>
<n-icon>
<OptionsIcon />
</n-icon>
</template>
{{ messageStyle }}
</n-button>
</n-dropdown>
</div>
</n-space>
<div v-if="loading && list.length === 0" class="skeleton-wrap">
<message-skeleton :num="pageSize" />
</div>
<div v-else>
<div class="empty-wrap" v-if="list.length === 0">
<n-empty size="large" description="暂无数据" />
</div>
<div v-else>
<n-list-item v-for="m in list" :key="m.id">
<message-item :message="m" @send-whisper="onSendWhisper" @reload="loadMessages" />
<message-item :message="m" @send-whisper="onSendWhisper" @reload="reloadMessages" />
</n-list-item>
</div>
</div>
</n-list>
<div class="pagination-wrap" v-if="totalPage > 0">
<n-pagination
:page="page"
@update:page="updatePage"
:page-slot="!store.state.collapsedRight ? 8 : 5"
:page-count="totalPage"
/>
<n-space v-if="totalPage > 0" justify="center">
<InfiniteLoading class="load-more" :slots="{ complete: '没有更多消息了', error: '加载出错' }" @infinite="nextPage">
<template #spinner>
<div class="load-more-wrap">
<n-spin :size="14" v-if="!noMore" />
<span class="load-more-spinner">{{ noMore ? '' : '' }}</span>
</div>
</template>
</InfiniteLoading>
</n-space>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
import { h, ref, onMounted, computed } from 'vue';
import type { Component } from 'vue'
import { NIcon, DropdownOption } from 'naive-ui'
import { useRoute } from 'vue-router';
import InfiniteLoading from "v3-infinite-loading";
import { getMessages } from '@/api/user';
import {
LayersOutline as AllIcon,
AtOutline as SystemIcon,
PaperPlaneOutline as WhisperIcon,
PersonAddOutline as RequestingIcon,
ChatbubbleEllipsesOutline as UnreadIcon,
OptionsOutline as OptionsIcon,
} from '@vicons/ionicons5'
const route = useRoute();
const store = useStore();
const loading = ref(false);
const noMore = ref(false);
const page = ref(+(route.query.p as string) || 1);
const pageSize = ref(10);
const pageSize = ref(20);
const totalPage = ref(0);
const list = ref<Item.MessageProps[]>([]);
const messageStyle = ref<'' | '' | '' | '' | ''>('')
const messageStyleVal = ref<'all' | 'system' | 'whisper' | 'requesting' | 'unread'>('all')
const showWhisper = ref(false);
const whisperReceiver = ref<Item.UserInfo>({
id: 0,
@ -56,31 +103,234 @@ const whisperReceiver = ref<Item.UserInfo>({
status: 1,
});
const reset = () => {
noMore.value = false;
page.value = 1;
totalPage.value = 0;
list.value = [];
}
const renderIcon = (icon: Component) => {
return () => {
return h(NIcon, null, {
default: () => h(icon)
})
}
}
const options = computed(() => {
let opts: DropdownOption[];
switch (messageStyle.value) {
case '':
opts = [
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'unread',
icon: renderIcon(UnreadIcon)
}
]
break;
case '':
opts = [
{
label: '',
key: 'all',
icon: renderIcon(AllIcon)
},
{
label: '',
key: 'system',
icon: renderIcon(SystemIcon)
},
{
label: '',
key: 'whisper',
icon: renderIcon(WhisperIcon)
},
{
label: '',
key: 'requesting',
icon: renderIcon(RequestingIcon)
}
]
break;
default:
opts = [];
break;
}
return opts;
});
const handleAction = (
item: 'all' | 'system' | 'whisper' | 'requesting' | 'unread'
) => {
switch (item) {
case 'all':
messageStyle.value = '';
break;
case 'system':
messageStyle.value = '';
break;
case 'whisper':
messageStyle.value = '';
break;
case 'requesting':
messageStyle.value = '';
break;
case 'unread':
messageStyle.value = '';
break;
}
messageStyleVal.value = item
reset();
loadMessages();
};
const handleUnreadMessage = () => {
handleAction('unread')
}
const handleReadAll = () => {
// TODO: 标记全部未读消息为已读
reset();
loadMessages();
}
const onSendWhisper = (user: Item.UserInfo) => {
whisperReceiver.value = user;
showWhisper.value = true;
};
const whisperSuccess = () => {
showWhisper.value = false;
};
const reloadMessages = () => {
reset();
loadMessages();
};
const loadMessages = () => {
loading.value = true;
getMessages({
style: messageStyleVal.value,
page: page.value,
page_size: pageSize.value,
})
.then((res) => {
}).then((res) => {
loading.value = false;
if (res.list.length === 0) {
noMore.value = true
}
if (page.value > 1) {
list.value = list.value.concat(res.list);
} else {
list.value = res.list;
window.scrollTo(0, 0);
}
totalPage.value = Math.ceil(res.pager.total_rows / pageSize.value);
})
.catch((err) => {
.catch((_err) => {
loading.value = false;
if (page.value > 1) {
page.value--
}
});
};
const updatePage = (p: number) => {
page.value = p;
const nextPage = () => {
if (page.value < totalPage.value || totalPage.value == 0) {
noMore.value = false;
page.value++;
loadMessages();
} else {
noMore.value = true;
}
};
onMounted(() => {
loadMessages();
@ -88,11 +338,33 @@ onMounted(() => {
</script>
<style lang="less" scoped>
.pagination-wrap {
padding: 10px;
.load-more {
margin: 20px;
.load-more-wrap {
display: flex;
flex-direction: row;
justify-content: center;
overflow: hidden;
align-items: center;
gap: 14px;
.load-more-spinner {
font-size: 14px;
opacity: 0.65;
}
}
}
.title {
padding-top: 4px;
opacity: 0.9;
}
.title-action {
display: flex;
align-items: center;
margin-left: 20px;
}
.title-filter {
margin-right: 20px;
}
.dark {
.empty-wrap {

@ -6,7 +6,7 @@
<n-list-item>
<n-spin :show="loading">
<div class="detail-wrap" v-if="post.id > 1">
<post-detail :post="post" @reload="loadPost" />
<post-detail :post="post" @reload="reloadPost" />
</div>
<div class="empty-wrap" v-else>
<n-empty size="large" description="暂无数据" />
@ -97,6 +97,14 @@ const commentTab = (tab: "default" | "newest") => {
loadComments(stateHandler);
};
const reloadPost = (post_id: number) => {
getPost({
id: post_id,
}).then((res) => {
post.value = res;
}).catch((_err) => {});
};
const loadPost = () => {
post.value = {
id: 0,

Loading…
Cancel
Save