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 用例全绿)
这个提交包含在:
@@ -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');
|
||||
});
|
||||
});
|
||||
在新工单中引用
屏蔽一个用户