2026-04-28 11:34:07 +08:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import {
|
|
|
|
|
CalculatorOutlined,
|
|
|
|
|
FieldTimeOutlined,
|
|
|
|
|
QuestionCircleOutlined,
|
|
|
|
|
ReloadOutlined,
|
|
|
|
|
ThunderboltOutlined,
|
2026-05-01 20:39:09 +08:00
|
|
|
} from '@ant-design/icons-vue'
|
|
|
|
|
import type { AIPointUsageLog } from '@geo/shared-types'
|
|
|
|
|
import { useQuery } from '@tanstack/vue-query'
|
|
|
|
|
import type { TableColumnsType } from 'ant-design-vue'
|
|
|
|
|
import { computed, ref } from 'vue'
|
|
|
|
|
import { useI18n } from 'vue-i18n'
|
2026-04-28 11:34:07 +08:00
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
import { workspaceApi } from '@/lib/api'
|
|
|
|
|
import { formatDateTime } from '@/lib/display'
|
2026-04-28 11:34:07 +08:00
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
const { t } = useI18n()
|
|
|
|
|
const page = ref(1)
|
|
|
|
|
const pageSize = ref(10)
|
2026-04-28 11:34:07 +08:00
|
|
|
|
|
|
|
|
const quotaQuery = useQuery({
|
2026-05-01 20:39:09 +08:00
|
|
|
queryKey: ['workspace', 'quota-summary', 'ai-points'],
|
2026-04-28 11:34:07 +08:00
|
|
|
queryFn: () => workspaceApi.quotaSummary(),
|
2026-05-01 20:39:09 +08:00
|
|
|
})
|
2026-04-28 11:34:07 +08:00
|
|
|
|
|
|
|
|
const usageQuery = useQuery({
|
2026-05-01 20:39:09 +08:00
|
|
|
queryKey: computed(() => ['workspace', 'ai-point-usage', page.value, pageSize.value]),
|
2026-04-28 11:34:07 +08:00
|
|
|
queryFn: () => workspaceApi.aiPointUsage({ page: page.value, page_size: pageSize.value }),
|
2026-05-01 20:39:09 +08:00
|
|
|
})
|
2026-04-28 11:34:07 +08:00
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
const quotaSummary = computed(() => quotaQuery.data.value)
|
|
|
|
|
const usageRows = computed(() => usageQuery.data.value?.items ?? [])
|
|
|
|
|
const usageTotal = computed(() => usageQuery.data.value?.total ?? 0)
|
2026-04-28 11:34:07 +08:00
|
|
|
const aiPointUsedPercent = computed(() => {
|
2026-05-01 20:39:09 +08:00
|
|
|
const total = quotaSummary.value?.ai_points_total ?? 0
|
|
|
|
|
const used = quotaSummary.value?.ai_points_used ?? 0
|
2026-04-28 11:34:07 +08:00
|
|
|
if (total <= 0) {
|
2026-05-01 20:39:09 +08:00
|
|
|
return 0
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
2026-05-01 20:39:09 +08:00
|
|
|
return Math.min(100, Math.round((used / total) * 100))
|
|
|
|
|
})
|
|
|
|
|
const loading = computed(() => quotaQuery.isPending.value || usageQuery.isPending.value)
|
|
|
|
|
const refreshing = computed(() => quotaQuery.isFetching.value || usageQuery.isFetching.value)
|
2026-04-28 11:34:07 +08:00
|
|
|
|
|
|
|
|
const columns = computed<TableColumnsType<AIPointUsageLog>>(() => [
|
|
|
|
|
{
|
2026-05-01 20:39:09 +08:00
|
|
|
title: t('aiPoints.table.usageType'),
|
|
|
|
|
dataIndex: 'usage_type',
|
|
|
|
|
key: 'usage_type',
|
2026-04-28 11:34:07 +08:00
|
|
|
},
|
|
|
|
|
{
|
2026-05-01 20:39:09 +08:00
|
|
|
title: t('aiPoints.table.points'),
|
|
|
|
|
dataIndex: 'points',
|
|
|
|
|
key: 'points',
|
2026-05-13 15:59:39 +08:00
|
|
|
width: 188,
|
2026-05-01 20:39:09 +08:00
|
|
|
align: 'right',
|
2026-04-28 11:34:07 +08:00
|
|
|
},
|
|
|
|
|
{
|
2026-05-01 20:39:09 +08:00
|
|
|
title: t('aiPoints.table.requestChars'),
|
|
|
|
|
dataIndex: 'request_chars',
|
|
|
|
|
key: 'request_chars',
|
2026-04-28 11:34:07 +08:00
|
|
|
width: 150,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-05-01 20:39:09 +08:00
|
|
|
title: t('common.status'),
|
|
|
|
|
dataIndex: 'status',
|
|
|
|
|
key: 'status',
|
2026-04-28 11:34:07 +08:00
|
|
|
width: 118,
|
|
|
|
|
},
|
|
|
|
|
{
|
2026-05-01 20:39:09 +08:00
|
|
|
title: t('common.createdAt'),
|
|
|
|
|
dataIndex: 'created_at',
|
|
|
|
|
key: 'created_at',
|
2026-04-28 11:34:07 +08:00
|
|
|
width: 172,
|
|
|
|
|
},
|
2026-05-01 20:39:09 +08:00
|
|
|
])
|
2026-04-28 11:34:07 +08:00
|
|
|
|
|
|
|
|
function refresh(): void {
|
2026-05-01 20:39:09 +08:00
|
|
|
void quotaQuery.refetch()
|
|
|
|
|
void usageQuery.refetch()
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleTableChange(nextPage: number, nextPageSize: number): void {
|
2026-05-01 20:39:09 +08:00
|
|
|
page.value = nextPage
|
|
|
|
|
pageSize.value = nextPageSize
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getAIPointUsageTypeLabel(usageType: string): string {
|
2026-05-01 20:39:09 +08:00
|
|
|
const key = `shell.aiUsageTypes.${usageType}`
|
|
|
|
|
const label = t(key)
|
2026-05-13 15:59:39 +08:00
|
|
|
return label === key ? t('aiPoints.usageTypeFallback') : label
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getAIPointStatusMeta(status: string): { label: string; color: string } {
|
2026-05-01 20:39:09 +08:00
|
|
|
const key = `shell.aiUsageStatus.${status}`
|
|
|
|
|
const label = t(key)
|
|
|
|
|
const normalizedLabel = label === key ? status : label
|
2026-04-28 11:34:07 +08:00
|
|
|
switch (status) {
|
2026-05-01 20:39:09 +08:00
|
|
|
case 'completed':
|
|
|
|
|
return { label: normalizedLabel, color: 'success' }
|
|
|
|
|
case 'pending':
|
|
|
|
|
return { label: normalizedLabel, color: 'processing' }
|
|
|
|
|
case 'refunded':
|
|
|
|
|
return { label: normalizedLabel, color: 'default' }
|
2026-04-28 11:34:07 +08:00
|
|
|
default:
|
2026-05-01 20:39:09 +08:00
|
|
|
return { label: normalizedLabel, color: 'error' }
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 15:59:39 +08:00
|
|
|
function getAIPointStatusHelp(status: string): string | null {
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 'pending':
|
|
|
|
|
return t('shell.aiUsagePendingHelp')
|
|
|
|
|
case 'refunded':
|
|
|
|
|
return t('aiPoints.refundedHelp')
|
|
|
|
|
default:
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getAIPointAmountMeta(item: AIPointUsageLog): {
|
|
|
|
|
primary: string
|
|
|
|
|
detail: string
|
|
|
|
|
tone: 'charge' | 'refund' | 'neutral'
|
|
|
|
|
} {
|
|
|
|
|
switch (item.status) {
|
|
|
|
|
case 'refunded':
|
|
|
|
|
return {
|
|
|
|
|
primary: t('aiPoints.amount.netZero'),
|
|
|
|
|
detail: t('aiPoints.amount.refundedFlow', { points: item.points }),
|
|
|
|
|
tone: 'refund',
|
|
|
|
|
}
|
|
|
|
|
case 'pending':
|
|
|
|
|
return {
|
|
|
|
|
primary: `-${item.points}`,
|
|
|
|
|
detail: t('aiPoints.amount.pendingFlow', { points: item.points }),
|
|
|
|
|
tone: 'charge',
|
|
|
|
|
}
|
|
|
|
|
case 'completed':
|
|
|
|
|
return {
|
|
|
|
|
primary: `-${item.points}`,
|
|
|
|
|
detail: t('aiPoints.amount.completedFlow', { points: item.points }),
|
|
|
|
|
tone: 'charge',
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return {
|
|
|
|
|
primary: t('aiPoints.amount.zero'),
|
|
|
|
|
detail: t('aiPoints.amount.failedFlow'),
|
|
|
|
|
tone: 'neutral',
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-04-28 18:35:58 +08:00
|
|
|
<div class="ai-points-view">
|
|
|
|
|
<section class="ai-points-view__top-card">
|
|
|
|
|
<div class="ai-points-view__header">
|
|
|
|
|
<div class="ai-points-view__header-title">
|
2026-05-01 20:39:09 +08:00
|
|
|
<h2>{{ t('route.aiPoints.title') }}</h2>
|
|
|
|
|
<p>{{ t('route.aiPoints.description') }}</p>
|
2026-04-28 18:35:58 +08:00
|
|
|
</div>
|
|
|
|
|
<div class="ai-points-view__header-actions">
|
|
|
|
|
<a-button :loading="refreshing" @click="refresh">
|
|
|
|
|
<template #icon><ReloadOutlined /></template>
|
2026-05-01 20:39:09 +08:00
|
|
|
{{ t('common.refresh') }}
|
2026-04-28 18:35:58 +08:00
|
|
|
</a-button>
|
|
|
|
|
</div>
|
2026-04-28 11:34:07 +08:00
|
|
|
</div>
|
|
|
|
|
|
2026-05-01 20:39:09 +08:00
|
|
|
<a-divider style="margin: 0; border-color: #f0f0f0" />
|
2026-04-28 11:34:07 +08:00
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
<div class="ai-points-view__summary">
|
|
|
|
|
<div class="ai-points-balance">
|
2026-05-01 20:39:09 +08:00
|
|
|
<span class="summary-label">{{ t('aiPoints.balance') }}</span>
|
|
|
|
|
<strong>{{ quotaSummary?.ai_points_balance ?? '--' }}</strong>
|
2026-04-28 18:35:58 +08:00
|
|
|
<a-progress :percent="aiPointUsedPercent" :show-info="false" stroke-color="#1677ff" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="summary-metric">
|
|
|
|
|
<div class="summary-metric-icon">
|
|
|
|
|
<ThunderboltOutlined />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="summary-metric-content">
|
2026-05-01 20:39:09 +08:00
|
|
|
<span>{{ t('common.used') }}</span>
|
|
|
|
|
<strong>
|
|
|
|
|
{{ quotaSummary?.ai_points_used ?? '--' }} /
|
|
|
|
|
{{ quotaSummary?.ai_points_total ?? '--' }}
|
|
|
|
|
</strong>
|
2026-04-28 18:35:58 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="summary-metric">
|
|
|
|
|
<div class="summary-metric-icon">
|
|
|
|
|
<CalculatorOutlined />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="summary-metric-content">
|
2026-05-01 20:39:09 +08:00
|
|
|
<span>{{ t('aiPoints.baseChars') }}</span>
|
|
|
|
|
<strong>{{ quotaSummary?.ai_point_base_chars ?? '--' }}</strong>
|
2026-04-28 18:35:58 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-28 11:34:07 +08:00
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
<div class="summary-metric">
|
|
|
|
|
<div class="summary-metric-icon">
|
|
|
|
|
<FieldTimeOutlined />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="summary-metric-content">
|
2026-05-01 20:39:09 +08:00
|
|
|
<span>{{ t('shell.resetAt') }}</span>
|
2026-04-28 18:35:58 +08:00
|
|
|
<strong>{{ formatDateTime(quotaSummary?.ai_points_reset_at) }}</strong>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-04-28 11:34:07 +08:00
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
<section class="ai-points-view__table-card">
|
|
|
|
|
<div class="ai-points-view__table-head">
|
2026-04-28 11:34:07 +08:00
|
|
|
<div>
|
2026-05-01 20:39:09 +08:00
|
|
|
<p class="eyebrow">{{ t('aiPoints.ledgerTitle') }}</p>
|
|
|
|
|
<h3>{{ t('aiPoints.ledgerTotal', { total: usageTotal }) }}</h3>
|
2026-04-28 11:34:07 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<a-table
|
|
|
|
|
:columns="columns"
|
|
|
|
|
:data-source="usageRows"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:locale="{ emptyText: t('shell.aiUsageEmpty') }"
|
|
|
|
|
:pagination="{
|
|
|
|
|
current: page,
|
|
|
|
|
pageSize,
|
|
|
|
|
total: usageTotal,
|
|
|
|
|
showSizeChanger: true,
|
|
|
|
|
onChange: handleTableChange,
|
|
|
|
|
onShowSizeChange: handleTableChange,
|
|
|
|
|
}"
|
|
|
|
|
row-key="id"
|
|
|
|
|
>
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
<template v-if="column.key === 'usage_type'">
|
|
|
|
|
<div class="usage-type-cell">
|
|
|
|
|
<strong>{{ getAIPointUsageTypeLabel(record.usage_type) }}</strong>
|
2026-05-20 21:59:36 +08:00
|
|
|
<span v-if="record.error_message" class="usage-error">
|
2026-05-01 20:39:09 +08:00
|
|
|
{{ record.error_message }}
|
|
|
|
|
</span>
|
2026-04-28 11:34:07 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-else-if="column.key === 'points'">
|
2026-06-08 11:56:18 +08:00
|
|
|
<div class="points-cell" :class="`points-cell--${getAIPointAmountMeta(record).tone}`">
|
2026-05-13 15:59:39 +08:00
|
|
|
<strong>{{ getAIPointAmountMeta(record).primary }}</strong>
|
|
|
|
|
<span>{{ getAIPointAmountMeta(record).detail }}</span>
|
|
|
|
|
</div>
|
2026-04-28 11:34:07 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-else-if="column.key === 'request_chars'">
|
|
|
|
|
<span class="usage-rule">
|
2026-05-01 20:39:09 +08:00
|
|
|
{{ t('shell.aiUsageRule', { chars: record.request_chars, base: record.base_chars }) }}
|
2026-04-28 11:34:07 +08:00
|
|
|
</span>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-else-if="column.key === 'status'">
|
2026-05-01 20:39:09 +08:00
|
|
|
<a-tag
|
|
|
|
|
class="status-tag"
|
|
|
|
|
:color="getAIPointStatusMeta(record.status).color"
|
|
|
|
|
:bordered="false"
|
|
|
|
|
>
|
2026-04-28 11:34:07 +08:00
|
|
|
{{ getAIPointStatusMeta(record.status).label }}
|
2026-05-13 15:59:39 +08:00
|
|
|
<a-tooltip
|
|
|
|
|
v-if="getAIPointStatusHelp(record.status)"
|
|
|
|
|
:title="getAIPointStatusHelp(record.status)"
|
|
|
|
|
>
|
2026-04-28 11:34:07 +08:00
|
|
|
<QuestionCircleOutlined class="status-help-icon" />
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</a-tag>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<template v-else-if="column.key === 'created_at'">
|
|
|
|
|
{{ formatDateTime(record.created_at) }}
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</a-table>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 22px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__top-card {
|
|
|
|
|
background: #fff;
|
|
|
|
|
border: 1px solid #e6edf5;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ai-points-view__header {
|
2026-04-28 11:34:07 +08:00
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
2026-04-28 18:35:58 +08:00
|
|
|
align-items: flex-start;
|
|
|
|
|
padding: 24px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__header-title h2 {
|
2026-04-28 11:34:07 +08:00
|
|
|
margin: 0;
|
2026-04-28 18:35:58 +08:00
|
|
|
font-size: 20px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
line-height: 1.4;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__header-title p {
|
|
|
|
|
margin: 6px 0 0 0;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: #8c8c8c;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__header-actions {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 12px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__summary {
|
2026-04-28 11:34:07 +08:00
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: minmax(260px, 1.5fr) repeat(3, minmax(160px, 1fr));
|
2026-04-28 18:35:58 +08:00
|
|
|
gap: 16px;
|
|
|
|
|
padding: 24px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ai-points-balance,
|
2026-04-28 18:35:58 +08:00
|
|
|
.summary-metric {
|
|
|
|
|
border: 1px solid #e6edf5;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: #fafcff;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ai-points-balance {
|
2026-04-28 18:35:58 +08:00
|
|
|
padding: 20px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.summary-label,
|
|
|
|
|
.summary-metric span,
|
|
|
|
|
.usage-type-cell span,
|
|
|
|
|
.usage-rule {
|
2026-04-28 18:35:58 +08:00
|
|
|
color: #8c8c8c;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ai-points-balance strong {
|
|
|
|
|
display: block;
|
2026-04-28 18:35:58 +08:00
|
|
|
margin: 8px 0 16px;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
font-size: 32px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1.2;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.summary-metric {
|
2026-04-28 18:35:58 +08:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
padding: 20px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.summary-metric-icon {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 48px;
|
|
|
|
|
height: 48px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
background: #e6f4ff;
|
2026-04-28 11:34:07 +08:00
|
|
|
color: #1677ff;
|
2026-04-28 18:35:58 +08:00
|
|
|
font-size: 20px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.summary-metric-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 4px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.summary-metric-content span {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
line-height: 1.4;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.summary-metric-content strong {
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
line-height: 1.2;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__table-card {
|
|
|
|
|
padding: 24px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border: 1px solid #e6edf5;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ai-points-view__table-head {
|
2026-04-28 11:34:07 +08:00
|
|
|
display: flex;
|
2026-04-28 18:35:58 +08:00
|
|
|
align-items: flex-start;
|
2026-04-28 11:34:07 +08:00
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 16px;
|
2026-04-28 18:35:58 +08:00
|
|
|
margin-bottom: 18px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__table-head h3 {
|
|
|
|
|
margin: 6px 0 0;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: #1a1a1a;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.usage-type-cell {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 3px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.usage-type-cell strong {
|
2026-04-28 18:35:58 +08:00
|
|
|
color: #1a1a1a;
|
|
|
|
|
font-weight: 600;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.usage-type-cell span,
|
|
|
|
|
.usage-rule {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.usage-error {
|
|
|
|
|
color: #cf1322 !important;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 15:59:39 +08:00
|
|
|
.points-cell {
|
|
|
|
|
display: grid;
|
|
|
|
|
gap: 2px;
|
|
|
|
|
justify-items: end;
|
|
|
|
|
min-width: 132px;
|
|
|
|
|
line-height: 1.35;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.points-cell strong {
|
2026-04-28 11:34:07 +08:00
|
|
|
color: #cf1322;
|
2026-04-28 18:35:58 +08:00
|
|
|
font-weight: 600;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-13 15:59:39 +08:00
|
|
|
.points-cell span {
|
|
|
|
|
color: #8c8c8c;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.points-cell--refund strong {
|
|
|
|
|
color: #595959;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.points-cell--refund span {
|
2026-04-28 11:34:07 +08:00
|
|
|
color: #389e0d;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-13 15:59:39 +08:00
|
|
|
.points-cell--neutral strong {
|
|
|
|
|
color: #595959;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-28 11:34:07 +08:00
|
|
|
.status-tag {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-help-icon {
|
|
|
|
|
color: #1677ff;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
cursor: help;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 1100px) {
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__summary {
|
2026-04-28 11:34:07 +08:00
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 760px) {
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__header {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-28 18:35:58 +08:00
|
|
|
.ai-points-view__summary {
|
2026-04-28 11:34:07 +08:00
|
|
|
grid-template-columns: 1fr;
|
|
|
|
|
}
|
2026-04-28 18:35:58 +08:00
|
|
|
|
|
|
|
|
.ai-points-view__table-head {
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
}
|
2026-04-28 11:34:07 +08:00
|
|
|
}
|
|
|
|
|
</style>
|