Merge remote-tracking branch 'origin/ai-hub' into ai-hub
This commit is contained in:
@@ -13,13 +13,14 @@ public class PremiumPackageManager : DomainService
|
|||||||
{
|
{
|
||||||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot, Guid> _premiumPackageRepository;
|
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot, Guid> _premiumPackageRepository;
|
||||||
private readonly ILogger<PremiumPackageManager> _logger;
|
private readonly ILogger<PremiumPackageManager> _logger;
|
||||||
|
private readonly ISqlSugarRepository<AiRechargeAggregateRoot> _rechargeRepository;
|
||||||
public PremiumPackageManager(
|
public PremiumPackageManager(
|
||||||
ISqlSugarRepository<PremiumPackageAggregateRoot, Guid> premiumPackageRepository,
|
ISqlSugarRepository<PremiumPackageAggregateRoot, Guid> premiumPackageRepository,
|
||||||
ILogger<PremiumPackageManager> logger)
|
ILogger<PremiumPackageManager> logger, ISqlSugarRepository<AiRechargeAggregateRoot> rechargeRepository)
|
||||||
{
|
{
|
||||||
_premiumPackageRepository = premiumPackageRepository;
|
_premiumPackageRepository = premiumPackageRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_rechargeRepository = rechargeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -57,6 +58,20 @@ public class PremiumPackageManager : DomainService
|
|||||||
|
|
||||||
await _premiumPackageRepository.InsertAsync(premiumPackage);
|
await _premiumPackageRepository.InsertAsync(premiumPackage);
|
||||||
|
|
||||||
|
// 创建充值记录
|
||||||
|
var rechargeRecord = new AiRechargeAggregateRoot
|
||||||
|
{
|
||||||
|
UserId = userId,
|
||||||
|
RechargeAmount = totalAmount,
|
||||||
|
Content = packageName,
|
||||||
|
ExpireDateTime = premiumPackage.ExpireDateTime,
|
||||||
|
Remark = "自助充值",
|
||||||
|
ContactInfo = null
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存充值记录到数据库
|
||||||
|
await _rechargeRepository.InsertAsync(rechargeRecord);
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
$"用户 {userId} 购买尊享包成功: {packageName}, Token数量: {tokenAmount}, 金额: {totalAmount}");
|
$"用户 {userId} 购买尊享包成功: {packageName}, Token数量: {tokenAmount}, 金额: {totalAmount}");
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ namespace Yi.Framework.Bbs.Domain.Entities.Integral
|
|||||||
[SugarTable("SignIn")]
|
[SugarTable("SignIn")]
|
||||||
|
|
||||||
[SugarIndex($"index_{nameof(CreatorId)}", nameof(CreatorId), OrderByType.Asc)]
|
[SugarIndex($"index_{nameof(CreatorId)}", nameof(CreatorId), OrderByType.Asc)]
|
||||||
public class SignInAggregateRoot : AggregateRoot<Guid>, ICreationAuditedObject
|
public class
|
||||||
|
SignInAggregateRoot : AggregateRoot<Guid>, ICreationAuditedObject
|
||||||
{
|
{
|
||||||
|
|
||||||
[SugarColumn(IsPrimaryKey = true)]
|
[SugarColumn(IsPrimaryKey = true)]
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ async function onReLogin() {
|
|||||||
}
|
}
|
||||||
function handleThirdPartyLogin(type: any) {
|
function handleThirdPartyLogin(type: any) {
|
||||||
const redirectUri = encodeURIComponent(`${window.location.origin}/chat`);
|
const redirectUri = encodeURIComponent(`${window.location.origin}/chat`);
|
||||||
console.log('cccc', type);
|
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
`${SSO_SEVER_URL}/login?client_id=${type}&redirect_uri=${redirectUri}`,
|
`${SSO_SEVER_URL}/login?client_id=${type}&redirect_uri=${redirectUri}`,
|
||||||
'SSOLogin',
|
'SSOLogin',
|
||||||
@@ -149,7 +148,6 @@ function handleLoginAgainYi() {
|
|||||||
&& event.data.type === 'SSO_LOGIN_SUCCESS'
|
&& event.data.type === 'SSO_LOGIN_SUCCESS'
|
||||||
&& !isHandled) {
|
&& !isHandled) {
|
||||||
isHandled = true;
|
isHandled = true;
|
||||||
console.log('111');
|
|
||||||
try {
|
try {
|
||||||
// 清理监听
|
// 清理监听
|
||||||
window.removeEventListener('message', messageHandler);
|
window.removeEventListener('message', messageHandler);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
<!-- 切换模型 -->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { GetSessionListVO } from '@/api/model/types';
|
import type { GetSessionListVO } from '@/api/model/types';
|
||||||
import { Lock } from '@element-plus/icons-vue';
|
import { Lock } from '@element-plus/icons-vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import Popover from '@/components/Popover/index.vue';
|
import Popover from '@/components/Popover/index.vue';
|
||||||
|
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
import { useModelStore } from '@/stores/modules/model';
|
import { useModelStore } from '@/stores/modules/model';
|
||||||
@@ -10,6 +12,7 @@ import { showProductPackage } from '@/utils/product-package.ts';
|
|||||||
import { isUserVip } from '@/utils/user';
|
import { isUserVip } from '@/utils/user';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
// 检查模型是否可用
|
// 检查模型是否可用
|
||||||
@@ -19,14 +22,21 @@ function isModelAvailable(item: GetSessionListVO) {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await modelStore.requestModelList();
|
await modelStore.requestModelList();
|
||||||
if (modelStore.modelList.length > 0 && !modelStore.currentModelInfo?.modelId) {
|
// 设置默认模型
|
||||||
|
if (
|
||||||
|
modelStore.modelList.length > 0
|
||||||
|
&& (!modelStore.currentModelInfo || !modelStore.currentModelInfo.modelId)
|
||||||
|
) {
|
||||||
modelStore.setCurrentModelInfo(modelStore.modelList[0]);
|
modelStore.setCurrentModelInfo(modelStore.modelList[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const currentModelName = computed(() => modelStore.currentModelInfo?.modelName);
|
const currentModelName = computed(
|
||||||
|
() => modelStore.currentModelInfo && modelStore.currentModelInfo.modelName,
|
||||||
|
);
|
||||||
const popoverList = computed(() => modelStore.modelList);
|
const popoverList = computed(() => modelStore.modelList);
|
||||||
|
|
||||||
|
/* 弹出面板 开始 */
|
||||||
const popoverStyle = ref({
|
const popoverStyle = ref({
|
||||||
width: '200px',
|
width: '200px',
|
||||||
padding: '4px',
|
padding: '4px',
|
||||||
@@ -34,33 +44,38 @@ const popoverStyle = ref({
|
|||||||
background: 'var(--el-bg-color, #fff)',
|
background: 'var(--el-bg-color, #fff)',
|
||||||
border: '1px solid var(--el-border-color-light)',
|
border: '1px solid var(--el-border-color-light)',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
boxShadow: '0 2px 12px rgba(0,0,0,0.1)',
|
boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
|
||||||
});
|
});
|
||||||
const popoverRef = ref();
|
const popoverRef = ref();
|
||||||
|
|
||||||
|
// 显示
|
||||||
async function showPopover() {
|
async function showPopover() {
|
||||||
|
// 获取最新的模型列表
|
||||||
await modelStore.requestModelList();
|
await modelStore.requestModelList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------
|
// 点击
|
||||||
模型点击逻辑
|
// 处理模型点击
|
||||||
-------------------------------- */
|
|
||||||
function handleModelClick(item: GetSessionListVO) {
|
function handleModelClick(item: GetSessionListVO) {
|
||||||
if (!isModelAvailable(item)) {
|
if (!isModelAvailable(item)) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
`
|
`
|
||||||
<div class="text-center leading-relaxed">
|
<div class="text-center leading-relaxed">
|
||||||
<h3 class="text-lg font-bold mb-3">${isUserVip() ? 'YiXinAI-VIP 会员' : '成为 YiXinAI-VIP'}</h3>
|
<h3 class="text-lg font-bold mb-3">${isUserVip() ? 'YiXinAI-VIP 会员' : '成为 YiXinAI-VIP'}</h3>
|
||||||
<p class="mb-2">
|
<p class="mb-2">
|
||||||
${isUserVip()
|
${
|
||||||
|
isUserVip()
|
||||||
? '您已是尊贵会员,享受全部 AI 模型与专属服务。感谢支持!'
|
? '您已是尊贵会员,享受全部 AI 模型与专属服务。感谢支持!'
|
||||||
: '解锁所有 AI 模型,无限加速,专属客服,尽享尊贵体验。'}
|
: '解锁所有 AI 模型,无限加速,专属客服,尽享尊贵体验。'
|
||||||
</p>
|
}
|
||||||
<p class="text-sm text-gray-500">
|
</p>
|
||||||
${isUserVip() ? '您可随时访问产品页面查看更多特权内容。' : '请点击右上角登录按钮,登录后进行购买!'}
|
${
|
||||||
</p>
|
isUserVip()
|
||||||
</div>
|
? '<p class="text-sm text-gray-500">您可随时访问产品页面查看更多特权内容。</p>'
|
||||||
`,
|
: '<p class="text-sm text-gray-500">请点击右上角登录按钮,登录后进行购买!</p>'
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
isUserVip() ? '会员状态' : '会员尊享',
|
isUserVip() ? '会员状态' : '会员尊享',
|
||||||
{
|
{
|
||||||
confirmButtonText: '产品查看',
|
confirmButtonText: '产品查看',
|
||||||
@@ -70,7 +85,18 @@ function handleModelClick(item: GetSessionListVO) {
|
|||||||
center: true,
|
center: true,
|
||||||
roundButton: true,
|
roundButton: true,
|
||||||
},
|
},
|
||||||
).then(() => showProductPackage());
|
)
|
||||||
|
.then(() => {
|
||||||
|
showProductPackage();
|
||||||
|
|
||||||
|
// router.push({
|
||||||
|
// name: 'products', // 使用命名路由
|
||||||
|
// query: { from: isUserVip() ? 'vip' : 'user' }, // 可选:添加来源标识
|
||||||
|
// });
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 点击右上角关闭或“关闭”按钮,不执行任何操作
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
modelStore.setCurrentModelInfo(item);
|
modelStore.setCurrentModelInfo(item);
|
||||||
@@ -83,8 +109,8 @@ function handleModelClick(item: GetSessionListVO) {
|
|||||||
规则2:金色光泽(VIP/付费)
|
规则2:金色光泽(VIP/付费)
|
||||||
规则3:彩色流光(尊享/高级)
|
规则3:彩色流光(尊享/高级)
|
||||||
-------------------------------- */
|
-------------------------------- */
|
||||||
function getModelStyleClass(item: GetSessionListVO) {
|
function getModelStyleClass(modelName: any) {
|
||||||
const name = item.modelName.toLowerCase();
|
const name = modelName.toLowerCase();
|
||||||
|
|
||||||
// 规则3:彩色流光
|
// 规则3:彩色流光
|
||||||
if (name.includes('claude-sonnet-4-5-20250929')) {
|
if (name.includes('claude-sonnet-4-5-20250929')) {
|
||||||
@@ -123,7 +149,7 @@ function getWrapperClass(item: GetSessionListVO) {
|
|||||||
? 'hover:scale-[1.03] hover:shadow-[0_0_8px_rgba(0,0,0,0.1)] hover:border-gray-300'
|
? 'hover:scale-[1.03] hover:shadow-[0_0_8px_rgba(0,0,0,0.1)] hover:border-gray-300'
|
||||||
: 'opacity-60 cursor-not-allowed',
|
: 'opacity-60 cursor-not-allowed',
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-2 border-blue-700 shadow-[0_0_10px_rgba(29,78,216,0.6)]'
|
? 'border-2 border-blue-700 shadow-[0_0_10px_rgba(29,78,216,1)]'
|
||||||
: 'border border-transparent cursor-pointer',
|
: 'border border-transparent cursor-pointer',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -135,33 +161,55 @@ function getWrapperClass(item: GetSessionListVO) {
|
|||||||
ref="popoverRef"
|
ref="popoverRef"
|
||||||
placement="top-start"
|
placement="top-start"
|
||||||
:offset="[4, 0]"
|
:offset="[4, 0]"
|
||||||
|
popover-class="popover-content"
|
||||||
:popover-style="popoverStyle"
|
:popover-style="popoverStyle"
|
||||||
trigger="clickTarget"
|
trigger="clickTarget"
|
||||||
@show="showPopover"
|
@show="showPopover"
|
||||||
>
|
>
|
||||||
|
<!-- 触发元素插槽 -->
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-1 p-2 rounded-md border border-blue-500 text-blue-600 cursor-pointer select-none model-select-box "
|
class="model-select-box select-none flex items-center gap-4px p-10px rounded-10px cursor-pointer font-size-12px border-[rgba()] leading-snug"
|
||||||
>
|
>
|
||||||
<SvgIcon name="models" size="12" />
|
<div class="model-select-box-icon">
|
||||||
<span class="text-sm font-medium">{{ currentModelName }}</span>
|
<SvgIcon name="models" size="12" />
|
||||||
|
</div>
|
||||||
|
<div :class="getModelStyleClass(currentModelName)" class="model-select-box-text font-size-12px">
|
||||||
|
{{ currentModelName }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="flex flex-col gap-1 max-h-100 overflow-y-auto p-1">
|
<div class="popover-content-box">
|
||||||
<div
|
<div
|
||||||
v-for="item in popoverList"
|
v-for="item in popoverList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:class="getWrapperClass(item)"
|
:class="getWrapperClass(item)"
|
||||||
@click="handleModelClick(item)"
|
@click="handleModelClick(item)"
|
||||||
>
|
>
|
||||||
<span :class="getModelStyleClass(item)">
|
<Popover
|
||||||
{{ item.modelName }}
|
trigger-class="popover-trigger-item-text"
|
||||||
</span>
|
popover-class="rounded-tooltip"
|
||||||
|
placement="right"
|
||||||
|
trigger="hover"
|
||||||
|
:offset="[12, 0]"
|
||||||
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<span :class="getModelStyleClass(item.modelName)">
|
||||||
|
{{ item.modelName }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
class="popover-content-box-item-text text-wrap max-w-200px rounded-lg p-8px font-size-12px line-height-tight"
|
||||||
|
>
|
||||||
|
{{ item.remark }}
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<!-- VIP锁定图标 -->
|
||||||
<el-icon
|
<el-icon
|
||||||
v-if="!isModelAvailable(item)"
|
v-if="!isModelAvailable(item)"
|
||||||
class="absolute right-1 top-1/2 -translate-y-1/2 text-gray-400"
|
class="absolute right-1 top-1/2 transform -translate-y-1/2"
|
||||||
>
|
>
|
||||||
<Lock />
|
<Lock />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@@ -171,7 +219,7 @@ function getWrapperClass(item: GetSessionListVO) {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped lang="scss">
|
||||||
.model-select-box {
|
.model-select-box {
|
||||||
color: var(--el-color-primary, #409eff);
|
color: var(--el-color-primary, #409eff);
|
||||||
background: var(--el-color-primary-light-9, rgb(235.9 245.3 255));
|
background: var(--el-color-primary-light-9, rgb(235.9 245.3 255));
|
||||||
@@ -179,6 +227,35 @@ function getWrapperClass(item: GetSessionListVO) {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popover-content-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
height: 200px;
|
||||||
|
overflow: hidden auto;
|
||||||
|
|
||||||
|
:deep(.popover-trigger-item-text) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-box-item-text {
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 滚动条样式
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: #cccccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 彩色流光动画 */
|
/* 彩色流光动画 */
|
||||||
@keyframes gradientFlow {
|
@keyframes gradientFlow {
|
||||||
0%, 100% { background-position: 0 50%; }
|
0%, 100% { background-position: 0 50%; }
|
||||||
|
|||||||
@@ -18,21 +18,23 @@ const packageData = ref<any>({
|
|||||||
const usagePercent = computed(() => {
|
const usagePercent = computed(() => {
|
||||||
if (packageData.value.totalQuota === 0)
|
if (packageData.value.totalQuota === 0)
|
||||||
return 0;
|
return 0;
|
||||||
return Math.round((packageData.value.usedQuota / packageData.value.totalQuota) * 100);
|
return Number(((packageData.value.usedQuota / packageData.value.totalQuota) * 100).toFixed(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
const remainingPercent = computed(() => {
|
const remainingPercent = computed(() => {
|
||||||
return 100 - usagePercent.value;
|
if (packageData.value.totalQuota === 0)
|
||||||
|
return 0;
|
||||||
|
return Number(((packageData.value.remainingQuota / packageData.value.totalQuota) * 100).toFixed(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取进度条颜色
|
// 获取进度条颜色(基于剩余百分比)
|
||||||
const progressColor = computed(() => {
|
const progressColor = computed(() => {
|
||||||
const percent = usagePercent.value;
|
const percent = remainingPercent.value;
|
||||||
if (percent >= 90)
|
if (percent <= 10)
|
||||||
return '#f56c6c'; // 红色
|
return '#f56c6c'; // 红色 - 剩余很少
|
||||||
if (percent >= 70)
|
if (percent <= 30)
|
||||||
return '#e6a23c'; // 橙色
|
return '#e6a23c'; // 橙色 - 剩余较少
|
||||||
return '#67c23a'; // 绿色
|
return '#67c23a'; // 绿色 - 剩余充足
|
||||||
});
|
});
|
||||||
|
|
||||||
// 格式化数字 - 转换为万为单位
|
// 格式化数字 - 转换为万为单位
|
||||||
@@ -194,14 +196,14 @@ function onProductPackage() {
|
|||||||
<!-- 进度条 -->
|
<!-- 进度条 -->
|
||||||
<div class="progress-section">
|
<div class="progress-section">
|
||||||
<div class="progress-header">
|
<div class="progress-header">
|
||||||
<span class="progress-label">使用进度</span>
|
<span class="progress-label">剩余进度</span>
|
||||||
<span class="progress-percent" :style="{ color: progressColor }">
|
<span class="progress-percent" :style="{ color: progressColor }">
|
||||||
{{ usagePercent }}%
|
{{ remainingPercent }}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-progress
|
<el-progress
|
||||||
:percentage="usagePercent"
|
:percentage="remainingPercent"
|
||||||
:color="progressColor"
|
:color="progressColor"
|
||||||
:stroke-width="20"
|
:stroke-width="20"
|
||||||
:show-text="false"
|
:show-text="false"
|
||||||
@@ -210,11 +212,11 @@ function onProductPackage() {
|
|||||||
<div class="progress-legend">
|
<div class="progress-legend">
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot " :style="{ background: progressColor }" />
|
<span class="legend-dot " :style="{ background: progressColor }" />
|
||||||
<span class="legend-text">已使用: {{ usagePercent }}%</span>
|
<span class="legend-text">剩余: {{ remainingPercent }}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot remaining-dot" />
|
<span class="legend-dot used-dot" />
|
||||||
<span class="legend-text">剩余: {{ remainingPercent }}%</span>
|
<span class="legend-text">已使用: {{ usagePercent }}%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ onMounted(() => {
|
|||||||
>
|
>
|
||||||
<h3 class="text-lg font-bold mb-3">
|
<h3 class="text-lg font-bold mb-3">
|
||||||
请扫码加入微信交流群<br>
|
请扫码加入微信交流群<br>
|
||||||
备注ai获取专属客服支持
|
备注“AI”获取专属客服支持
|
||||||
</h3>
|
</h3>
|
||||||
<div class="mb-4 flex items-center justify-center space-x-2">
|
<div class="mb-4 flex items-center justify-center space-x-2">
|
||||||
<span class="font-semibold">站长微信账号:</span>
|
<span class="font-semibold">站长微信账号:</span>
|
||||||
@@ -182,15 +182,15 @@ onMounted(() => {
|
|||||||
@click="showWechatFullscreenImage"
|
@click="showWechatFullscreenImage"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4">
|
<!-- <div class="px-4"> -->
|
||||||
<h4>微信交流群</h4>
|
<!-- <h4>微信交流群</h4> -->
|
||||||
<img
|
<!-- <img -->
|
||||||
:src="wxGroupQD"
|
<!-- :src="wxGroupQD" -->
|
||||||
class="w-50 py-5 h-70 border border-gray-200 rounded-lg shadow-md cursor-pointer hover:shadow-lg transition-transform hover:scale-105"
|
<!-- class="w-50 py-5 h-70 border border-gray-200 rounded-lg shadow-md cursor-pointer hover:shadow-lg transition-transform hover:scale-105" -->
|
||||||
alt="微信二维码"
|
<!-- alt="微信二维码" -->
|
||||||
@click="showWxGroupFullscreenImage"
|
<!-- @click="showWxGroupFullscreenImage" -->
|
||||||
>
|
<!-- > -->
|
||||||
</div>
|
<!-- </div> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 全屏放大二维码 -->
|
<!-- 全屏放大二维码 -->
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
// 从store获取模型列表
|
// 从store获取模型列表
|
||||||
const modelStore = useModelStore();
|
const modelStore = useModelStore();
|
||||||
const modelList = computed(() => modelStore.modelList);
|
const modelList = computed(() => modelStore.modelList);
|
||||||
console.log('modelList---', modelList);
|
|
||||||
|
|
||||||
// 计算网格布局的列数
|
// 计算网格布局的列数
|
||||||
const gridTemplateColumns = computed(() => {
|
const gridTemplateColumns = computed(() => {
|
||||||
|
|||||||
@@ -74,12 +74,11 @@ function openDialog() {
|
|||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
}
|
}
|
||||||
function handleConfirm(activeNav: string) {
|
function handleConfirm(activeNav: string) {
|
||||||
console.log('确认操作,当前导航:', activeNav);
|
|
||||||
ElMessage.success('操作成功');
|
ElMessage.success('操作成功');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导航切换
|
||||||
function handleNavChange(nav: string) {
|
function handleNavChange(nav: string) {
|
||||||
console.log('导航切换:', nav);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击
|
// 点击
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ const debouncedSend = useDebounceFn(async () => {
|
|||||||
});
|
});
|
||||||
senderValue.value = ''; // 清空输入框
|
senderValue.value = ''; // 清空输入框
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error: any) {
|
||||||
console.error('发送消息失败:', error);
|
console.error('发送消息失败:', error);
|
||||||
ElMessage.error('发送消息失败,请重试');
|
ElMessage.error(error);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
isSending.value = false;
|
isSending.value = false;
|
||||||
|
|||||||
@@ -46,10 +46,13 @@ const inputValue = ref('');
|
|||||||
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
|
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
|
||||||
const bubbleItems = ref<MessageItem[]>([]);
|
const bubbleItems = ref<MessageItem[]>([]);
|
||||||
const bubbleListRef = ref<BubbleListInstance | null>(null);
|
const bubbleListRef = ref<BubbleListInstance | null>(null);
|
||||||
|
const isSending = ref(false);
|
||||||
|
|
||||||
const { stream, loading: isLoading, cancel } = useHookFetch({
|
const { stream, loading: isLoading, cancel } = useHookFetch({
|
||||||
request: send,
|
request: send,
|
||||||
onError: async (error) => {
|
onError: async (error) => {
|
||||||
|
isLoading.value = false;
|
||||||
|
|
||||||
if (error.status === 403) {
|
if (error.status === 403) {
|
||||||
const data = await (error.response.json());
|
const data = await (error.response.json());
|
||||||
// 弹窗提示
|
// 弹窗提示
|
||||||
@@ -168,12 +171,13 @@ function handleDataChunk(chunk: AnyObject) {
|
|||||||
function handleError(err: any) {
|
function handleError(err: any) {
|
||||||
console.error('Fetch error:', err);
|
console.error('Fetch error:', err);
|
||||||
}
|
}
|
||||||
const isSending = ref(false);
|
|
||||||
|
|
||||||
async function startSSE(chatContent: string) {
|
async function startSSE(chatContent: string) {
|
||||||
if (isSending.value)
|
if (isSending.value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
isSending.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 清空输入框
|
// 清空输入框
|
||||||
inputValue.value = '';
|
inputValue.value = '';
|
||||||
@@ -205,14 +209,20 @@ async function startSSE(chatContent: string) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
handleError(err); // 其他错误
|
handleError(err); // 其他错误
|
||||||
|
// ElMessage.error('消息发送失败,请重试');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
isSending.value = false;
|
isSending.value = false;
|
||||||
|
|
||||||
// 停止打字器状态
|
// 停止打字器状态和加载状态
|
||||||
if (bubbleItems.value.length) {
|
if (bubbleItems.value.length) {
|
||||||
bubbleItems.value[bubbleItems.value.length - 1].typing = false;
|
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||||
|
latest.typing = false;
|
||||||
|
latest.loading = false;
|
||||||
|
if (latest.thinkingStatus === 'thinking') {
|
||||||
|
latest.thinkingStatus = 'end';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,8 +231,14 @@ async function startSSE(chatContent: string) {
|
|||||||
async function cancelSSE() {
|
async function cancelSSE() {
|
||||||
try {
|
try {
|
||||||
cancel(); // 直接调用,无需参数
|
cancel(); // 直接调用,无需参数
|
||||||
|
isSending.value = false;
|
||||||
if (bubbleItems.value.length) {
|
if (bubbleItems.value.length) {
|
||||||
bubbleItems.value[bubbleItems.value.length - 1].typing = false;
|
const latest = bubbleItems.value[bubbleItems.value.length - 1];
|
||||||
|
latest.typing = false;
|
||||||
|
latest.loading = false;
|
||||||
|
if (latest.thinkingStatus === 'thinking') {
|
||||||
|
latest.thinkingStatus = 'end';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -254,7 +270,6 @@ function addMessage(message: string, isUser: boolean) {
|
|||||||
|
|
||||||
// 展开收起 事件展示
|
// 展开收起 事件展示
|
||||||
function handleChange(payload: { value: boolean; status: ThinkingStatus }) {
|
function handleChange(payload: { value: boolean; status: ThinkingStatus }) {
|
||||||
console.log('value', payload.value, 'status', payload.status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteCard(_item: FilesCardProps, index: number) {
|
function handleDeleteCard(_item: FilesCardProps, index: number) {
|
||||||
@@ -279,7 +294,6 @@ watch(
|
|||||||
|
|
||||||
// 复制
|
// 复制
|
||||||
function copy(item: any) {
|
function copy(item: any) {
|
||||||
console.log('复制', item);
|
|
||||||
navigator.clipboard.writeText(item.content || '')
|
navigator.clipboard.writeText(item.content || '')
|
||||||
.then(() => ElMessage.success('已复制到剪贴板'))
|
.then(() => ElMessage.success('已复制到剪贴板'))
|
||||||
.catch(() => ElMessage.error('复制失败'));
|
.catch(() => ElMessage.error('复制失败'));
|
||||||
@@ -312,7 +326,7 @@ function copy(item: any) {
|
|||||||
<div class="footer-container">
|
<div class="footer-container">
|
||||||
<div class="footer-time">
|
<div class="footer-time">
|
||||||
<span v-if="item.creationTime "> {{ item.creationTime }}</span>
|
<span v-if="item.creationTime "> {{ item.creationTime }}</span>
|
||||||
<span style="margin-left: 10px;" v-if="((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) " class="footer-token">
|
<span v-if="((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) " style="margin-left: 10px;" class="footer-token">
|
||||||
{{ ((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) ? `token:${item?.tokenUsage?.total}` : '' }}</span>
|
{{ ((item.role === 'ai' || item.role === 'assistant') && item?.tokenUsage?.total) ? `token:${item?.tokenUsage?.total}` : '' }}</span>
|
||||||
<el-button icon="DocumentCopy" size="small" circle @click="copy(item)" />
|
<el-button icon="DocumentCopy" size="small" circle @click="copy(item)" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ async function onReLogin() {
|
|||||||
}
|
}
|
||||||
function handleThirdPartyLogin(type: any) {
|
function handleThirdPartyLogin(type: any) {
|
||||||
const redirectUri = encodeURIComponent(`${window.location.origin}/chat`);
|
const redirectUri = encodeURIComponent(`${window.location.origin}/chat`);
|
||||||
console.log('cccc', type);
|
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
`${SSO_SEVER_URL}/login?client_id=${type}&redirect_uri=${redirectUri}`,
|
`${SSO_SEVER_URL}/login?client_id=${type}&redirect_uri=${redirectUri}`,
|
||||||
'SSOLogin',
|
'SSOLogin',
|
||||||
|
|||||||
Reference in New Issue
Block a user