From 41a45c8975594c80bb934740f76a1ab80c3478c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=A2=E5=AE=87=E5=AE=81?= Date: Tue, 29 Sep 2026 16:45:18 +0700 Subject: [PATCH] =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E6=94=B9=E6=8E=A8=E8=8D=90=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/web/comment.go | 43 --- app/api/web/help.go | 10 +- app/api/web/play.go | 170 ++++-------- app/deploy_prod.sh | 2 + app/router/gin.go | 5 - app/service/commentser/list.go | 135 ---------- app/service/commentser/tplfuncs.go | 11 - app/service/commentser/types.go | 83 ------ app/static/css/play.css | 29 ++ app/static/css/playComments.css | 396 ---------------------------- app/static/js/pages/play.js | 2 +- app/static/js/pages/playComments.js | 260 ------------------ app/template/base/playComments.html | 69 ----- app/template/pages/help.html | 4 - app/template/pages/play.html | 74 +++--- app/template/pages/seriesPlay.html | 74 +++--- 16 files changed, 160 insertions(+), 1207 deletions(-) delete mode 100644 app/api/web/comment.go delete mode 100644 app/service/commentser/list.go delete mode 100644 app/service/commentser/tplfuncs.go delete mode 100644 app/service/commentser/types.go delete mode 100644 app/static/css/playComments.css delete mode 100644 app/static/js/pages/playComments.js delete mode 100644 app/template/base/playComments.html diff --git a/app/api/web/comment.go b/app/api/web/comment.go deleted file mode 100644 index a62c99e..0000000 --- a/app/api/web/comment.go +++ /dev/null @@ -1,43 +0,0 @@ -package web - -import ( - "net/http" - "strconv" - "videoweb/app/service/commentser" - - "github.com/gin-gonic/gin" -) - -// CommentList BFF:代理 GET /api/app/comment/list。 -// 文档:http://172.104.181.99:25319/swagger/index.html#/评论APP/get_api_app_comment_list -func CommentList(c *gin.Context) { - objectID, _ := strconv.ParseInt(c.Query("objectId"), 10, 64) - objectType, _ := strconv.Atoi(c.Query("objectType")) - pageNum, _ := strconv.Atoi(c.Query("pageNum")) - pageSize, _ := strconv.Atoi(c.Query("pageSize")) - sortOrder, _ := strconv.Atoi(c.Query("sortOrder")) - if objectID <= 0 { - c.JSON(http.StatusBadRequest, gin.H{ - "code": 400, - "msg": "objectId required", - "tip": "缺少评论对象", - "data": nil, - }) - return - } - data := commentser.FetchListOrEmpty(c, commentser.ListQuery{ - ObjectID: objectID, - ObjectType: objectType, - PageNum: pageNum, - PageSize: pageSize, - SortOrder: sortOrder, - }) - c.JSON(http.StatusOK, gin.H{ - "code": 200, - "msg": "ok", - "tip": "成功", - "data": data, - }) -} - -// CommentSend BFF:代理 POST /api/app/comment/send。 diff --git a/app/api/web/help.go b/app/api/web/help.go index eff84f5..f1830fa 100644 --- a/app/api/web/help.go +++ b/app/api/web/help.go @@ -10,14 +10,12 @@ import ( func Help(c *gin.Context) { HTMLOK(c, "help.html", gin.H{ "faqItems": []seoser.FAQItem{ - {Question: "黄果短剧网页版可以看全集吗?", Answer: "可以。免费集直接观看,付费集可用金币按集解锁,解锁后永久有效。"}, - {Question: "解锁过的剧集还需要再付费吗?", Answer: "不需要。已解锁的剧集可在登录后继续观看,无需重复付费。"}, - {Question: "如何充值金币?", Answer: "登录后进入个人中心,选择金币充值,即可查看可用的充值方式。"}, - {Question: "支持哪些支付方式?", Answer: "可用的支付方式会在充值页面列出,请以页面显示为准。"}, - {Question: "忘记密码怎么办?", Answer: "请通过意见反馈联系我们,提交账号信息后由客服协助处理。"}, + {Question: "黄果短剧网页版可以看全集吗?", Answer: "免费集可直接在网页观看;标记为 App 专享的剧集,下载黄果短剧 App 即可免费观看完整内容。"}, + {Question: "网页版需要注册账号吗?", Answer: "不需要。网页版无需注册或登录,打开即可浏览和观看。"}, + {Question: "如何下载黄果短剧 App?", Answer: "点击页面顶部或播放页的「下载 App」按钮,按提示完成安装即可。"}, + {Question: "网站打不开怎么办?", Answer: "请收藏页脚的最新地址、备用地址与海外永久地址,任一地址均可访问。"}, {Question: "内容多久更新一次?", Answer: "黄果短剧持续更新内容,欢迎关注首页的新剧和最近上新。"}, {Question: "如何联系客服或洽谈合作?", Answer: "请发送邮件至 support@huanguoai.net,我们会尽快回复。"}, - {Question: "如何注销账号?", Answer: "请通过意见反馈或客服邮箱提交注销申请。"}, }, }) } diff --git a/app/api/web/play.go b/app/api/web/play.go index 9798e42..5763d6a 100644 --- a/app/api/web/play.go +++ b/app/api/web/play.go @@ -2,7 +2,6 @@ package web import ( "strings" - "videoweb/app/service/commentser" "videoweb/app/service/playletser" "videoweb/app/service/videoser" @@ -76,20 +75,6 @@ func Play(c *gin.Context) { }) } - commentObjectID := playlet.ID - commentObjectType := commentser.ObjectTypePlaylet - if chapterID > 0 { - commentObjectID = chapterID - commentObjectType = commentser.ObjectTypeChapter - } - comments := commentser.FetchListOrEmpty(c, commentser.ListQuery{ - ObjectID: commentObjectID, - ObjectType: commentObjectType, - PageNum: 1, - PageSize: 4, - SortOrder: commentser.SortHot, - }) - canWatch := false episode := 0 if chapter != nil { @@ -119,45 +104,36 @@ func Play(c *gin.Context) { } data := gin.H{ - "title": title, - "navActive": "list", - "seoInfo": seo, - "playlet": playlet, - "chapter": chapter, - "chapterID": chapterID, - "episode": episode, - "canWatch": canWatch, - "mainURL": mainURL, - "previewURL": previewURL, - "hasPreview": previewURL != "", - "nextChapterID": playletser.NextChapterID(playlet, chapterID), - "advitise": playlet.Advitise, - "related": playlet.RelatedRecommendData, - "guessLike": guessLike.List, - "guessPage": guessLike.PageNum, - "guessPageSize": guessLike.PageSize, - "guessTotal": guessLike.TotalCount, - "hotList": hotList, - "hotMoreURL": playletser.AllListURL("all", 0, 0, 2, 1), - "comments": comments.List, - "commentTotal": comments.TotalCount, - "commentPage": comments.PageNum, - "commentSize": comments.PageSize, - "commentObjectID": commentObjectID, - "commentObjectType": commentObjectType, + "title": title, + "navActive": "list", + "seoInfo": seo, + "playlet": playlet, + "chapter": chapter, + "chapterID": chapterID, + "episode": episode, + "canWatch": canWatch, + "mainURL": mainURL, + "previewURL": previewURL, + "hasPreview": previewURL != "", + "nextChapterID": playletser.NextChapterID(playlet, chapterID), + "advitise": playlet.Advitise, + "related": playlet.RelatedRecommendData, + "guessLike": guessLike.List, + "guessPage": guessLike.PageNum, + "guessPageSize": guessLike.PageSize, + "guessTotal": guessLike.TotalCount, + "hotList": hotList, + "hotMoreURL": playletser.AllListURL("all", 0, 0, 2, 1), "playResponse": gin.H{ - "playlet": playlet, - "chapter": chapter, - "chapterID": chapterID, - "canWatch": canWatch, - "mainURL": mainURL, - "previewURL": previewURL, - "hasPreview": previewURL != "", - "guessLike": guessLike, - "hotList": hotList, - "comments": comments, - "commentObjectID": commentObjectID, - "commentObjectType": commentObjectType, + "playlet": playlet, + "chapter": chapter, + "chapterID": chapterID, + "canWatch": canWatch, + "mainURL": mainURL, + "previewURL": previewURL, + "hasPreview": previewURL != "", + "guessLike": guessLike, + "hotList": hotList, }, } data["canonicalPath"] = playletser.PlayPath(playlet.Slug, playlet.ID) @@ -192,27 +168,6 @@ func SeriesPlay(c *gin.Context) { videoURL := strings.TrimSpace(video.Video.URL) canWatch := videoURL != "" - commentObjectID := video.ID - // 视频评论:objectType=20(swagger:10-短剧 11-短剧剧集 20-视频) - commentObjectType := commentser.ObjectTypeVideo - comments := commentser.FetchListOrEmpty(c, commentser.ListQuery{ - ObjectID: commentObjectID, - ObjectType: commentObjectType, - PageNum: 1, - PageSize: 4, - SortOrder: commentser.SortHot, - }) - if comments.PageNum <= 0 { - comments.PageNum = 1 - } - if comments.PageSize <= 0 { - comments.PageSize = 4 - } - commentTotal := comments.TotalCount - if commentTotal <= 0 && video.CountComment > 0 { - commentTotal = video.CountComment - } - hot := playletser.FetchAllListOrEmpty(c, playletser.AllListQuery{ PageNum: 1, PageSize: 8, @@ -245,51 +200,30 @@ func SeriesPlay(c *gin.Context) { } data := gin.H{ - "title": title, - "navActive": "aiSeries", - "seoInfo": video.SeoInfo, - "video": video, - "videoID": video.ID, - "videoSlug": video.Slug, - "cover": cover, - "videoURL": videoURL, - "canWatch": canWatch, - "guessLike": guessLike.List, - "guessPage": guessLike.PageNum, - "guessPageSize": guessLike.PageSize, - "guessTotal": guessLike.TotalCount, - "guessPlayletID": guessPlayletID, - "advitise": video.Advitise, - "comments": comments.List, - "commentTotal": commentTotal, - "commentPage": comments.PageNum, - "commentSize": comments.PageSize, - "commentObjectID": commentObjectID, - "commentObjectType": commentObjectType, - "hotList": hotList, - "hotMoreURL": playletser.AllListURL("all", 0, 0, 2, 1), + "title": title, + "navActive": "aiSeries", + "seoInfo": video.SeoInfo, + "video": video, + "videoID": video.ID, + "videoSlug": video.Slug, + "cover": cover, + "videoURL": videoURL, + "canWatch": canWatch, + "guessLike": guessLike.List, + "guessPage": guessLike.PageNum, + "guessPageSize": guessLike.PageSize, + "guessTotal": guessLike.TotalCount, + "guessPlayletID": guessPlayletID, + "advitise": video.Advitise, + "hotList": hotList, + "hotMoreURL": playletser.AllListURL("all", 0, 0, 2, 1), "videoResponse": gin.H{ - "video": video, - "videoURL": videoURL, - "canWatch": canWatch, - "comments": comments, - "hotList": hotList, - "guessLike": guessLike, - "guessPlayletID": guessPlayletID, - "commentObjectID": commentObjectID, - "commentObjectType": commentObjectType, - }, - "commentDebug": gin.H{ - "path": "/api/app/comment/list", - "objectId": commentObjectID, - "objectType": commentObjectType, // 20-视频 - "pageNum": comments.PageNum, - "pageSize": comments.PageSize, - "totalCount": commentTotal, - "list": comments.List, - "apis": []string{ - "GET /api/app/comment/list", - }, + "video": video, + "videoURL": videoURL, + "canWatch": canWatch, + "hotList": hotList, + "guessLike": guessLike, + "guessPlayletID": guessPlayletID, }, "playDebug": gin.H{ "canWatch": canWatch, diff --git a/app/deploy_prod.sh b/app/deploy_prod.sh index 744701b..5157cbb 100755 --- a/app/deploy_prod.sh +++ b/app/deploy_prod.sh @@ -1,6 +1,8 @@ #!/bin/bash set -e echo "开始" +# 本机 Go 装在 ~/sdk/go,未加入 PATH 时在这里补上 +command -v go >/dev/null 2>&1 || export PATH="$HOME/sdk/go/bin:$PATH" rm -rf ./web_huangguo2_h5 ./web_huangguo2_h5.tar.gz GOOS=linux GOARCH=amd64 go build -o web_huangguo2_h5 tar zvcf web_huangguo2_h5.tar.gz \ diff --git a/app/router/gin.go b/app/router/gin.go index 876f290..652752b 100644 --- a/app/router/gin.go +++ b/app/router/gin.go @@ -8,7 +8,6 @@ import ( "strings" "videoweb/app/api/web" "videoweb/app/appg" - "videoweb/app/service/commentser" "videoweb/app/service/playletser" "videoweb/app/service/seoser" "videoweb/app/service/translationser" @@ -62,9 +61,6 @@ func setGinMode() { func setupEngine(engine *gin.Engine) { tplFuncs := playletser.TemplateFuncs() - for name, fn := range commentser.TemplateFuncs() { - tplFuncs[name] = fn - } tplFuncs["InJSONScript"] = inJSONScript tplFuncs["navHref"] = translationser.NavHref tplFuncs["navIsDownload"] = translationser.NavIsDownload @@ -209,7 +205,6 @@ func registerRoutes(r *gin.Engine) { // 播放页 BFF r.GET("/api/playlet/guess-like/list", web.GuessLikeList) - r.GET("/api/comment/list", web.CommentList) r.GET("/api/web/search/suggest", web.SearchSuggest) // 搜索引擎抓取、发现与站点所有权验证。 diff --git a/app/service/commentser/list.go b/app/service/commentser/list.go deleted file mode 100644 index 05c41f4..0000000 --- a/app/service/commentser/list.go +++ /dev/null @@ -1,135 +0,0 @@ -package commentser - -import ( - "fmt" - "net/url" - "strconv" - "strings" - "time" - "videoweb/app/service/upstream" - "videoweb/pub/common/log" - - "github.com/gin-gonic/gin" -) - -const ( - listPath = "/api/app/comment/list" - defaultCommentSize = 10 -) - -// ListQuery 评论列表查询参数。 -type ListQuery struct { - ObjectID int64 - ObjectType int - PageNum int - PageSize int - SortOrder int -} - -// Normalize 校正分页与排序默认值。 -func (q *ListQuery) Normalize() { - if q.PageNum <= 0 { - q.PageNum = 1 - } - if q.PageSize <= 0 { - q.PageSize = defaultCommentSize - } - if q.PageSize > 50 { - q.PageSize = 50 - } - q.ObjectType = NormalizeObjectType(q.ObjectType) - if q.SortOrder != SortLatest { - q.SortOrder = SortHot - } -} - -// FetchList 分页查询评论。 -func FetchList(c *gin.Context, q ListQuery) (*GetCommentListResp, error) { - q.Normalize() - if q.ObjectID <= 0 { - return nil, fmt.Errorf("comment list invalid objectId") - } - params := url.Values{} - params.Set("objectId", strconv.FormatInt(q.ObjectID, 10)) - params.Set("objectType", strconv.Itoa(q.ObjectType)) - params.Set("pageNum", strconv.Itoa(q.PageNum)) - params.Set("pageSize", strconv.Itoa(q.PageSize)) - params.Set("sortOrder", strconv.Itoa(q.SortOrder)) - - requestPath := listPath + "?" + params.Encode() - env, err := fetchListOnce(c, requestPath) - if err != nil { - return nil, err - } - if env.Code != 200 { - return nil, fmt.Errorf("comment list code=%d msg=%s", env.Code, env.Msg) - } - return &env.Data, nil -} - -func fetchListOnce(c *gin.Context, path string) (*APIEnvelope[GetCommentListResp], error) { - var env APIEnvelope[GetCommentListResp] - if err := upstream.GetJSON(c, path, &env); err != nil { - return nil, err - } - return &env, nil -} - -// FetchListOrEmpty 失败时返回空列表。 -func FetchListOrEmpty(c *gin.Context, q ListQuery) *GetCommentListResp { - data, err := FetchList(c, q) - if err != nil { - log.Error("fetch comment list failed", log.E(err)) - return &GetCommentListResp{ - ObjectID: q.ObjectID, - PageNum: q.PageNum, - PageSize: q.PageSize, - } - } - if data == nil { - return &GetCommentListResp{ - ObjectID: q.ObjectID, - PageNum: q.PageNum, - PageSize: q.PageSize, - } - } - return data -} - -// ParseSortOrder 解析站点评论排序(1 热门 / 2 最新)。 -func ParseSortOrder(s string) int { - n, err := strconv.Atoi(strings.TrimSpace(s)) - if err != nil || n != SortLatest { - return SortHot - } - return SortLatest -} - -// FormatCreatedAt 展示用时间格式化(非字段映射)。 -func FormatCreatedAt(raw string) string { - raw = strings.TrimSpace(raw) - if raw == "" { - return "" - } - layouts := []string{ - time.RFC3339Nano, - time.RFC3339, - "2006-01-02T15:04:05Z07:00", - "2006-01-02 15:04:05", - } - for _, layout := range layouts { - if t, err := time.Parse(layout, raw); err == nil { - return t.Local().Format("2006/1/2 15:04") - } - } - return raw -} - -// AvatarOrDefault 头像空时回退默认图。 -func AvatarOrDefault(avatar string) string { - avatar = strings.TrimSpace(avatar) - if avatar == "" { - return "/static/images/profile/avatar.png" - } - return avatar -} diff --git a/app/service/commentser/tplfuncs.go b/app/service/commentser/tplfuncs.go deleted file mode 100644 index 999a11c..0000000 --- a/app/service/commentser/tplfuncs.go +++ /dev/null @@ -1,11 +0,0 @@ -package commentser - -import "html/template" - -// TemplateFuncs 评论展示辅助。 -func TemplateFuncs() template.FuncMap { - return template.FuncMap{ - "formatCommentTime": FormatCreatedAt, - "commentAvatar": AvatarOrDefault, - } -} diff --git a/app/service/commentser/types.go b/app/service/commentser/types.go deleted file mode 100644 index dad8fa1..0000000 --- a/app/service/commentser/types.go +++ /dev/null @@ -1,83 +0,0 @@ -package commentser - -// CommentSendReq 对应 swagger proto.CommentSendReq -// parentsId 为 0 发一级评论,大于 0 发二级评论;replyCommentId 为实际被回复的评论。 -type CommentSendReq struct { - ObjectID int64 `json:"objectId"` - ObjectType int `json:"objectType"` - Text string `json:"text"` - ParentsID int64 `json:"parentsId"` - ReplyCommentID int64 `json:"replyCommentId"` -} - -// CommentLikeSetReq 对应 swagger proto.CommentLikeSetReq -type CommentLikeSetReq struct { - CommentID int64 `json:"commentId"` - IsLike bool `json:"isLike"` -} - -// CommentLikeSetResp 对应 swagger proto.CommentLikeSetResp -type CommentLikeSetResp struct { - CommentID int64 `json:"commentId"` - CountLike int64 `json:"countLike"` - IsLike int `json:"isLike"` -} - -// CommentInfo 对应 swagger proto.CommentInfo -type CommentInfo struct { - ID int64 `json:"id"` - ObjectID int64 `json:"objectId"` - ObjectType int `json:"objectType"` - Text string `json:"text"` - UserID int64 `json:"userId"` - UserName string `json:"userName"` - UserAvatar string `json:"userAvatar"` - CountLike int64 `json:"countLike"` - CommentCount int64 `json:"commentCount"` - IsLike int `json:"isLike"` - CreatedAt string `json:"createdAt"` - Level int `json:"level,omitempty"` - ParentsID int64 `json:"parentsId,omitempty"` - ReplyCommentID int64 `json:"replyCommentId,omitempty"` - ReplyUserName string `json:"replyUserName,omitempty"` - Children []CommentInfo `json:"children,omitempty"` -} - -// GetCommentListResp 对应 swagger proto.GetCommentListResp -type GetCommentListResp struct { - List []CommentInfo `json:"list"` - ObjectID int64 `json:"objectId"` - PageNum int `json:"pageNum"` - PageSize int `json:"pageSize"` - TotalCount int64 `json:"totalCount"` -} - -// APIEnvelope 上游统一响应壳 -type APIEnvelope[T any] struct { - Code int `json:"code"` - Msg string `json:"msg"` - Tip string `json:"tip"` - Data T `json:"data"` -} - -const ( - // ObjectTypePlaylet 短剧评论 - ObjectTypePlaylet = 10 - // ObjectTypeChapter 短剧剧集评论 - ObjectTypeChapter = 11 - // ObjectTypeVideo 视频评论 - ObjectTypeVideo = 20 - - SortHot = 1 // 热门 - SortLatest = 2 // 最新 -) - -// NormalizeObjectType 校正评论对象类型:10-短剧 11-短剧剧集 20-视频。 -func NormalizeObjectType(objectType int) int { - switch objectType { - case ObjectTypeChapter, ObjectTypeVideo: - return objectType - default: - return ObjectTypePlaylet - } -} diff --git a/app/static/css/play.css b/app/static/css/play.css index bffa0b1..e545974 100644 --- a/app/static/css/play.css +++ b/app/static/css/play.css @@ -1857,3 +1857,32 @@ a.play-synopsis__tag:hover { line-height: 16px; } } + +/* 猜你喜欢放在左栏(原评论位置),右栏为热门榜单 */ +.play-dual > .play-like { + flex: 1; + min-width: 0; + padding: 0; +} + +.play-dual .play-like__grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + gap: 20px 16px; +} + +.play-dual .card--play { + width: auto; + min-width: 0; +} + +@media (max-width: 768px) { + .play-dual > .play-like { + padding: 0; + } + + .play-dual .play-like__grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px 8px; + } +} diff --git a/app/static/css/playComments.css b/app/static/css/playComments.css deleted file mode 100644 index bd4ff3f..0000000 --- a/app/static/css/playComments.css +++ /dev/null @@ -1,396 +0,0 @@ -/* 播放页评论区(含输入框、列表、内联回复) */ - -.play-comments { - flex: 1 1 0; - min-width: 0; - display: flex; - flex-direction: column; - gap: 16px; -} - -.play-comments__head { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 14px; -} - -.play-comments__title { - margin: 0; - font-size: 22px; - font-weight: 600; - line-height: 30px; -} - -.play-comments__count { - color: var(--text-dim); - font-size: 14px; - line-height: 22px; -} - -.play-composer { - display: flex; - align-items: center; - gap: 12px; -} - -.play-composer__avatar { - width: 40px; - height: 40px; - flex-shrink: 0; - display: inline-flex; - align-items: center; - justify-content: center; - background: #232328; - border-radius: 999px; - overflow: hidden; -} - -.play-composer__avatar-img { - width: 100%; - height: 100%; - object-fit: cover; - display: block; -} - -.play-composer__field { - flex: 1 1 0; - min-width: 0; - height: 44px; - padding: 0 16px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - background: rgba(255, 255, 255, 0.06); - border: 1px solid var(--line); - border-radius: var(--radius-md); -} - -.play-composer__input { - flex: 1 1 0; - min-width: 0; - height: 100%; - padding: 0; - border: 0; - outline: none; - background: transparent; - color: var(--text); - font: inherit; - font-size: 15px; - line-height: 24px; -} - -.play-composer__input::placeholder { - color: var(--text-dim); -} - -.play-composer__count { - flex-shrink: 0; - color: var(--text-dim); - font-size: 14px; - line-height: 20px; -} - -.play-composer__login, -.play-composer__send { - flex-shrink: 0; -} - -.play-composer__send:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.play-comment-list { - margin: 0; - padding: 0; - list-style: none; -} - -.play-comment { - display: flex; - align-items: flex-start; - gap: 12px; - padding: 16px 0; - border-bottom: 1px solid var(--line-soft); -} - -.play-comment:last-child { - border-bottom: 0; -} - -.play-comment__avatar { - width: 40px; - height: 40px; - flex-shrink: 0; - border-radius: 999px; - object-fit: cover; -} - -.play-comment__body { - flex: 1 1 0; - min-width: 0; - display: flex; - flex-direction: column; - gap: 8px; -} - -.play-comment__meta { - display: flex; - align-items: center; - gap: 10px; -} - -.play-comment__name { - font-size: 14px; - font-weight: 500; - line-height: 20px; -} - -.play-comment__time { - color: var(--text-dim); - font-size: 12px; - line-height: 18px; -} - -.play-comment__text { - margin: 0; - color: var(--text-muted); - font-size: 14px; - line-height: 22px; -} - -.play-comment--empty { - justify-content: center; - align-items: center; - border-bottom: 0; -} - -.play-comment--empty .play-comment__text { - padding: 12px 0; - color: var(--muted, #888); - text-align: center; - width: 100%; -} - -.play-comment__reply { - padding: 10px 14px; - color: var(--text-muted); - font-size: 13px; - line-height: 20px; - background: rgba(255, 255, 255, 0.04); - border-radius: var(--radius-sm); -} - -.play-comment__actions { - display: flex; - align-items: center; - gap: 20px; -} - -.play-comment__action { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 0; - border: 0; - background: transparent; - color: var(--text-dim); - font: inherit; - font-size: 12px; - line-height: 18px; - cursor: pointer; -} - -.play-comment__action:disabled { - cursor: default; - opacity: 0.7; -} - -.play-comment__action.is-active { - color: var(--brand, #e9c977); -} - -.play-comment__action.is-active .icon { - opacity: 1; - filter: none; -} - -.play-comment__action .icon { - width: 14px; - height: 14px; - opacity: 0.75; -} - -.play-comment__inline { - width: 100%; - margin-top: 4px; - padding: 10px 12px; - display: flex; - flex-direction: column; - gap: 8px; - background: rgba(255, 255, 255, 0.04); - border: 1px solid var(--line-soft); - border-radius: var(--radius-sm); - box-sizing: border-box; -} - -.play-comment__inline-hint { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - color: var(--text-dim); - font-size: 12px; - line-height: 18px; -} - -.play-comment__inline-hint button { - flex-shrink: 0; - padding: 0; - border: 0; - background: transparent; - color: var(--brand, #e9c977); - font: inherit; - font-size: 12px; - line-height: 18px; - cursor: pointer; -} - -.play-comment__inline-row { - display: flex; - align-items: center; - gap: 8px; -} - -.play-comment__inline-input { - flex: 1 1 0; - min-width: 0; - height: 36px; - padding: 0 12px; - border: 1px solid var(--line); - border-radius: 999px; - background: rgba(0, 0, 0, 0.35); - color: var(--text); - font: inherit; - font-size: 13px; - line-height: 20px; - outline: none; -} - -.play-comment__inline-input::placeholder { - color: var(--text-dim); -} - -.play-comment__inline-input:focus, -.play-comment__inline-input:focus-visible { - border-color: rgba(233, 201, 119, 0.55); -} - -.play-comment__inline-send { - flex-shrink: 0; - height: 36px; - padding: 0 14px; - border: 0; - border-radius: 999px; - background: var(--brand, #e9c977); - color: #141417; - font: inherit; - font-size: 13px; - font-weight: 600; - line-height: 20px; - cursor: pointer; -} - -.play-comment__inline-send:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.play-comments__more { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - padding-top: 12px; - color: var(--text-muted); - font-size: 14px; - line-height: 22px; - background: transparent; - border: 0; - cursor: pointer; -} - -.play-comments__more[hidden] { - display: none; -} - -.play-comments__more:disabled { - opacity: 0.6; - cursor: default; -} - -.play-comments__more .icon { - width: 16px; - height: 16px; - transform: rotate(90deg); -} - -@media (max-width: 768px) { - .play-comments { - width: 100%; - padding: 0; - gap: 16px; - } - - .play-comments__head { - gap: 14px; - flex-wrap: nowrap; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - scrollbar-width: none; - } - - .play-comments__head::-webkit-scrollbar { - display: none; - } - - .play-comments__title { - flex-shrink: 0; - font-size: 22px; - line-height: 30px; - } - - .play-comments__count { - flex-shrink: 0; - } - - .play-composer { - width: 100%; - gap: 12px; - } - - .play-composer__avatar { - flex-shrink: 0; - } - - .play-composer__field { - min-width: 0; - } - - .play-composer__login, - .play-composer__send { - flex-shrink: 0; - height: 40px; - padding: 0 16px; - } - - .play-comment-list { - width: 100%; - } - - .play-comments__more { - width: 100%; - } -} diff --git a/app/static/js/pages/play.js b/app/static/js/pages/play.js index b37c619..3422ad2 100644 --- a/app/static/js/pages/play.js +++ b/app/static/js/pages/play.js @@ -702,7 +702,7 @@ if (btn.getAttribute("data-play-bound") === "1") return; btn.setAttribute("data-play-bound", "1"); btn.addEventListener("click", function (event) { - // 预告片免登录 / 免解锁;仅正片未解锁时拦充值 + // 预告片直接播放;仅正片为 App 专享时弹下载提示 if ( btn.hasAttribute("data-play-coin-open") && !isPreviewMode() diff --git a/app/static/js/pages/playComments.js b/app/static/js/pages/playComments.js deleted file mode 100644 index aa4a69e..0000000 --- a/app/static/js/pages/playComments.js +++ /dev/null @@ -1,260 +0,0 @@ -// 播放页评论:只读列表 + 「查看更多评论」追加翻页 -(function () { - var commentsRoot = document.querySelector("[data-play-comments]"); - if (!commentsRoot) return; - - var listEl = document.querySelector("[data-comment-list]"); - var totalEl = document.querySelector("[data-comment-total-label]"); - var moreBtn = document.querySelector("[data-comment-more]"); - var loadingMore = false; - var PAGE_SIZE = 4; - - function toast(message, type) { - if (typeof window.showToast === "function") { - window.showToast(message, type); - return; - } - } - - function toInt(v, fallback) { - var n = parseInt(v, 10); - return isNaN(n) ? fallback || 0 : n; - } - - function escapeHtml(s) { - return String(s == null ? "" : s) - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """); - } - - function getPageSize() { - return Math.max( - 1, - toInt(commentsRoot.getAttribute("data-comment-size"), PAGE_SIZE) - ); - } - - function getPage() { - return Math.max( - 1, - toInt(commentsRoot.getAttribute("data-comment-page"), 1) - ); - } - - function getLoaded() { - if (!listEl) return 0; - return listEl.querySelectorAll(".play-comment:not(.play-comment--empty)").length; - } - - function setPagerState(page, loaded, total) { - if (page != null) commentsRoot.setAttribute("data-comment-page", String(page)); - if (loaded != null) commentsRoot.setAttribute("data-comment-loaded", String(loaded)); - if (total != null) commentsRoot.setAttribute("data-comment-total", String(total)); - syncMoreButton(total != null ? total : toInt(commentsRoot.getAttribute("data-comment-total"), 0)); - } - - function syncMoreButton(total) { - if (!moreBtn) return; - var loaded = getLoaded(); - var hasMore = total > loaded; - if (hasMore) { - moreBtn.hidden = false; - moreBtn.removeAttribute("hidden"); - } else { - moreBtn.hidden = true; - moreBtn.setAttribute("hidden", ""); - } - } - - function formatTime(raw) { - var s = String(raw || "").trim(); - if (!s) return "刚刚"; - var d = new Date(s); - if (isNaN(d.getTime())) return s; - return ( - d.getFullYear() + - "/" + - (d.getMonth() + 1) + - "/" + - d.getDate() + - " " + - String(d.getHours()).padStart(2, "0") + - ":" + - String(d.getMinutes()).padStart(2, "0") - ); - } - - function renderCommentItem(item) { - item = item || {}; - var avatar = String(item.userAvatar || "").trim(); - var name = escapeHtml(item.userName || "匿名用户"); - var text = escapeHtml(item.text || ""); - var time = escapeHtml(formatTime(item.createdAt)); - var like = toInt(item.countLike, 0); - var avatarHtml = avatar - ? '' - : ''; - var children = Array.isArray(item.children) ? item.children : []; - var replies = children - .map(function (child) { - var cName = escapeHtml(child.userName || "用户"); - var replyTo = child.replyUserName - ? " 回复 " + escapeHtml(child.replyUserName) - : ""; - return ( - '
' + - cName + - replyTo + - ":" + - escapeHtml(child.text || "") + - "
" - ); - }) - .join(""); - var commentId = escapeHtml(item.id || ""); - return ( - '
  • ' + - avatarHtml + - '
    ' + - '
    ' + - '' + - name + - "" + - '' + - time + - "" + - "
    " + - '

    ' + - text + - "

    " + - replies + - '
    ' + - '' + - '' + - "" + - escapeHtml(like) + - "" + - "" + - "
  • " - ); - } - - function getLoadedIds() { - var ids = Object.create(null); - if (!listEl) return ids; - listEl.querySelectorAll(".play-comment[data-comment-id]").forEach(function (el) { - var id = String(el.getAttribute("data-comment-id") || "").trim(); - if (id) ids[id] = true; - }); - return ids; - } - - function updateTotal(n) { - if (!totalEl) return; - totalEl.textContent = n > 0 ? n + " 条" : "暂无评论"; - } - - // 下一页追加到现有列表底部,不替换已展示评论。 - function appendList(list, totalCount) { - if (!listEl) return; - list = Array.isArray(list) ? list : []; - var total = - typeof totalCount === "number" - ? totalCount - : toInt(commentsRoot.getAttribute("data-comment-total"), 0); - if (typeof totalCount === "number") { - updateTotal(totalCount); - } - if (!list.length) { - setPagerState(getPage(), getLoaded(), total); - return; - } - - var empty = listEl.querySelector(".play-comment--empty"); - if (empty) empty.remove(); - - var existing = getLoadedIds(); - var fresh = list.filter(function (item) { - var id = String((item && item.id) || "").trim(); - return id && !existing[id]; - }); - if (!fresh.length) { - setPagerState(getPage(), getLoaded(), total); - return; - } - - var wrap = document.createElement("ul"); - wrap.innerHTML = fresh.map(renderCommentItem).join(""); - var nodes = []; - while (wrap.firstChild) { - nodes.push(wrap.firstChild); - listEl.appendChild(wrap.firstChild); - } - if (typeof window.initLazyImages === "function") { - nodes.forEach(function (node) { - if (node && node.nodeType === 1) window.initLazyImages(node); - }); - window.initLazyImages(listEl); - } - setPagerState(getPage(), getLoaded(), total); - } - - function fetchCommentPage(pageNum) { - if (!window.HttpRequest || typeof window.HttpRequest.get !== "function") { - return Promise.reject(new Error("评论服务未就绪")); - } - var objectId = toInt(commentsRoot.getAttribute("data-object-id"), 0); - var objectType = toInt(commentsRoot.getAttribute("data-object-type"), 10); - if (objectId <= 0) return Promise.reject(new Error("缺少评论对象")); - var pageSize = getPageSize(); - return window.HttpRequest.get("/api/comment/list", { - params: { - objectId: objectId, - objectType: objectType, - pageNum: pageNum, - pageSize: pageSize, - sortOrder: 1, - }, - }).then(function (res) { - if (!res || Number(res.code) !== 200 || !res.data) { - throw new Error((res && (res.tip || res.msg)) || "加载失败"); - } - return res.data; - }); - } - - function loadMoreComments() { - if (loadingMore || !moreBtn) return; - var nextPage = getPage() + 1; - loadingMore = true; - moreBtn.disabled = true; - fetchCommentPage(nextPage) - .then(function (data) { - var list = data.list || []; - var total = toInt(data.totalCount, 0); - commentsRoot.setAttribute("data-comment-page", String(nextPage)); - // 只追加,绝不 renderList 整表替换 - appendList(list, total); - }) - .catch(function (err) { - toast((err && err.message) || "加载失败,请稍后重试", "error"); - }) - .finally(function () { - loadingMore = false; - if (moreBtn) moreBtn.disabled = false; - }); - } - - if (moreBtn) { - moreBtn.addEventListener("click", function (event) { - event.preventDefault(); - loadMoreComments(); - }); - } -})(); diff --git a/app/template/base/playComments.html b/app/template/base/playComments.html deleted file mode 100644 index e5d13e5..0000000 --- a/app/template/base/playComments.html +++ /dev/null @@ -1,69 +0,0 @@ -{{define "playComments"}} - -{{/* 播放页评论区:首屏 4 条,超出显示「查看更多评论」客户端翻页 */}} -{{$c := .chapter}} -{{$pageSize := .commentSize}} -{{$total := .commentTotal}} -{{$shown := len .comments}} -
    -
    -

    评论

    - {{if gt $total 0}}{{$total}} 条{{else if and $c (gt $c.CountComment 0)}}{{$c.CountComment}} 条{{else if and .video (gt .video.CountComment 0)}}{{.video.CountComment}} 条{{else}}暂无评论{{end}} -
    - - - - -
    - -{{end}} diff --git a/app/template/pages/help.html b/app/template/pages/help.html index c6c5d5e..9225a37 100644 --- a/app/template/pages/help.html +++ b/app/template/pages/help.html @@ -49,10 +49,6 @@ 如何联系客服或洽谈合作?

    请发送邮件至 support@huanguoai.net,我们会尽快回复。

    -
    - 如何注销账号? -

    请通过意见反馈或客服邮箱提交注销申请。

    -