feat(UI): 首页与个人中心按原型改版,修复纯白图标在白底上不可见

一、首页两个入口按钮改版(用户反馈「太小了」)

原为「裸图标 + 单行文字」的矮按钮(min-height 76rpx),改为
「白色圆角图标块 + 右侧主副两行文字」的大卡片(padding 22rpx 20rpx):
- 自主填报:便捷填报,快速提交
- 填报记录:历史记录,随时查看
主按钮渐变由 #f97316→#ef4444 改为 #f9863c→#f2620f 并加橙色投影。
结构对齐 create.vue 已有的 sales-entry-* 卡片。

二、个人中心三张卡片改版(用户提供原型图)

- 积分卡:改为「绿底图标块 + 大号数字 + 我的积分」,右侧新增
  「积分明细 ›」胶囊按钮;去掉原先那条突兀的 20px 绿色粗顶边。
  数字字号 30rpx → 40rpx。
- 自主填报 / 客户拜访:由单行居中文字改为「图标块 + 主标题 + 副标题」,
  副标题分别为「及时上报、真实记录」「拜访记录、有效跟进」。
- 药审核:改为整行卡片(图标 + 药审核/待审核任务 + 红色数字徽标 + 箭头),
  徽标数据来自新增接口 /yxe/yxe-audit/pending-count(见 yxe-java 侧提交),
  仅 can_auth === 1 时拉取,退出登录时清零。

三、修复纯白图标在近白底上不可见(用户反馈「图片是白色的看不清」)

根因不在 opacity 或主题色,而是图源本身就是纯白图形 + 透明背景:
  文件                       颜色构成            内容占画布
  0fd31838....png (驻店)     透明 + #FFFFFF      75% x 50%
  f5fbb2e4....png (跑店)     白色及半透明白       88% x 88%
  9c6c9884....png (新任务)   纯白                90% x 90%
  eebe6469....png (已完成)   纯白                 -
而承载它们的底色分别是 #f8fafc(选择页卡片)与平均亮度 250/255 的
近白底图(首页 tab),白图标贴白底自然完全看不见。此类图标只适合
放在深色/彩色底上,同一张 0fd31838....png 在个人中心药审核卡里
配金色底时一直正常,故长期未暴露。

改法:统一垫一层绿色圆角底块反衬白色图形
- select.vue:新增 .type-icon-wrap(未选中 #2fc6a7→#1aa88c,
  选中加深为 #16a085→#0e7a66 + 投影,顺带强化选中态)
- index.vue:新增 .tab-icon-wrap(未激活绿圆底;激活时置
  background:none,避免两层绿有色差露出一圈描边)

顺带修一个隐藏问题:两张填报图源的内容占比差很多,同尺寸渲染会让
驻店图标明显偏小,故为 store 单独放大(.type-icon-store 92rpx)。

验证:用 Pillow 按 rpx→px 实际比例合成修复前后对比图逐张核对,
确认修复前未激活图标完全不可见、修复后清晰、激活态无色差边。
模板标签、style 括号、类名定义/引用均通过脚本校验。

注:f5fbb2e4....png 渲染出来实际是「时钟」形状,用在「跑店填报」
语义是否合适待确认,本次只解决可见性,未更换图标资源。
This commit is contained in:
2026-10-12 16:11:58 +08:00
parent 2992f4ee49
commit e192296ea7
4 changed files with 321 additions and 68 deletions
+3
View File
@@ -12,6 +12,9 @@ export const updateUserReq = data => put('/system/user/self', data)
export const countUserIncomeReq = () => get('/system/user/countUserIncome')
// 药审核待审计数(药动销 / 跑店巡店 / 驻店填报 / 跑店填报 / 药分享 汇总)
export const auditPendingCountReq = () => get('/yxe/yxe-audit/pending-count')
export const userInfoUpdateReq = data => post('/system/user/update', data)
export const userInfoUpdateDataReq = data => post('/system/user/update-data', data)
+32 -4
View File
@@ -25,7 +25,10 @@
:class="['type-card', form.type === item.value ? 'type-card-active' : '']"
@tap="form.type = item.value"
>
<image class="type-icon" :src="item.icon" mode="aspectFit"/>
<view :class="['type-icon-wrap', form.type === item.value ? 'type-icon-wrap-active' : '']">
<image :class="['type-icon', item.value === 'store' ? 'type-icon-store' : '']"
:src="item.icon" mode="aspectFit"/>
</view>
<view class="type-body">
<view class="type-title">{{ item.title }}</view>
<view class="type-desc">{{ item.desc }}</view>
@@ -217,10 +220,35 @@ export default {
background: #f1fbf8;
}
.type-icon {
width: 108rpx;
height: 96rpx;
/* 图标本身是纯白图形 + 透明底,直接放浅色卡片上会看不见,
所以垫一层彩色圆角底块反衬;选中后加深,兼作选中态提示。
底色取偏深的绿,保证白色图形有足够对比度。 */
.type-icon-wrap {
flex-shrink: 0;
width: 112rpx;
height: 100rpx;
border-radius: 24rpx;
background: linear-gradient(135deg, #2fc6a7, #1aa88c);
display: flex;
align-items: center;
justify-content: center;
}
.type-icon-wrap-active {
background: linear-gradient(135deg, #16a085, #0e7a66);
box-shadow: 0 8rpx 20rpx rgba(47, 198, 167, 0.32);
}
.type-icon {
width: 62rpx;
height: 62rpx;
}
/* 两张图源的内容占比差得多(visit 约占画布 88%×88%,store 只占 75%×50%),
同尺寸渲染会让驻店图标明显偏小,这里按可见图形补正。 */
.type-icon-store {
width: 92rpx;
height: 92rpx;
}
.type-body {
+81 -16
View File
@@ -29,12 +29,22 @@
<!-- </view>-->
<view class="self-report-entry">
<view class="entry-btn primary" @tap="openSelfReportCreate">
<u-icon name="edit-pen" size="18" color="#ffffff"/>
<text>自主填报</text>
<view class="entry-icon">
<u-icon name="edit-pen" size="26" color="#f97316"/>
</view>
<view class="entry-text">
<text class="entry-title">自主填报</text>
<text class="entry-desc">便捷填报,快速提交</text>
</view>
</view>
<view class="entry-btn" @tap="openSelfReportRecord">
<u-icon name="order" size="18" color="#d9661f"/>
<text>填报记录</text>
<view class="entry-icon">
<u-icon name="order" size="26" color="#f97316"/>
</view>
<view class="entry-text">
<text class="entry-title">填报记录</text>
<text class="entry-desc">历史记录,随时查看</text>
</view>
</view>
</view>
</view>
@@ -43,6 +53,7 @@
<view v-for="tab in tabs" :key="tab" :class="['tab', activeTab === tab ? 'active' : '']"
@tap="activeTab = tab">
<view class="tab-main">
<view class="tab-icon-wrap">
<image v-if="tab === '新任务'" class="tab-icon"
src="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/9c6c98843c2e30247355d86ea47552aa.png"
mode="aspectFit"/>
@@ -52,6 +63,7 @@
<image v-if="tab === '已完成'" class="tab-icon"
src="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/eebe6469eb153413dc881f513063f821.png"
mode="aspectFit"/>
</view>
<text class="tab-text">{{ tab }}</text>
</view>
<view v-if="tab === '待执行' && pendingCount" class="tab-badge">{{ pendingCount }}</view>
@@ -736,9 +748,29 @@ export default {
line-height: 1;
}
/* tab 图标是纯白图形 + 透明底,而 tab 底图接近纯白,
未激活时白图标贴白底看不清,所以垫一层浅绿圆底反衬。
激活态下 .tab-main 自己已填 #2fc6a7,两层绿会透出一圈色差边,
所以激活时改成完全透明,白图标直接坐在药丸底上。 */
.tab-icon-wrap {
flex-shrink: 0;
width: 34rpx;
height: 34rpx;
border-radius: 999rpx;
background: #2fc6a7;
display: flex;
align-items: center;
justify-content: center;
}
.tab.active .tab-icon-wrap {
background: none;
box-shadow: none;
}
.tab-icon {
width: 28rpx;
height: 28rpx;
width: 24rpx;
height: 24rpx;
}
.tab.active .tab-main {
@@ -845,23 +877,56 @@ export default {
.entry-btn {
flex: 1;
min-height: 76rpx;
border-radius: 20rpx;
border: 1rpx solid #f2a75e;
background: #fff4de;
color: #d9661f;
min-width: 0;
box-sizing: border-box;
padding: 22rpx 20rpx;
border-radius: 24rpx;
border: 1rpx solid #f6dcb4;
background: #fff5e2;
color: #e0631a;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
font-size: 26rpx;
font-weight: 700;
gap: 14rpx;
}
.entry-btn.primary {
background: linear-gradient(to right, #f97316, #ef4444);
background: linear-gradient(135deg, #f9863c, #f2620f);
border-color: transparent;
color: #ffffff;
box-shadow: 0 8rpx 20rpx rgba(242, 98, 15, 0.25);
}
.entry-icon {
flex-shrink: 0;
width: 72rpx;
height: 72rpx;
border-radius: 20rpx;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
}
.entry-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.entry-title {
font-size: 32rpx;
font-weight: 700;
line-height: 1.2;
white-space: nowrap;
}
.entry-desc {
font-size: 20rpx;
line-height: 1.2;
opacity: 0.72;
white-space: nowrap;
}
.category-wrap {
+195 -38
View File
@@ -46,12 +46,21 @@
<!-- <view class="income-label">晒单收益</view>-->
<!-- </view>-->
<view class="income-item" @click="openPoints">
<view class="flex justify-start items-center">
<view class="income-value income-value--points">{{ pointSummary.currentPoints }}</view>
<view class="income-unit income-unit--points">积分</view>
<view class="income-icon">
<u-icon name="integral" size="28" color="#2fc6a7"/>
</view>
<view class="income-text">
<view class="income-line">
<text class="income-value income-value--points">{{ pointSummary.currentPoints }}</text>
<text class="income-unit income-unit--points">积分</text>
</view>
<view class="income-label">我的积分</view>
</view>
<view class="income-more">
<text>积分明细</text>
<u-icon name="arrow-right" size="13" color="#2fc6a7"/>
</view>
</view>
<!-- <view class="income-btn" @tap="openWithdraw">去提现</view>-->
</view>
@@ -81,17 +90,36 @@
<view class="feature-banner-group">
<view class="self-report-banner" @tap="openSelfReport">
<text>自主填报</text>
<view class="feature-icon">
<u-icon name="edit-pen" size="24" color="#ffffff"/>
</view>
<view class="feature-text">
<text class="feature-title">自主填报</text>
<text class="feature-desc">及时上报、真实记录</text>
</view>
</view>
<view class="self-report-banner" @tap="openVisit">
<text>客户拜访</text>
<view class="feature-icon">
<u-icon name="account-fill" size="24" color="#ffffff"/>
</view>
<view class="feature-text">
<text class="feature-title">客户拜访</text>
<text class="feature-desc">拜访记录、有效跟进</text>
</view>
</view>
</view>
<view class="audit-banner" @tap="openAudit" v-if="user.canAuth === 1">
<image class="audit-icon"
src="https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/0fd31838127cd57b1c1db44f2c749353.png"
mode="aspectFit"></image>
<text>药审核</text>
<view class="audit-icon-wrap">
<u-icon name="file-text" size="26" color="#ffffff"/>
</view>
<view class="audit-text">
<text class="audit-title">药审核</text>
<text class="audit-desc">待审核任务</text>
</view>
<view class="audit-right">
<view v-if="auditPendingCount" class="audit-badge">{{ auditPendingCount }}</view>
<u-icon name="arrow-right" size="14" color="#ffffff"/>
</view>
</view>
<view class="quick-card">
@@ -254,7 +282,7 @@
</template>
<script>
import {countUserIncomeReq, userInfoReq} from "@/api/user";
import {auditPendingCountReq, countUserIncomeReq, userInfoReq} from "@/api/user";
import Login from '@/components/Login.vue'
import AppNavbar from "@/components/AppNavbar.vue";
import {clearUserInfo, getUserInfo} from "@/util/user";
@@ -290,6 +318,7 @@ export default {
pointSummary: {
currentPoints: 0,
},
auditPendingCount: 0,
currentCity: '定位中',
serviceList: [],
servicePopupVisible: false,
@@ -342,10 +371,25 @@ export default {
const {data} = await userInfoReq()
this.isLoggedIn = true
this.user = data
await Promise.all([
const tasks = [
this.getUserIncomeData(),
this.getUserPointData()
])
]
// 药审核入口仅对审核员可见,待审数也只有此时才拉取
if (data && data.canAuth === 1) {
tasks.push(this.getAuditPendingCount())
} else {
this.auditPendingCount = 0
}
await Promise.all(tasks)
},
async getAuditPendingCount() {
try {
const {data} = await auditPendingCountReq()
this.auditPendingCount = Number(data && data.total) || 0
} catch (error) {
this.auditPendingCount = 0
}
},
async getLastCashLog() {
const {data} = await dealerWithdrawPageReq({
@@ -533,6 +577,7 @@ export default {
this.pointSummary = {
currentPoints: 0,
}
this.auditPendingCount = 0
// this.$refs.Login.open()
}
}
@@ -654,28 +699,65 @@ export default {
.income-card {
margin: 16rpx 20rpx 0;
background: #ffffff;
border-radius: 20rpx;
padding: 18rpx;
border-radius: 24rpx;
padding: 22rpx 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16rpx;
flex-wrap: wrap;
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.06);
border: 1px solid #2fc6a7;
border-top: 20px solid #2fc6a7;
border: 1rpx solid #e6f6f2;
}
.income-item {
display: flex;
flex-direction: column;
gap: 4rpx;
align-items: center;
gap: 18rpx;
flex: 1;
min-width: 0;
}
.income-icon {
flex-shrink: 0;
width: 80rpx;
height: 80rpx;
border-radius: 24rpx;
background: #e6f9f4;
display: flex;
align-items: center;
justify-content: center;
}
.income-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.income-line {
display: flex;
align-items: baseline;
gap: 6rpx;
}
.income-more {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 4rpx;
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: #e6f9f4;
color: #2fc6a7;
font-size: 24rpx;
font-weight: 700;
}
.income-value {
font-size: 30rpx;
font-size: 40rpx;
font-weight: 700;
color: #ff3b30;
}
@@ -708,24 +790,21 @@ export default {
.feature-banner-group {
margin: 20rpx 20rpx 0;
display: flex;
align-items: center;
align-items: stretch;
justify-content: center;
gap: 16rpx;
}
.audit-banner {
flex: 1;
padding: 24rpx 14rpx;
background: linear-gradient(164.72deg, rgba(215,177,115,1) 28.42%,rgba(226,190,133,1) 60.06%,rgba(214,173,106,1) 89.27%);
margin: 20rpx 20rpx 0;
padding: 24rpx;
background: linear-gradient(135deg, #f2c063, #e8a63c);
color: #ffffff;
border-radius: 40rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: center;
gap: 10rpx;
font-size: 34rpx;
font-weight: 700;
margin: 0 20rpx 20rpx;
gap: 18rpx;
box-shadow: 0 10rpx 24rpx rgba(216, 158, 58, 0.22);
}
.self-report-row {
@@ -734,22 +813,100 @@ export default {
.self-report-banner {
flex: 1;
border-radius: 40rpx;
min-width: 0;
border-radius: 28rpx;
background: linear-gradient(135deg, #33d0b3, #22c3a1);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
font-weight: 700;
gap: 14rpx;
box-shadow: 0 10rpx 24rpx rgba(22, 163, 74, 0.18);
margin-bottom: 20rpx;
padding: 24rpx 14rpx;
padding: 24rpx 20rpx;
}
.audit-icon {
width: 32rpx;
height: 32rpx;
.feature-icon {
flex-shrink: 0;
width: 68rpx;
height: 68rpx;
border-radius: 20rpx;
background: rgba(255, 255, 255, 0.22);
display: flex;
align-items: center;
justify-content: center;
}
.feature-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.feature-title {
font-size: 30rpx;
font-weight: 700;
line-height: 1.2;
white-space: nowrap;
}
.feature-desc {
font-size: 20rpx;
line-height: 1.2;
opacity: 0.85;
white-space: nowrap;
}
.audit-icon-wrap {
flex-shrink: 0;
width: 68rpx;
height: 68rpx;
border-radius: 20rpx;
background: rgba(255, 255, 255, 0.22);
display: flex;
align-items: center;
justify-content: center;
}
.audit-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.audit-title {
font-size: 30rpx;
font-weight: 700;
line-height: 1.2;
}
.audit-desc {
font-size: 20rpx;
line-height: 1.2;
opacity: 0.85;
}
.audit-right {
flex-shrink: 0;
display: flex;
align-items: center;
gap: 10rpx;
}
.audit-badge {
min-width: 44rpx;
height: 44rpx;
padding: 0 12rpx;
border-radius: 999rpx;
background: #ffffff;
color: #e0631a;
font-size: 24rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.quick-card {