feat:修改星榜的样式

This commit is contained in:
zheng020 2026-06-14 22:34:09 +08:00
parent 19890d5908
commit d60f597cfa
6 changed files with 122 additions and 34 deletions

View File

@ -163,3 +163,91 @@ Fall back to Grep/Glob/Read **only** when the graph doesn't cover what you need.
- 一次修复引入新 bug调试时间翻倍
- 用户对代码质量失去信任
- 提交历史变成"反复横跳"的打补丁记录
---
## 接口开发规范
### 核心规则:添加/修改接口必须使用工程化方式完成
**每次新增或修改 API 接口时,必须以工程化、标准化方式完成**,禁止"能跑就行"的临时拼凑。完成后必须能直接通过代码审查,不需要大改。
### 工程化要求清单
1. **分层架构**(强制):
- `handler`(控制器):接收请求、参数绑定与校验、调用 service、组装响应
- `service`(业务层):业务逻辑编排、事务控制、调用 repository
- `repository` / `dao`(数据层):纯数据库操作,不含业务逻辑
- handler 中**禁止**直接调用 repository / 直接写 SQL
- service 中**禁止**直接操作 HTTP 请求/响应对象
2. **请求/响应 DTO**
- 入参和出参使用独立的结构体(`XxxRequest` / `XxxResponse`
- **禁止**直接用 DB model 当作入参或返回值
- 字段命名遵循项目既有规范snake_case / camelCase
- 敏感字段密码、手机号、token在响应中**必须脱敏或排除**
3. **参数校验**
- 使用 `binding` / `validate` tag 在 handler 层做必填、长度、格式、枚举校验
- 业务规则校验放在 service 层
- 校验失败的错误信息要明确指出哪个字段、什么问题
4. **错误处理**
- 使用项目统一的错误码/错误类型(如 `ErrCodeXxx`
- **禁止**把原始 error 直接返回给前端
- **禁止**用 `_` 吞掉错误
- 关键业务错误必须打 ERROR 级别日志(含 trace_id / request_id
5. **日志规范**
- 接口入口:记录 method、path、request_id、用户身份脱敏
- 业务关键节点:状态流转、跨服务调用、缓存命中/未命中
- 异常退出:必须记录 stack trace 或 error cause
6. **API 文档**
- 同步更新 Swagger / OpenAPI 注释
- 包含:接口描述、请求参数、响应示例、错误码列表、权限要求
- 字段类型、是否必填、示例值都要写清楚
7. **数据库变更**
- 表结构变更必须写 migration
- 索引、外键、唯一约束、默认值要显式声明
- 影响现有数据的变更要考虑兼容方案默认值、backfill
8. **缓存策略**
- 是否需要缓存、用什么 key、过期时间、缓存更新/失效策略要明确
- **禁止**缓存与 DB 数据不一致的方案(如只 set 不 delete
9. **测试**
- service 层核心业务逻辑必须覆盖单元测试
- handler 层至少一个 happy path + 一个 error case
- 数据库相关测试考虑使用事务回滚或测试容器
10. **遵循项目既有约定**
- 命名风格、目录结构、文件命名、错误码定义与项目保持一致
- 复用项目已有的工具函数、中间件、错误处理逻辑
- **禁止**引入与项目风格冲突的新写法(例如项目用 snake_case 却写 camelCase
### 禁止的反模式
- ❌ 在 handler 里直接写 SQL / ORM 调用
- ❌ 把 DB model 直接作为 API 入参或返回值
- ❌ 复制粘贴老接口代码不做适配(路径、参数、错误处理不一致)
- ❌ 用 `if err != nil { return err }` 一把梭,没有业务错误码
- ❌ 缺少或忘记更新 API 文档
- ❌ 改了表结构但没写 migration
- ❌ 没有写测试或测试只覆盖了 happy path
- ❌ 临时引入新的库/框架(未和项目既有技术栈对齐)
### 完成自检
接口写完后,逐项确认:
- [ ] 分层结构正确handler / service / repository 各司其职)
- [ ] 入参/出参是独立 DTO
- [ ] 参数校验完整(必填、长度、格式、边界)
- [ ] 错误处理统一(错误码 + 友好提示 + 日志)
- [ ] 关键路径有日志
- [ ] Swagger 文档已更新
- [ ] DB 变更已写 migration
- [ ] 相关测试已编写并通过
- [ ] 命名、风格与项目既有代码一致

View File

@ -111,7 +111,9 @@
"
mode="aspectFit"
/>
<text class="like-count">{{ formatCount(item.like_count) }}</text>
<text class="like-count">{{
formatCount(item.like_count)
}}</text>
</view>
<text class="user-name">{{
item.owner_nickname || item.creator_name || item.name || ""
@ -132,22 +134,13 @@
</view>
<!-- 分页底部状态加载中 / 没有更多了 / 暂无数据 -->
<view
v-if="loadingMore"
class="load-more-tip load-more-tip-loading"
>
<view v-if="loadingMore" class="load-more-tip load-more-tip-loading">
<text class="load-more-text">加载中...</text>
</view>
<view
v-else-if="!hasMore && items.length > 0"
class="load-more-tip"
>
<view v-else-if="!hasMore && items.length > 0" class="load-more-tip">
<text class="load-more-text"> 没有更多了 </text>
</view>
<view
v-else-if="!loading && items.length === 0"
class="load-more-tip"
>
<view v-else-if="!loading && items.length === 0" class="load-more-tip">
<text class="load-more-text">暂无数据</text>
</view>
</view>
@ -203,7 +196,7 @@ const PAGE_SIZE = 10;
const tabs = [
{
key: "hot",
label: "热度榜",
label: "点赞榜",
icon: "/static/square/galaxy/dianzanbang.png",
iconWidth: 64,
iconHeight: 72,
@ -380,7 +373,11 @@ const loadData = async ({ append = false } = {}) => {
if (!raw) return;
// URL /
const instant = it.cover_url;
if (instant && instant !== PLACEHOLDER_IMAGE && instant === getInstantAssetCoverUrl(raw)) {
if (
instant &&
instant !== PLACEHOLDER_IMAGE &&
instant === getInstantAssetCoverUrl(raw)
) {
// URLgetAssetCoverRealUrl
}
getAssetCoverRealUrl(raw)
@ -447,7 +444,7 @@ onUnmounted(() => {
display: flex;
flex-direction: column;
overflow: hidden;
// padding: 0 9.5rpx;
// padding: 0 9.5rpx;
border-radius: 24rpx;
position: relative;
background: url("/static/square/galaxy/xbbj.png") center no-repeat;
@ -465,7 +462,7 @@ onUnmounted(() => {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 80rpx;
padding: 0 64rpx;
position: relative;
top: 16rpx;
margin: 0 24rpx;
@ -476,12 +473,16 @@ onUnmounted(() => {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(184deg, rgba(255, 90, 93, 0.47) -36.55%, rgba(194, 235, 255, 0.47) 121.2%);
filter: blur(5.849999904632568px);
background: linear-gradient(
184deg,
rgba(255, 90, 93, 0.47) -36.55%,
rgba(194, 235, 255, 0.47) 121.2%
);
filter: blur(5.9px);
// opacity: 0.8;
// Figma filter: blur(), backdrop-filter()
// filter: blur(3.7px);
-webkit-filter: blur(3.7px);
-webkit-filter: blur(5.9px);
border-top-left-radius: 14px;
border-top-right-radius: 13px;
border-bottom-right-radius: 8px;
@ -491,7 +492,7 @@ filter: blur(5.849999904632568px);
.ranking-tab-item {
height: 80rpx;
width: 88rpx;
width: 99.2rpx;
display: flex;
align-items: center;
flex-direction: column;
@ -500,7 +501,7 @@ filter: blur(5.849999904632568px);
}
.ranking-tab-item.active {
width: 96rpx;
width: 99.2rpx;
height: 160rpx;
top: 40rpx;
z-index: 1;
@ -536,7 +537,6 @@ filter: blur(5.849999904632568px);
.ranking-tab-icon {
display: block;
position: absolute;
}
.ranking-tab-item.active .ranking-tab-label {
@ -796,7 +796,7 @@ filter: blur(5.849999904632568px);
/* Top 排名标签(顶到右边) */
.top-badge {
margin-left: auto; /* 推到右侧 */
margin-right:8rpx;
margin-right: 16rpx;
min-width: 100rpx;
height: 36rpx;
border-radius: 18rpx;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB