初始版本
This commit is contained in:
658
app/pages/console/coupons.vue
Normal file
658
app/pages/console/coupons.vue
Normal file
@@ -0,0 +1,658 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<a-page-header title="优惠券" sub-title="可用优惠与使用记录">
|
||||
<template #extra>
|
||||
<a-input
|
||||
v-model:value="codeInput"
|
||||
placeholder="输入兑换码"
|
||||
class="w-48"
|
||||
allow-clear
|
||||
>
|
||||
<template #suffix>
|
||||
<a-button type="link" size="small" style="padding: 0" :loading="redeeming" @click="handleRedeem">
|
||||
兑换
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-page-header>
|
||||
|
||||
<!-- 统计概要 -->
|
||||
<a-row :gutter="[16, 16]">
|
||||
<a-col :xs="8" :md="4" v-for="stat in stats" :key="stat.label">
|
||||
<div class="mini-stat" :class="stat.color">
|
||||
<div class="mini-stat-value">{{ stat.value }}</div>
|
||||
<div class="mini-stat-label">{{ stat.label }}</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<a-card :bordered="false" class="card">
|
||||
<a-spin :spinning="loading">
|
||||
<!-- Tab 切换 -->
|
||||
<a-tabs v-model:activeKey="activeTab" @change="onTabChange">
|
||||
<a-tab-pane key="available" tab="可用优惠券" />
|
||||
<a-tab-pane key="used" tab="已使用" />
|
||||
<a-tab-pane key="expired" tab="已过期" />
|
||||
</a-tabs>
|
||||
|
||||
<!-- 可用优惠券列表 -->
|
||||
<div v-if="activeTab === 'available'" class="coupon-list">
|
||||
<div v-if="availableCoupons.length === 0" class="coupon-empty">
|
||||
<a-empty description="暂无可用优惠券">
|
||||
<template #image>
|
||||
<div class="empty-icon">🎫</div>
|
||||
</template>
|
||||
</a-empty>
|
||||
</div>
|
||||
<div v-else class="coupon-grid">
|
||||
<div
|
||||
v-for="coupon in availableCoupons"
|
||||
:key="coupon.id"
|
||||
class="coupon-card"
|
||||
:class="coupon.typeClass"
|
||||
>
|
||||
<div class="coupon-left">
|
||||
<div class="coupon-amount">
|
||||
<span class="coupon-prefix">{{ coupon.discountType === 'percent' ? '' : '¥' }}</span>
|
||||
<span class="coupon-number">{{ coupon.amount }}</span>
|
||||
<span class="coupon-suffix" v-if="coupon.discountType === 'percent'">%</span>
|
||||
</div>
|
||||
<div class="coupon-condition">{{ coupon.condition }}</div>
|
||||
</div>
|
||||
<div class="coupon-divider">
|
||||
<div class="divider-circle top" />
|
||||
<div class="divider-line" />
|
||||
<div class="divider-circle bottom" />
|
||||
</div>
|
||||
<div class="coupon-right">
|
||||
<div class="coupon-name">{{ coupon.name }}</div>
|
||||
<div class="coupon-scope">{{ coupon.scope }}</div>
|
||||
<div class="coupon-expire">
|
||||
<ClockCircleOutlined style="font-size: 11px; margin-right: 3px" />
|
||||
{{ coupon.expireText }}
|
||||
</div>
|
||||
<a-button
|
||||
size="small"
|
||||
class="coupon-use-btn"
|
||||
@click="handleUse(coupon)"
|
||||
>
|
||||
立即使用
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已使用列表 -->
|
||||
<div v-if="activeTab === 'used'" class="coupon-list">
|
||||
<div v-if="usedCoupons.length === 0" class="coupon-empty">
|
||||
<a-empty description="暂无使用记录" />
|
||||
</div>
|
||||
<a-table
|
||||
v-else
|
||||
:data-source="usedCoupons"
|
||||
:pagination="false"
|
||||
size="middle"
|
||||
:row-key="(r: any) => r.id"
|
||||
>
|
||||
<a-table-column title="优惠券" key="name" width="200">
|
||||
<template #default="{ record }">
|
||||
<span class="used-name">{{ record.name }}</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="面额" key="amount" width="120">
|
||||
<template #default="{ record }">
|
||||
<span class="used-amount">
|
||||
{{ record.discountType === 'percent' ? `${record.amount}%` : `¥${record.amount}` }}
|
||||
</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="适用范围" key="scope" width="180">
|
||||
<template #default="{ record }">
|
||||
{{ record.scope }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="使用时间" key="usedAt" width="180">
|
||||
<template #default="{ record }">
|
||||
{{ record.usedAt || '-' }}
|
||||
</template>
|
||||
</a-table-column>
|
||||
<a-table-column title="关联订单" key="orderNo" width="200">
|
||||
<template #default="{ record }">
|
||||
<span v-if="record.orderNo" class="text-gray-500">{{ record.orderNo }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</a-table-column>
|
||||
</a-table>
|
||||
</div>
|
||||
|
||||
<!-- 已过期列表 -->
|
||||
<div v-if="activeTab === 'expired'" class="coupon-list">
|
||||
<div v-if="expiredCoupons.length === 0" class="coupon-empty">
|
||||
<a-empty description="暂无过期优惠券" />
|
||||
</div>
|
||||
<div v-else class="expired-grid">
|
||||
<div v-for="coupon in expiredCoupons" :key="coupon.id" class="expired-card">
|
||||
<div class="expired-left">
|
||||
<div class="expired-amount">
|
||||
{{ coupon.discountType === 'percent' ? `${coupon.amount}%` : `¥${coupon.amount}` }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="expired-right">
|
||||
<div class="expired-name">{{ coupon.name }}</div>
|
||||
<div class="expired-reason">已过期 · {{ coupon.expireText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 引导提示 -->
|
||||
<div v-if="activeTab === 'available'" class="guide-banner mt-4">
|
||||
<div class="guide-icon">🎁</div>
|
||||
<div class="guide-text">
|
||||
<div class="guide-title">还没有优惠券?</div>
|
||||
<div class="guide-desc">参与活动、充值会员或关注公众号获取更多优惠</div>
|
||||
</div>
|
||||
<a-button type="link" @click="navigateTo('/market')">
|
||||
前往应用商店
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ClockCircleOutlined, GiftOutlined } from '@ant-design/icons-vue'
|
||||
import { getUserInfo } from '@/api/layout'
|
||||
import { pageShopUserCoupon, listShopUserCoupon, addShopUserCoupon } from '@/api/shop/shopUserCoupon'
|
||||
import { listShopCoupon } from '@/api/shop/shopCoupon'
|
||||
import type { ShopUserCoupon } from '@/api/shop/shopUserCoupon/model'
|
||||
import type { ShopCoupon } from '@/api/shop/shopCoupon/model'
|
||||
|
||||
definePageMeta({ layout: 'console' })
|
||||
|
||||
// ─── 状态 ────────────────────────────────────────────────────
|
||||
const loading = ref(false)
|
||||
const activeTab = ref('available')
|
||||
const codeInput = ref('')
|
||||
const redeeming = ref(false)
|
||||
const currentUserId = ref<number | null>(null)
|
||||
|
||||
// ─── 优惠券类型映射 ──────────────────────────────────────────
|
||||
const TYPE_MAP: Record<number, { discountType: 'fixed' | 'percent'; amount: number; typeClass: string }> = {
|
||||
10: { discountType: 'fixed', amount: 0, typeClass: '' }, // 满减券
|
||||
20: { discountType: 'percent', amount: 0, typeClass: 'blue' }, // 折扣券
|
||||
30: { discountType: 'fixed', amount: 0, typeClass: 'green' }, // 免费券
|
||||
}
|
||||
|
||||
const SCOPE_MAP: Record<number, string> = {
|
||||
10: '全部商品',
|
||||
20: '指定商品',
|
||||
30: '指定分类',
|
||||
}
|
||||
|
||||
// ─── 数据转换 ────────────────────────────────────────────────
|
||||
function transformCoupon(raw: ShopUserCoupon) {
|
||||
const typeInfo = TYPE_MAP[raw.type || 10] || TYPE_MAP[10]
|
||||
let amount = typeInfo.amount
|
||||
if (raw.type === 10 && raw.reducePrice) {
|
||||
amount = Number(raw.reducePrice)
|
||||
} else if (raw.type === 20 && raw.discount) {
|
||||
amount = raw.discount
|
||||
}
|
||||
|
||||
const minPrice = raw.minPrice ? Number(raw.minPrice) : 0
|
||||
const condition = minPrice > 0 ? `满${minPrice}元可用` : '无门槛'
|
||||
|
||||
const scope = SCOPE_MAP[raw.applyRange || 10] || '全部商品'
|
||||
const expireText = formatExpire(raw.startTime, raw.endTime)
|
||||
|
||||
return {
|
||||
id: String(raw.id || ''),
|
||||
name: raw.name || '优惠券',
|
||||
amount,
|
||||
discountType: typeInfo.discountType,
|
||||
condition,
|
||||
scope,
|
||||
expireText,
|
||||
typeClass: typeInfo.typeClass,
|
||||
usedAt: raw.useTime || '',
|
||||
orderNo: raw.orderNo || '',
|
||||
status: raw.status as number,
|
||||
}
|
||||
}
|
||||
|
||||
function formatExpire(start?: string, end?: string) {
|
||||
if (!end) return '永久有效'
|
||||
const e = new Date(end)
|
||||
const now = new Date()
|
||||
const diff = e.getTime() - now.getTime()
|
||||
const days = Math.ceil(diff / 86400000)
|
||||
if (days <= 0) return '已过期'
|
||||
if (days <= 7) return `${days}天后过期`
|
||||
if (days <= 30) return `${Math.ceil(days / 7)}周后过期`
|
||||
return end.replace(/T.*/, '')
|
||||
}
|
||||
|
||||
// ─── 数据列表 ────────────────────────────────────────────────
|
||||
const allCoupons = ref<ReturnType<typeof transformCoupon>[]>([])
|
||||
|
||||
const availableCoupons = computed(() => allCoupons.value.filter(c => c.status === 0))
|
||||
const usedCoupons = computed(() => allCoupons.value.filter(c => c.status === 1))
|
||||
const expiredCoupons = computed(() => allCoupons.value.filter(c => c.status === 2))
|
||||
|
||||
const stats = computed(() => [
|
||||
{ label: '可用', value: String(availableCoupons.value.length), color: 'green' },
|
||||
{ label: '已使用', value: String(usedCoupons.value.length), color: 'blue' },
|
||||
{ label: '已过期', value: String(expiredCoupons.value.length), color: 'gray' },
|
||||
])
|
||||
|
||||
// ─── 加载数据 ────────────────────────────────────────────────
|
||||
async function ensureUser() {
|
||||
if (currentUserId.value) return
|
||||
try {
|
||||
const user = await getUserInfo()
|
||||
currentUserId.value = user.userId ?? null
|
||||
} catch {
|
||||
// fallback to localStorage
|
||||
if (import.meta.client) {
|
||||
const uid = localStorage.getItem('UserId')
|
||||
if (uid) currentUserId.value = Number(uid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCoupons() {
|
||||
loading.value = true
|
||||
try {
|
||||
await ensureUser()
|
||||
const userId = currentUserId.value
|
||||
if (!userId) {
|
||||
message.warning('请先登录')
|
||||
return
|
||||
}
|
||||
|
||||
const res = await pageShopUserCoupon({
|
||||
userId,
|
||||
page: 1,
|
||||
limit: 200,
|
||||
})
|
||||
const list = res?.list || []
|
||||
allCoupons.value = list.map(transformCoupon)
|
||||
} catch (e) {
|
||||
console.error('加载优惠券失败', e)
|
||||
// 不阻塞页面,显示空状态即可
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onTabChange() {
|
||||
// Tab 切换不需要重新加载,数据已全部获取
|
||||
}
|
||||
|
||||
// ─── 兑换功能 ────────────────────────────────────────────────
|
||||
async function handleRedeem() {
|
||||
const code = codeInput.value?.trim()
|
||||
if (!code) {
|
||||
message.warning('请输入兑换码')
|
||||
return
|
||||
}
|
||||
|
||||
redeeming.value = true
|
||||
try {
|
||||
await ensureUser()
|
||||
const userId = currentUserId.value
|
||||
|
||||
// 通过兑换码查找对应优惠券模板
|
||||
const coupons = await listShopCoupon({ keywords: code })
|
||||
if (!coupons || coupons.length === 0) {
|
||||
message.error('兑换码无效,请检查后重试')
|
||||
return
|
||||
}
|
||||
|
||||
const couponTemplate = coupons[0]
|
||||
if (couponTemplate.status === 1 || couponTemplate.enabled === '0') {
|
||||
message.error('该优惠券已停用')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否已领完
|
||||
if (couponTemplate.totalCount !== -1 && couponTemplate.issuedCount !== undefined && couponTemplate.issuedCount >= couponTemplate.totalCount) {
|
||||
message.error('该优惠券已被领完')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否已领取过
|
||||
if (couponTemplate.limitPerUser !== -1) {
|
||||
const myCoupons = await listShopUserCoupon({ userId: userId!, keywords: couponTemplate.name })
|
||||
if (myCoupons && myCoupons.length >= (couponTemplate.limitPerUser || 1)) {
|
||||
message.warning('您已领取过该优惠券,每人限领 ' + couponTemplate.limitPerUser + ' 张')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 领取优惠券
|
||||
await addShopUserCoupon({
|
||||
couponId: couponTemplate.id,
|
||||
userId: userId!,
|
||||
name: couponTemplate.name,
|
||||
description: couponTemplate.description,
|
||||
type: couponTemplate.type,
|
||||
reducePrice: couponTemplate.reducePrice,
|
||||
discount: couponTemplate.discount,
|
||||
minPrice: couponTemplate.minPrice,
|
||||
applyRange: couponTemplate.applyRange,
|
||||
applyRangeConfig: couponTemplate.applyRangeConfig,
|
||||
startTime: couponTemplate.startTime as string | undefined,
|
||||
endTime: couponTemplate.endTime as string | undefined,
|
||||
status: 0,
|
||||
obtainType: 10, // 主动领取
|
||||
obtainSource: '兑换码领取',
|
||||
})
|
||||
|
||||
message.success('兑换成功!')
|
||||
codeInput.value = ''
|
||||
await loadCoupons()
|
||||
} catch (e) {
|
||||
console.error('兑换失败', e)
|
||||
message.error(e instanceof Error ? e.message : '兑换失败,请稍后重试')
|
||||
} finally {
|
||||
redeeming.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 使用优惠券 ──────────────────────────────────────────────
|
||||
function handleUse(coupon: { scope: string }) {
|
||||
navigateTo('/market')
|
||||
}
|
||||
|
||||
// ─── 初始化 ──────────────────────────────────────────────────
|
||||
onMounted(() => {
|
||||
loadCoupons()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* 迷你统计 */
|
||||
.mini-stat {
|
||||
padding: 14px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid transparent;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mini-stat.green { background: #f0fdf4; border-color: #bbf7d0; }
|
||||
.mini-stat.blue { background: #eff6ff; border-color: #dbeafe; }
|
||||
.mini-stat.gray { background: #f9fafb; border-color: #e5e7eb; }
|
||||
|
||||
.mini-stat-value {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.mini-stat-label {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* 列表容器 */
|
||||
.coupon-list { min-height: 200px; }
|
||||
|
||||
.coupon-empty {
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 优惠券卡片 */
|
||||
.coupon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.coupon-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.coupon-card {
|
||||
display: flex;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
border: 1px solid #f0f0f0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.coupon-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 左侧金额区 */
|
||||
.coupon-left {
|
||||
width: 120px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16px 12px;
|
||||
background: linear-gradient(135deg, #f5f3ff, #ede9fe);
|
||||
}
|
||||
|
||||
.coupon-card.blue .coupon-left {
|
||||
background: linear-gradient(135deg, #eff6ff, #dbeafe);
|
||||
}
|
||||
|
||||
.coupon-card.green .coupon-left {
|
||||
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||
}
|
||||
|
||||
.coupon-card.orange .coupon-left {
|
||||
background: linear-gradient(135deg, #fff7ed, #fed7aa);
|
||||
}
|
||||
|
||||
.coupon-amount {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.coupon-prefix {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
.coupon-card.blue .coupon-prefix { color: #3b82f6; }
|
||||
.coupon-card.green .coupon-prefix { color: #16a34a; }
|
||||
.coupon-card.orange .coupon-prefix { color: #ea580c; }
|
||||
|
||||
.coupon-number {
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
.coupon-card.blue .coupon-number { color: #3b82f6; }
|
||||
.coupon-card.green .coupon-number { color: #16a34a; }
|
||||
.coupon-card.orange .coupon-number { color: #ea580c; }
|
||||
|
||||
.coupon-suffix {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
.coupon-card.blue .coupon-suffix { color: #3b82f6; }
|
||||
.coupon-card.green .coupon-suffix { color: #16a34a; }
|
||||
.coupon-card.orange .coupon-suffix { color: #ea580c; }
|
||||
|
||||
.coupon-condition {
|
||||
font-size: 11px;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.coupon-divider {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0 2px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.divider-circle {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: #f9fafb;
|
||||
border: 1px solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.divider-circle.top { margin-bottom: -8px; z-index: 1; }
|
||||
.divider-circle.bottom { margin-top: -8px; z-index: 1; }
|
||||
|
||||
.divider-line {
|
||||
width: 1px;
|
||||
flex: 1;
|
||||
border-left: 1px dashed #e0e0e0;
|
||||
}
|
||||
|
||||
/* 右侧信息 */
|
||||
.coupon-right {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.coupon-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.coupon-scope {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.coupon-expire {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.35);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.coupon-use-btn {
|
||||
align-self: flex-start;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 已使用列表 */
|
||||
.used-name {
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.used-amount {
|
||||
font-weight: 600;
|
||||
color: #4f46e5;
|
||||
}
|
||||
|
||||
/* 已过期卡片 */
|
||||
.expired-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.expired-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 14px 16px;
|
||||
border-radius: 10px;
|
||||
background: #fafafa;
|
||||
border: 1px solid #f0f0f0;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.expired-left {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #9ca3af;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.expired-right { flex: 1; min-width: 0; }
|
||||
|
||||
.expired-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.expired-reason {
|
||||
font-size: 11px;
|
||||
color: rgba(0, 0, 0, 0.35);
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
/* 引导横幅 */
|
||||
.guide-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 18px 20px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #faf5ff, #f5f3ff);
|
||||
border: 1px solid #ede9fe;
|
||||
}
|
||||
|
||||
.guide-icon {
|
||||
font-size: 32px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.guide-text { flex: 1; }
|
||||
|
||||
.guide-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
|
||||
.guide-desc {
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user