feat: 配额优化

This commit is contained in:
2026-02-25 21:30:24 +08:00
parent 2e93211697
commit 79a5c1f3ed
29 changed files with 1225 additions and 489 deletions

View File

@@ -0,0 +1,100 @@
<template>
<span v-if="displayText" :class="['points-tag', `points-tag--${size}`, { 'points-tag--free': isFree }]">
<span v-if="showIcon" class="points-tag__icon"></span>
<span class="points-tag__text">{{ displayText }}</span>
</span>
</template>
<script setup>
import { computed } from 'vue'
import { usePointsConfigStore } from '@/stores/pointsConfig'
const props = defineProps({
// 模型代码
modelCode: {
type: String,
default: ''
},
// 直接传入积分数(优先级高于 modelCode
points: {
type: Number,
default: null
},
// 尺寸
size: {
type: String,
default: 'default', // small, default, large
validator: (value) => ['small', 'default', 'large'].includes(value)
},
// 是否显示图标
showIcon: {
type: Boolean,
default: false
}
})
const pointsConfigStore = usePointsConfigStore()
// 获取积分数
const consumePoints = computed(() => {
if (props.points !== null) {
return props.points
}
if (props.modelCode) {
return pointsConfigStore.getConsumePoints(props.modelCode)
}
return null
})
// 是否免费
const isFree = computed(() => consumePoints.value === 0)
// 显示文本
const displayText = computed(() => {
if (consumePoints.value === null) {
return ''
}
return pointsConfigStore.formatPoints(consumePoints.value)
})
</script>
<style scoped lang="less">
.points-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: 4px;
font-weight: 500;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
white-space: nowrap;
&--small {
padding: 1px 6px;
font-size: 11px;
}
&--default {
padding: 2px 8px;
font-size: 12px;
}
&--large {
padding: 4px 12px;
font-size: 14px;
}
&--free {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}
&__icon {
font-size: 0.9em;
}
&__text {
line-height: 1;
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<teleport to="body">
<transition name="drawer-fade">
<div v-if="visible" class="chat-overlay" @click.self="handleOverlayClick">
<div v-if="visible" class="chat-overlay" @click.self="handleClose">
<div class="chat-drawer" :class="{ 'chat-drawer--visible': visible }" @click.stop>
<!-- Background Effect -->
<div class="drawer-bg-pattern"></div>
@@ -104,7 +104,7 @@
@click="modelMode = 'standard'"
>
标准
<span class="model-cost">-10 积分</span>
<PointsTag :points="10" size="small" />
</button>
<button
class="model-tab pro"
@@ -113,7 +113,7 @@
>
<ThunderboltFilled />
深度
<span class="model-cost">-50 积分</span>
<PointsTag :points="50" size="small" />
</button>
</div>
</div>
@@ -157,7 +157,7 @@
</template>
<script setup>
import { ref, watch, nextTick } from 'vue'
import { ref, watch, nextTick, onMounted } from 'vue'
import {
CloseOutlined,
RobotOutlined,
@@ -173,6 +173,15 @@ import { message, Modal } from 'ant-design-vue'
import { sendChatStream } from '@/api/agent'
import { copyToClipboard } from '@/utils/clipboard'
import HistoryPanel from './HistoryPanel.vue'
import PointsTag from '@/components/PointsTag.vue'
import { usePointsConfigStore } from '@/stores/pointsConfig'
const pointsConfigStore = usePointsConfigStore()
// 在组件挂载时加载积分配置
onMounted(() => {
pointsConfigStore.loadConfig()
})
const props = defineProps({
visible: { type: Boolean, default: false },
@@ -232,8 +241,6 @@ const doClose = () => {
emit('update:visible', false)
}
const handleOverlayClick = () => handleClose()
const adjustTextareaHeight = () => {
const el = textareaRef.value
if (el) {