feat: add pin topic support

pull/455/head
Michael Li 2 years ago
parent 6c51c79a39
commit 8967359c15
No known key found for this signature in database

@ -5,6 +5,7 @@ All notable changes to paopao-ce are documented in this file.
### Added
- add all-in-one docker image build scripts.
- frontend: add tweets filter support use tag for home page and make it as default behavior.
- add pin topic support.
## 0.5.2
### Change

@ -20,6 +20,7 @@ type Priv interface {
UnfollowTopic(*web.UnfollowTopicReq) mir.Error
FollowTopic(*web.FollowTopicReq) mir.Error
PinTopic(*web.PinTopicReq) (*web.PinTopicResp, mir.Error)
StickTopic(*web.StickTopicReq) (*web.StickTopicResp, mir.Error)
ThumbsDownTweetReply(*web.TweetReplyThumbsReq) mir.Error
ThumbsUpTweetReply(*web.TweetReplyThumbsReq) mir.Error
@ -91,6 +92,20 @@ func RegisterPrivServant(e *gin.Engine, s Priv, m ...PrivChain) {
}
s.Render(c, nil, s.FollowTopic(req))
})
router.Handle("POST", "/topic/pin", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.PinTopicReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.PinTopic(req)
s.Render(c, resp, err)
})
router.Handle("POST", "/topic/stick", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -406,6 +421,10 @@ func (UnimplementedPrivServant) FollowTopic(req *web.FollowTopicReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) PinTopic(req *web.PinTopicReq) (*web.PinTopicResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) StickTopic(req *web.StickTopicReq) (*web.StickTopicResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -9,6 +9,7 @@ const (
TagTypeHot TagType = "hot"
TagTypeNew TagType = "new"
TagTypeFollow TagType = "follow"
TagTypePin TagType = "pin"
TagTypeHotExtral TagType = "hot_extral"
)
@ -40,6 +41,7 @@ type TagItem struct {
QuoteNum int64 `json:"quote_num"`
IsFollowing int8 `json:"is_following"`
IsTop int8 `json:"is_top"`
IsPin int8 `json:"is_pin"`
}
func (t *TagInfo) Format() *TagItem {
@ -51,5 +53,6 @@ func (t *TagInfo) Format() *TagItem {
QuoteNum: t.QuoteNum,
IsFollowing: 0,
IsTop: 0,
IsPin: 0,
}
}

@ -16,10 +16,11 @@ type TopicService interface {
TagsByKeyword(keyword string) (cs.TagInfoList, error)
GetHotTags(userId int64, limit int, offset int) (cs.TagList, error)
GetNewestTags(userId int64, limit int, offset int) (cs.TagList, error)
GetFollowTags(userId int64, limit int, offset int) (cs.TagList, error)
GetFollowTags(userId int64, is_pin bool, limit int, offset int) (cs.TagList, error)
FollowTopic(userId int64, topicId int64) error
UnfollowTopic(userId int64, topicId int64) error
StickTopic(userId int64, topicId int64) (int8, error)
PinTopic(userId int64, topicId int64) (int8, error)
}
// TopicServantA 话题服务(版本A)

@ -25,6 +25,7 @@ type TopicUser struct {
Remark string `json:"-"`
QuoteNum int64 `json:"quote_num"`
IsTop int8 `json:"is_top"`
IsPin int8 `json:"is_pin"`
ReserveA string `json:"-"`
ReserveB string `json:"-"`
}

@ -33,6 +33,7 @@ type topicSrvA struct {
type topicInfo struct {
TopicId int64
IsTop int8
IsPin int8
}
func newTopicService(db *gorm.DB) core.TopicService {
@ -110,17 +111,18 @@ func (s *topicSrv) GetNewestTags(userId int64, limit int, offset int) (cs.TagLis
return s.tagsFormatA(userId, tags)
}
func (s *topicSrv) GetFollowTags(userId int64, limit int, offset int) (cs.TagList, error) {
func (s *topicSrv) GetFollowTags(userId int64, is_pin bool, limit int, offset int) (cs.TagList, error) {
if userId < 0 {
return nil, nil
}
userTopics := []*topicInfo{}
err := s.db.Model(&dbr.TopicUser{}).
Where("user_id=?", userId).
Order("is_top DESC").
Limit(limit).
Offset(offset).
Find(&userTopics).Error
db := s.db.Model(&dbr.TopicUser{}).Order("is_top DESC").Limit(limit).Offset(offset)
if is_pin {
db = db.Where("user_id=? AND is_pin=1", userId)
} else {
db = db.Where("user_id=?", userId)
}
err := db.Find(&userTopics).Error
if err != nil {
return nil, err
}
@ -207,7 +209,7 @@ func (s *topicSrv) tagsFormatA(userId int64, tags cs.TagList) (cs.TagList, error
}
for _, tag := range tags {
if info, exist := userTopicsMap[tag.ID]; exist {
tag.IsFollowing, tag.IsTop = 1, info.IsTop
tag.IsFollowing, tag.IsTop, tag.IsPin = 1, info.IsTop, info.IsPin
}
}
}
@ -238,7 +240,7 @@ func (s *topicSrv) tagsFormatB(userTopicsMap map[int64]*topicInfo, tags cs.TagIn
if len(userTopicsMap) > 0 {
for _, tag := range tagList {
if info, exist := userTopicsMap[tag.ID]; exist {
tag.IsFollowing, tag.IsTop = 1, info.IsTop
tag.IsFollowing, tag.IsTop, tag.IsPin = 1, info.IsTop, info.IsPin
}
}
}
@ -409,3 +411,27 @@ func (s *topicSrv) StickTopic(userId int64, topicId int64) (status int8, err err
db.Commit()
return
}
func (s *topicSrv) PinTopic(userId int64, topicId int64) (status int8, err error) {
db := s.db.Begin()
defer db.Rollback()
m := &dbr.TopicUser{}
err = db.Model(m).
Where("user_id=? and topic_id=?", userId, topicId).
UpdateColumn("is_pin", gorm.Expr("1-is_pin")).Error
if err != nil {
return
}
status = -1
err = db.Model(m).Where("user_id=? and topic_id=?", userId, topicId).Select("is_pin").Scan(&status).Error
if err != nil {
return
}
if status < 0 {
return -1, errors.New("topic not exist")
}
db.Commit()
return
}

@ -20,6 +20,7 @@ const (
TagTypeHot = cs.TagTypeHot
TagTypeNew = cs.TagTypeNew
TagTypeFollow = cs.TagTypeFollow
TagTypePin = cs.TagTypePin
TagTypeHotExtral = cs.TagTypeHotExtral
)

@ -207,6 +207,15 @@ type StickTopicResp struct {
StickStatus int8 `json:"top_status"`
}
type PinTopicReq struct {
SimpleInfo `json:"-" binding:"-"`
TopicId int64 `json:"topic_id" binding:"required"`
}
type PinTopicResp struct {
PinStatus int8 `json:"pin_status"`
}
type FollowTopicReq struct {
SimpleInfo `json:"-" binding:"-"`
TopicId int64 `json:"topic_id" binding:"required"`

@ -95,6 +95,7 @@ var (
ErrFollowTopicFailed = xerror.NewError(90001, "关注话题失败")
ErrUnfollowTopicFailed = xerror.NewError(90002, "取消关注话题失败")
ErrStickTopicFailed = xerror.NewError(90003, "更行话题置顶状态失败")
ErrPinTopicFailed = xerror.NewError(90005, "更行话题钉住状态失败")
ErrThumbsUpTweetComment = xerror.NewError(90101, "评论点赞失败")
ErrThumbsDownTweetComment = xerror.NewError(90102, "评论点踩失败")
ErrThumbsUpTweetReply = xerror.NewError(90103, "评论回复点赞失败")

@ -384,7 +384,9 @@ func (s *looseSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Err
case web.TagTypeNew:
tags, err = s.Ds.GetNewestTags(req.Uid, num, 0)
case web.TagTypeFollow:
tags, err = s.Ds.GetFollowTags(req.Uid, num, 0)
tags, err = s.Ds.GetFollowTags(req.Uid, false, num, 0)
case web.TagTypePin:
tags, err = s.Ds.GetFollowTags(req.Uid, true, num, 0)
case web.TagTypeHotExtral:
extralNum := req.ExtralNum
if extralNum <= 0 {
@ -392,7 +394,7 @@ func (s *looseSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Err
}
tags, err = s.Ds.GetHotTags(req.Uid, num, 0)
if err == nil {
extralTags, err = s.Ds.GetFollowTags(req.Uid, extralNum, 0)
extralTags, err = s.Ds.GetFollowTags(req.Uid, false, extralNum, 0)
}
default:
// TODO: return good error

@ -124,6 +124,17 @@ func (s *privSrv) StickTopic(req *web.StickTopicReq) (*web.StickTopicResp, mir.E
}, nil
}
func (s *privSrv) PinTopic(req *web.PinTopicReq) (*web.PinTopicResp, mir.Error) {
status, err := s.Ds.PinTopic(req.Uid, req.TopicId)
if err != nil {
logrus.Errorf("user(%d) pin topic(%d) failed: %s", req.Uid, req.TopicId, err)
return nil, web.ErrPinTopicFailed
}
return &web.PinTopicResp{
PinStatus: status,
}, nil
}
func (s *privSrv) UploadAttachment(req *web.UploadAttachmentReq) (*web.UploadAttachmentResp, mir.Error) {
defer req.File.Close()

@ -75,9 +75,12 @@ type Priv struct {
// ThumbsDownTweetReply 点踩评论回复
ThumbsDownTweetReply func(Post, web.TweetReplyThumbsReq) `mir:"/tweet/reply/thumbsdown"`
// StickTopic 置顶动态
// StickTopic 置顶话题
StickTopic func(Post, web.StickTopicReq) web.StickTopicResp `mir:"/topic/stick"`
// PinTopic 钉住话题
PinTopic func(Post, web.PinTopicReq) web.PinTopicResp `mir:"/topic/pin"`
// FollowTopic 关注话题
FollowTopic func(Post, web.FollowTopicReq) `mir:"/topic/follow"`

@ -0,0 +1,2 @@
ALTER TABLE `p_topic_user` DROP COLUMN `is_pin`;
DROP INDEX IF EXISTS `idx_topic_user_uid_ispin`;

@ -0,0 +1,2 @@
ALTER TABLE `p_topic_user` ADD COLUMN `is_pin` TINYINT NOT NULL DEFAULT 0 COMMENT '是否钉住 0 为未钉住、1 为已钉住';
CREATE INDEX `idx_topic_user_uid_ispin` ON `p_topic_user` (`user_id`, `is_pin`) USING BTREE;

@ -0,0 +1,2 @@
ALTER TABLE p_topic_user DROP COLUMN is_pin;
DROP INDEX IF EXISTS idx_topic_user_uid_ispin;

@ -0,0 +1,2 @@
ALTER TABLE p_topic_user ADD COLUMN is_pin SMALLINT NOT NULL DEFAULT 0; -- 是否钉住 0 为未钉住、1 为已钉住
CREATE INDEX idx_topic_user_uid_ispin ON p_topic_user USING btree ( user_id, is_pin );

@ -0,0 +1,2 @@
ALTER TABLE "p_topic_user" DROP COLUMN "is_pin";
DROP INDEX IF EXISTS "idx_topic_user_uid_ispin";

@ -0,0 +1,6 @@
ALTER TABLE "p_topic_user" ADD COLUMN "is_pin" integer NOT NULL DEFAULT 0;
CREATE INDEX "main"."idx_topic_user_uid_ispin"
ON "p_topic_user" (
"user_id" ASC,
"is_pin" ASC
);

@ -324,6 +324,7 @@ CREATE TABLE `p_topic_user` (
`remark` VARCHAR ( 512 ) COMMENT '备注',
`quote_num` BIGINT COMMENT '引用数',
`is_top` TINYINT NOT NULL DEFAULT '0' COMMENT '是否置顶 0 为未置顶、1 为已置顶',
`is_pin` TINYINT NOT NULL DEFAULT '0' COMMENT '是否钉住 0 为未钉住、1 为已钉住',
`created_on` BIGINT NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` BIGINT NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` BIGINT NOT NULL DEFAULT '0' COMMENT '删除时间',
@ -331,7 +332,8 @@ CREATE TABLE `p_topic_user` (
`reserve_a` VARCHAR ( 255 ) COMMENT '保留字段a',
`reserve_b` VARCHAR ( 255 ) COMMENT '保留字段b',
PRIMARY KEY ( `id` ) USING BTREE,
UNIQUE KEY `idx_topic_user_uid_tid` ( `topic_id`, `user_id` ) USING BTREE
UNIQUE KEY `idx_topic_user_uid_tid` ( `topic_id`, `user_id` ) USING BTREE,
KEY `idx_topic_user_uid_ispin` ( `user_id`, `is_pin`) USING BTREE
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户话题';
-- ----------------------------

@ -265,6 +265,7 @@ CREATE TABLE p_topic_user (
remark VARCHAR ( 512 ),-- 备注
quote_num BIGINT,-- 引用数
is_top SMALLINT NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
is_pin SMALLINT NOT NULL DEFAULT 0,-- 是否钉住 0 为未钉住、1 为已钉住
created_on BIGINT NOT NULL DEFAULT 0,-- 创建时间
modified_on BIGINT NOT NULL DEFAULT 0,-- 修改时间
deleted_on BIGINT NOT NULL DEFAULT 0,-- 删除时间
@ -273,6 +274,7 @@ CREATE TABLE p_topic_user (
reserve_b VARCHAR ( 255 ) -- 保留字段b
);
CREATE UNIQUE INDEX idx_topic_user_uid_tid ON p_topic_user USING btree ( topic_id, user_id );
CREATE INDEX idx_topic_user_uid_ispin ON p_topic_user USING btree ( user_id, is_pin );
CREATE SEQUENCE IF NOT EXISTS user_id_seq AS BIGINT MINVALUE 100058 NO MAXVALUE;
DROP TABLE IF EXISTS p_user;

@ -341,6 +341,7 @@ CREATE TABLE "p_topic_user" (
"remark" text ( 512 ),-- 备注
"quote_num" integer,-- 引用数
"is_top" integer NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
"is_pin" integer NOT NULL DEFAULT 0,-- 是否钉住 0 为未钉住、1 为已钉住
"created_on" integer NOT NULL DEFAULT 0,-- 创建时间
"modified_on" integer NOT NULL DEFAULT 0,-- 修改时间
"deleted_on" integer NOT NULL DEFAULT 0,-- 删除时间
@ -680,6 +681,11 @@ ON "p_topic_user" (
"topic_id",
"user_id"
);
CREATE INDEX "main"."idx_topic_user_uid_ispin"
ON "p_topic_user" (
"user_id" ASC,
"is_pin" ASC
);
-- ----------------------------
-- Indexes structure for table p_user

@ -1 +0,0 @@
import{_ as i}from"./main-nav.vue_vue_type_style_index_0_lang-BjPLRZmS.js";import{u as s}from"./vue-router-4Hko0_3l.js";import{G as a,e as c,a2 as u}from"./naive-ui-h5SFsZhx.js";import{d as l,f as d,k as t,w as o,e as f,A as x}from"./@vue-Hd4uXz5f.js";import{_ as g}from"./index-hCGTp9pQ.js";import"./vuex-qScXS-uk.js";import"./vooks-v147mXjr.js";import"./evtd-9ZCiDXyn.js";import"./@vicons-V6UxFD2Y.js";import"./seemly-hKSMrbh9.js";import"./vueuc-xP2DxDTa.js";import"./@css-render-oW_PeE7K.js";import"./vdirs-gz97tqc5.js";import"./@juggle--NVrOerG.js";import"./css-render-Adblu2bf.js";import"./@emotion-vV6BesBt.js";import"./lodash-es-KEIJqYRD.js";import"./treemate-hmrDCADh.js";import"./async-validator-BHjhHa7C.js";import"./date-fns-E8ESfRGG.js";import"./axios-kMxbiGYq.js";import"./moment-jIwEdMgI.js";/* empty css */const h=l({__name:"404",setup(k){const n=s(),e=()=>{n.push({path:"/"})};return(w,v)=>{const r=i,p=c,_=u,m=a;return f(),d("div",null,[t(r,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(_,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:e},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}}),O=g(h,[["__scopeId","data-v-e62daa85"]]);export{O as default};

@ -1 +0,0 @@
import{_ as N}from"./post-skeleton-7Ej5SbSK.js";import{_ as R}from"./main-nav.vue_vue_type_style_index_0_lang-BjPLRZmS.js";import{u as z}from"./vuex-qScXS-uk.js";import{b as F}from"./vue-router-4Hko0_3l.js";import{J as S,_ as V}from"./index-hCGTp9pQ.js";import{G as A,R as H,J,H as P}from"./naive-ui-h5SFsZhx.js";import{d as j,H as n,b as q,f as e,k as a,w as p,e as o,bf as u,Z as l,F as D,x as E,t as _,j as s,l as G,v as I}from"./@vue-Hd4uXz5f.js";import"./vooks-v147mXjr.js";import"./evtd-9ZCiDXyn.js";import"./@vicons-V6UxFD2Y.js";import"./axios-kMxbiGYq.js";import"./moment-jIwEdMgI.js";/* empty css */import"./seemly-hKSMrbh9.js";import"./vueuc-xP2DxDTa.js";import"./@css-render-oW_PeE7K.js";import"./vdirs-gz97tqc5.js";import"./@juggle--NVrOerG.js";import"./css-render-Adblu2bf.js";import"./@emotion-vV6BesBt.js";import"./lodash-es-KEIJqYRD.js";import"./treemate-hmrDCADh.js";import"./async-validator-BHjhHa7C.js";import"./date-fns-E8ESfRGG.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"},Z=j({__name:"Anouncement",setup($){const d=z(),g=F(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),m=n(0),h=c=>{i.value=c};return q(()=>{}),(c,K)=>{const k=R,y=H,w=N,x=J,B=P,C=A;return o(),e("div",null,[a(k,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[m.value>1?(o(),e("div",L,[a(y,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(o(),e("div",M,[a(w,{num:f.value},null,8,["num"])])):(o(),e("div",O,[r.value.length===0?(o(),e("div",T,[a(x,{size:"large",description:"暂无数据"})])):l("",!0),(o(!0),e(D,null,E(r.value,t=>(o(),I(B,{key:t.id},{default:p(()=>[s("div",U,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:G({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(S)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}}),kt=V(Z,[["__scopeId","data-v-d4d04859"]]);export{kt as default};

@ -1 +0,0 @@
import{_ as D}from"./whisper-eOJK7zKF.js";import{_ as R,a as U}from"./post-item.vue_vue_type_style_index_0_lang-ZiwC8_Kt.js";import{_ as q}from"./post-skeleton-7Ej5SbSK.js";import{_ as E}from"./main-nav.vue_vue_type_style_index_0_lang-BjPLRZmS.js";import{u as G}from"./vuex-qScXS-uk.js";import{b as J}from"./vue-router-4Hko0_3l.js";import{W as L}from"./v3-infinite-loading-9ocfqcSa.js";import{T as Z,u as K,f as Q,_ as X}from"./index-hCGTp9pQ.js";import{d as Y,H as t,b as ee,f as n,k as a,w as u,v as d,Z as h,e as o,bf as f,F as b,x as $,j as z,t as oe}from"./@vue-Hd4uXz5f.js";import{F as se,G as te,a as ne,J as ae,k as ie,H as le}from"./naive-ui-h5SFsZhx.js";import"./content-9A-T5SpY.js";import"./@vicons-V6UxFD2Y.js";import"./paopao-video-player-iTSRV7j7.js";import"./copy-to-clipboard-l6UqHK6O.js";import"./@babel-5-cIlDoe.js";import"./toggle-selection-fekekO1r.js";import"./vooks-v147mXjr.js";import"./evtd-9ZCiDXyn.js";import"./axios-kMxbiGYq.js";import"./moment-jIwEdMgI.js";/* empty css */import"./seemly-hKSMrbh9.js";import"./vueuc-xP2DxDTa.js";import"./@css-render-oW_PeE7K.js";import"./vdirs-gz97tqc5.js";import"./@juggle--NVrOerG.js";import"./css-render-Adblu2bf.js";import"./@emotion-vV6BesBt.js";import"./lodash-es-KEIJqYRD.js";import"./treemate-hmrDCADh.js";import"./async-validator-BHjhHa7C.js";import"./date-fns-E8ESfRGG.js";const re={key:0,class:"skeleton-wrap"},_e={key:1},ue={key:0,class:"empty-wrap"},ce={key:1},me={key:2},pe={class:"load-more-wrap"},de={class:"load-more-spinner"},fe=Y({__name:"Collection",setup(ve){const v=G(),A=J(),B=se(),c=t(!1),_=t(!1),s=t([]),l=t(+A.query.p||1),w=t(20),m=t(0),g=t(!1),k=t({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),y=e=>{k.value=e,g.value=!0},I=()=>{g.value=!1},x=e=>{B.success({title:"提示",content:"确定"+(e.user.is_following?"取消关注":"关注")+"该用户吗?",positiveText:"确定",negativeText:"取消",onPositiveClick:()=>{e.user.is_following?K({user_id:e.user.id}).then(r=>{window.$message.success("操作成功"),C(e.user_id,!1)}).catch(r=>{}):Q({user_id:e.user.id}).then(r=>{window.$message.success("关注成功"),C(e.user_id,!0)}).catch(r=>{})}})};function C(e,r){for(let p in s.value)s.value[p].user_id==e&&(s.value[p].user.is_following=r)}const F=()=>{c.value=!0,Z({page:l.value,page_size:w.value}).then(e=>{c.value=!1,e.list.length===0&&(_.value=!0),l.value>1?s.value=s.value.concat(e.list):(s.value=e.list,window.scrollTo(0,0)),m.value=Math.ceil(e.pager.total_rows/w.value)}).catch(e=>{c.value=!1,l.value>1&&l.value--})},M=()=>{l.value<m.value||m.value==0?(_.value=!1,l.value++,F()):_.value=!0};return ee(()=>{F()}),(e,r)=>{const p=E,O=q,P=ae,T=R,S=le,H=U,N=D,V=te,W=ie,j=ne;return o(),n("div",null,[a(p,{title:"收藏"}),a(V,{class:"main-content-wrap",bordered:""},{default:u(()=>[c.value&&s.value.length===0?(o(),n("div",re,[a(O,{num:w.value},null,8,["num"])])):(o(),n("div",_e,[s.value.length===0?(o(),n("div",ue,[a(P,{size:"large",description:"暂无数据"})])):h("",!0),f(v).state.desktopModelShow?(o(),n("div",ce,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(T,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))])):(o(),n("div",me,[(o(!0),n(b,null,$(s.value,i=>(o(),d(S,{key:i.id},{default:u(()=>[a(H,{post:i,isOwner:f(v).state.userInfo.id==i.user_id,addFollowAction:!0,onSendWhisper:y,onHandleFollowAction:x},null,8,["post","isOwner"])]),_:2},1024))),128))]))])),a(N,{show:g.value,user:k.value,onSuccess:I},null,8,["show","user"])]),_:1}),m.value>0?(o(),d(j,{key:0,justify:"center"},{default:u(()=>[a(f(L),{class:"load-more",slots:{complete:"没有更多收藏了",error:"加载出错"},onInfinite:M},{spinner:u(()=>[z("div",pe,[_.value?h("",!0):(o(),d(W,{key:0,size:14})),z("span",de,oe(_.value?"没有更多收藏了":"加载更多"),1)])]),_:1})]),_:1})):h("",!0)])}}}),Ze=X(fe,[["__scopeId","data-v-735372fb"]]);export{Ze as default};

@ -1 +0,0 @@
import{_ as W}from"./whisper-eOJK7zKF.js";import{d as P,c as A,r as R,e as s,f as p,k as t,w as o,y as E,t as d,A as G,j as a,bf as g,h as S,H as r,b as J,v as C,Z as b,F as M,x as K}from"./@vue-Hd4uXz5f.js";import{K as L,_ as x,X as U}from"./index-hCGTp9pQ.js";import{k as X,r as Z}from"./@vicons-V6UxFD2Y.js";import{j as N,o as Q,e as Y,P as ee,O as te,G as ne,a as oe,J as se,k as ae,H as ce}from"./naive-ui-h5SFsZhx.js";import{_ as ie}from"./post-skeleton-7Ej5SbSK.js";import{_ as re}from"./main-nav.vue_vue_type_style_index_0_lang-BjPLRZmS.js";import{W as le}from"./v3-infinite-loading-9ocfqcSa.js";import{b as _e}from"./vue-router-4Hko0_3l.js";import"./vuex-qScXS-uk.js";import"./axios-kMxbiGYq.js";import"./moment-jIwEdMgI.js";/* empty css */import"./seemly-hKSMrbh9.js";import"./vueuc-xP2DxDTa.js";import"./evtd-9ZCiDXyn.js";import"./@css-render-oW_PeE7K.js";import"./vooks-v147mXjr.js";import"./vdirs-gz97tqc5.js";import"./@juggle--NVrOerG.js";import"./css-render-Adblu2bf.js";import"./@emotion-vV6BesBt.js";import"./lodash-es-KEIJqYRD.js";import"./treemate-hmrDCADh.js";import"./async-validator-BHjhHa7C.js";import"./date-fns-E8ESfRGG.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=P({__name:"contact-item",props:{contact:{}},emits:["send-whisper"],setup(z,{emit:w}){const _=w,l=e=>()=>S(N,null,{default:()=>S(e)}),n=z,c=A(()=>[{label:"私信 @"+n.contact.username,key:"whisper",icon:l(Z)}]),m=e=>{switch(e){case"whisper":const i={id:n.contact.user_id,avatar:n.contact.avatar,username:n.contact.username,nickname:n.contact.nickname,is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1};_("send-whisper",i);break}};return(e,i)=>{const f=Q,k=R("router-link"),y=Y,$=ee,v=te;return s(),p("div",ue,[t(v,{"content-indented":""},{avatar:o(()=>[t(f,{size:54,src:e.contact.avatar},null,8,["src"])]),header:o(()=>[a("span",pe,[t(k,{onClick:i[0]||(i[0]=E(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{s:e.contact.username}}},{default:o(()=>[G(d(e.contact.nickname),1)]),_:1},8,["to"])]),a("span",me," @"+d(e.contact.username),1),a("div",de,[a("span",fe," UID. "+d(e.contact.user_id),1),a("span",ve,d(g(L)(e.contact.created_on))+" 加入 ",1)])]),"header-extra":o(()=>[a("div",he,[t($,{placement:"bottom-end",trigger:"click",size:"small",options:c.value,onSelect:m},{default:o(()=>[t(y,{quaternary:"",circle:""},{icon:o(()=>[t(g(N),null,{default:o(()=>[t(g(X))]),_:1})]),_:1})]),_:1},8,["options"])])]),_:1})])}}}),we=x(ge,[["__scopeId","data-v-42e975ce"]]),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=P({__name:"Contacts",setup(z){const w=_e(),_=r(!1),l=r(!1),n=r([]),c=r(+w.query.p||1),m=r(20),e=r(0),i=r(!1),f=r({id:0,avatar:"",username:"",nickname:"",is_admin:!1,is_friend:!0,is_following:!1,created_on:0,follows:0,followings:0,status:1}),k=h=>{f.value=h,i.value=!0},y=()=>{i.value=!1},$=()=>{c.value<e.value||e.value==0?(l.value=!1,c.value++,v()):l.value=!0};J(()=>{v()});const v=(h=!1)=>{n.value.length===0&&(_.value=!0),U({page:c.value,page_size:m.value}).then(u=>{_.value=!1,u.list.length===0&&(l.value=!0),c.value>1?n.value=n.value.concat(u.list):(n.value=u.list,h&&setTimeout(()=>{window.scrollTo(0,99999)},50)),e.value=Math.ceil(u.pager.total_rows/m.value)}).catch(u=>{_.value=!1,c.value>1&&c.value--})};return(h,u)=>{const B=re,V=ie,j=se,q=we,D=ce,F=W,H=ne,O=ae,T=oe;return s(),p(M,null,[a("div",null,[t(B,{title:"好友"}),t(H,{class:"main-content-wrap",bordered:""},{default:o(()=>[_.value&&n.value.length===0?(s(),p("div",ke,[t(V,{num:m.value},null,8,["num"])])):(s(),p("div",ye,[n.value.length===0?(s(),p("div",$e,[t(j,{size:"large",description:"暂无数据"})])):b("",!0),(s(!0),p(M,null,K(n.value,I=>(s(),C(D,{class:"list-item",key:I.user_id},{default:o(()=>[t(q,{contact:I,onSendWhisper:k},null,8,["contact"])]),_:2},1024))),128))])),t(F,{show:i.value,user:f.value,onSuccess:y},null,8,["show","user"])]),_:1})]),e.value>0?(s(),C(T,{key:0,justify:"center"},{default:o(()=>[t(g(le),{class:"load-more",slots:{complete:"没有更多好友了",error:"加载出错"},onInfinite:$},{spinner:o(()=>[a("div",Ce,[l.value?b("",!0):(s(),C(O,{key:0,size:14})),a("span",be,d(l.value?"没有更多好友了":"加载更多"),1)])]),_:1})]),_:1})):b("",!0)],64)}}}),Qe=x(ze,[["__scopeId","data-v-69277f0c"]]);export{Qe 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

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

@ -1 +0,0 @@
import{E as I,F as A,G as M,H as O,I as z,_ as D}from"./index-hCGTp9pQ.js";import{D as G}from"./@vicons-V6UxFD2Y.js";import{d as q,H as u,c as T,b as B,r as H,e as c,f as r,k as n,w as s,t as h,A as C,Z as m,v as $,bf as w,E as j,am as P,F as Z,x}from"./@vue-Hd4uXz5f.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 Y,g as ee,a as oe,k as te}from"./naive-ui-h5SFsZhx.js";import{_ as ne}from"./main-nav.vue_vue_type_style_index_0_lang-BjPLRZmS.js";import{u as se}from"./vuex-qScXS-uk.js";import"./vue-router-4Hko0_3l.js";import"./axios-kMxbiGYq.js";import"./moment-jIwEdMgI.js";/* empty css */import"./seemly-hKSMrbh9.js";import"./vueuc-xP2DxDTa.js";import"./evtd-9ZCiDXyn.js";import"./@css-render-oW_PeE7K.js";import"./vooks-v147mXjr.js";import"./vdirs-gz97tqc5.js";import"./@juggle--NVrOerG.js";import"./css-render-Adblu2bf.js";import"./@emotion-vV6BesBt.js";import"./lodash-es-KEIJqYRD.js";import"./treemate-hmrDCADh.js";import"./async-validator-BHjhHa7C.js";import"./date-fns-E8ESfRGG.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 i=u(!1),t=F,g=T(()=>t.tag.user?t.tag.user.avatar:I),_=T(()=>{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":A({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":A({topic_id:t.tag.id}).then(o=>{t.tag.is_top=o.top_status,window.$message.success("取消置顶")}).catch(o=>{console.log(o)});break}};return B(()=>{i.value=!1}),(e,o)=>{const d=H("router-link"),k=J,a=V,f=K,v=Q,p=R,b=W;return!e.checkFollowing||e.checkFollowing&&e.tag.is_following===1?(c(),r("div",ae,[n(b,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?m("",!0):(c(),r("span",ce,"("+h(e.tag.quote_num)+")",1)),e.showAction?(c(),r("span",le,"("+h(e.tag.quote_num)+")",1)):m("",!0)]),_:1}))]),"header-extra":s(()=>[e.showAction?(c(),r("div",ie,[n(p,{placement:"bottom-end",trigger:"click",size:"small",options:_.value,onSelect:l},{default:s(()=>[n(v,{type:"success",quaternary:"",circle:"",block:""},{icon:s(()=>[n(f,null,{default:s(()=>[n(w(G))]),_:1})]),_:1})]),_:1},8,["options"])])):m("",!0)]),_:1})])):m("",!0)}}}),ue=q({__name:"Topic",setup(F){const i=se(),t=u([]),g=u("hot"),_=u(!1),l=u(!1),e=u(!1);j(l,()=>{l.value||(window.$message.success("保存成功"),i.commit("refreshTopicFollow"))});const o=T({get:()=>{let a="编辑";return l.value&&(a="保存"),a},set:a=>{}}),d=()=>{_.value=!0,z({type:g.value,num:50}).then(a=>{t.value=a.topics,_.value=!1}).catch(a=>{console.log(a),_.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=Y,b=V,E=ee,L=_e,N=oe,S=te,U=X;return c(),r("div",null,[n(v,{title:"话题"}),n(U,{class:"main-content-wrap tags-wrap",bordered:""},{default:s(()=>[n(E,{type:"line",animated:"","onUpdate:value":k},P({default:s(()=>[n(p,{name:"hot",tab:"热门"}),n(p,{name:"new",tab:"最新"}),w(i).state.userLogined?(c(),$(p,{key:0,name:"follow",tab:"关注"})):m("",!0)]),_:2},[w(i).state.userLogined?{name:"suffix",fn:s(()=>[n(b,{checked:l.value,"onUpdate:checked":f[0]||(f[0]=y=>l.value=y),checkable:""},{default:s(()=>[C(h(o.value),1)]),_:1},8,["checked"])]),key:"0"}:void 0]),1024),n(S,{show:_.value},{default:s(()=>[n(N,null,{default:s(()=>[(c(!0),r(Z,null,x(t.value,y=>(c(),$(L,{tag:y,showAction:w(i).state.userLogined&&l.value,checkFollowing:e.value},null,8,["tag","showAction","checkFollowing"]))),256))]),_:1})]),_:1},8,["show"])]),_:1})])}}}),Se=D(ue,[["__scopeId","data-v-1fb31ecf"]]);export{Se as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
import{ab as A}from"./index-hCGTp9pQ.js";import{u as B}from"./vuex-qScXS-uk.js";import{u as E}from"./vue-router-4Hko0_3l.js";import{j as z}from"./vooks-v147mXjr.js";import{a3 as C,a4 as P,a5 as D,a6 as N}from"./@vicons-V6UxFD2Y.js";import{u as R,a3 as H,a4 as I,j as V,e as $,a5 as j,h as q}from"./naive-ui-h5SFsZhx.js";import{d as x,H as h,b as F,e as n,f,bf as a,k as e,w as t,Z as c,j as L,v as i,A as U,t as Z,F as G}from"./@vue-Hd4uXz5f.js";const J={key:0},K={class:"navbar"},ae=x({__name:"main-nav",props:{title:{default:""},back:{type:Boolean,default:!1},theme:{type:Boolean,default:!0}},setup(w){const o=B(),m=E(),l=h(!1),g=h("left"),u=w,_=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")||_(z()==="dark"),o.state.desktopModelShow||(window.$store=o,window.$message=R())}),(s,d)=>{const b=A,y=H,M=I,r=V,p=$,O=j,S=q;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(),i(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(),i(p,{key:1,class:"back-btn",onClick:k,quaternary:"",circle:"",size:"small"},{icon:t(()=>[e(r,null,{default:t(()=>[e(a(P))]),_:1})]),_:1})):c("",!0),U(" "+Z(u.title)+" ",1),u.theme?(n(),i(O,{key:2,value:a(o).state.theme==="dark","onUpdate:value":_,size:"small",class:"theme-switch-wrap"},{"checked-icon":t(()=>[e(r,{component:a(D)},null,8,["component"])]),"unchecked-icon":t(()=>[e(r,{component:a(N)},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 +0,0 @@
import{U as _}from"./naive-ui-h5SFsZhx.js";import{d as c,e as s,f as n,x as p,j as o,k as t,F as l}from"./@vue-Hd4uXz5f.js";import{_ as m}from"./index-hCGTp9pQ.js";const i={class:"user"},d={class:"content"},u=c({__name:"post-skeleton",props:{num:{default:1}},setup(f){return(a,k)=>{const e=_;return s(!0),n(l,null,p(new Array(a.num),r=>(s(),n("div",{class:"skeleton-item",key:r},[o("div",i,[t(e,{circle:"",size:"small"})]),o("div",d,[t(e,{text:"",repeat:3}),t(e,{text:"",style:{width:"60%"}})])]))),128)}}}),g=m(u,[["__scopeId","data-v-ab0015b4"]]);export{g as _};

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

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

@ -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-hCGTp9pQ.js"></script>
<script type="module" crossorigin src="/assets/index-8lD1klqH.js"></script>
<link rel="modulepreload" crossorigin href="/assets/@vue-Hd4uXz5f.js">
<link rel="modulepreload" crossorigin href="/assets/vue-router-4Hko0_3l.js">
<link rel="modulepreload" crossorigin href="/assets/vuex-qScXS-uk.js">

@ -286,6 +286,17 @@ export const stickTopic = (
});
};
/** 置顶/取消置顶话题 */
export const pinTopic = (
data: NetParams.PostPinTopic
): Promise<NetReq.PostPinTopic> => {
return request({
method: "post",
url: "/v1/topic/pin",
data,
});
};
/** 关注话题 */
export const followTopic = (
data: NetParams.PostFollowTopic

@ -1,5 +1,5 @@
<template>
<div v-if="!checkFollowing || (checkFollowing && tag.is_following === 1)" class="tag-item">
<div v-if="(!checkFollowing && !checkPin) || (checkFollowing && tag.is_following === 1) || (checkPin && tag.is_following === 1 && tag.is_pin === 1)" class="tag-item">
<n-thing>
<template #header>
<n-tag
@ -56,7 +56,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 { pinTopic, stickTopic, followTopic, unfollowTopic } from '@/api/post';
import defaultUserAvatar from '@/assets/img/logo.png';
const hasFollowing= ref(false);
@ -65,6 +65,7 @@ const props = withDefaults(
tag: Item.TagProps;
showAction: boolean;
checkFollowing: boolean;
checkPin: boolean;
}>(),
{}
);
@ -75,7 +76,7 @@ const tagUserAvatar = computed(() => {
} else {
return defaultUserAvatar
}
})
});
const tagOptions = computed(() => {
@ -86,6 +87,17 @@ const tagOptions = computed(() => {
key: 'follow',
});
} else {
if (props.tag.is_pin === 0) {
options.push({
label: '',
key: 'pin',
});
} else {
options.push({
label: '',
key: 'unpin',
});
}
if (props.tag.is_top === 0) {
options.push({
label: '',
@ -106,14 +118,14 @@ const tagOptions = computed(() => {
});
const handleTagAction = (
item: 'follow' | 'unfollow' | 'stick' | 'unstick'
item: 'follow' | 'unfollow' | 'pin' | 'unpin' | 'stick' | 'unstick'
) => {
switch (item) {
case 'follow':
followTopic({
topic_id: props.tag.id
})
.then((res) => {
.then((_res) => {
props.tag.is_following = 1
window.$message.success(`关注成功`);
})
@ -125,7 +137,7 @@ const handleTagAction = (
unfollowTopic({
topic_id: props.tag.id
})
.then((res) => {
.then((_res) => {
props.tag.is_following = 0
window.$message.success(`取消关注`);
})
@ -133,6 +145,30 @@ const handleTagAction = (
console.log(err);
});
break;
case 'pin':
pinTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_pin = 1;
window.$message.success(`钉住成功`);
})
.catch((err) => {
console.log(err);
});
break;
case 'unpin':
pinTopic({
topic_id: props.tag.id
})
.then((_res) => {
props.tag.is_pin = 0;
window.$message.success(`取消钉住`);
})
.catch((err) => {
console.log(err);
});
break;
case 'stick':
stickTopic({
topic_id: props.tag.id

@ -345,6 +345,8 @@ declare module Item {
is_following?: 0 | 1;
/** 是否置顶0为未置顶1为已置顶 */
is_top?: 0 | 1;
/** 是否钉住0为未钉住1为已钉住 */
is_pin?: 0 | 1;
/** 是否删除0为未删除1为已删除 */
is_del?: 0 | 1;
}

@ -202,7 +202,7 @@ declare module NetParams {
}
interface PostGetTags {
type: "hot" | "new" | "follow" | "hot_extral";
type: "hot" | "new" | "follow" | "pin" | "hot_extral";
num: number;
extral_num?: number;
}
@ -278,6 +278,10 @@ declare module NetParams {
topic_id: number;
}
interface PostPinTopic {
topic_id: number;
}
interface PostFollowTopic {
topic_id: number;
}

@ -203,6 +203,11 @@ declare module NetReq {
top_status: 0 | 1;
}
interface PostPinTopic {
/** 钉住状态0为未钉住1为钉住*/
pin_status: 0 | 1;
}
interface PostFollowTopic {}
interface PostUnfollowTopic {}

@ -6,8 +6,8 @@
<n-tabs type="line" animated @update:value="changeTab">
<n-tab-pane name="hot" tab="热门" />
<n-tab-pane name="new" tab="最新" />
<n-tab-pane v-if="store.state.userLogined"
name="follow" tab="关注" />
<n-tab-pane name="follow" tab="关注" v-if="store.state.userLogined" />
<n-tab-pane name="pin" tab="钉住" v-if="store.state.userLogined" />
<template v-if="store.state.userLogined" #suffix>
<n-tag v-model:checked="tagsChecked" checkable>
{{tagsEditText}}
@ -21,9 +21,13 @@
:tag="tag"
:showAction="store.state.userLogined && tagsChecked"
:checkFollowing="inFollowTab"
:checkPin="inPinTab"
>
</tag-item>
</n-space>
<div class="empty-wrap" v-if="tags.length === 0">
<n-empty size="large" description="暂无数据" />
</div>
</n-spin>
</n-list>
</div>
@ -36,10 +40,11 @@ import { useStore } from 'vuex';
const store = useStore();
const tags = ref<Item.TagProps[]>([]);
const tagType = ref<"hot" | "new" | "follow">('hot');
const tagType = ref<"hot" | "new" | "follow" | "pin">('hot');
const loading = ref(false);
const tagsChecked = ref(false)
const inFollowTab = ref(false)
const inPinTab = ref(false)
watch(tagsChecked, () => {
if (!tagsChecked.value) {
@ -70,17 +75,15 @@ const loadTags = () => {
loading.value = false;
})
.catch((err) => {
tags.value = [];
console.log(err);
loading.value = false;
});
};
const changeTab = (tab: "hot" | "new" | "follow") => {
const changeTab = (tab: "hot" | "new" | "follow" | "pin") => {
tagType.value = tab;
if (tab == "follow") {
inFollowTab.value = true
} else {
inFollowTab.value = false
}
inFollowTab.value = (tab === "follow")
inPinTab.value = (tab === "pin")
loadTags();
};
onMounted(() => {
@ -93,7 +96,7 @@ onMounted(() => {
padding: 20px;
}
.dark {
.tags-wrap {
.tags-wrap, .empty-wrap {
background-color: rgba(16, 16, 20, 0.75);
}
}

Loading…
Cancel
Save