Merge pull request #192 from rocboss/feature/friendship

add support Friendship feature
pull/193/head
ROC 2 years ago committed by GitHub
commit fde5173e96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -239,11 +239,20 @@ docker compose up --build
> 注意:默认提供的 docker-compose.yaml 初衷是搭建本机开发调试环境,如果需要产品部署供外网访问,请自行调优配置参数或使用其他方式部署。
### API 文档
构建时将 `docs` 添加到TAGS中:
* `config.yaml` 添加 `Docs:OpenAPI` 功能项:
```yaml
...
Features:
Default: ["Base", "MySQL", "Option", "LocalOSS", "LoggerFile", "Docs"]
Docs: ["Docs:OpenAPI"]
...
```
* 构建时将 `docs` 添加到TAGS中:
```sh
make run TAGS='docs'
# visit http://127.0.0.1:8008/docs
# visit http://127.0.0.1:8008/docs/openapi
```
### 配置说明
@ -323,10 +332,15 @@ release/paopao-ce --no-default-features --features sqlite3,localoss,loggerfile,r
`LoggerFile` 使用文件写日志(目前状态: 稳定);
`LoggerZinc` 使用[Zinc](https://github.com/zinclabs/zinc)写日志(目前状态: 稳定,推荐使用);
`LoggerMeili` 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志(目前状态: 内测阶段);
* 用户关系模式: Friendship/Followship
`Friendship` 弱关系好友模式,类似微信朋友圈(目前状态: 开发阶段);
`Followship` 关注者模式类似Twitter的Follow模式(目前状态: WIP);
* 支付: Alipay
`Alipay` 开启基于[支付宝开放平台](https://open.alipay.com/)的钱包功能;
* 短信验证码: SmsJuhe(需要开启sms)
`Sms` 开启短信验证码功能,用于手机绑定验证手机是否注册者的;功能如果没有开启,手机绑定时任意短信验证码都可以绑定手机;
`Sms` 开启短信验证码功能,用于手机绑定验证手机是否注册者的;功能如果没有开启,手机绑定时任意短信验证码都可以绑定手机;
* 开发文档: Docs:OpenAPI
`Docs:OpenAPI` 开启openapi文档功能提供web api文档说明(visit http://127.0.0.1:8008/docs/openapi)
* 其他: PhoneBind/OSS:Retention/OSS:TempDir
`PhoneBind` 手机绑定功能;
`OSS:Retention` 基于对象存储系统的对象过期自动删除特性实现 先创建临时对象再持久化的功能(目前状态: 内测阶段)
@ -456,7 +470,7 @@ MinIO: # MinIO 存储配置
短信通道使用的[聚合数据](https://www.juhe.cn/),如果申请不下来,可以考虑替换其他服务商。
代码结构比较简单,很方便扩展
代码结构比较简单,很方便扩展。 开发文档请参阅[docs](docs '开发文档').
## 👯‍♀️ 贡献

@ -11,11 +11,12 @@ Server: # 服务设置
ReadTimeout: 60
WriteTimeout: 60
Features:
Default: ["Base", "MySQL", "Option", "Zinc", "LocalOSS", "LoggerFile"]
Default: ["Base", "MySQL", "Option", "Zinc", "LocalOSS", "LoggerFile", "Friendship"]
Develop: ["Base", "MySQL", "BigCacheIndex", "Meili", "Sms", "AliOSS", "LoggerMeili", "OSS:Retention"]
Demo: ["Base", "MySQL", "Option", "Zinc", "Sms", "MinIO", "LoggerZinc", "Migration"]
Slim: ["Base", "Sqlite3", "LocalOSS", "LoggerFile", "OSS:TempDir"]
Base: ["Redis", "PhoneBind"]
Docs: ["Docs:OpenAPI"]
Option: ["SimpleCacheIndex"]
Sms: "SmsJuhe"
SmsJuhe:

@ -0,0 +1,6 @@
## 开发文档
本目录包含一些开发者文档。
* [openapi](openapi): api相关文档
* [proposal](proposal): 开发/设计 提按相关文档
* [deploy](deploy): 部署相关文档

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
## 部署文档
TODO

@ -1,7 +1,7 @@
//go:build docs
// +build docs
package docs
package openapi
import (
"embed"

@ -8,12 +8,12 @@
<!-- Important: rapi-doc uses utf8 charecters -->
<script
type="module"
src="/docs/assets/rapidoc-min.js"
src="/docs/openapi/assets/rapidoc-min.js"
></script>
</head>
<body>
<rapi-doc
spec-url="/docs/openapi.json"
spec-url="/docs/openapi/openapi.json"
render-style="read"
>
</rapi-doc>

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@ -0,0 +1 @@
### 图片等资源目录

@ -0,0 +1,26 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 000 | 北野 | 2022-11-04 |2022-11-04 | v1.0| 提议 |
## <我是标题>
---- 这里写简要介绍 ----
## 场景
---- 这里描述在什么使用场景下会需要本提按 ----
## 需求
---- 这里表述具体需求,在上面描述的场景下,我们需要获得的具体状态 ----
## 方案
---- 这里提出可能的解决方案 ----
## 疑问
---- 这里描述上述内容中的疑问,比如方案落地将会遇到的坑,其他方案与本文档的比较 ----
## 更新记录
#### v1.0(2022-11-04) - 北野
* 初始文档

@ -0,0 +1,11 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 001| 北野 | 2022-11-04 | 2022-11-04 | v0.0 | 提议 |
### 概述
TODO
### 更新记录
#### v0.0(2022-11-04) - 北野
* 初始文档,先占个位置

@ -0,0 +1,66 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 002| 北野 | 2022-11-04 | 2022-11-06 | v0.2 | 提议 |
### Friendship功能项的设计概要
Friendship功能提供好友间分享推文信息的机制更好的帮助用户建立自己的推文分享小圈子。Friendship本质上想优化的是泡泡广场页面推文列表的生成机制开启功能后推文列表只能获取 `公开/私密/好友` 的推文,每个用户都有属于自己的个性化推文列表。在提供个性化推文列表生成机制的同时,好友体系的建立也顺便帮助用户建立自己的个性化有限范围内的灵魂社交小圈子,只有相互间拥有个性化认同感的用户才能互为好友。
### 场景
* 私域小站点,流量小,但是希望控制推文访问权限;
* 公域大站点或者小站点,推文数量庞大,更新频率高,泡泡广场列表有刷屏风险,需要一种更好的推文列表生成机制,为用户提供个性化推文列表服务,优化用户体验;
* 单纯想建立一个好友体系;
### 需求
* 需要类似微信朋友圈这样的在好友间进行推文分享;
* 希望对推文访问权限进行控制,实现推文在有限空间内自由访问;
* 部署站点推文数量庞大,需要一种更好的推文列表生成机制,为用户提供个性化推文列表服务;
* 建立一个类似于微信朋友圈的弱关系好友体系,使得用户可以控制每一条推文的授权访问(公开/私密/好友可见)
### 方案
#### 设计要点
* 建立好友体系,提供添加/解除好友关系的机制;
* 浏览好友列表的机制;
* 发送推文时可以设置推文的访问权限(可见性设置)
* 推文展示时标记推文的可见性描述;
#### 设计细节
* 预览
| |
| ----- |
| **泡泡广场** |
| ![](.assets/002-02.png)![](.assets/002-01.png)|
| ![](.assets/002-07.png)|
| ![](.assets/002-08.png)|
### 疑问
1. 什么是弱关系好友体系?
**弱关系好友** 在Friendship中的表现就是 **你只能通过对方发表的推文去判断这个人的思想三观是否符合你的脾胃进而让你决定是否要与对方建立好友关系;这种好友关系前期是非常薄弱,只能通过相互间的推文产生灵魂共鸣,进而互为兴趣、互为好友。** 有别于弱关系,微信的生态就天然建立在强关系之上,微信好友大部分都是自己熟人、朋友甚至亲人,因此微信的朋友圈就是强关系好友体系下的小圈子社交,有时谨言慎行就非常必要,导致有很多人随着年龄成长到某个阶段,对微信朋友圈非常不感冒,很少甚至没有发朋友圈的欲望了,究其原因有一部分可能是圈内好友太熟了甚至有点“严肃”。需要注意的是,有些人的表达欲望并没有因为微信朋友圈这种 **强关系好友** *圈内社交环境* 而降低,仅仅只是被压抑住了,那么一个**弱关系好友**体系或许可以打开这部分人的表达欲望,使得他们可以在有限可控圈子内尽情表达、享受言论的自由。
2. 如何形成这种好友体系?
形成好友体系分 **建立、维持、解除** 好友关系。Friendship提供 *建立/解除* 好友关系的机制,泡泡广场的推文列表依据这个好友体系为每个用户生成个性化推文列表。而好友关系的**维持**本质上就是用户推文的持久更新、思想的持续演化使得好友对你一直保持兴趣Friendship也为每一条推文的访问权限进行**标记(私密/好友可见)**,这可以传达这样一种信息: **“时刻让好友知道我非常在乎好友、对好友特殊关照,你看这条推文就是只有我的好友(也就是你)才可以看到,够意思吧”!** 顺便一说,每一条推文的访问权限标记(公开/私密/好友可见)是建立Friendship弱关系体系的隐性催化剂可以加速Friendship的形成。
3. 如何开启这个功能?
在配置文件config.yaml中的`Features`中添加`Friendship`功能项开启该功能:
```yaml
...
# features中加上 Friendship
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "MinIO", "Friendship"]
Base: ["Redis", "PhoneBind"]
...
```
### 更新记录
#### v0.1(2022-11-04) - 北野
* 初始文档
#### v0.2(2022-11-06) - 北野
* 添加初始文档内容

@ -0,0 +1,28 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 003| 北野 | 2022-11-04 | 2022-11-04 | v0.0 | 提议 |
### 关于Followship功能项的设计
---- 这里写简要介绍 ----
### 场景
---- 这里描述在什么使用场景下会需要本提按 ----
### 需求
实现类似于Twitter的追随者关注模式的推文传播模型使得广场页面可以浏览已关注用户的推文。
TODO-TL;DR...
### 方案
TODO
### 疑问
1. 如何开启这个功能?
TODO
### 更新记录
#### v0.0(2022-11-04) - 北野
* 初始文档, 先占个位置

@ -0,0 +1,4 @@
## Draft
* [001-关于paopao-ce的设计定位](001-关于paopao-ce的设计定位.md "关于paopao-ce的设计定位")
* [002-关于Friendship功能项的设计](002-关于Friendship功能项的设计.md "关于Friendship功能项的设计")
* [003-关于Followship功能项的设计](003-关于Followship功能项的设计.md "关于Followship功能项的设计")

@ -0,0 +1,33 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| <编号000> | <作者> | <发表时间> | <变更时间> | <版本号v1.0> | <提议/提案/决议/冻结> |
* 编号: 填写提案编号三位数比如001。
* 作者: 填写发表者。
* 发表时间: 填写首次发表时间,之后保持不变。
* 变更时间: 填写变更时间,首次发表时,变更时间和发表时间一样。
* 版本: 当前文档版本号, 样式为v1.0/v2.0 保持两位数,微调加次版本号, 大调加大版本号。
* 状态: 首次发表 状态为 **提议** 经过讨论,多数人认同,状态为 **提案**; 审议通过,决定对方案落地,状态为 **决议**; 审议未通过,方案暂时不落地,状态变更为 **冻结**。
### <我是标题>
---- 这里写简要介绍 ----
### 场景
---- 这里描述在什么使用场景下会需要本提按 ----
### 需求
---- 这里表述具体需求,在上面描述的场景下,我们需要获得的具体状态 ----
### 方案
---- 这里提出可能的解决方案 ----
### 疑问
---- 这里描述上述内容中的疑问,比如方案落地将会遇到的坑,其他方案与本文档的比较 ----
### 更新记录
#### <版本号v1.0>(<变更时间>) - <作者>
* 变更描述

@ -36,6 +36,7 @@ const (
type act uint8
type FriendFilter map[int64]types.Empty
type FriendSet map[string]types.Empty
type Action struct {
Act act
@ -47,13 +48,12 @@ type AuthorizationManageService interface {
IsAllow(user *model.User, action *Action) bool
BeFriendFilter(userId int64) FriendFilter
BeFriendIds(userId int64) ([]int64, error)
MyFriendSet(userId int64) FriendSet
}
func (f FriendFilter) IsFriend(userId int64) bool {
// _, yesno := f[userId]
// return yesno
// so, you are friend with all world now
return true
_, yeah := f[userId]
return yeah
}
// IsAllow default true if user is admin

@ -19,16 +19,15 @@ type DataService interface {
TweetManageService
TweetHelpService
// 附件检测服务
AttachmentCheckService
// 评论服务
CommentService
CommentManageService
// 用户服务
UserManageService
ContactManageService
// 安全服务
SecurityService
AttachmentCheckService
}

@ -2,6 +2,7 @@ package core
import (
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/internal/model/rest"
)
// UserManageService 用户管理服务
@ -13,5 +14,14 @@ type UserManageService interface {
GetUsersByKeyword(keyword string) ([]*model.User, error)
CreateUser(user *model.User) (*model.User, error)
UpdateUser(user *model.User) error
}
// ContactManageService 联系人管理服务
type ContactManageService interface {
RequestingFriend(userId int64, friendId int64, greetings string) error
AddFriend(userId int64, friendId int64) error
RejectFriend(userId int64, friendId int64) error
DeleteFriend(userId int64, friendId int64) error
GetContacts(userId int64, offset int, limit int) (*rest.ContactsResp, error)
IsFriend(userID int64, friendID int64) bool
}

@ -13,6 +13,7 @@ import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/internal/model/rest"
"github.com/rocboss/paopao-ce/pkg/types"
"github.com/sirupsen/logrus"
)
@ -28,6 +29,7 @@ type postsEntry struct {
type bigCacheIndexServant struct {
ips core.IndexPostsService
ams core.AuthorizationManageService
indexActionCh chan *core.IndexAction
cachePostsCh chan *postsEntry
@ -133,7 +135,7 @@ func (s *bigCacheIndexServant) handleIndexAction(action *core.IndexAction) {
switch act {
case core.IdxActCreatePost, core.IdxActDeletePost:
if post.Visibility == model.PostVisitPrivate {
s.deleteCacheByUserId(post.UserID)
s.deleteCacheByUserId(post.UserID, true)
return
}
}
@ -141,29 +143,34 @@ func (s *bigCacheIndexServant) handleIndexAction(action *core.IndexAction) {
// 如果在s.preventDuration时间内就清除所有缓存否则只清除自个儿的缓存
// TODO: 需要优化只清除受影响的缓存,后续完善
if time.Since(s.lastCacheResetTime) > s.preventDuration {
s.cache.Reset()
s.lastCacheResetTime = time.Now()
logrus.Debugf("bigCacheIndexServant.handleIndexAction reset cache by %s", action.Act)
s.deleteCacheByUserId(post.UserID, false)
} else {
s.deleteCacheByUserId(post.UserID)
s.deleteCacheByUserId(post.UserID, true)
}
}
func (s *bigCacheIndexServant) deleteCacheByUserId(id int64) {
func (s *bigCacheIndexServant) deleteCacheByUserId(id int64, oneself bool) {
var keys []string
userId := strconv.FormatInt(id, 10)
friendSet := core.FriendSet{}
if !oneself {
friendSet = s.ams.MyFriendSet(id)
}
friendSet[userId] = types.Empty{}
// 获取需要删除缓存的key目前是仅删除自个儿的缓存
for it := s.cache.Iterator(); it.SetNext(); {
entry, err := it.Value()
if err != nil {
logrus.Debugf("bigCacheIndexServant.deleteCacheByUserId usrId: %s err:%s", userId, err)
logrus.Debugf("bigCacheIndexServant.deleteCacheByUserId userId: %s err:%s", userId, err)
return
}
key := entry.Key()
keyParts := strings.Split(key, ":")
if len(keyParts) > 2 && keyParts[0] == "index" && keyParts[1] == userId {
keys = append(keys, key)
if len(keyParts) > 2 && keyParts[0] == "index" {
if _, ok := friendSet[keyParts[1]]; ok {
keys = append(keys, key)
}
}
}
@ -172,7 +179,7 @@ func (s *bigCacheIndexServant) deleteCacheByUserId(id int64) {
s.cache.Delete(k)
}
s.lastCacheResetTime = time.Now()
logrus.Debugf("bigCacheIndexServant.deleteCacheByUserId userId:%d", id)
logrus.Debugf("bigCacheIndexServant.deleteCacheByUserId userId:%s oneself:%t keys:%d", userId, oneself, len(keys))
}
func (s *bigCacheIndexServant) Name() string {

@ -9,7 +9,7 @@ import (
"github.com/sirupsen/logrus"
)
func NewBigCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) {
func NewBigCacheIndexService(ips core.IndexPostsService, ams core.AuthorizationManageService) (core.CacheIndexService, core.VersionInfo) {
s := conf.BigCacheIndexSetting
config := bigcache.DefaultConfig(s.ExpireInSecond)
@ -23,7 +23,8 @@ func NewBigCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndex
}
cacheIndex := &bigCacheIndexServant{
ips: indexPosts,
ips: ips,
ams: ams,
cache: cache,
preventDuration: 10 * time.Second,
}

@ -3,6 +3,7 @@ package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/pkg/types"
"gorm.io/gorm"
)
@ -14,6 +15,12 @@ type authorizationManageServant struct {
db *gorm.DB
}
func newAuthorizationManageService(db *gorm.DB) core.AuthorizationManageService {
return &authorizationManageServant{
db: db,
}
}
func (s *authorizationManageServant) IsAllow(user *model.User, action *core.Action) bool {
// user is activation if had bind phone
isActivation := (len(user.Phone) != 0)
@ -22,17 +29,40 @@ func (s *authorizationManageServant) IsAllow(user *model.User, action *core.Acti
return action.Act.IsAllow(user, action.UserId, isFriend, isActivation)
}
func (s *authorizationManageServant) MyFriendSet(userId int64) core.FriendSet {
ids, err := (&model.Contact{UserId: userId}).MyFriendIds(s.db)
if err != nil {
return core.FriendSet{}
}
resp := make(core.FriendSet, len(ids))
for _, id := range ids {
resp[id] = types.Empty{}
}
return resp
}
func (s *authorizationManageServant) BeFriendFilter(userId int64) core.FriendFilter {
// just empty now
return core.FriendFilter{}
ids, err := (&model.Contact{FriendId: userId}).BeFriendIds(s.db)
if err != nil {
return core.FriendFilter{}
}
resp := make(core.FriendFilter, len(ids))
for _, id := range ids {
resp[id] = types.Empty{}
}
return resp
}
func (s *authorizationManageServant) BeFriendIds(userId int64) ([]int64, error) {
// just empty now
return []int64{}, nil
return (&model.Contact{FriendId: userId}).BeFriendIds(s.db)
}
func (s *authorizationManageServant) isFriend(userId int64, friendId int64) bool {
// just true now
return true
contact, err := (&model.Contact{UserId: friendId, FriendId: userId}).GetByUserFriend(s.db)
if err == nil || contact.Status == model.ContactStatusAgree {
return true
}
return false
}

@ -0,0 +1,268 @@
package jinzhu
import (
"time"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/internal/model/rest"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var (
_ core.ContactManageService = (*contactManageServant)(nil)
)
type contactManageServant struct {
db *gorm.DB
}
func newContactManageService(db *gorm.DB) core.ContactManageService {
return &contactManageServant{
db: db,
}
}
func (s *contactManageServant) fetchOrNewContact(db *gorm.DB, userId int64, friendId int64, status int8) (*model.Contact, error) {
contact := &model.Contact{
UserId: userId,
FriendId: friendId,
}
contact, err := contact.FetchUser(db)
if err != nil {
contact = &model.Contact{
UserId: userId,
FriendId: friendId,
Status: status,
}
if contact, err = contact.Create(db); err != nil {
logrus.Errorf("contactManageServant.fetchOrNewContact create new contact err:%s", err)
return nil, err
}
}
return contact, nil
}
func (s *contactManageServant) RequestingFriend(userId int64, friendId int64, greetings string) (err error) {
db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
contact, e := s.fetchOrNewContact(db, userId, friendId, model.ContactStatusRequesting)
if e != nil {
err = e
return
}
// 如果已经好友,啥也不干
if contact.Status == model.ContactStatusAgree {
return nil
} else if contact.Status == model.ContactStatusReject || contact.Status == model.ContactStatusDeleted {
contact.Status = model.ContactStatusRequesting
contact.IsDel = 0 // remove deleted flag if needed
if err = contact.UpdateInUnscoped(db); err != nil {
logrus.Errorf("contactManageServant.RequestingFriend update exsit contact err:%s", err)
return
}
}
msg := &model.Message{
SenderUserID: userId,
ReceiverUserID: friendId,
Type: model.MsgTypeRequestingFriend,
Brief: "请求添加好友,并附言:",
Content: greetings,
ReplyID: int64(model.ContactStatusRequesting),
}
if _, err = msg.Create(db); err != nil {
logrus.Errorf("contactManageServant.RequestingFriend create message err:%s", err)
return
}
return nil
}
func (s *contactManageServant) AddFriend(userId int64, friendId int64) (err error) {
db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
contact := &model.Contact{
UserId: friendId,
FriendId: userId,
}
if contact, err = contact.GetByUserFriend(db); err != nil {
return
}
// 如果还不是请求好友,啥也不干
if contact.Status != model.ContactStatusRequesting {
logrus.Debugf("contactManageServant.AddFriend not reuesting status now so skip")
return nil
}
contact.Status = model.ContactStatusAgree
if err = contact.Update(db); err != nil {
return err
}
contact, err = s.fetchOrNewContact(db, userId, friendId, model.ContactStatusAgree)
if err != nil {
return
}
// 如果已经好友,啥也不干
if contact.Status != model.ContactStatusAgree {
contact.Status = model.ContactStatusAgree
contact.IsDel = 0 // remove deleted flag
if err = contact.UpdateInUnscoped(db); err != nil {
logrus.Errorf("contactManageServant.AddFriend update contact err:%s", err)
return
}
}
args := []any{userId, friendId, friendId, userId, model.MsgTypeRequestingFriend, model.ContactStatusRequesting}
msgs, e := (&model.Message{}).FetchBy(db, model.Predicates{
"((sender_user_id = ? AND receiver_user_id = ?) OR (sender_user_id = ? AND receiver_user_id = ?)) AND type = ? AND reply_id = ?": args,
})
if e != nil {
err = e
return
}
for _, msg := range msgs {
msg.ReplyID = int64(model.ContactStatusAgree)
if err = msg.Update(db); err != nil {
return
}
}
return nil
}
func (s *contactManageServant) RejectFriend(userId int64, friendId int64) (err error) {
db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
contact := &model.Contact{
UserId: friendId,
FriendId: userId,
}
if contact, err = contact.GetByUserFriend(db); err != nil {
return
}
// 如果还不是请求好友,啥也不干
if contact.Status != model.ContactStatusRequesting {
return nil
}
contact.Status = model.ContactStatusReject
if err = contact.Update(db); err != nil {
return err
}
args := []any{friendId, userId, model.MsgTypeRequestingFriend, model.ContactStatusRequesting}
msgs, e := (&model.Message{}).FetchBy(db, model.Predicates{
"sender_user_id = ? AND receiver_user_id = ? AND type = ? AND reply_id = ?": args,
})
if e != nil {
err = e
return
}
for _, msg := range msgs {
msg.ReplyID = int64(model.ContactStatusReject)
if err = msg.Update(db); err != nil {
return
}
}
return nil
}
func (s *contactManageServant) DeleteFriend(userId int64, friendId int64) (err error) {
db := s.db.Begin()
defer func() {
if err == nil {
db.Commit()
} else {
db.Rollback()
}
}()
contact := &model.Contact{
UserId: userId,
FriendId: friendId,
}
contacts, e := contact.FetchByUserFriendAll(db)
if e != nil {
return e
}
for _, contact := range contacts {
// 如果还不是好友,啥也不干
if contact.Status != model.ContactStatusAgree {
continue
}
contact.Status = model.ContactStatusDeleted
contact.DeletedOn = time.Now().Unix()
contact.IsDel = 1
if err = contact.Update(db); err != nil {
return
}
}
return nil
}
func (s *contactManageServant) GetContacts(userId int64, offset int, limit int) (*rest.ContactsResp, error) {
contact := &model.Contact{}
condition := model.ConditionsT{
"user_id": userId,
"status": model.ContactStatusAgree,
}
contacts, err := contact.List(s.db, condition, offset, limit)
if err != nil {
return nil, err
}
total, err := contact.Count(s.db, condition)
if err != nil {
return nil, err
}
resp := &rest.ContactsResp{
Contacts: make([]rest.ContactItem, 0, len(contacts)),
Total: total,
}
for _, c := range contacts {
if c.User != nil {
resp.Contacts = append(resp.Contacts, rest.ContactItem{
UserId: c.FriendId,
UserName: c.User.Username,
Nickname: c.User.Nickname,
Avatar: c.User.Avatar,
Phone: c.User.Phone,
})
}
}
return resp, nil
}
func (s *contactManageServant) IsFriend(userId int64, friendId int64) bool {
contact := &model.Contact{
UserId: friendId,
FriendId: userId,
}
contact, err := contact.GetByUserFriend(s.db)
if err == nil && contact.Status == model.ContactStatusAgree {
return true
}
return false
}

@ -1,6 +1,6 @@
// Core service implement base gorm+mysql/postgresql/sqlite3.
// Jinzhu is the primary developer of gorm so use his name as
// pakcage name as a saluter.
// package name as a saluter.
package jinzhu
@ -29,6 +29,7 @@ type dataServant struct {
core.CommentService
core.CommentManageService
core.UserManageService
core.ContactManageService
core.SecurityService
core.AttachmentCheckService
}
@ -46,7 +47,8 @@ func NewDataService() (core.DataService, core.VersionInfo) {
i = newSimpleIndexPostsService(db)
c, v = cache.NewSimpleCacheIndexService(i)
} else if conf.CfgIf("BigCacheIndex") {
c, v = cache.NewBigCacheIndexService(i)
a := newAuthorizationManageService(db)
c, v = cache.NewBigCacheIndexService(i, a)
} else {
c, v = cache.NewNoneCacheIndexService(i)
}
@ -63,6 +65,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
CommentService: newCommentService(db),
CommentManageService: newCommentManageService(db),
UserManageService: newUserManageService(db),
ContactManageService: newContactManageService(db),
SecurityService: newSecurityService(db),
AttachmentCheckService: security.NewAttachmentCheckService(),
}
@ -70,9 +73,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
}
func NewAuthorizationManageService() core.AuthorizationManageService {
return &authorizationManageServant{
db: conf.MustGormDB(),
}
return newAuthorizationManageService(conf.MustGormDB())
}
func (s *dataServant) Name() string {
@ -80,5 +81,5 @@ func (s *dataServant) Name() string {
}
func (s *dataServant) Version() *semver.Version {
return semver.MustParse("v0.1.0")
return semver.MustParse("v0.2.0")
}

@ -88,8 +88,3 @@ func (s *userManageServant) CreateUser(user *model.User) (*model.User, error) {
func (s *userManageServant) UpdateUser(user *model.User) error {
return user.Update(s.db)
}
func (s *userManageServant) IsFriend(userId int64, friendId int64) bool {
// just true now
return true
}

@ -0,0 +1,119 @@
package model
import (
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
const (
ContactStatusRequesting int8 = iota + 1
ContactStatusAgree
ContactStatusReject
ContactStatusDeleted
)
type Contact struct {
*Model
User *User `json:"-" gorm:"foreignKey:ID;references:FriendId"`
UserId int64 `json:"user_id"`
FriendId int64 `json:"friend_id"`
GroupId int64 `json:"group_id"`
Remark string `json:"remark"`
Status int8 `json:"status"` // 1请求好友, 2已同意好友, 3已拒绝好友, 4已删除好友
IsTop int8 `json:"is_top"`
IsBlack int8 `json:"is_black"`
NoticeEnable int8 `json:"notice_enable"`
}
func (c *Contact) FetchUser(db *gorm.DB) (*Contact, error) {
var contact Contact
err := db.Omit("User").Unscoped().Where("user_id = ? AND friend_id = ?", c.UserId, c.FriendId).First(&contact).Error
if err != nil {
logrus.Debugf("Contact.FetchUser fetch user error:%s", err)
return nil, err
}
return &contact, nil
}
func (c *Contact) GetByUserFriend(db *gorm.DB) (*Contact, error) {
var contact Contact
err := db.Omit("User").Where("user_id = ? AND friend_id = ?", c.UserId, c.FriendId).First(&contact).Error
if err != nil {
return nil, err
}
return &contact, nil
}
func (c *Contact) FetchByUserFriendAll(db *gorm.DB) ([]*Contact, error) {
var contacts []*Contact
if err := db.Omit("User").
Where("(user_id = ? AND friend_id = ?) OR (user_id = ? AND friend_id = ?)",
c.UserId, c.FriendId, c.FriendId, c.UserId).
Find(&contacts).Error; err != nil {
return nil, err
}
return contacts, nil
}
func (c *Contact) List(db *gorm.DB, conditions ConditionsT, offset, limit int) ([]*Contact, error) {
var contacts []*Contact
var err error
tn := db.NamingStrategy.TableName("Contact") + "."
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
for k, v := range conditions {
if k != "ORDER" {
db = db.Where(tn+k, v)
}
}
db.Joins("User").Order("`User`.`nickname` ASC")
if err = db.Find(&contacts).Error; err != nil {
return nil, err
}
return contacts, nil
}
func (c *Contact) BeFriendIds(db *gorm.DB) (ids []int64, err error) {
if err = db.Model(c).Omit("User").Select("user_id").Where("friend_id = ? AND status = ?", c.FriendId, ContactStatusAgree).Find(&ids).Error; err != nil {
return nil, err
}
return
}
func (c *Contact) MyFriendIds(db *gorm.DB) (ids []string, err error) {
if err = db.Model(c).Omit("User").Select("friend_id").Where("user_id = ? AND status = ?", c.UserId, ContactStatusAgree).Find(&ids).Error; err != nil {
return nil, err
}
return
}
func (m *Contact) Count(db *gorm.DB, conditions ConditionsT) (int64, error) {
var count int64
for k, v := range conditions {
if k != "ORDER" {
db = db.Where(k, v)
}
}
if err := db.Model(m).Omit("User").Count(&count).Error; err != nil {
return 0, err
}
return count, nil
}
func (c *Contact) Create(db *gorm.DB) (*Contact, error) {
err := db.Omit("User").Create(&c).Error
return c, err
}
func (c *Contact) Update(db *gorm.DB) error {
return db.Model(&Contact{}).Omit("User").Where("id = ?", c.Model.ID).Save(c).Error
}
func (c *Contact) UpdateInUnscoped(db *gorm.DB) error {
return db.Unscoped().Omit("User").Save(c).Error
}

@ -92,15 +92,24 @@ func (m *Message) Get(db *gorm.DB) (*Message, error) {
if m.ReceiverUserID > 0 {
db = db.Where("receiver_user_id = ?", m.ReceiverUserID)
}
err := db.First(&message).Error
if err != nil {
return &message, err
if err := db.First(&message).Error; err != nil {
return nil, err
}
return &message, nil
}
func (m *Message) FetchBy(db *gorm.DB, predicates Predicates) ([]*Message, error) {
var messages []*Message
for k, v := range predicates {
db = db.Where(k, v...)
}
db = db.Where("is_del = 0")
if err := db.Find(&messages).Error; err != nil {
return nil, err
}
return messages, nil
}
func (c *Message) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*Message, error) {
var messages []*Message
var err error

@ -0,0 +1,31 @@
package rest
type RequestingFriendReq struct {
UserId int64 `json:"user_id" binding:"required"`
Greetings string `json:"greetings" binding:"required"`
}
type AddFriendReq struct {
UserId int64 `json:"user_id" binding:"required"`
}
type RejectFriendReq struct {
UserId int64 `json:"user_id" binding:"required"`
}
type DeleteFriendReq struct {
UserId int64 `json:"user_id"`
}
type ContactItem struct {
UserId int64 `json:"user_id"`
UserName string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Phone string `json:"phone"`
}
type ContactsResp struct {
Contacts []ContactItem `json:"contacts"`
Total int64 `json:"total"`
}

@ -0,0 +1,11 @@
package rest
type UserProfileResp struct {
ID int64 `json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
IsAdmin bool `json:"is_admin"`
IsFriend bool `json:"is_friend"`
}

@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/internal/model/rest"
"github.com/rocboss/paopao-ce/internal/service"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/convert"
@ -90,6 +91,132 @@ func Register(c *gin.Context) {
})
}
func RequestingFriend(c *gin.Context) {
param := rest.RequestingFriendReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
user, ok := userFrom(c)
if !ok {
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
}
if user.ID == param.UserId {
response.ToErrorResponse(errcode.NoRequestingFriendToSelf)
return
}
if err := service.RequestingFriend(user, &param); err != nil {
logrus.Errorf("service.RequestingFriend err: %v", err)
response.ToErrorResponse(errcode.SendRequestingFriendFailed)
return
}
response.ToResponse(nil)
}
func AddFriend(c *gin.Context) {
param := rest.AddFriendReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
user, ok := userFrom(c)
if !ok {
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
}
if user.ID == param.UserId {
response.ToErrorResponse(errcode.NoActionToSelf)
return
}
if err := service.AddFriend(user, &param); err != nil {
logrus.Errorf("service.AddFriend err: %v", err)
response.ToErrorResponse(errcode.AddFriendFailed)
return
}
response.ToResponse(nil)
}
func RejectFriend(c *gin.Context) {
param := rest.RejectFriendReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
user, ok := userFrom(c)
if !ok {
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
}
if user.ID == param.UserId {
response.ToErrorResponse(errcode.NoActionToSelf)
return
}
if err := service.RejectFriend(user, &param); err != nil {
logrus.Errorf("service.RejectFriend err: %v", err)
response.ToErrorResponse(errcode.RejectFriendFailed)
return
}
response.ToResponse(nil)
}
func DeleteFriend(c *gin.Context) {
param := rest.DeleteFriendReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
user, ok := userFrom(c)
if !ok {
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
}
if user.ID == param.UserId {
response.ToErrorResponse(errcode.NoActionToSelf)
return
}
if err := service.DeleteFriend(user, &param); err != nil {
logrus.Errorf("service.DeleteFriend err: %v", err)
response.ToErrorResponse(errcode.DeleteFriendFailed)
return
}
response.ToResponse(nil)
}
func GetContacts(c *gin.Context) {
response := app.NewResponse(c)
user, ok := userFrom(c)
if !ok {
response.ToErrorResponse(errcode.UnauthorizedAuthNotExist)
}
offset, limit := app.GetPageOffset(c)
resp, err := service.GetContacts(user, offset, limit)
if err != nil {
logrus.Errorf("service.DeleteFriend err: %v", err)
response.ToErrorResponse(errcode.GetContactsFailed)
return
}
response.ToResponseList(resp.Contacts, resp.Total)
}
// 获取用户基本信息
func GetUserInfo(c *gin.Context) {
param := service.AuthRequest{}
@ -241,7 +368,10 @@ func BindUserPhone(c *gin.Context) {
// 执行绑定
user.Phone = param.Phone
service.UpdateUserInfo(user)
if err := service.UpdateUserInfo(user); err != nil {
response.ToErrorResponse(err)
return
}
response.ToResponse(nil)
}
@ -279,29 +409,24 @@ func ChangeUserStatus(c *gin.Context) {
func GetUserProfile(c *gin.Context) {
response := app.NewResponse(c)
username := c.Query("username")
user, _ := userFrom(c)
user, err := service.GetUserByUsername(username)
resp, err := service.GetUserByUsername(user, username)
if err != nil {
logrus.Errorf("service.GetUserByUsername err: %v\n", err)
response.ToErrorResponse(errcode.NoExistUsername)
return
}
response.ToResponse(gin.H{
"id": user.ID,
"nickname": user.Nickname,
"username": user.Username,
"status": user.Status,
"avatar": user.Avatar,
"is_admin": user.IsAdmin,
})
response.ToResponse(resp)
}
func GetUserPosts(c *gin.Context) {
response := app.NewResponse(c)
username := c.Query("username")
user, exists := userFrom(c)
user, err := service.GetUserByUsername(username)
other, err := service.GetUserByUsername(user, username)
if err != nil {
logrus.Errorf("service.GetUserByUsername err: %v\n", err)
response.ToErrorResponse(errcode.NoExistUsername)
@ -309,16 +434,15 @@ func GetUserPosts(c *gin.Context) {
}
visibilities := []model.PostVisibleT{model.PostVisitPublic}
if u, exists := c.Get("USER"); exists {
self := u.(*model.User)
if self.ID == user.ID || self.IsAdmin {
if exists {
if user.ID == other.ID || user.IsAdmin {
visibilities = append(visibilities, model.PostVisitPrivate, model.PostVisitFriend)
} else if service.IsFriend(user.ID, self.ID) {
} else if other.IsFriend {
visibilities = append(visibilities, model.PostVisitFriend)
}
}
conditions := model.ConditionsT{
"user_id": user.ID,
"user_id": other.ID,
"visibility IN ?": visibilities,
"ORDER": "latest_replied_on DESC",
}

@ -5,10 +5,13 @@ package routers
import (
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/docs"
"github.com/rocboss/paopao-ce/docs/openapi"
"github.com/rocboss/paopao-ce/internal/conf"
)
// registerDocs register docs asset route
func registerDocs(e *gin.Engine) {
e.StaticFS("/docs", docs.NewFileSystem())
if conf.CfgIf("Docs:OpenAPI") {
e.StaticFS("/docs/openapi", openapi.NewFileSystem())
}
}

@ -60,9 +60,6 @@ func NewRouter() *gin.Engine {
// 获取话题列表
noAuthApi.GET("/tags", api.GetPostTags)
// 获取用户基本信息
noAuthApi.GET("/user/profile", api.GetUserProfile)
}
// 宽松鉴权路由组
@ -73,132 +70,140 @@ func NewRouter() *gin.Engine {
// 获取用户动态列表
looseApi.GET("/user/posts", api.GetUserPosts)
// 获取用户基本信息
looseApi.GET("/user/profile", api.GetUserProfile)
}
// 鉴权路由组
authApi := r.Group("/").Use(middleware.JWT())
privApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Priv())
adminApi := r.Group("/").Use(middleware.JWT()).Use(middleware.Admin())
{
// 同步索引
authApi.GET("/sync/index", api.SyncSearchIndex)
// 获取当前用户信息
authApi.GET("/user/info", api.GetUserInfo)
// 核心路由注册
routeCore(authApi, privApi, adminApi)
// 获取当前用户未读消息数量
authApi.GET("/user/msgcount/unread", api.GetUnreadMsgCount)
// 支付宝路由注册
routeAlipay(r, authApi)
// 获取消息列表
authApi.GET("/user/messages", api.GetMessages)
// Relationship相关路由注册
routeRelationship(authApi)
// 标记消息已读
authApi.POST("/user/message/read", api.ReadMessage)
// 默认404
e.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"msg": "Not Found",
})
})
// 发送用户私信
authApi.POST("/user/whisper", api.SendUserWhisper)
// 默认405
e.NoMethod(func(c *gin.Context) {
c.JSON(http.StatusMethodNotAllowed, gin.H{
"code": 405,
"msg": "Method Not Allowed",
})
})
// 获取用户收藏列表
authApi.GET("/user/collections", api.GetUserCollections)
return e
}
// 获取用户点赞列表
authApi.GET("/user/stars", api.GetUserStars)
func routeCore(authApi gin.IRoutes, privApi gin.IRoutes, adminApi gin.IRoutes) {
// 同步索引
authApi.GET("/sync/index", api.SyncSearchIndex)
// 绑定用户手机号
if conf.CfgIf("PhoneBind") {
authApi.POST("/user/phone", api.BindUserPhone)
}
// 获取当前用户信息
authApi.GET("/user/info", api.GetUserInfo)
// 修改密码
authApi.POST("/user/password", api.ChangeUserPassword)
// 获取当前用户未读消息数量
authApi.GET("/user/msgcount/unread", api.GetUnreadMsgCount)
// 修改昵称
authApi.POST("/user/nickname", api.ChangeNickname)
// 获取消息列表
authApi.GET("/user/messages", api.GetMessages)
// 修改头像
authApi.POST("/user/avatar", api.ChangeAvatar)
// 标记消息已读
authApi.POST("/user/message/read", api.ReadMessage)
// 检索用户
authApi.GET("/suggest/users", api.GetSuggestUsers)
// 发送用户私信
authApi.POST("/user/whisper", api.SendUserWhisper)
// 检索标签
authApi.GET("/suggest/tags", api.GetSuggestTags)
// 获取用户收藏列表
authApi.GET("/user/collections", api.GetUserCollections)
// 上传资源
privApi.POST("/attachment", api.UploadAttachment)
// 获取用户点赞列表
authApi.GET("/user/stars", api.GetUserStars)
// 下载资源预检
privApi.GET("/attachment/precheck", api.DownloadAttachmentPrecheck)
// 绑定用户手机号
authApi.POST("/user/phone", api.BindUserPhone)
// 下载资源
privApi.GET("/attachment", api.DownloadAttachment)
// 修改密码
authApi.POST("/user/password", api.ChangeUserPassword)
// 发布动态
privApi.POST("/post", api.CreatePost)
// 修改昵称
authApi.POST("/user/nickname", api.ChangeNickname)
// 删除动态
privApi.DELETE("/post", api.DeletePost)
// 修改头像
authApi.POST("/user/avatar", api.ChangeAvatar)
// 获取动态点赞状态
authApi.GET("/post/star", api.GetPostStar)
// 检索用户
authApi.GET("/suggest/users", api.GetSuggestUsers)
// 动态点赞操作
privApi.POST("/post/star", api.PostStar)
// 检索标签
authApi.GET("/suggest/tags", api.GetSuggestTags)
// 获取动态收藏状态
authApi.GET("/post/collection", api.GetPostCollection)
// 上传资源
privApi.POST("/attachment", api.UploadAttachment)
// 动态收藏操作
privApi.POST("/post/collection", api.PostCollection)
// 下载资源预检
privApi.GET("/attachment/precheck", api.DownloadAttachmentPrecheck)
// 锁定动态
privApi.POST("/post/lock", api.LockPost)
// 下载资源
privApi.GET("/attachment", api.DownloadAttachment)
// 置顶动态
privApi.POST("/post/stick", api.StickPost)
// 发布动态
privApi.POST("/post", api.CreatePost)
// 修改动态可见度
privApi.POST("/post/visibility", api.VisiblePost)
// 删除动态
privApi.DELETE("/post", api.DeletePost)
// 发布动态评论
privApi.POST("/post/comment", api.CreatePostComment)
// 获取动态点赞状态
authApi.GET("/post/star", api.GetPostStar)
// 删除动态评论
privApi.DELETE("/post/comment", api.DeletePostComment)
// 动态点赞操作
privApi.POST("/post/star", api.PostStar)
// 发布评论回复
privApi.POST("/post/comment/reply", api.CreatePostCommentReply)
// 获取动态收藏状态
authApi.GET("/post/collection", api.GetPostCollection)
// 删除评论回复
privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply)
// 动态收藏操作
privApi.POST("/post/collection", api.PostCollection)
// 管理·禁言/解封用户
adminApi.POST("/admin/user/status", api.ChangeUserStatus)
}
// 锁定动态
privApi.POST("/post/lock", api.LockPost)
// 支付宝路由注册
alipayRoute(r, authApi)
// 置顶动态
privApi.POST("/post/stick", api.StickPost)
// 默认404
e.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{
"code": 404,
"msg": "Not Found",
})
})
// 修改动态可见度
privApi.POST("/post/visibility", api.VisiblePost)
// 默认405
e.NoMethod(func(c *gin.Context) {
c.JSON(http.StatusMethodNotAllowed, gin.H{
"code": 405,
"msg": "Method Not Allowed",
})
})
// 发布动态评论
privApi.POST("/post/comment", api.CreatePostComment)
return e
// 删除动态评论
privApi.DELETE("/post/comment", api.DeletePostComment)
// 发布评论回复
privApi.POST("/post/comment/reply", api.CreatePostCommentReply)
// 删除评论回复
privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply)
// 管理·禁言/解封用户
adminApi.POST("/admin/user/status", api.ChangeUserStatus)
}
// routeLocalOSS register LocalOSS route if neeed
// routeLocalOSS register LocalOSS route if needed
func routeLocalOSS(e *gin.Engine) {
if !conf.CfgIf("LocalOSS") {
return
@ -213,7 +218,8 @@ func routeLocalOSS(e *gin.Engine) {
logrus.Infof("register LocalOSS route in /oss on save path: %s", savePath)
}
func alipayRoute(public gin.IRoutes, authApi gin.IRoutes) {
// routeAlipay register Alipay feature releated route if needed
func routeAlipay(public gin.IRoutes, authApi gin.IRoutes) {
if !conf.CfgIf("Alipay") {
return
}
@ -230,3 +236,39 @@ func alipayRoute(public gin.IRoutes, authApi gin.IRoutes) {
// 获取用户账单
authApi.GET("/user/wallet/bills", api.GetUserWalletBills)
}
// routeRelationship register Relationship releated routes
func routeRelationship(authApi gin.IRoutes) {
if conf.CfgIf("Friendship") {
routeFriendship(authApi)
} else if conf.CfgIf("Followship") {
routeFollowship(authApi)
} else {
// 暂时默认使用好友模式
// TODO: 后期提供一种无关系模式(既不是好友模式也不是关注者模式)作为默认的关系模式
routeFriendship(authApi)
}
}
// routeFriendship register Friendship feature releated routes
func routeFriendship(authApi gin.IRoutes) {
// 请求添加朋友
authApi.POST("/friend/requesting", api.RequestingFriend)
// 同意添加好友
authApi.POST("/friend/add", api.AddFriend)
// 拒绝添加好友
authApi.POST("/friend/reject", api.RejectFriend)
// 删除好友
authApi.POST("/friend/delete", api.DeleteFriend)
// 获取好友列表
authApi.GET("/user/contacts", api.GetContacts)
}
// routeFollowship register Followship feature releated routes
func routeFollowship(authApi gin.IRoutes) {
// TODO: register followship routes
}

@ -82,6 +82,11 @@ func GetMessages(userID int64, offset, limit int) ([]*model.MessageFormated, int
}
// 好友申请消息不需要获取其他信息
if mf.Type == model.MsgTypeRequestingFriend {
continue
}
if mf.PostID > 0 {
post, err := GetPost(mf.PostID)
if err == nil {

@ -11,6 +11,7 @@ import (
"github.com/gofrs/uuid"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/internal/model/rest"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/errcode"
"github.com/rocboss/paopao-ce/pkg/util"
@ -217,6 +218,38 @@ func Register(username, password string) (*model.User, error) {
return user, nil
}
func RequestingFriend(user *model.User, param *rest.RequestingFriendReq) error {
if _, err := ds.GetUserByID(param.UserId); err != nil {
return errcode.NotExistFriendId
}
return ds.RequestingFriend(user.ID, param.UserId, param.Greetings)
}
func AddFriend(user *model.User, param *rest.AddFriendReq) error {
if _, err := ds.GetUserByID(param.UserId); err != nil {
return errcode.NotExistFriendId
}
return ds.AddFriend(user.ID, param.UserId)
}
func RejectFriend(user *model.User, param *rest.RejectFriendReq) error {
if _, err := ds.GetUserByID(param.UserId); err != nil {
return errcode.NotExistFriendId
}
return ds.RejectFriend(user.ID, param.UserId)
}
func DeleteFriend(user *model.User, param *rest.DeleteFriendReq) error {
if _, err := ds.GetUserByID(param.UserId); err != nil {
return errcode.NotExistFriendId
}
return ds.DeleteFriend(user.ID, param.UserId)
}
func GetContacts(user *model.User, offset int, limit int) (*rest.ContactsResp, error) {
return ds.GetContacts(user.ID, offset, limit)
}
// GetUserInfo 获取用户信息
func GetUserInfo(param *AuthRequest) (*model.User, error) {
user, err := ds.GetUserByUsername(param.Username)
@ -246,18 +279,31 @@ func GetUserByID(id int64) (*model.User, error) {
return nil, errcode.NoExistUsername
}
func GetUserByUsername(username string) (*model.User, error) {
user, err := ds.GetUserByUsername(username)
func GetUserByUsername(user *model.User, username string) (*rest.UserProfileResp, error) {
other, err := ds.GetUserByUsername(username)
if err != nil {
return nil, err
}
if user.Model != nil && user.ID > 0 {
return user, nil
var resp *rest.UserProfileResp
if other.Model != nil && other.ID > 0 {
resp = &rest.UserProfileResp{
ID: other.ID,
Nickname: other.Nickname,
Username: other.Username,
Status: other.Status,
Avatar: other.Avatar,
IsAdmin: other.IsAdmin,
IsFriend: !(user == nil || user.ID == other.ID),
}
} else {
return nil, errcode.NoExistUsername
}
return nil, errcode.NoExistUsername
if user != nil && user.ID != other.ID {
resp.IsFriend = ds.IsFriend(user.ID, other.ID)
}
return resp, nil
}
// UpdateUserInfo 更新用户信息
@ -285,7 +331,8 @@ func ChangeUserAvatar(user *model.User, avatar string) (err *errcode.Error) {
}
user.Avatar = avatar
return UpdateUserInfo(user)
err = UpdateUserInfo(user)
return
}
// GetUserCollections 获取用户收藏列表

@ -57,4 +57,13 @@ var (
RechargeReqFail = NewError(70001, "充值请求失败")
RechargeNotifyError = NewError(70002, "充值回调失败")
GetRechargeFailed = NewError(70003, "充值详情获取失败")
NoRequestingFriendToSelf = NewError(80001, "不允许添加自己为好友")
NotExistFriendId = NewError(80002, "好友id不存在")
SendRequestingFriendFailed = NewError(80003, "申请添加朋友请求发送失败")
AddFriendFailed = NewError(80004, "添加好友失败")
RejectFriendFailed = NewError(80005, "拒绝好友失败")
DeleteFriendFailed = NewError(80006, "删除好友失败")
GetContactsFailed = NewError(80007, "获取联系人列表失败")
NoActionToSelf = NewError(80008, "不允许对自己操作")
)

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS `p_contact`;
DROP TABLE IF EXISTS `p_contact_group`;

@ -0,0 +1,34 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
CREATE TABLE `p_contact` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
`user_id` bigint unsigned NOT NULL COMMENT '用户ID',
`friend_id` bigint unsigned NOT NULL COMMENT '好友ID',
`group_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '好友分组ID:默认为0无分组',
`remark` varchar(32) NOT NULL DEFAULT '' COMMENT '好友备注',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '好友状态: 1请求好友, 2已好友, 3拒绝好友, 4已删好友',
`notice_enable` tinyint NOT NULL DEFAULT '0' COMMENT '是否有消息提醒, 0否, 1是',
`is_top` tinyint NOT NULL DEFAULT '0' COMMENT '是否置顶, 0否, 1是',
`is_black` tinyint NOT NULL DEFAULT '0' COMMENT '是否为黑名单, 0否, 1是',
`is_del` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除好友, 0否, 1是',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_user_friend_id` (`user_id`,`friend_id`) USING BTREE,
KEY `idx_user_friend_status` (`user_id`, `friend_id`, `status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='联系人';
CREATE TABLE `p_contact_group` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
`user_id` int NOT NULL DEFAULT '0' COMMENT '用户id',
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '分组名称',
`is_del` tinyint NOT NULL DEFAULT '1' COMMENT '是否删除, 0否, 1是',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='联系人分组';
SET FOREIGN_KEY_CHECKS = 1;

@ -0,0 +1,6 @@
PRAGMA foreign_keys = false;
DROP TABLE IF EXISTS "p_contact";
DROP TABLE IF EXISTS "p_contact_group";
PRAGMA foreign_keys = true;

@ -0,0 +1,44 @@
PRAGMA foreign_keys = false;
CREATE TABLE "p_contact" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"friend_id" integer NOT NULL,
"group_id" integer NOT NULL,
"remark" text(32) NOT NULL,
"status" integer NOT NULL,
"notice_enable" integer NOT NULL,
"is_top" integer NOT NULL,
"is_black" integer NOT NULL,
"is_del" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE "p_contact_group" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"name" text(32) NOT NULL,
"is_del" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX "main"."idx_user_friend_id"
ON "p_contact" (
"user_id" ASC,
"friend_id" ASC
);
CREATE INDEX "main"."idx_user_friend_status"
ON "p_contact" (
"user_id" ASC,
"friend_id" ASC,
"status" ASC
);
PRAGMA foreign_keys = true;

@ -270,6 +270,44 @@ CREATE TABLE `p_user` (
KEY `idx_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=100058 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户';
-- ----------------------------
-- Table structure for p_contact
-- ----------------------------
DROP TABLE IF EXISTS `p_contact`;
CREATE TABLE `p_contact` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
`user_id` bigint unsigned NOT NULL COMMENT '用户ID',
`friend_id` bigint unsigned NOT NULL COMMENT '好友ID',
`group_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '好友分组ID:默认为0无分组',
`remark` varchar(32) NOT NULL DEFAULT '' COMMENT '好友备注',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '好友状态: 1请求好友, 2已好友, 3拒绝好友, 4已删好友',
`is_top` tinyint NOT NULL DEFAULT '0' COMMENT '是否置顶, 0否, 1是',
`is_black` tinyint NOT NULL DEFAULT '0' COMMENT '是否为黑名单, 0否, 1是',
`is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除好友, 0否, 1是',
`notice_enable` tinyint NOT NULL DEFAULT '0' COMMENT '是否有消息提醒, 0否, 1是',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_user_friend_id` (`user_id`,`friend_id`) USING BTREE,
KEY `idx_user_friend_status` (`user_id`, `friend_id`, `status`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='联系人';
-- ----------------------------
-- Table structure for p_contact_group
-- ----------------------------
DROP TABLE IF EXISTS `p_contact_group`;
CREATE TABLE `p_contact_group` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '联系人ID',
`user_id` int NOT NULL DEFAULT '0' COMMENT '用户id',
`name` varchar(32) NOT NULL DEFAULT '' COMMENT '分组名称',
`is_delete` tinyint NOT NULL DEFAULT '1' COMMENT '是否删除, 0否, 1是',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='联系人分组';
-- ----------------------------
-- Table structure for p_wallet_recharge
-- ----------------------------

@ -90,6 +90,42 @@ CREATE TABLE "p_comment_reply" (
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_contact
-- ----------------------------
DROP TABLE IF EXISTS "p_contact";
CREATE TABLE "p_contact" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"friend_id" integer NOT NULL,
"group_id" integer NOT NULL,
"remark" text(32) NOT NULL,
"status" integer NOT NULL,
"is_top" integer NOT NULL,
"is_black" integer NOT NULL,
"is_delete" integer NOT NULL,
"notice_enable" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_contact_group
-- ----------------------------
DROP TABLE IF EXISTS "p_contact_group";
CREATE TABLE "p_contact_group" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"name" text(32) NOT NULL,
"is_delete" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_message
-- ----------------------------
@ -122,7 +158,6 @@ CREATE TABLE "p_post" (
"comment_count" integer NOT NULL,
"collection_count" integer NOT NULL,
"upvote_count" integer NOT NULL,
"visibility" integer NOT NULL,
"is_top" integer NOT NULL,
"is_essence" integer NOT NULL,
"is_lock" integer NOT NULL,
@ -134,7 +169,7 @@ CREATE TABLE "p_post" (
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
"is_del" integer NOT NULL, `visibility` integer NOT NULL DEFAULT '0',
PRIMARY KEY ("id")
);
@ -169,14 +204,6 @@ CREATE TABLE "p_post_collection" (
PRIMARY KEY ("id")
);
-- ----------------------------
-- Indexes structure for table p_post
-- ----------------------------
CREATE INDEX "main"."idx_visibility"
ON "p_post" (
"visibility" ASC
);
-- ----------------------------
-- Table structure for p_post_content
-- ----------------------------
@ -315,4 +342,27 @@ ON "p_comment" (
"post_id" ASC
);
-- ----------------------------
-- Indexes structure for table p_contact
-- ----------------------------
CREATE UNIQUE INDEX "main"."idx_user_friend_id"
ON "p_contact" (
"user_id" ASC,
"friend_id" ASC
);
CREATE INDEX "main"."idx_user_friend_status"
ON "p_contact" (
"user_id" ASC,
"friend_id" ASC,
"status" ASC
);
-- ----------------------------
-- Indexes structure for table p_post
-- ----------------------------
CREATE INDEX "main"."idx_visibility"
ON "p_post" (
"visibility" ASC
);
PRAGMA foreign_keys = true;

@ -1,3 +1,4 @@
components.d.ts
*Dockerfile*
node_modules
src-tauri/target

@ -1,19 +1,26 @@
VITE_HOST="https://api.paopao.info"
VITE_HOST=""
# 模块开启
VITE_ENABLE_WALLET=true
VITE_ENABLE_ANOUNCEMENT=false
VITE_ENABLE_WALLET=false
# 功能开启
VITE_ALLOW_TWEET_ATTACHMENT=true
VITE_ALLOW_TWEET_ATTACHMENT_PRICE=true
VITE_ALLOW_TWEET_VIDEO=true
VITE_ALLOW_TWEET_ATTACHMENT=false
VITE_ALLOW_TWEET_ATTACHMENT_PRICE=false
VITE_ALLOW_TWEET_VIDEO=false
VITE_ALLOW_TWEET_VISIBILITY=true
VITE_ALLOW_ACTIVATION=false
VITE_ALLOW_PHONE_BIND=true
# 局部参数
VITE_DEFAULT_TWEET_VISIBILITY=public
VITE_DEFAULT_TWEET_VISIBILITY=friend
VITE_COPYRIGHT_TOP="2022 paopao.info"
VITE_COPYRIGHT_LEFT="Roc's Me"
VITE_COPYRIGHT_LEFT_LINK=""
VITE_COPYRIGHT_RIGHT="泡泡(PaoPao)开源社区"
VITE_COPYRIGHT_RIGHT_LINK="https://paopao.info"
# 图片推文404参数
# 图片推文参数
VITE_DEFAULT_TWEET_IMAGE_404="https://paopao-assets.oss-cn-shanghai.aliyuncs.com/public/404.png"
# AliOSS 图片thumbnail参数
VITE_TWEET_IMAGE_THUMBNAIL="?x-oss-process=image/resize,m_fill,w_300,h_300,limit_0/auto-orient,1/format,png"

4
web/.gitignore vendored

@ -11,7 +11,6 @@ node_modules
dist
dist-ssr
*.local
.env
# Editor directories and files
.vscode/*
@ -24,5 +23,6 @@ dist-ssr
*.sln
*.sw?
yarn.lock
package-lock.json
components.d.ts
components.d.ts

@ -0,0 +1 @@
paopao-ce's web frontend.

@ -1,4 +0,0 @@
{
"version": "8",
"buildTime": "2022-06-13 17:16:22"
}

@ -1,46 +0,0 @@
import type { Plugin } from "vite"
import { resolve } from "path"
import { readFileSync, writeFileSync } from "fs"
function getFormatTime() {
let date = new Date();
let year: string | number = date.getFullYear();
let month: string | number = date.getMonth() + 1;
let day: string | number = date.getDate();
let h: string | number = date.getHours();
let m: string | number = date.getMinutes();
let s: string | number = date.getSeconds();
if (month < 10) month = "0"+month;
if (day < 10) day = "0"+day;
if (h < 10) h = "0"+h;
if (m < 10) m = "0"+m;
if (s < 10) s = "0"+s;
return [year, month, day].join("-") + " " + [h, m, s].join(":");
}
/**
*
* @returns
*/
export function changePackageVersion(): Plugin {
if (process.env.NODE_ENV === "production") {
let packagePath = resolve(__dirname, "./info.json");
let packageStr = readFileSync(packagePath, {
encoding: "utf-8"
});
let packageJson = JSON.parse(packageStr);
let version = packageJson.version;
let versionArr: (string | number)[] = version.split(".");
let lastVersionNum = +versionArr[versionArr.length - 1];
lastVersionNum++;
versionArr.splice(versionArr.length - 1, 1, lastVersionNum);
packageJson.version = versionArr.join(".");
packageJson.buildTime = getFormatTime();
writeFileSync(packagePath, JSON.stringify(packageJson, null, "\t"), {
encoding: "utf-8"
})
}
return {
name: "changePackageVersion"
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.wrap404[data-v-4c5da4ba]{min-height:500px;display:flex;align-items:center;justify-content:center}

@ -0,0 +1,34 @@
import{c as j,ce as k,cf as u,b as f,l as y,m as c,bz as B,o as F,n as M,d as O,u as N,q as E,x as V,s as S,ba as K,v as g,y as q,r as D,h as s,b2 as G,cg as J,K as Q,N as U,bt as X,bu as Y,bv as Z,bs as ee,z as oe,bx as re}from"./index.f480f018.js";const ne=r=>{const{lineHeight:o,borderRadius:i,fontWeightStrong:m,baseColor:n,dividerColor:v,actionColor:z,textColor1:t,textColor2:a,closeColor:h,closeColorHover:C,closeColorPressed:b,infoColor:l,successColor:e,warningColor:p,errorColor:x,fontSize:T}=r;return Object.assign(Object.assign({},k),{fontSize:T,lineHeight:o,titleFontWeight:m,borderRadius:i,border:`1px solid ${v}`,color:z,titleTextColor:t,iconColor:a,contentTextColor:a,closeColor:h,closeColorHover:C,closeColorPressed:b,borderInfo:`1px solid ${u(n,f(l,{alpha:.25}))}`,colorInfo:u(n,f(l,{alpha:.08})),titleTextColorInfo:t,iconColorInfo:l,contentTextColorInfo:a,closeColorInfo:h,closeColorHoverInfo:C,closeColorPressedInfo:b,borderSuccess:`1px solid ${u(n,f(e,{alpha:.25}))}`,colorSuccess:u(n,f(e,{alpha:.08})),titleTextColorSuccess:t,iconColorSuccess:e,contentTextColorSuccess:a,closeColorSuccess:h,closeColorHoverSuccess:C,closeColorPressedSuccess:b,borderWarning:`1px solid ${u(n,f(p,{alpha:.33}))}`,colorWarning:u(n,f(p,{alpha:.08})),titleTextColorWarning:t,iconColorWarning:p,contentTextColorWarning:a,closeColorWarning:h,closeColorHoverWarning:C,closeColorPressedWarning:b,borderError:`1px solid ${u(n,f(x,{alpha:.25}))}`,colorError:u(n,f(x,{alpha:.08})),titleTextColorError:t,iconColorError:x,contentTextColorError:a,closeColorError:h,closeColorHoverError:C,closeColorPressedError:b})},te={name:"Alert",common:j,self:ne};var le=te,se=y("alert",`
line-height: var(--n-line-height);
border-radius: var(--n-border-radius);
position: relative;
transition: background-color .3s var(--n-bezier);
background-color: var(--n-color);
text-align: start;
`,[c("icon",{color:"var(--n-icon-color)"}),y("alert-body",{border:"var(--n-border)",padding:"var(--n-padding)"},[c("title",{color:"var(--n-title-text-color)"}),c("content",{color:"var(--n-content-text-color)"})]),B({originalTransition:"transform .3s var(--n-bezier)",enterToProps:{transform:"scale(1)"},leaveToProps:{transform:"scale(0.9)"}}),c("icon",`
position: absolute;
left: 0;
top: 0;
align-items: center;
justify-content: center;
display: flex;
width: var(--n-icon-size);
height: var(--n-icon-size);
font-size: var(--n-icon-size);
margin: var(--n-icon-margin);
`),c("close",`
transition: color .3s var(--n-bezier);
position: absolute;
right: 0;
top: 0;
margin: var(--n-close-margin);
font-size: var(--n-close-size);
`),F("show-icon",[y("alert-body",{paddingLeft:"calc(var(--n-icon-margin-left) + var(--n-icon-size) + var(--n-icon-margin-right))"})]),y("alert-body",`
border-radius: var(--n-border-radius);
transition: border-color .3s var(--n-bezier);
`,[c("title",`
transition: color .3s var(--n-bezier);
font-size: 16px;
line-height: 19px;
font-weight: var(--n-title-font-weight);
`,[M("& +",[c("content",{marginTop:"9px"})])]),c("content",{transition:"color .3s var(--n-bezier)",fontSize:"var(--n-font-size)"})]),c("icon",{transition:"color .3s var(--n-bezier)"})]);const ie=Object.assign(Object.assign({},E.props),{title:{type:String,default:void 0},showIcon:{type:Boolean,default:!0},type:{type:String,default:"default"},closable:{type:Boolean,default:!1},onClose:Function,onAfterLeave:{type:Function,default:void 0},onAfterHide:{type:Function,validator:()=>!0,default:void 0}});var ce=O({name:"Alert",inheritAttrs:!1,props:ie,setup(r){const{mergedClsPrefixRef:o,inlineThemeDisabled:i,mergedRtlRef:m}=N(r),n=E("Alert","-alert",se,le,r,o),v=V("Alert",m,o),z=S(()=>{const{common:{cubicBezierEaseInOut:l},self:e}=n.value,{fontSize:p,borderRadius:x,titleFontWeight:T,lineHeight:P,iconSize:$,iconMargin:I,iconMarginRtl:R,closeSize:_,closeMargin:A,closeMarginRtl:W,padding:w}=e,{type:d}=r,{left:H,right:L}=K(I);return{"--n-bezier":l,"--n-color":e[g("color",d)],"--n-close-color":e[g("closeColor",d)],"--n-close-color-hover":e[g("closeColorHover",d)],"--n-close-color-pressed":e[g("closeColorPressed",d)],"--n-icon-color":e[g("iconColor",d)],"--n-border":e[g("border",d)],"--n-title-text-color":e[g("titleTextColor",d)],"--n-content-text-color":e[g("contentTextColor",d)],"--n-line-height":P,"--n-border-radius":x,"--n-font-size":p,"--n-title-font-weight":T,"--n-icon-size":$,"--n-icon-margin":I,"--n-icon-margin-rtl":R,"--n-close-size":_,"--n-close-margin":A,"--n-close-margin-rtl":W,"--n-padding":w,"--n-icon-margin-left":H,"--n-icon-margin-right":L}}),t=i?q("alert",S(()=>r.type[0]),z,r):void 0,a=D(!0),h=()=>{const{onAfterLeave:l,onAfterHide:e}=r;l&&l(),e&&e()};return{rtlEnabled:v,mergedClsPrefix:o,visible:a,handleCloseClick:()=>{var l;Promise.resolve((l=r.onClose)===null||l===void 0?void 0:l.call(r)).then(e=>{e!==!1&&(a.value=!1)})},handleAfterLeave:()=>{h()},mergedTheme:n,cssVars:i?void 0:z,themeClass:t==null?void 0:t.themeClass,onRender:t==null?void 0:t.onRender}},render(){var r;return(r=this.onRender)===null||r===void 0||r.call(this),s(re,{onAfterLeave:this.handleAfterLeave},{default:()=>{const{mergedClsPrefix:o,$slots:i}=this,m={class:[`${o}-alert`,this.themeClass,this.showIcon&&`${o}-alert--show-icon`,this.rtlEnabled&&`${o}-alert--rtl`],style:this.cssVars,role:"alert"};return this.visible?s("div",Object.assign({},G(this.$attrs,m)),this.closable&&s(J,{clsPrefix:o,class:`${o}-alert__close`,onClick:this.handleCloseClick}),this.showIcon&&s("div",{class:`${o}-alert__icon`,"aria-hidden":"true"},Q(i.icon,()=>[s(U,{clsPrefix:o},{default:()=>{switch(this.type){case"success":return s(ee,null);case"info":return s(Z,null);case"warning":return s(Y,null);case"error":return s(X,null);default:return null}}})])),s("div",{class:`${o}-alert-body`},oe(i.header,n=>{const v=n||this.title;return v?s("div",{class:`${o}-alert-body__title`},v):null}),i.default&&s("div",{class:`${o}-alert-body__content`},i))):null}})}});export{ce as _};

@ -0,0 +1 @@
.balance-wrap[data-v-543914f4]{padding:16px}.balance-wrap .balance-line[data-v-543914f4]{display:flex;justify-content:space-between}.balance-wrap .balance-line .balance-opts[data-v-543914f4]{display:flex;flex-direction:column}.bill-line[data-v-543914f4]{padding:16px;display:flex;justify-content:space-between}.bill-line .income[data-v-543914f4],.bill-line .out[data-v-543914f4]{font-weight:700}.bill-line .income[data-v-543914f4]{color:#18a058}.pagination-wrap[data-v-543914f4]{padding:10px;display:flex;justify-content:center;overflow:hidden}.qrcode-wrap[data-v-543914f4]{display:flex;flex-direction:column;align-items:center;justify-content:center}.qrcode-wrap .pay-tips[data-v-543914f4]{margin-top:16px}.qrcode-wrap .pay-sub-tips[data-v-543914f4]{margin-top:4px;font-size:12px;opacity:.75;display:flex;align-items:center}.dark .income[data-v-543914f4]{color:#63e2b7}

@ -0,0 +1 @@
import{_ as N}from"./post-skeleton.39a35f5b.js";import{_ as V}from"./main-nav.9110259b.js";import{ah as z,d as A,Z as E,ai as R,r as a,a1 as S,V as n,W as t,a3 as o,a4 as c,a2 as l,a6 as m,aa as F,ab as P,a5 as q,Y as s,a9 as _,c3 as D}from"./index.f480f018.js";import{f as I}from"./formatTime.02109bf5.js";import{_ as L}from"./List.49bcdf81.js";import{_ as M}from"./Pagination.1263479e.js";import{a as O,_ as T}from"./Skeleton.57e98fe3.js";const U={key:0,class:"pagination-wrap"},W={key:0,class:"skeleton-wrap"},Y={key:1},Z={key:0,class:"empty-wrap"},$={class:"bill-line"},j=A({setup(G){const d=E(),g=R(),v=a(!1),u=a([]),r=a(+g.query.p||1),f=a(20),p=a(0),h=i=>{r.value=i};return S(()=>{}),(i,H)=>{const y=V,k=M,x=N,w=O,C=T,B=L;return n(),t("div",null,[o(y,{title:"\u516C\u544A"}),o(B,{class:"main-content-wrap",bordered:""},{footer:c(()=>[p.value>1?(n(),t("div",U,[o(k,{page:r.value,"onUpdate:page":h,"page-slot":l(d).state.collapsedRight?5:8,"page-count":p.value},null,8,["page","page-slot","page-count"])])):m("",!0)]),default:c(()=>[v.value?(n(),t("div",W,[o(x,{num:f.value},null,8,["num"])])):(n(),t("div",Y,[u.value.length===0?(n(),t("div",Z,[o(w,{size:"large",description:"\u6682\u65E0\u6570\u636E"})])):m("",!0),(n(!0),t(F,null,P(u.value,e=>(n(),q(C,{key:e.id},{default:c(()=>[s("div",$,[s("div",null,"NO."+_(e.id),1),s("div",null,_(e.reason),1),s("div",{class:D({income:e.change_amount>=0,out:e.change_amount<0})},_((e.change_amount>0?"+":"")+(e.change_amount/100).toFixed(2)),3),s("div",null,_(l(I)(e.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});var te=z(j,[["__scopeId","data-v-543914f4"]]);export{te as default};

@ -0,0 +1 @@
import{_ as z}from"./post-item.03452002.js";import{_ as B}from"./post-skeleton.39a35f5b.js";import{_ as E}from"./main-nav.9110259b.js";import{ah as P,d as R,Z as V,ai as b,aj as F,r as n,a1 as M,V as e,W as o,a3 as a,a4 as p,cm as N,a2 as S,a6 as m,aa as $,ab as j,a5 as q}from"./index.f480f018.js";import{_ as I}from"./List.49bcdf81.js";import{_ as L}from"./Pagination.1263479e.js";import{a as T,_ as U}from"./Skeleton.57e98fe3.js";import"./content.ef1e095a.js";import"./formatTime.02109bf5.js";import"./Thing.eef6b133.js";const W={key:0,class:"pagination-wrap"},Z={key:0,class:"skeleton-wrap"},A={key:1},D={key:0,class:"empty-wrap"},G=R({setup(H){const d=V(),g=b();F();const s=n(!1),_=n([]),l=n(+g.query.p||1),c=n(20),u=n(0),r=()=>{s.value=!0,N({page:l.value,page_size:c.value}).then(t=>{s.value=!1,_.value=t.list,u.value=Math.ceil(t.pager.total_rows/c.value),window.scrollTo(0,0)}).catch(t=>{s.value=!1})},v=t=>{l.value=t,r()};return M(()=>{r()}),(t,J)=>{const f=E,h=L,k=B,y=T,w=z,C=U,x=I;return e(),o("div",null,[a(f,{title:"\u6536\u85CF"}),a(x,{class:"main-content-wrap",bordered:""},{footer:p(()=>[u.value>1?(e(),o("div",W,[a(h,{page:l.value,"onUpdate:page":v,"page-slot":S(d).state.collapsedRight?5:8,"page-count":u.value},null,8,["page","page-slot","page-count"])])):m("",!0)]),default:p(()=>[s.value?(e(),o("div",Z,[a(k,{num:c.value},null,8,["num"])])):(e(),o("div",A,[_.value.length===0?(e(),o("div",D,[a(y,{size:"large",description:"\u6682\u65E0\u6570\u636E"})])):m("",!0),(e(!0),o($,null,j(_.value,i=>(e(),q(C,{key:i.id},{default:p(()=>[a(w,{post:i},null,8,["post"])]),_:2},1024))),128))]))]),_:1})])}}});var se=P(G,[["__scopeId","data-v-2f4c0166"]]);export{se as default};

@ -0,0 +1 @@
.pagination-wrap[data-v-2f4c0166]{padding:10px;display:flex;justify-content:center;overflow:hidden}

@ -0,0 +1 @@
import{ah as k,d as C,aj as D,V as e,W as n,Y as c,a3 as o,a9 as v,ad as E,Z as N,ai as P,r as l,a1 as R,cn as S,a4 as g,a2 as U,a6 as y,aa as q,ab as M,a5 as T}from"./index.f480f018.js";import{_ as j}from"./post-skeleton.39a35f5b.js";import{_ as F}from"./main-nav.9110259b.js";import{_ as L}from"./List.49bcdf81.js";import{_ as W}from"./Pagination.1263479e.js";import{a as Y,_ as Z}from"./Skeleton.57e98fe3.js";const A={class:"avatar"},G={class:"base-info"},H={class:"username"},J={class:"uid"},K=C({props:{contact:null},setup(s){const p=D(),m=t=>{p.push({name:"user",query:{username:t}})};return(t,a)=>{const _=E;return e(),n("div",{class:"contact-item",onClick:a[0]||(a[0]=u=>m(s.contact.username))},[c("div",A,[o(_,{size:"large",src:s.contact.avatar},null,8,["src"])]),c("div",G,[c("div",H,[c("strong",null,v(s.contact.nickname),1),c("span",null," @"+v(s.contact.username),1)]),c("div",J,"UID. "+v(s.contact.user_id),1)])])}}});var O=k(K,[["__scopeId","data-v-23bc18c8"]]);const Q={key:0,class:"pagination-wrap"},X={key:0,class:"skeleton-wrap"},ee={key:1},te={key:0,class:"empty-wrap"},ae=C({setup(s){const p=N(),m=P(),t=l(!1),a=l([]),_=l(+m.query.p||1),u=l(20),d=l(0),w=r=>{_.value=r,f()};R(()=>{f()});const f=(r=!1)=>{a.value.length===0&&(t.value=!0),S({page:_.value,page_size:u.value}).then(i=>{t.value=!1,a.value=i.list,d.value=Math.ceil(i.pager.total_rows/u.value),r&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(i=>{t.value=!1})};return(r,i)=>{const x=F,$=W,B=j,b=Y,z=O,I=Z,V=L;return e(),n("div",null,[o(x,{title:"\u597D\u53CB"}),o(V,{class:"main-content-wrap",bordered:""},{footer:g(()=>[d.value>1?(e(),n("div",Q,[o($,{page:_.value,"onUpdate:page":w,"page-slot":U(p).state.collapsedRight?5:8,"page-count":d.value},null,8,["page","page-slot","page-count"])])):y("",!0)]),default:g(()=>[t.value?(e(),n("div",X,[o(B,{num:u.value},null,8,["num"])])):(e(),n("div",ee,[a.value.length===0?(e(),n("div",te,[o(b,{size:"large",description:"\u6682\u65E0\u6570\u636E"})])):y("",!0),(e(!0),n(q,null,M(a.value,h=>(e(),T(I,{key:h.user_id},{default:g(()=>[o(z,{contact:h},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})])}}});var ue=k(ae,[["__scopeId","data-v-e9de9e50"]]);export{ue as default};

@ -0,0 +1 @@
.contact-item[data-v-23bc18c8]{display:flex;width:100%;padding:12px 16px}.contact-item[data-v-23bc18c8]:hover{background:#f7f9f9;cursor:pointer}.contact-item .avatar[data-v-23bc18c8]{width:55px}.contact-item .base-info[data-v-23bc18c8]{position:relative;width:calc(100% - 55px)}.contact-item .base-info .username[data-v-23bc18c8]{line-height:16px;font-size:16px}.contact-item .base-info .uid[data-v-23bc18c8]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.dark .contact-item[data-v-23bc18c8]:hover{background:#18181c}.pagination-wrap[data-v-e9de9e50]{padding:10px;display:flex;justify-content:center;overflow:hidden}

Binary file not shown.

@ -0,0 +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-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}.pagination-wrap[data-v-7e0dfc66]{padding:10px;display:flex;justify-content:center;overflow:hidden}

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,56 @@
import{l as t,n as r,m as o,d as a,u as d,b$ as s,h as n}from"./index.f480f018.js";var p=t("input-group",`
display: inline-flex;
width: 100%;
flex-wrap: nowrap;
vertical-align: bottom;
`,[r(">",[t("input",[r("&:not(:last-child)",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`),r("&:not(:first-child)",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
margin-left: -1px!important;
`)]),t("button",[r("&:not(:last-child)",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`,[o("state-border, border",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`)]),r("&:not(:first-child)",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`,[o("state-border, border",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`)])]),r("*",[r("&:not(:last-child)",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`,[r(">",[t("input",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`),t("base-selection",[t("base-selection-label",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`),t("base-selection-tags",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`),o("box-shadow, border, state-border",`
border-top-right-radius: 0!important;
border-bottom-right-radius: 0!important;
`)])])]),r("&:not(:first-child)",`
margin-left: -1px!important;
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`,[r(">",[t("input",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`),t("base-selection",[t("base-selection-label",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`),t("base-selection-tags",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`),o("box-shadow, border, state-border",`
border-top-left-radius: 0!important;
border-bottom-left-radius: 0!important;
`)])])])])])]);const b={};var u=a({name:"InputGroup",props:b,setup(i){const{mergedClsPrefixRef:e}=d(i);return s("-input-group",p,e),{mergedClsPrefix:e}},render(){const{mergedClsPrefix:i}=this;return n("div",{class:`${i}-input-group`},this.$slots)}});export{u as _};

@ -0,0 +1,53 @@
import{n as d,l as n,o as z,m as i,aA as y,aB as _,d as P,u as R,q as c,A as $,s as j,y as B,h as a,e as M,aC as I}from"./index.f480f018.js";var L=d([n("list",`
--n-merged-border-color: var(--n-border-color);
--n-merged-color: var(--n-color)
font-size: var(--n-font-size);
transition:
background-color .3s var(--n-bezier),
color .3s var(--n-bezier),
border-color .3s var(--n-bezier);
padding: 0;
list-style-type: none;
color: var(--n-text-color);
background-color: var(--n-merged-color);
`,[z("bordered",`
border-radius: var(--n-border-radius);
border: 1px solid var(--n-merged-border-color);
`,[n("list-item",`
padding: 12px 20px;
`,[d("&:not(:last-child)",`
border-bottom: 1px solid var(--n-merged-border-color);
`)]),i("header, footer",`
padding: 12px 20px;
`,[d("&:not(:last-child)",`
border-bottom: 1px solid var(--n-merged-border-color);
`)])]),i("header, footer",`
padding: 12px 0;
box-sizing: border-box;
transition: border-color .3s var(--n-bezier);
`,[d("&:not(:last-child)",`
border-bottom: 1px solid var(--n-merged-border-color);
`)]),n("list-item",`
padding: 12px 0;
box-sizing: border-box;
display: flex;
flex-wrap: nowrap;
align-items: center;
transition: border-color .3s var(--n-bezier);
`,[i("prefix",`
margin-right: 20px;
flex: 0;
`),i("suffix",`
margin-left: 20px;
flex: 0;
`),i("main",`
flex: 1;
`),d("&:not(:last-child)",`
border-bottom: 1px solid var(--n-merged-border-color);
`)])]),y(n("list",`
--merged-color: var(--n-color-modal);
--merged-border-color: var(--n-border-color-modal);
`)),_(n("list",`
--merged-color: var(--n-color-popover);
--merged-border-color: var(--n-border-color-popover);
`))]);const O=Object.assign(Object.assign({},c.props),{size:{type:String,default:"medium"},bordered:{type:Boolean,default:!1}}),T=M("n-list");var k=P({name:"List",props:O,setup(r){const{mergedClsPrefixRef:o,inlineThemeDisabled:e}=R(r),s=c("List","-list",L,I,r,o);$(T,{mergedClsPrefixRef:o});const t=j(()=>{const{common:{cubicBezierEaseInOut:b},self:{fontSize:m,textColor:p,color:v,colorModal:f,colorPopover:u,borderColor:g,borderColorModal:x,borderColorPopover:h,borderRadius:C}}=s.value;return{"--n-font-size":m,"--n-bezier":b,"--n-text-color":p,"--n-color":v,"--n-border-radius":C,"--n-border-color":g,"--n-border-color-modal":x,"--n-border-color-popover":h,"--n-color-modal":f,"--n-color-popover":u}}),l=e?B("list",void 0,t,r):void 0;return{mergedClsPrefix:o,cssVars:e?void 0:t,themeClass:l==null?void 0:l.themeClass,onRender:l==null?void 0:l.onRender}},render(){var r;const{$slots:o,mergedClsPrefix:e,onRender:s}=this;return s==null||s(),a("ul",{class:[`${e}-list`,this.bordered&&`${e}-list--bordered`,this.themeClass],style:this.cssVars},o.header?a("div",{class:`${e}-list__header`},o.header()):null,(r=o.default)===null||r===void 0?void 0:r.call(o),o.footer?a("div",{class:`${e}-list__footer`},o.footer()):null)}});export{k as _,T as l};

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.message-item[data-v-44920a13]{padding:16px}.message-item.unread[data-v-44920a13]{background:#fcfffc}.message-item .sender-wrap[data-v-44920a13]{display:flex;align-items:center}.message-item .sender-wrap .username[data-v-44920a13]{opacity:.75;font-size:14px}.message-item .timestamp[data-v-44920a13]{opacity:.75;font-size:12px;display:flex;align-items:center}.message-item .timestamp .timestamp-txt[data-v-44920a13]{margin-left:6px}.message-item .brief-wrap[data-v-44920a13]{margin-top:10px}.message-item .brief-wrap .brief-content[data-v-44920a13],.message-item .brief-wrap .whisper-content-wrap[data-v-44920a13],.message-item .brief-wrap .requesting-friend-wrap[data-v-44920a13]{display:flex;width:100%}.message-item .view-link[data-v-44920a13]{margin-left:8px;display:flex;align-items:center}.message-item .status-info[data-v-44920a13]{margin-left:8px;align-items:center}.dark .message-item.unread[data-v-44920a13]{background:#0f180b}.skeleton-item[data-v-d0b0628c]{padding:12px;display:flex}.skeleton-item .content[data-v-d0b0628c]{width:100%}.pagination-wrap[data-v-532e9e3c]{padding:10px;display:flex;justify-content:center;overflow:hidden}

@ -0,0 +1 @@
import{d as e,V as o,W as s,Y as r}from"./index.f480f018.js";const t={xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink",viewBox:"0 0 24 24"},n=r("path",{d:"M6 10c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2z",fill:"currentColor"},null,-1),l=[n];var m=e({name:"MoreHorizFilled",render:function(i,a){return o(),s("svg",t,l)}});export{m as M};

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 @@
.reply-compose-wrap .reply-switch[data-v-3616e66c]{text-align:right;font-size:12px;margin:10px 0}.reply-compose-wrap .reply-switch .show[data-v-3616e66c]{color:#18a058;cursor:pointer}.reply-compose-wrap .reply-switch .hide[data-v-3616e66c]{opacity:.75;cursor:pointer}.reply-item[data-v-1e3df20d]{display:flex;flex-direction:column;font-size:12px;padding:8px;border-bottom:1px solid #f3f3f3}.reply-item .header-wrap[data-v-1e3df20d]{display:flex;align-items:center;justify-content:space-between}.reply-item .header-wrap .username[data-v-1e3df20d]{max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.reply-item .header-wrap .username .reply-name[data-v-1e3df20d]{margin:0 3px;opacity:.75}.reply-item .header-wrap .timestamp[data-v-1e3df20d]{opacity:.75;text-align:right;display:flex;align-items:center;max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.reply-item .base-wrap[data-v-1e3df20d]{display:flex}.reply-item .base-wrap .content[data-v-1e3df20d]{width:calc(100% - 40px);margin-top:4px;font-size:12px;text-align:justify;line-height:2}.reply-item .base-wrap .reply-switch[data-v-1e3df20d]{width:40px;text-align:right;font-size:12px;margin:10px 0 0}.reply-item .base-wrap .reply-switch .show[data-v-1e3df20d]{color:#18a058;cursor:pointer}.reply-item .base-wrap .reply-switch .hide[data-v-1e3df20d]{opacity:.75;cursor:pointer}.dark .reply-item[data-v-1e3df20d]{border-bottom:1px solid #262628}.comment-item[data-v-6b2cb186]{width:100%;padding:16px;box-sizing:border-box}.comment-item .nickname-wrap[data-v-6b2cb186]{font-size:14px}.comment-item .username-wrap[data-v-6b2cb186]{font-size:14px;opacity:.75}.comment-item .opt-wrap[data-v-6b2cb186]{display:flex;align-items:center}.comment-item .opt-wrap .timestamp[data-v-6b2cb186]{opacity:.75;font-size:12px}.comment-item .opt-wrap .del-btn[data-v-6b2cb186]{margin-left:4px}.comment-item .comment-text[data-v-6b2cb186]{display:block;text-align:justify;overflow:hidden;white-space:pre-wrap;word-break:break-all}.comment-item .opt-item[data-v-6b2cb186]{display:flex;align-items:center;opacity:.7}.comment-item .opt-item .opt-item-icon[data-v-6b2cb186]{margin-right:10px}.reply-wrap[data-v-6b2cb186]{margin-top:10px;border-radius:5px;background:#fafafc}.reply-wrap .reply-item[data-v-6b2cb186]:last-child{border-bottom:none}.dark .reply-wrap[data-v-6b2cb186]{background:#18181c}.compose-wrap[data-v-0702a552]{width:100%;padding:16px;box-sizing:border-box}.compose-wrap .compose-line[data-v-0702a552]{display:flex;flex-direction:row}.compose-wrap .compose-line .compose-user[data-v-0702a552]{width:42px;height:42px;display:flex;align-items:center}.compose-wrap .compose-line.compose-options[data-v-0702a552]{margin-top:6px;padding-left:42px;display:flex;justify-content:space-between}.compose-wrap .compose-line.compose-options .submit-wrap[data-v-0702a552]{display:flex;align-items:center}.compose-wrap .compose-line.compose-options .submit-wrap .cancel-btn[data-v-0702a552]{margin-right:8px}.compose-wrap .login-wrap[data-v-0702a552]{display:flex;justify-content:center;width:100%}.compose-wrap .login-wrap .login-banner[data-v-0702a552]{margin-bottom:12px;opacity:.8}.compose-wrap .login-wrap button[data-v-0702a552]{margin:0 4px}.attachment[data-v-0702a552]{display:flex;align-items:center}.attachment .text-statistic[data-v-0702a552]{margin-left:8px;width:18px;height:18px;transform:rotate(180deg)}.attachment-list-wrap[data-v-0702a552]{margin-top:12px;margin-left:42px}.attachment-list-wrap .n-upload-file-info__thumbnail[data-v-0702a552]{overflow:hidden}.detail-item{width:100%;padding:16px;box-sizing:border-box;background:#f7f9f9}.detail-item .nickname-wrap{font-size:14px}.detail-item .username-wrap{font-size:14px;opacity:.75}.detail-item .top-tag{transform:scale(.75)}.detail-item .options{opacity:.75}.detail-item .post-text{font-size:16px;text-align:justify;overflow:hidden;white-space:pre-wrap;word-break:break-all}.detail-item .opts-wrap{margin-top:20px}.detail-item .opts-wrap .opt-item{display:flex;align-items:center;opacity:.7}.detail-item .opts-wrap .opt-item .opt-item-icon{margin-right:10px}.detail-item .opts-wrap .opt-item.hover{cursor:pointer}.detail-item .n-thing .n-thing-avatar-header-wrapper{align-items:center}.detail-item .timestamp{opacity:.75;font-size:12px;margin-top:10px}.dark .detail-item{background:#18181c}.detail-wrap[data-v-41747510]{min-height:100px}

@ -0,0 +1 @@
import{_ as N}from"./post-item.03452002.js";import{_ as S}from"./post-skeleton.39a35f5b.js";import{_ as U}from"./main-nav.9110259b.js";import{ah as D,d as M,Z as R,ai as $,r as c,a1 as q,V as e,W as o,a3 as t,a2 as _,a5 as h,a4 as r,a6 as d,c4 as F,Y as s,a9 as f,aa as L,ab as T,ad as W,c1 as Y,c2 as Z}from"./index.f480f018.js";import{_ as j}from"./List.49bcdf81.js";import{_ as A}from"./Pagination.1263479e.js";import{a as G,_ as H}from"./Skeleton.57e98fe3.js";import"./content.ef1e095a.js";import"./formatTime.02109bf5.js";import"./Thing.eef6b133.js";const J={class:"profile-baseinfo"},K={class:"avatar"},O={class:"base-info"},Q={class:"username"},X={class:"uid"},ee={key:0,class:"pagination-wrap"},te={key:0,class:"skeleton-wrap"},ae={key:1},se={key:0,class:"empty-wrap"},ne=M({setup(oe){const a=R(),k=$(),l=c(!1),u=c([]),i=c(+k.query.p||1),p=c(20),m=c(0),v=()=>{l.value=!0,F({username:a.state.userInfo.username,page:i.value,page_size:p.value}).then(n=>{l.value=!1,u.value=n.list,m.value=Math.ceil(n.pager.total_rows/p.value),window.scrollTo(0,0)}).catch(n=>{l.value=!1})},y=n=>{i.value=n,v()};return q(()=>{v()}),(n,_e)=>{const w=U,b=W,I=A,E=Y,P=Z,x=S,B=G,C=N,z=H,V=j;return e(),o("div",null,[t(w,{title:"\u4E3B\u9875"}),_(a).state.userInfo.id>0?(e(),h(V,{key:0,class:"main-content-wrap profile-wrap",bordered:""},{footer:r(()=>[m.value>1?(e(),o("div",ee,[t(I,{page:i.value,"onUpdate:page":y,"page-slot":_(a).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):d("",!0)]),default:r(()=>[s("div",J,[s("div",K,[t(b,{size:"large",src:_(a).state.userInfo.avatar},null,8,["src"])]),s("div",O,[s("div",Q,[s("strong",null,f(_(a).state.userInfo.nickname),1),s("span",null," @"+f(_(a).state.userInfo.username),1)]),s("div",X,"UID. "+f(_(a).state.userInfo.id),1)])]),t(P,{class:"profile-tabs-wrap",animated:""},{default:r(()=>[t(E,{name:"post",tab:"\u6CE1\u6CE1"})]),_:1}),l.value?(e(),o("div",te,[t(x,{num:p.value},null,8,["num"])])):(e(),o("div",ae,[u.value.length===0?(e(),o("div",se,[t(B,{size:"large",description:"\u6682\u65E0\u6570\u636E"})])):d("",!0),(e(!0),o(L,null,T(u.value,g=>(e(),h(z,{key:g.id},{default:r(()=>[t(C,{post:g},null,8,["post"])]),_:2},1024))),128))]))]),_:1})):d("",!0)])}}});var ge=D(ne,[["__scopeId","data-v-2fe5c266"]]);export{ge as default};

@ -0,0 +1 @@
.profile-baseinfo[data-v-2fe5c266]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-2fe5c266]{width:55px}.profile-baseinfo .base-info[data-v-2fe5c266]{position:relative;width:calc(100% - 55px)}.profile-baseinfo .base-info .username[data-v-2fe5c266]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .uid[data-v-2fe5c266]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-tabs-wrap[data-v-2fe5c266]{padding:0 16px}.pagination-wrap[data-v-2fe5c266]{padding:10px;display:flex;justify-content:center;overflow:hidden}

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.setting-card[data-v-6eb93a4d]{margin-top:-1px;border-radius:0}.setting-card .form-submit-wrap[data-v-6eb93a4d]{display:flex;justify-content:flex-end}.setting-card .base-line[data-v-6eb93a4d]{line-height:2;display:flex;align-items:center}.setting-card .base-line .base-label[data-v-6eb93a4d]{opacity:.75;margin-right:12px}.setting-card .base-line .nickname-input[data-v-6eb93a4d]{margin-right:10px;width:120px}.setting-card .avatar[data-v-6eb93a4d]{display:flex;flex-direction:column;align-items:flex-start;margin-bottom:20px}.setting-card .avatar .avatar-img[data-v-6eb93a4d]{margin-bottom:10px}.setting-card .hash-link[data-v-6eb93a4d]{margin-left:12px}.setting-card .phone-bind-wrap[data-v-6eb93a4d]{margin-top:20px}.setting-card .phone-bind-wrap .captcha-img-wrap[data-v-6eb93a4d]{width:100%;display:flex;align-items:center}.setting-card .phone-bind-wrap .captcha-img[data-v-6eb93a4d]{width:125px;height:34px;border-radius:3px;margin-left:10px;overflow:hidden;cursor:pointer}.setting-card .phone-bind-wrap .captcha-img img[data-v-6eb93a4d]{width:100%;height:100%}

File diff suppressed because one or more lines are too long

@ -0,0 +1,34 @@
import{l as r,m as l,n as c,d as _,u as s,q as v,bq as $,s as b,y as w,h as i,aa as E}from"./index.f480f018.js";var z=r("thing",`
display: flex;
transition: color .3s var(--n-bezier);
font-size: var(--n-font-size);
color: var(--n-text-color);
`,[r("thing-avatar",`
margin-right: 12px;
margin-top: 2px;
`),r("thing-avatar-header-wrapper",`
display: flex;
flex-wrap: nowrap;
`,[r("thing-header-wrapper",`
flex: 1;
`)]),r("thing-main",`
flex-grow: 1;
`,[r("thing-header",`
display: flex;
margin-bottom: 4px;
justify-content: space-between;
align-items: center;
`,[l("title",`
font-size: 16px;
font-weight: var(--n-title-font-weight);
transition: color .3s var(--n-bezier);
color: var(--n-title-text-color);
`)]),l("description",[c("&:not(:last-child)",`
margin-bottom: 4px;
`)]),l("content",[c("&:not(:first-child)",`
margin-top: 12px;
`)]),l("footer",[c("&:not(:first-child)",`
margin-top: 12px;
`)]),l("action",[c("&:not(:first-child)",`
margin-top: 12px;
`)])])]);const C=Object.assign(Object.assign({},v.props),{title:String,titleExtra:String,description:String,content:String,contentIndented:{type:Boolean,default:!1}});var T=_({name:"Thing",props:C,setup(t,{slots:e}){const{mergedClsPrefixRef:h,inlineThemeDisabled:g}=s(t),x=v("Thing","-thing",z,$,t,h),o=b(()=>{const{self:{titleTextColor:d,textColor:n,titleFontWeight:u,fontSize:f},common:{cubicBezierEaseInOut:m}}=x.value;return{"--n-bezier":m,"--n-font-size":f,"--n-text-color":n,"--n-title-font-weight":u,"--n-title-text-color":d}}),a=g?w("thing",void 0,o,t):void 0;return()=>{var d;const{value:n}=h;return(d=a==null?void 0:a.onRender)===null||d===void 0||d.call(a),i("div",{class:[`${n}-thing`,a==null?void 0:a.themeClass],style:g?void 0:o.value},e.avatar&&t.contentIndented?i("div",{class:`${n}-thing-avatar`},e.avatar()):null,i("div",{class:`${n}-thing-main`},!t.contentIndented&&(e.header||t.title||e["header-extra"]||t.titleExtra||e.avatar)?i("div",{class:`${n}-thing-avatar-header-wrapper`},e.avatar?i("div",{class:`${n}-thing-avatar`},e.avatar()):null,e.header||t.title||e["header-extra"]||t.titleExtra?i("div",{class:`${n}-thing-header-wrapper`},i("div",{class:`${n}-thing-header`},e.header||t.title?i("div",{class:`${n}-thing-header__title`},e.header?e.header():t.title):null,e["header-extra"]||t.titleExtra?i("div",{class:`${n}-thing-header__extra`},e["header-extra"]?e["header-extra"]():t.titleExtra):null),e.description||t.description?i("div",{class:`${n}-thing-main__description`},e.description?e.description():t.description):null):null):i(E,null,e.header||t.title||e["header-extra"]||t.titleExtra?i("div",{class:`${n}-thing-header`},e.header||t.title?i("div",{class:`${n}-thing-header__title`},e.header?e.header():t.title):null,e["header-extra"]||t.titleExtra?i("div",{class:`${n}-thing-header__extra`},e["header-extra"]?e["header-extra"]():t.titleExtra):null):null,e.description||t.description?i("div",{class:`${n}-thing-main__description`},e.description?e.description():t.description):null),e.default||t.content?i("div",{class:`${n}-thing-main__content`},e.default?e.default():t.content):null,e.footer?i("div",{class:`${n}-thing-main__footer`},e.footer()):null,e.action?i("div",{class:`${n}-thing-main__action`},e.action()):null))}}});export{T as _};

@ -0,0 +1 @@
.tags-wrap[data-v-ec1d2778]{padding:20px}.tags-wrap .tag-item .tag-hot[data-v-ec1d2778]{margin-left:12px;font-size:12px;opacity:.75}

@ -0,0 +1 @@
import{_ as T}from"./main-nav.9110259b.js";import{ah as w,d as x,r as s,a1 as B,al as D,V as _,W as r,a3 as a,a4 as n,c0 as V,aa as q,ab as C,c1 as E,c2 as N,ag as F,b_ as I,a5 as L,a8 as M,a9 as m,Y as S,ad as U,an as W}from"./index.f480f018.js";import{_ as Y}from"./List.49bcdf81.js";const $={class:"tag-hot"},j=x({setup(z){const c=s([]),u=s("hot"),o=s(!1),l=()=>{o.value=!0,V({type:u.value,num:50}).then(e=>{c.value=e,o.value=!1}).catch(e=>{o.value=!1})},i=e=>{u.value=e,l()};return B(()=>{l()}),(e,A)=>{const d=T,p=E,g=N,v=D("router-link"),f=U,h=W,y=F,b=I,k=Y;return _(),r("div",null,[a(d,{title:"\u8BDD\u9898"}),a(k,{class:"main-content-wrap tags-wrap",bordered:""},{default:n(()=>[a(g,{type:"line",animated:"","onUpdate:value":i},{default:n(()=>[a(p,{name:"hot",tab:"\u70ED\u95E8"}),a(p,{name:"new",tab:"\u6700\u65B0"})]),_:1}),a(b,{show:o.value},{default:n(()=>[a(y,null,{default:n(()=>[(_(!0),r(q,null,C(c.value,t=>(_(),L(h,{class:"tag-item",type:"success",round:"",key:t.id},{avatar:n(()=>[a(f,{src:t.user.avatar},null,8,["src"])]),default:n(()=>[a(v,{class:"hash-link",to:{name:"home",query:{q:t.tag,t:"tag"}}},{default:n(()=>[M(" #"+m(t.tag),1)]),_:2},1032,["to"]),S("span",$,"("+m(t.quote_num)+")",1)]),_:2},1024))),128))]),_:1})]),_:1},8,["show"])]),_:1})])}}});var K=w(j,[["__scopeId","data-v-ec1d2778"]]);export{K as default};

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 @@
.whisper-wrap .whisper-line[data-v-3e021430]{margin-top:10px}.whisper-wrap .whisper-line.send-wrap .n-button[data-v-3e021430]{width:100%}.whisper-wrap .whisper-line[data-v-bf9bc04e]{margin-top:10px}.whisper-wrap .whisper-line.send-wrap .n-button[data-v-bf9bc04e]{width:100%}.profile-tabs-wrap[data-v-7a6aaa02]{padding:0 16px}.profile-baseinfo[data-v-7a6aaa02]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-7a6aaa02]{width:55px}.profile-baseinfo .base-info[data-v-7a6aaa02]{position:relative;width:calc(100% - 55px)}.profile-baseinfo .base-info .username[data-v-7a6aaa02]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .uid[data-v-7a6aaa02]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-baseinfo .base-info .top-tag[data-v-7a6aaa02]{transform:scale(.75)}.profile-baseinfo .user-opts[data-v-7a6aaa02]{position:absolute;top:16px;right:16px;opacity:.75}.pagination-wrap[data-v-7a6aaa02]{padding:10px;display:flex;justify-content:center;overflow:hidden}

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.balance-wrap[data-v-4ef16ff5]{padding:16px}.balance-wrap .balance-line[data-v-4ef16ff5]{display:flex;justify-content:space-between}.balance-wrap .balance-line .balance-opts[data-v-4ef16ff5]{display:flex;flex-direction:column}.bill-line[data-v-4ef16ff5]{padding:16px;display:flex;justify-content:space-between}.bill-line .income[data-v-4ef16ff5],.bill-line .out[data-v-4ef16ff5]{font-weight:700}.bill-line .income[data-v-4ef16ff5]{color:#18a058}.pagination-wrap[data-v-4ef16ff5]{padding:10px;display:flex;justify-content:center;overflow:hidden}.qrcode-wrap[data-v-4ef16ff5]{display:flex;flex-direction:column;align-items:center;justify-content:center}.qrcode-wrap .pay-tips[data-v-4ef16ff5]{margin-top:16px}.qrcode-wrap .pay-sub-tips[data-v-4ef16ff5]{margin-top:4px;font-size:12px;opacity:.75;display:flex;align-items:center}.dark .income[data-v-4ef16ff5]{color:#63e2b7}

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.link-wrap[data-v-4c9a59cc]{margin-bottom:10px}.link-wrap .link-item[data-v-4c9a59cc]{display:flex;align-items:center}.link-wrap .link-item .hash-link .link-txt[data-v-4c9a59cc]{margin-left:4px;word-break:break-all}.images-wrap{margin-top:10px}.post-img{display:flex;margin:0;border-radius:3px;overflow:hidden;background:rgba(0,0,0,.1);border:1px solid #eee}.post-img img{width:100%;height:100%}.x1{height:140px}.x2{height:90px}.x3{height:80px}.dark .post-img{border:1px solid #333}@media screen and (max-width: 821px){.x1{height:100px}.x2{height:70px}.x3{height:50px}}.attach-item[data-v-ca4bb796]{margin:10px 0}

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.nav-title-card{z-index:99;width:100%;top:0;position:sticky;border-radius:0;border-bottom:0;background-color:#ffffffbf;backdrop-filter:blur(12px)}.nav-title-card .navbar{height:30px;position:relative;display:flex;align-items:center}.nav-title-card .navbar .back-btn{margin-right:8px}.nav-title-card .navbar .theme-switch-wrap{position:absolute;right:0;top:calc(50% - 9px)}.dark .nav-title-card{background-color:#101014bf}

@ -0,0 +1 @@
import{p as O,H as $,C as j,B as D,_ as E,a as F,b as L,c as M}from"./content.ef1e095a.js";import{d as R,ai as S,aj as I,Z as P,s as W,al as Y,V as o,W as f,a3 as i,am as Z,a2 as t,a4 as n,aa as A,ab as G,a7 as v,Y as u,a8 as p,a9 as c,a5 as r,a6 as _,ad as J,an as K,ae as Q,ag as U}from"./index.f480f018.js";import{f as X}from"./formatTime.02109bf5.js";import{_ as tt}from"./Thing.eef6b133.js";const et={class:"nickname-wrap"},st={class:"username-wrap"},at=p(" \u7F6E\u9876 "),nt=p(" \u79C1\u5BC6 "),ot=p(" \u597D\u53CB\u53EF\u89C1 "),it={class:"timestamp"},rt=["innerHTML"],_t={class:"opt-item"},ct={class:"opt-item"},pt={class:"opt-item"},ht=R({props:{post:null},setup(x){const C=x;S();const m=I(),B=P(),e=W(()=>{let a=Object.assign({texts:[],imgs:[],videos:[],links:[],attachments:[],charge_attachments:[]},C.post);return a.contents.map(s=>{(+s.type==1||+s.type==2)&&a.texts.push(s),+s.type==3&&a.imgs.push(s),+s.type==4&&a.videos.push(s),+s.type==6&&a.links.push(s),+s.type==7&&a.attachments.push(s),+s.type==8&&a.charge_attachments.push(s)}),a}),k=a=>{m.push({name:"post",query:{id:a}})},b=(a,s)=>{if(a.target.dataset.detail){const l=a.target.dataset.detail.split(":");if(l.length===2){B.commit("refresh"),l[0]==="tag"?m.push({name:"home",query:{q:l[1],t:"tag"}}):m.push({name:"user",query:{username:l[1]}});return}}k(s)};return(a,s)=>{const l=J,z=Y("router-link"),d=K,y=E,w=F,T=L,q=M,h=Q,N=U,V=tt;return o(),f("div",{class:"post-item",onClick:s[2]||(s[2]=g=>k(t(e).id))},[i(V,{"content-indented":""},Z({avatar:n(()=>[i(l,{round:"",size:30,src:t(e).user.avatar},null,8,["src"])]),header:n(()=>[u("span",et,[i(z,{onClick:s[0]||(s[0]=v(()=>{},["stop"])),class:"username-link",to:{name:"user",query:{username:t(e).user.username}}},{default:n(()=>[p(c(t(e).user.nickname),1)]),_:1},8,["to"])]),u("span",st," @"+c(t(e).user.username),1),t(e).is_top?(o(),r(d,{key:0,class:"top-tag",type:"warning",size:"small",round:""},{default:n(()=>[at]),_:1})):_("",!0),t(e).visibility==1?(o(),r(d,{key:1,class:"top-tag",type:"error",size:"small",round:""},{default:n(()=>[nt]),_:1})):_("",!0),t(e).visibility==2?(o(),r(d,{key:2,class:"top-tag",type:"info",size:"small",round:""},{default:n(()=>[ot]),_:1})):_("",!0)]),"header-extra":n(()=>[u("span",it,c(t(e).ip_loc?t(e).ip_loc+" \xB7 ":t(e).ip_loc)+" "+c(t(X)(t(e).created_on)),1)]),footer:n(()=>[t(e).attachments.length>0?(o(),r(y,{key:0,attachments:t(e).attachments},null,8,["attachments"])):_("",!0),t(e).charge_attachments.length>0?(o(),r(y,{key:1,attachments:t(e).charge_attachments,price:t(e).attachment_price},null,8,["attachments","price"])):_("",!0),t(e).imgs.length>0?(o(),r(w,{key:2,imgs:t(e).imgs},null,8,["imgs"])):_("",!0),t(e).videos.length>0?(o(),r(T,{key:3,videos:t(e).videos},null,8,["videos"])):_("",!0),t(e).links.length>0?(o(),r(q,{key:4,links:t(e).links},null,8,["links"])):_("",!0)]),action:n(()=>[i(N,{justify:"space-between"},{default:n(()=>[u("div",_t,[i(h,{size:"18",class:"opt-item-icon"},{default:n(()=>[i(t($))]),_:1}),p(" "+c(t(e).upvote_count),1)]),u("div",ct,[i(h,{size:"18",class:"opt-item-icon"},{default:n(()=>[i(t(j))]),_:1}),p(" "+c(t(e).comment_count),1)]),u("div",pt,[i(h,{size:"18",class:"opt-item-icon"},{default:n(()=>[i(t(D))]),_:1}),p(" "+c(t(e).collection_count),1)])]),_:1})]),_:2},[t(e).texts.length>0?{name:"description",fn:n(()=>[(o(!0),f(A,null,G(t(e).texts,g=>(o(),f("span",{key:g.id,class:"post-text",onClick:s[1]||(s[1]=v(H=>b(H,t(e).id),["stop"])),innerHTML:t(O)(g.content).content},null,8,rt))),128))])}:void 0]),1024)])}}});export{ht as _};

@ -0,0 +1 @@
.post-item{width:100%;padding:16px;box-sizing:border-box}.post-item .nickname-wrap{font-size:14px}.post-item .username-wrap{font-size:14px;opacity:.75}.post-item .top-tag{transform:scale(.75)}.post-item .timestamp{opacity:.75;font-size:12px}.post-item .post-text{text-align:justify;overflow:hidden;white-space:pre-wrap;word-break:break-all}.post-item .opt-item{display:flex;align-items:center;opacity:.7}.post-item .opt-item .opt-item-icon{margin-right:10px}.post-item:hover{background:#f7f9f9;cursor:pointer}.post-item .n-thing-avatar{margin-top:0}.post-item .n-thing-header{line-height:16px;margin-bottom:8px!important}.dark .post-item:hover{background:#18181c}

@ -0,0 +1 @@
.skeleton-item[data-v-a6d56894]{padding:12px;display:flex}.skeleton-item .user[data-v-a6d56894]{width:42px}.skeleton-item .content[data-v-a6d56894]{width:calc(100% - 42px)}

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

Loading…
Cancel
Save