fix(ui): 优惠券页/消息页主题配色跟随全局主题切换

- coupons.vue / notifications.vue 由写死深色改为复用全局 CSS 变量(--bg-primary/--bg-surface/--color-brand-gradient/--text-*/--border-subtle/--nav-bg),随 theme-warm(暖阳晨曦)与默认冷光暗夜自动适配
- 大神工作台新增的服务包表单样式同步复用主题变量
- 新增回归护栏 theme-consistency.spec.ts(97 套件/673 用例全绿)
这个提交包含在:
xcbyy
2026-10-19 15:27:04 +08:00
父节点 b70a24327b
当前提交 ae569d0ca6
共有 4 个文件被更改,包括 112 次插入 和 54 次删除

查看文件

@@ -2166,7 +2166,7 @@ const switchBackToPlayer = () => {
}
.srv-op {
font-size: 24rpx;
color: #6366F1;
color: var(--color-brand, #6366F1);
padding: 4rpx 8rpx;
}
.srv-op-danger {
@@ -2185,7 +2185,7 @@ const switchBackToPlayer = () => {
.form-label {
width: 180rpx;
font-size: 26rpx;
color: #94A3B8;
color: var(--text-secondary, #94A3B8);
flex-shrink: 0;
}
.form-input {
@@ -2193,8 +2193,8 @@ const switchBackToPlayer = () => {
height: 68rpx;
padding: 0 16rpx;
border-radius: 12rpx;
background: rgba(255, 255, 255, 0.06);
color: #E2E8F0;
background: var(--bg-surface-elevated, rgba(255, 255, 255, 0.06));
color: var(--text-primary, #E2E8F0);
font-size: 26rpx;
}
.form-textarea {
@@ -2202,14 +2202,14 @@ const switchBackToPlayer = () => {
min-height: 120rpx;
padding: 12rpx 16rpx;
border-radius: 12rpx;
background: rgba(255, 255, 255, 0.06);
color: #E2E8F0;
background: var(--bg-surface-elevated, rgba(255, 255, 255, 0.06));
color: var(--text-primary, #E2E8F0);
font-size: 26rpx;
}
.form-hint {
display: block;
font-size: 22rpx;
color: #64748B;
color: var(--text-muted, #64748B);
line-height: 1.6;
}
</style>

查看文件

@@ -34,10 +34,10 @@
<view class="coupon-body">
<!-- 券中心 -->
<block v-if="activeTab === 'center'">
<view v-if="isLoading" class="empty-tip"><text>加载中…</text></view>
<view v-if="isLoading" class="empty-tip"><text class="empty-text">加载中…</text></view>
<view v-else-if="centerList.length === 0" class="empty-tip">
<text class="empty-title">暂无可领取优惠券</text>
<text>平台会不定期发放新人券与品类券,敬请关注</text>
<text class="empty-text">平台会不定期发放新人券与品类券,敬请关注</text>
</view>
<view v-else class="coupon-list">
<view v-for="c in centerList" :key="c.id" class="coupon-card">
@@ -74,7 +74,7 @@
</view>
<view v-if="mineList.length === 0" class="empty-tip">
<text class="empty-title">暂无优惠券</text>
<text>去「券中心」领取平台优惠券吧</text>
<text class="empty-text">去「券中心」领取平台优惠券吧</text>
</view>
<view v-else class="coupon-list">
<view
@@ -142,8 +142,12 @@ const formatDate = (v) => {
};
const loadCenter = async () => {
const list = await getMarketingCoupons();
centerList.value = Array.isArray(list) ? list : [];
try {
const list = await getMarketingCoupons();
centerList.value = Array.isArray(list) ? list : [];
} catch (_) {
centerList.value = [];
}
};
const loadMine = async () => {
@@ -152,17 +156,19 @@ const loadMine = async () => {
mineList.value = [];
return;
}
const res = await getMyCoupons(uid);
const all = Array.isArray(res?.list) ? res.list : [];
mineList.value = all.filter((x) => x.status === mineFilter.value);
try {
const res = await getMyCoupons(uid);
const all = Array.isArray(res?.list) ? res.list : [];
mineList.value = all.filter((x) => x.status === mineFilter.value);
} catch (_) {
mineList.value = [];
}
};
const reloadAll = async () => {
isLoading.value = true;
try {
await Promise.all([loadCenter(), loadMine()]);
} catch (_) {
// 忽略网络异常
} finally {
isLoading.value = false;
isRefreshing.value = false;
@@ -214,15 +220,16 @@ onShow(() => {
</script>
<style scoped>
/* 颜色全部取自全局主题变量(App.vue 中按 theme-warm 覆盖),随主题切换自动适配 */
.coupon-page {
min-height: 100vh;
background: #0a0f1d;
background: var(--bg-primary, #0a0f1d);
}
.custom-nav-bar {
position: sticky;
top: 0;
z-index: 50;
background: rgba(10, 15, 29, 0.92);
background: var(--nav-bg, #0a0f1d);
padding-top: var(--safe-header-gap, 0);
}
.nav-content {
@@ -247,7 +254,7 @@ onShow(() => {
text-align: center;
}
.nav-title {
color: #fff;
color: var(--text-primary, #fff);
font-size: 17px;
font-weight: 600;
}
@@ -257,7 +264,7 @@ onShow(() => {
.tab-bar {
display: flex;
gap: 12px;
padding: 8px 16px 0;
padding: 10px 16px 0;
}
.tab-item {
flex: 1;
@@ -266,13 +273,15 @@ onShow(() => {
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.06);
background: var(--bg-surface-elevated, #1e2942);
border: 1px solid var(--border-subtle, #1e293b);
}
.tab-item.active {
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
background: var(--color-brand-gradient, linear-gradient(135deg, #6366f1, #8b5cf6));
border-color: transparent;
}
.tab-text {
color: #cbd5e1;
color: var(--text-secondary, #cbd5e1);
font-size: 13px;
font-weight: 600;
}
@@ -280,7 +289,7 @@ onShow(() => {
color: #fff;
}
.coupon-scroll {
height: calc(100vh - 88px - var(--safe-header-gap, 0px));
height: calc(100vh - 90px - var(--safe-header-gap, 0px));
}
.coupon-body {
padding: 12px 16px 40px;
@@ -291,11 +300,11 @@ onShow(() => {
margin-bottom: 10px;
}
.filter-item {
color: #7d89a8;
color: var(--text-muted, #7d89a8);
font-size: 13px;
}
.filter-item.active {
color: #8b9cff;
color: var(--color-brand, #8b9cff);
font-weight: 600;
}
.coupon-list {
@@ -309,7 +318,8 @@ onShow(() => {
gap: 12px;
padding: 14px;
border-radius: 14px;
background: linear-gradient(135deg, #1b2340 0%, #141a30 100%);
background: var(--bg-surface, #141a30);
border: 1px solid var(--border-subtle, #1e293b);
}
.coupon-card.used {
opacity: 0.55;
@@ -323,12 +333,12 @@ onShow(() => {
flex-shrink: 0;
}
.amount-num {
color: #ffd166;
color: var(--color-brand, #ffd166);
font-size: 20px;
font-weight: 700;
}
.amount-sub {
color: #8d99b8;
color: var(--text-muted, #8d99b8);
font-size: 10px;
margin-top: 2px;
}
@@ -340,23 +350,24 @@ onShow(() => {
gap: 4px;
}
.coupon-name {
color: #fff;
color: var(--text-primary, #fff);
font-size: 14px;
font-weight: 600;
}
.coupon-desc {
color: #94a3b8;
color: var(--text-secondary, #94a3b8);
font-size: 12px;
}
.coupon-extra {
color: #6d7997;
color: var(--text-muted, #6d7997);
font-size: 11px;
}
.coupon-btn {
flex-shrink: 0;
padding: 7px 12px;
border-radius: 16px;
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
background: var(--color-brand-gradient, linear-gradient(135deg, #6366f1, #8b5cf6));
box-shadow: var(--shadow-brand, none);
}
.coupon-btn text {
color: #fff;
@@ -364,16 +375,17 @@ onShow(() => {
font-weight: 600;
}
.coupon-btn.disabled {
background: rgba(255, 255, 255, 0.12);
background: var(--bg-surface-elevated, rgba(255, 255, 255, 0.12));
box-shadow: none;
}
.coupon-btn.disabled text {
color: #8d99b8;
color: var(--text-muted, #8d99b8);
}
.coupon-state {
flex-shrink: 0;
}
.coupon-state text {
color: #8d99b8;
color: var(--text-muted, #8d99b8);
font-size: 12px;
}
.empty-tip {
@@ -384,12 +396,12 @@ onShow(() => {
gap: 8px;
}
.empty-title {
color: #e6eaf5;
color: var(--text-primary, #e6eaf5);
font-size: 15px;
font-weight: 600;
}
.empty-tip text {
color: #7d89a8;
.empty-text {
color: var(--text-muted, #7d89a8);
font-size: 12px;
text-align: center;
line-height: 1.6;

查看文件

@@ -169,15 +169,16 @@ onShow(() => {
</script>
<style scoped>
/* 颜色全部取自全局主题变量(App.vue 按 theme-warm 覆盖),随主题切换自动适配 */
.notif-page {
min-height: 100vh;
background: #0a0f1d;
background: var(--bg-primary, #0a0f1d);
}
.custom-nav-bar {
position: sticky;
top: 0;
z-index: 50;
background: rgba(10, 15, 29, 0.92);
background: var(--nav-bg, #0a0f1d);
padding-top: var(--safe-header-gap, 0);
}
.nav-content {
@@ -202,7 +203,7 @@ onShow(() => {
text-align: center;
}
.nav-title {
color: #fff;
color: var(--text-primary, #fff);
font-size: 17px;
font-weight: 600;
}
@@ -211,7 +212,7 @@ onShow(() => {
text-align: right;
}
.nav-action-text {
color: #8b9cff;
color: var(--color-brand, #8b9cff);
font-size: 13px;
}
.nav-capsule-placeholder {
@@ -226,19 +227,20 @@ onShow(() => {
.notif-summary {
padding: 16px;
border-radius: 16px;
background: linear-gradient(135deg, #1b2340 0%, #141a30 100%);
background: var(--bg-surface, #141a30);
border: 1px solid var(--border-subtle, #1e293b);
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 14px;
}
.summary-title {
color: #fff;
color: var(--text-primary, #fff);
font-size: 16px;
font-weight: 700;
}
.summary-sub {
color: #9aa7c7;
color: var(--text-secondary, #9aa7c7);
font-size: 12px;
line-height: 1.5;
}
@@ -250,12 +252,12 @@ onShow(() => {
gap: 8px;
}
.empty-title {
color: #e6eaf5;
color: var(--text-primary, #e6eaf5);
font-size: 15px;
font-weight: 600;
}
.empty-text {
color: #7d89a8;
color: var(--text-muted, #7d89a8);
font-size: 12px;
text-align: center;
line-height: 1.6;
@@ -269,13 +271,14 @@ onShow(() => {
position: relative;
padding: 14px 14px 14px 18px;
border-radius: 14px;
background: #141a30;
background: var(--bg-surface, #141a30);
border: 1px solid var(--border-subtle, #1e293b);
display: flex;
align-items: flex-start;
gap: 10px;
}
.notif-card.is-unread {
background: #182143;
background: var(--bg-surface-elevated, #182143);
}
.notif-dot {
width: 8px;
@@ -295,19 +298,19 @@ onShow(() => {
gap: 8px;
}
.notif-title {
color: #fff;
color: var(--text-primary, #fff);
font-size: 14px;
font-weight: 600;
}
.notif-time {
color: #6d7997;
color: var(--text-muted, #6d7997);
font-size: 11px;
flex-shrink: 0;
}
.notif-content {
display: block;
margin-top: 6px;
color: #a8b3d1;
color: var(--text-secondary, #a8b3d1);
font-size: 12px;
line-height: 1.6;
}

查看文件

@@ -0,0 +1,43 @@
// 已读取AGENTS.md
import * as fs from 'fs';
import * as path from 'path';
/**
* 第八十一批:新页面必须复用全局主题变量(随 theme-warm/theme-cool 切换自适应)。
* 背景:优惠券页/消息页初版写死深色,切到「暖阳晨曦」主题时不跟随,配色不一致。
*/
describe('第八十一批主题一致性闸门', () => {
const root = path.resolve(__dirname, '../../');
const read = (p: string) => fs.readFileSync(path.join(root, p), 'utf-8');
const coupons = read('pwuniapp/pages/user/coupons.vue');
const notifications = read('pwuniapp/pages/user/notifications.vue');
const workspace = read('pwuniapp/pages/expert/workspace.vue');
it('优惠券页使用全局主题变量而非写死配色', () => {
for (const v of ['var(--bg-primary', 'var(--bg-surface', 'var(--color-brand-gradient', 'var(--text-primary', 'var(--text-secondary', 'var(--text-muted', 'var(--border-subtle', 'var(--nav-bg']) {
expect(coupons).toContain(v);
}
expect(coupons).toContain("'theme-' + currentThemeKey");
});
it('消息通知页使用全局主题变量', () => {
for (const v of ['var(--bg-primary', 'var(--bg-surface', 'var(--color-brand', 'var(--text-primary', 'var(--text-muted', 'var(--border-subtle', 'var(--nav-bg']) {
expect(notifications).toContain(v);
}
expect(notifications).toContain("'theme-' + currentThemeKey");
});
it('大神工作台新增表单样式复用主题变量', () => {
expect(workspace).toContain('var(--bg-surface-elevated');
expect(workspace).toContain('var(--color-brand');
expect(workspace).toContain('var(--text-secondary');
expect(workspace).toContain('var(--text-primary');
});
it('全局主题变量在 App.vue 中按主题定义', () => {
const app = read('pwuniapp/App.vue');
expect(app).toContain(':root, page {');
expect(app).toContain('.theme-warm {');
expect(app).toContain('--color-brand-gradient');
});
});