Compare commits

...

29 Commits

Author SHA1 Message Date
chenchun
e73678c788 style: 全部样式更新2.0 2025-08-05 14:09:39 +08:00
ccnetcore
09a2f91cbf style: 优化样式1.1 2025-08-04 23:55:48 +08:00
ccnetcore
5b024e9443 style: 重写ele 2025-08-04 23:34:13 +08:00
ccnetcore
225932eff1 style: 上线全局样式 2025-08-04 23:29:25 +08:00
ccnetcore
3e647ef14d style: 全局修改样式主题 2025-08-04 22:35:45 +08:00
chenchun
7cb3aea2e6 style: 调整样式 2025-08-04 18:27:18 +08:00
chenchun
7f4b8f1c8a feat: 添加暗色主题支持
- 在HTML根元素添加dark类名以启用暗色模式
- 引入Element Plus暗色主题CSS变量文件
- 格式化代码缩进和结构,提升代码可读性
2025-08-04 17:07:01 +08:00
chenchun
17f9ac6d54 style: 优化防抖样式 2025-08-01 17:58:07 +08:00
chenchun
3f8e6e48c0 Merge remote-tracking branch 'origin/ai-hub' into ai-hub 2025-07-28 14:39:09 +08:00
chenchun
bda4fdf69d feat: 兼容代码补全功能 2025-07-28 14:39:02 +08:00
Gsh
5c85ed13fd fix: 加载进度优化与登录弹窗优化 2025-07-28 13:43:46 +08:00
chenchun
1986901031 Merge remote-tracking branch 'origin/ai-hub' into ai-hub 2025-07-28 13:15:49 +08:00
chenchun
e1d3ec21e5 feat: 支持错误处理 2025-07-28 13:15:42 +08:00
Gsh
f45283dade Merge remote-tracking branch 'origin/ai-hub' into ai-hub 2025-07-28 12:59:42 +08:00
Gsh
31c44d8df7 fix: 登录弹窗超时功能取消 2025-07-28 12:59:07 +08:00
chenchun
bf443963c8 fix: 修复ThorChatCompletionsRequest中Messages属性的可空类型问题 2025-07-28 12:50:48 +08:00
chenchun
a0eb234539 feat: 兼容了用量使用显示 2025-07-22 10:40:23 +08:00
ccnetcore
b6d670c240 perf: 兼容deepseek格式 2025-07-21 22:03:55 +08:00
ccnetcore
b5fb2c42c6 feat: 兼容deepseek协议 2025-07-21 21:57:14 +08:00
ccnetcore
d72cc529ba perf: 优化流式输出 2025-07-21 21:15:02 +08:00
Gsh
660bd00cae fix: apikey加载状态 2025-07-20 22:12:48 +08:00
Gsh
b5489711ec fix: 加载优化 2025-07-20 21:01:41 +08:00
Gsh
76717c4f8a Merge remote-tracking branch 'origin/ai-hub' into ai-hub 2025-07-20 17:23:33 +08:00
ccnetcore
3d53d0bcd6 style: 完成进度条加载 2025-07-20 17:14:05 +08:00
ccnetcore
c7c9428b68 style: 完成进度条加载 2025-07-20 17:01:17 +08:00
ccnetcore
991a970d6a style: 完成进度条加载 2025-07-20 16:40:54 +08:00
ccnetcore
cbe93b9f7e style: 完成进度条加载 2025-07-20 15:15:05 +08:00
ccnetcore
5d7217b775 feat: 完成支持functioncall功能 2025-07-18 23:12:20 +08:00
ccnetcore
d6836b8bcf feat: 提交cicd产物 2025-07-18 21:08:51 +08:00
65 changed files with 961 additions and 426 deletions

View File

@@ -9,13 +9,7 @@ namespace Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
/// </summary>
public class ThorChatCompletionsRequest
{
public ThorChatCompletionsRequest()
{
Messages = new List<ThorChatMessage>();
}
[JsonPropertyName("store")]
public bool? Store { get; set; }
[JsonPropertyName("store")] public bool? Store { get; set; }
/// <summary>
/// 表示对话中支持的模态类型数组。可以为 null。
@@ -26,14 +20,59 @@ public class ThorChatCompletionsRequest
/// <summary>
/// 表示对话中的音频请求参数。可以为 null。
/// </summary>
[JsonPropertyName("audio")] public ThorChatAudioRequest? Audio { get; set; }
[JsonPropertyName("audio")]
public ThorChatAudioRequest? Audio { get; set; }
/// <summary>
/// 包含迄今为止对话的消息列表
/// </summary>
[JsonPropertyName("messages")]
public List<ThorChatMessage> Messages { get; set; }
public List<ThorChatMessage>? Messages { get; set; }
/// <summary>
/// 兼容-代码补全
/// </summary>
[JsonPropertyName("suffix")]
public string? Suffix { get; set; }
/// <summary>
/// 兼容-代码补全
/// </summary>
[JsonPropertyName("prompt")]
public string? Prompt { get; set; }
private const string CodeCompletionPrompt = """
Provide the provided content, predict and complete the code:
Requirement:
1:Only need to output completion content, do not add additional content
2:The last part of the code that needs to be completed is :{0}
3:The following is the provided context and the preceding section :{1}
4Do not include ``` markdown format in the output, display it directly in plain text
5The returned content should not be duplicated with the given part. Remove the duplicated parts and complete them backwards
6: The returned comments need to be in Chinese
""";
/// <summary>
/// 兼容代码补全
/// </summary>
public void CompatibleCodeCompletion()
{
if (Messages is null || !Messages.Any())
{
Messages = new List<ThorChatMessage>()
{
new ThorChatMessage
{
Role = "user",
Content = string.Format(CodeCompletionPrompt, Prompt, Suffix)
}
};
}
Suffix = null;
Prompt = null;
}
/// <summary>
/// 模型唯一编码值,如 gpt-4gpt-3.5-turbo,moonshot-v1-8k看底层具体平台定义
/// </summary>
@@ -229,18 +268,25 @@ public class ThorChatCompletionsRequest
{
if (value is JsonElement jsonElement)
{
if (jsonElement.ValueKind == JsonValueKind.String)
{
ToolChoice = new ThorToolChoice
{
Type = jsonElement.GetString()
};
}
else if (jsonElement.ValueKind == JsonValueKind.Object)
// if (jsonElement.ValueKind == JsonValueKind.String)
// {
// ToolChoice = new ThorToolChoice
// {
// Type = jsonElement.GetString()
// };
// }
if (jsonElement.ValueKind == JsonValueKind.Object)
{
ToolChoice = jsonElement.Deserialize<ThorToolChoice>();
}
}
else if (value is string text)
{
ToolChoice = new ThorToolChoice
{
Type = text
};
}
else
{
ToolChoice = (ThorToolChoice)value;

View File

@@ -108,33 +108,33 @@ public class AzureDatabricksChatCompletionsService(ILogger<AzureDatabricksChatCo
continue;
}
var content = result?.Choices?.FirstOrDefault()?.Delta;
if (first && content?.Content == OpenAIConstant.ThinkStart)
{
isThink = true;
continue;
// 需要将content的内容转换到其他字段
}
if (isThink && content?.Content?.Contains(OpenAIConstant.ThinkEnd) == true)
{
isThink = false;
// 需要将content的内容转换到其他字段
continue;
}
if (isThink && result?.Choices != null)
{
// 需要将content的内容转换到其他字段
foreach (var choice in result.Choices)
{
choice.Delta.ReasoningContent = choice.Delta.Content;
choice.Delta.Content = string.Empty;
}
}
first = false;
// var content = result?.Choices?.FirstOrDefault()?.Delta;
//
// if (first && content?.Content == OpenAIConstant.ThinkStart)
// {
// isThink = true;
// continue;
// // 需要将content的内容转换到其他字段
// }
//
// if (isThink && content?.Content?.Contains(OpenAIConstant.ThinkEnd) == true)
// {
// isThink = false;
// // 需要将content的内容转换到其他字段
// continue;
// }
//
// if (isThink && result?.Choices != null)
// {
// // 需要将content的内容转换到其他字段
// foreach (var choice in result.Choices)
// {
// choice.Delta.ReasoningContent = choice.Delta.Content;
// choice.Delta.Content = string.Empty;
// }
// }
//
// first = false;
yield return result;
}

View File

@@ -30,6 +30,7 @@ public class AzureOpenAiChatCompletionCompletionsService(ILogger<AzureOpenAiChat
if (response.StatusCode >= HttpStatusCode.BadRequest)
{
var error = await response.Content.ReadAsStringAsync();
logger.LogError("Azure对话异常 , StatusCode: {StatusCode} 错误响应内容:{Content}", response.StatusCode,
error);

View File

@@ -0,0 +1,178 @@
using System.Diagnostics;
using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.AiGateWay.Exceptions;
using Yi.Framework.AiHub.Domain.Shared.Dtos;
namespace Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorDeepSeek.Chats;
public sealed class DeepSeekChatCompletionsService(ILogger<DeepSeekChatCompletionsService> logger)
: IChatCompletionService
{
public async IAsyncEnumerable<ThorChatCompletionsResponse> CompleteChatStreamAsync(AiModelDescribe options,
ThorChatCompletionsRequest chatCompletionCreate,
CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(options.Endpoint))
{
options.Endpoint = "https://api.deepseek.com/v1";
}
using var openai =
Activity.Current?.Source.StartActivity("OpenAI 对话流式补全");
var response = await HttpClientFactory.GetHttpClient(options.Endpoint).HttpRequestRaw(
options?.Endpoint.TrimEnd('/') + "/chat/completions",
chatCompletionCreate, options.ApiKey);
openai?.SetTag("Model", chatCompletionCreate.Model);
openai?.SetTag("Response", response.StatusCode.ToString());
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new UnauthorizedAccessException();
}
if (response.StatusCode == HttpStatusCode.PaymentRequired)
{
throw new PaymentRequiredException();
}
// 如果限流则抛出限流异常
if (response.StatusCode == HttpStatusCode.TooManyRequests)
{
throw new ThorRateLimitException();
}
// 大于等于400的状态码都认为是异常
if (response.StatusCode >= HttpStatusCode.BadRequest)
{
logger.LogError("OpenAI对话异常 , StatusCode: {StatusCode} ", response.StatusCode);
throw new BusinessException("OpenAI对话异常", response.StatusCode.ToString());
}
using var stream = new StreamReader(await response.Content.ReadAsStreamAsync(cancellationToken));
using StreamReader reader = new(await response.Content.ReadAsStreamAsync(cancellationToken));
string? line = string.Empty;
var first = true;
var isThink = false;
while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
{
line += Environment.NewLine;
if (line.StartsWith('{'))
{
logger.LogInformation("OpenAI对话异常 , StatusCode: {StatusCode} Response: {Response}", response.StatusCode,
line);
throw new BusinessException("OpenAI对话异常", line);
}
if (line.StartsWith(OpenAIConstant.Data))
line = line[OpenAIConstant.Data.Length..];
line = line.Trim();
if (string.IsNullOrWhiteSpace(line)) continue;
if (line == OpenAIConstant.Done)
{
break;
}
if (line.StartsWith(':'))
{
continue;
}
var result = JsonSerializer.Deserialize<ThorChatCompletionsResponse>(line,
ThorJsonSerializer.DefaultOptions);
// var content = result?.Choices?.FirstOrDefault()?.Delta;
//
// // if (first && string.IsNullOrWhiteSpace(content?.Content) && string.IsNullOrEmpty(content?.ReasoningContent))
// // {
// // continue;
// // }
//
// if (first && content.Content == OpenAIConstant.ThinkStart)
// {
// isThink = true;
// //continue;
// // 需要将content的内容转换到其他字段
// }
//
// if (isThink && content.Content.Contains(OpenAIConstant.ThinkEnd))
// {
// isThink = false;
// // 需要将content的内容转换到其他字段
// //continue;
// }
//
// if (isThink)
// {
// // 需要将content的内容转换到其他字段
// foreach (var choice in result.Choices)
// {
// //choice.Delta.ReasoningContent = choice.Delta.Content;
// //choice.Delta.Content = string.Empty;
// }
// }
// first = false;
yield return result;
}
}
public async Task<ThorChatCompletionsResponse> CompleteChatAsync(AiModelDescribe options,
ThorChatCompletionsRequest chatCompletionCreate,
CancellationToken cancellationToken)
{
using var openai =
Activity.Current?.Source.StartActivity("OpenAI 对话补全");
if (string.IsNullOrWhiteSpace(options.Endpoint))
{
options.Endpoint = "https://api.deepseek.com/v1";
}
var response = await HttpClientFactory.GetHttpClient(options.Endpoint).PostJsonAsync(
options?.Endpoint.TrimEnd('/') + "/chat/completions",
chatCompletionCreate, options.ApiKey).ConfigureAwait(false);
openai?.SetTag("Model", chatCompletionCreate.Model);
openai?.SetTag("Response", response.StatusCode.ToString());
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new BusinessException("渠道未登录,请联系管理人员", "401");
}
// 如果限流则抛出限流异常
if (response.StatusCode == HttpStatusCode.TooManyRequests)
{
throw new ThorRateLimitException();
}
// 大于等于400的状态码都认为是异常
if (response.StatusCode >= HttpStatusCode.BadRequest)
{
var error = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
logger.LogError("OpenAI对话异常 , StatusCode: {StatusCode} Response: {Response}", response.StatusCode, error);
throw new BusinessException("OpenAI对话异常", response.StatusCode.ToString());
}
var result =
await response.Content.ReadFromJsonAsync<ThorChatCompletionsResponse>(
cancellationToken: cancellationToken).ConfigureAwait(false);
return result;
}
}

View File

@@ -29,10 +29,18 @@ public class MessageAggregateRoot : FullAuditedAggregateRoot<Guid>
ModelId = modelId;
if (tokenUsage is not null)
{
long inputTokenCount = tokenUsage.PromptTokens
?? tokenUsage.InputTokens
?? 0;
long outputTokenCount = tokenUsage.CompletionTokens
?? tokenUsage.OutputTokens
?? 0;
this.TokenUsage = new TokenUsageValueObject
{
OutputTokenCount = tokenUsage.OutputTokens ?? 0,
InputTokenCount = tokenUsage.InputTokens ?? 0,
OutputTokenCount = outputTokenCount,
InputTokenCount = inputTokenCount,
TotalTokenCount = tokenUsage.TotalTokens ?? 0
};
}
@@ -44,7 +52,7 @@ public class MessageAggregateRoot : FullAuditedAggregateRoot<Guid>
public Guid? SessionId { get; set; }
[SugarColumn(ColumnDataType = StaticConfig.CodeFirst_BigString)]
public string Content { get; set; }
public string? Content { get; set; }
public string Role { get; set; }
public string ModelId { get; set; }

View File

@@ -37,22 +37,22 @@ public class UsageStatisticsAggregateRoot : FullAuditedAggregateRoot<Guid>
/// <summary>
/// 使用输出token总数
/// </summary>
public int UsageOutputTokenCount { get; set; }
public long UsageOutputTokenCount { get; set; }
/// <summary>
/// 使用输入总数
/// </summary>
public int UsageInputTokenCount { get; set; }
public long UsageInputTokenCount { get; set; }
/// <summary>
/// 总token使用数量
/// </summary>
public int TotalTokenCount { get; set; }
public long TotalTokenCount { get; set; }
/// <summary>
/// 新增一次聊天统计
/// </summary>
public void AddOnceChat(int inputTokenCount, int outputTokenCount)
public void AddOnceChat(long inputTokenCount, long outputTokenCount)
{
UsageTotalNumber += 1;
UsageOutputTokenCount += outputTokenCount;

View File

@@ -2,9 +2,9 @@
public class TokenUsageValueObject
{
public int OutputTokenCount { get; set; }
public long OutputTokenCount { get; set; }
public int InputTokenCount { get; set; }
public long InputTokenCount { get; set; }
public long TotalTokenCount { get; set; }
}

View File

@@ -83,7 +83,7 @@ public class AiGateWayManager : DomainService
var modelDescribe = await GetModelAsync(request.Model);
var chatService =
LazyServiceProvider.GetRequiredKeyedService<IChatCompletionService>(modelDescribe.HandlerName);
await foreach (var result in chatService.CompleteChatStreamAsync(modelDescribe, request, cancellationToken))
{
yield return result;
@@ -109,8 +109,7 @@ public class AiGateWayManager : DomainService
_specialCompatible.Compatible(request);
var response = httpContext.Response;
// 设置响应头,声明是 json
response.ContentType = "application/json; charset=UTF-8";
await using var writer = new StreamWriter(response.Body, Encoding.UTF8, leaveOpen: true);
//response.ContentType = "application/json; charset=UTF-8";
var modelDescribe = await GetModelAsync(request.Model);
var chatService =
LazyServiceProvider.GetRequiredKeyedService<IChatCompletionService>(modelDescribe.HandlerName);
@@ -120,7 +119,7 @@ public class AiGateWayManager : DomainService
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
new MessageInputDto
{
Content = request.Messages.LastOrDefault().Content ?? string.Empty,
Content = request.Messages?.LastOrDefault().Content ?? string.Empty,
ModelId = request.Model,
TokenUsage = data.Usage,
});
@@ -133,16 +132,10 @@ public class AiGateWayManager : DomainService
TokenUsage = data.Usage
});
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage.InputTokens ?? 0,
data.Usage.OutputTokens ?? 0);
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, data.Usage);
}
var body = JsonConvert.SerializeObject(data, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
await writer.WriteLineAsync(body);
await writer.FlushAsync(cancellationToken);
await response.WriteAsJsonAsync(data, cancellationToken);
}
/// <summary>
@@ -163,15 +156,14 @@ public class AiGateWayManager : DomainService
{
var response = httpContext.Response;
// 设置响应头,声明是 SSE 流
response.ContentType = "text/event-stream";
response.Headers.Append("Cache-Control", "no-cache");
response.Headers.Append("Connection", "keep-alive");
response.ContentType = "text/event-stream;charset=utf-8;";
response.Headers.TryAdd("Cache-Control", "no-cache");
response.Headers.TryAdd("Connection", "keep-alive");
var gateWay = LazyServiceProvider.GetRequiredService<AiGateWayManager>();
var completeChatResponse = gateWay.CompleteChatStreamAsync(request, cancellationToken);
var tokenUsage = new ThorUsageResponse();
await using var writer = new StreamWriter(response.Body, Encoding.UTF8, leaveOpen: true);
//缓存队列算法
// 创建一个队列来缓存消息
@@ -189,14 +181,14 @@ public class AiGateWayManager : DomainService
{
if (messageQueue.TryDequeue(out var message))
{
await writer.WriteLineAsync(message);
await writer.FlushAsync(cancellationToken);
await response.WriteAsync(message, Encoding.UTF8, cancellationToken).ConfigureAwait(false);
await response.Body.FlushAsync(cancellationToken).ConfigureAwait(false);
}
if (!isComplete)
{
// 如果没有完成,才等待,已完成,全部输出
await Task.Delay(outputInterval, cancellationToken);
await Task.Delay(outputInterval, cancellationToken).ConfigureAwait(false);
}
}
}, cancellationToken);
@@ -207,24 +199,21 @@ public class AiGateWayManager : DomainService
{
await foreach (var data in completeChatResponse)
{
if (data.Usage is not null && data.Usage.TotalTokens is not null)
if (data.Usage is not null)
{
tokenUsage = data.Usage;
}
var message = JsonConvert.SerializeObject(data, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
var message = System.Text.Json.JsonSerializer.Serialize(data, ThorJsonSerializer.DefaultOptions);
backupSystemContent.Append(data.Choices.FirstOrDefault()?.Delta.Content);
// 将消息加入队列而不是直接写入
messageQueue.Enqueue($"data: {message}\n");
messageQueue.Enqueue($"data: {message}\n\n");
}
}
catch (Exception e)
{
_logger.LogError(e, $"Ai对话异常");
var errorContent = $"Ai异常异常信息\n当前Ai模型{request.Model}\n异常信息{e.Message}";
var errorContent = $"Ai异常异常信息\n当前Ai模型{request.Model}\n异常信息{e.Message}\n异常堆栈:{e}";
var model = new ThorChatCompletionsResponse()
{
Choices = new List<ThorChatChoiceResponse>()
@@ -243,11 +232,11 @@ public class AiGateWayManager : DomainService
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
backupSystemContent.Append(errorContent);
messageQueue.Enqueue($"data: {message}\n");
messageQueue.Enqueue($"data: {message}\n\n");
}
//断开连接
messageQueue.Enqueue("data: [DONE]\n");
messageQueue.Enqueue("data: [DONE]\n\n");
// 标记完成并发送结束标记
isComplete = true;
@@ -258,7 +247,7 @@ public class AiGateWayManager : DomainService
await _aiMessageManager.CreateUserMessageAsync(userId.Value, sessionId,
new MessageInputDto
{
Content = request.Messages.LastOrDefault()?.Content ?? string.Empty,
Content = request.Messages?.LastOrDefault()?.Content ?? string.Empty,
ModelId = request.Model,
TokenUsage = tokenUsage,
});
@@ -271,8 +260,7 @@ public class AiGateWayManager : DomainService
TokenUsage = tokenUsage
});
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage.InputTokens ?? 0,
tokenUsage.OutputTokens ?? 0);
await _usageStatisticsManager.SetUsageAsync(userId.Value, request.Model, tokenUsage);
}
}
}

View File

@@ -1,5 +1,6 @@
using Medallion.Threading;
using Volo.Abp.Domain.Services;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
@@ -17,8 +18,16 @@ public class UsageStatisticsManager : DomainService
private IDistributedLockProvider DistributedLock =>
LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
public async Task SetUsageAsync(Guid userId, string modelId, int inputTokenCount, int outputTokenCount)
public async Task SetUsageAsync(Guid userId, string modelId, ThorUsageResponse? tokenUsage)
{
long inputTokenCount = tokenUsage?.PromptTokens
?? tokenUsage.InputTokens
?? 0;
long outputTokenCount = tokenUsage?.CompletionTokens
?? tokenUsage.OutputTokens
?? 0;
await using (await DistributedLock.AcquireLockAsync($"UsageStatistics:{userId.ToString()}"))
{
var entity = await _repository._DbQueryable.FirstAsync(x => x.UserId == userId && x.ModelId == modelId);
@@ -37,8 +46,4 @@ public class UsageStatisticsManager : DomainService
}
}
}
}
internal class LazyServiceProvider
{
}

View File

@@ -1,8 +1,11 @@
using Dm.util;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain;
using Yi.Framework.AiHub.Application.Contracts.Dtos.OpenAi;
using Yi.Framework.AiHub.Domain.AiGateWay;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureDatabricks.Chats;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureOpenAI.Chats;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorDeepSeek.Chats;
using Yi.Framework.AiHub.Domain.Shared;
using Yi.Framework.Mapster;
@@ -25,10 +28,17 @@ namespace Yi.Framework.AiHub.Domain
nameof(AzureOpenAiChatCompletionCompletionsService));
services.AddKeyedTransient<IChatCompletionService, AzureDatabricksChatCompletionsService>(
nameof(AzureDatabricksChatCompletionsService));
services.AddKeyedTransient<IChatCompletionService, DeepSeekChatCompletionsService>(
nameof(DeepSeekChatCompletionsService));
//ai模型特殊性兼容处理
Configure<SpecialCompatibleOptions>(options =>
{
options.Handles.add(request =>
{
request.CompatibleCodeCompletion();
});
options.Handles.Add(request =>
{
if (request.Model == "o1")
@@ -36,6 +46,25 @@ namespace Yi.Framework.AiHub.Domain
request.Temperature = null;
}
});
options.Handles.Add(request =>
{
if (request.Model.StartsWith("o3-mini") || request.Model.StartsWith("o4-mini"))
{
request.MaxCompletionTokens = request.MaxTokens;
request.MaxTokens = null;
request.Temperature = null;
}
});
options.Handles.Add(request =>
{
if (request.Stream == true)
{
request.StreamOptions = new ThorStreamOptions()
{
IncludeUsage = true
};
}
});
});
}

View File

@@ -1,6 +1,6 @@
@echo on
set SERVER_USER=ccnetcore
set SERVER_USER=root
set SERVER_IP=yxai.chat
set FILE_PATH=publish_02.zip
set REMOTE_PATH=/home/yi/build/publish_02.zip

View File

@@ -5,8 +5,6 @@
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ElMessage": true,
"ElMessageBox": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,

View File

@@ -2,24 +2,158 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="baidu-site-verification" content="codeva-mkVpSFmYJm" />
<meta name="description" content="意心AI一站式多模型 AI 平台,提供 GPT-4o、DeepSeek 等服务" />
<meta name="description" content="各大主流AI无限制使用直连AIclaude ,DeepSeek,open-ai" />
<meta name="keywords" content="意心AI, GPT-4.5, 多模型AI, AI工具" />
<meta name="keywords" content="橙子chengzi,橙子老哥ccnetcore意社区" />
<meta name="author" content="橙子chengzi,橙子老哥ccnetcore" />
<meta name="version" content="%VITE_APP_VERSION%" />
<meta name="version" content="%VITE_WEB_TITLE%" />
<meta charset="UTF-8"/>
<link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="baidu-site-verification" content="codeva-mkVpSFmYJm"/>
<meta name="description" content="意心AI一站式多模型 AI 平台,提供 GPT-4o、DeepSeek 等服务"/>
<meta name="description" content="各大主流AI无限制使用直连AIclaude ,DeepSeek,open-ai"/>
<meta name="keywords" content="意心AI, GPT-4.5, 多模型AI, AI工具"/>
<meta name="keywords" content="橙子chengzi,橙子老哥ccnetcore意社区"/>
<meta name="author" content="橙子chengzi,橙子老哥ccnetcore"/>
<meta name="version" content="%VITE_APP_VERSION%"/>
<meta name="version" content="%VITE_WEB_TITLE%"/>
<title>%VITE_WEB_TITLE%</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<style>
/* 全局样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 加载动画样式 */
.loader-container {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: #fff;
z-index: 1000;
transition: opacity 0.5s ease;
}
.loader-title {
font-size: clamp(1.5rem, 3vw, 2.5rem);
font-weight: bold;
margin-bottom: 0.5rem;
letter-spacing: -0.02em;
}
.loader-subtitle {
font-size: 0.875rem;
color: #555;
margin-bottom: 1.5rem;
}
.loader-logo {
font-size: 3rem;
margin-bottom: 1.5rem;
display: flex;
justify-content: center;
}
.pulse-box {
width: 32px;
height: 32px;
background: #000;
display: inline-block;
transform-origin: center;
animation: pulse 1.2s infinite ease-in-out;
}
@keyframes pulse {
0% {
transform: scale(1) rotate(0deg);
opacity: 1;
}
50% {
transform: scale(1.2) rotate(45deg);
opacity: 0.8;
}
100% {
transform: scale(1) rotate(90deg);
opacity: 1;
}
}
.loader-text {
font-size: 1.5rem;
font-weight: bold;
margin-bottom: 1rem;
}
.loader-progress-bar {
width: 90%;
max-width: 400px;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.loader-progress {
height: 100%;
width: 0%;
background: #000;
transition: width 0.3s ease-out;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<!-- 加载动画容器 -->
<div id="loader" class="loader-container">
<div class="loader-title">意心Ai</div>
<div class="loader-subtitle">海外地址仅首次访问预计加载约10秒</div>
<div class="loader-logo">
<div class="pulse-box"></div>
</div>
<div class="loader-text" id="progress-text">0%</div>
<div class="loader-progress-bar">
<div id="progress-bar" class="loader-progress"></div>
</div>
</div>
<!-- 加载进度脚本:放在 main.ts 之前,保证先执行 -->
<script>
// 立即执行函数改为更简单的写法,减少解析时间
(function(){
const bar = document.getElementById('progress-bar');
const text = document.getElementById('progress-text');
let progress = 0;
function update() {
progress = Math.min(progress + 2 + Math.random() * 3, 95);
bar.style.width = progress + '%';
text.textContent = Math.floor(progress) + '%';
if(progress < 95) requestAnimationFrame(update);
}
update();
window.finishLoading = function() {
bar.style.width = '100%';
text.textContent = '100%';
setTimeout(() => {
document.getElementById('loader').style.opacity = '0';
setTimeout(() => document.getElementById('loader').remove(), 500);
}, 300);
};
})();
</script>
<div id="app"></div>
<script async type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@@ -1,6 +1,6 @@
@echo on
set SERVER_USER=ccnetcore
set SERVER_USER=root
set SERVER_IP=yxai.chat
set FILE_PATH=publish_aihub_02.zip
set REMOTE_PATH=/home/yi/build/publish_aihub_02.zip

View File

@@ -1,4 +1,6 @@
<script setup lang="ts"></script>
<script setup lang="ts">
</script>
<template>
<router-view />

View File

@@ -85,8 +85,10 @@ function handleThirdPartyLogin(type: any) {
const resUserInfo = await getUserInfo();
userStore.setUserInfo(resUserInfo.data);
// 关闭弹窗
if (popup && !popup.closed)
if (popup && !popup.closed) {
popup.close();
}
// 后续逻辑
ElMessage.success('登录成功');
userStore.closeLoginDialog();
@@ -105,14 +107,14 @@ function handleThirdPartyLogin(type: any) {
window.addEventListener('message', messageHandler);
// 超时自动清理
setTimeout(() => {
if (!isHandled) {
window.removeEventListener('message', messageHandler);
if (popup && !popup.closed)
popup.close();
ElMessage.warning('登录超时');
}
}, 60 * 1000); // 60分钟超时关闭
// setTimeout(() => {
// if (!isHandled) {
// window.removeEventListener('message', messageHandler);
// if (popup && !popup.closed)
// popup.close();
// ElMessage.warning('登录超时');
// }
// }, 60 * 1000); // 60分钟超时关闭
}
// 让意社区重新登录,先让意社区退出登录,再重新登录
@@ -161,14 +163,14 @@ function handleLoginAgainYi() {
window.addEventListener('message', messageHandler);
// 超时自动清理
setTimeout(() => {
if (!isHandled) {
window.removeEventListener('message', messageHandler);
if (popup && !popup.closed)
popup.close();
ElMessage.warning('登录超时');
}
}, 60 * 1000); // 60分钟超时关闭
// setTimeout(() => {
// if (!isHandled) {
// window.removeEventListener('message', messageHandler);
// if (popup && !popup.closed)
// popup.close();
// ElMessage.warning('登录超时');
// }
// }, 60 * 1000); // 60分钟超时关闭
}
const wxSrc = computed(

View File

@@ -8,10 +8,19 @@ import { isUserVip } from '@/utils/user';
const apiKey = ref('');
const showKey = ref(false);
const loading = ref(false);
const loading = ref(true);
const displayedKey = ref('');
const showSuccessDialog = ref(false);
const svg = `
<path class="path" d="
M 30 15
L 28 17
M 25.61 25.61
A 15 15, 0, 0, 1, 15 30
A 15 15, 0, 1, 1, 27.99 7.5
L 15 15
" style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
`;
const isOpening = ref(false);
const confettis = ref<any[]>([]);
const router = useRouter();
@@ -147,13 +156,21 @@ function generateConfetti() {
setTimeout(() => confettis.value = [], 2000);
}
onMounted(() => {
fetchApiKey();
onMounted(async () => {
await fetchApiKey();
loading.value = false;
});
</script>
<template>
<div class="api-key-management">
<div
v-loading="loading"
class="api-key-management"
element-loading-text="Loading..."
:element-loading-spinner="svg"
element-loading-svg-view-box="-10, -10, 50, 50"
element-loading-background="rgba(122, 122, 122, 0.8)"
>
<!-- 未领取状态 -->
<div v-if="!apiKey " class="unclaimed-state">
<div class="gift-container" @click="handleClaim">
@@ -212,7 +229,7 @@ onMounted(() => {
</div>
<div class="key-actions">
<el-button type="warning" :loading="loading" @click="handleReset">
<el-button type="warning" @click="handleReset">
重置密钥
</el-button>
<p class="key-hint">
@@ -220,53 +237,52 @@ onMounted(() => {
</p>
</div>
</div>
<!-- 使用说明 -->
<div class="usage-guide">
<el-divider />
<h3>使用说明</h3>
<div class="guide-content">
<p><strong>API地址</strong>https://ai.ccnetcore.com</p>
<p><strong>密钥</strong>上面申请的token</p>
<p><strong>模型</strong>聊天界面显示的模型名称</p>
</div>
<div class="guide-images">
<el-image
style="max-width: 100%; margin: 10px 0;"
src="/images/api_usage_instructions.png"
fit="contain"
/>
</div>
</div>
<!-- 使用说明 -->
<div class="usage-guide">
<el-divider />
<h3>使用说明</h3>
<div class="guide-content">
<p><strong>API地址</strong>https://ai.ccnetcore.com</p>
<p><strong>密钥</strong>上面申请的token</p>
<p><strong>模型</strong>聊天界面显示的模型名称</p>
</div>
<!-- 领取成功弹窗 -->
<el-dialog
v-model="showSuccessDialog"
title="领取成功"
width="400px"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="success-dialog">
<el-icon color="#67C23A" :size="60">
<CircleCheck />
</el-icon>
<p class="success-message">
恭喜您成功领取API密钥
</p>
<p class="success-tip">
请妥善保管您的密钥
</p>
</div>
<template #footer>
<el-button type="primary" @click="showSuccessDialog = false">
确定
</el-button>
</template>
</el-dialog>
<div class="guide-images">
<el-image
style="max-width: 100%; margin: 10px 0;"
src="/images/api_usage_instructions.png"
fit="contain"
/>
</div>
</div>
<!-- 领取成功弹窗 -->
<el-dialog
v-model="showSuccessDialog"
title="领取成功"
width="400px"
:show-close="false"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="success-dialog">
<el-icon color="#67C23A" :size="60">
<CircleCheck />
</el-icon>
<p class="success-message">
恭喜您成功领取API密钥
</p>
<p class="success-tip">
请妥善保管您的密钥
</p>
</div>
<template #footer>
<el-button type="primary" @click="showSuccessDialog = false">
确定
</el-button>
</template>
</el-dialog>
</template>
<style scoped>

View File

@@ -16,6 +16,11 @@ const designStore = useDesignStore();
// const { isMobile } = useScreenStore();
/** 获取布局格式 */
const layout = computed((): LayoutType => designStore.layout);
onMounted(() => {
console.log('111--');
// 通知 index.html 的 loading 动画进度拉满并淡出
(window as any)?.finishLoading();
});
</script>
<template>

View File

@@ -9,18 +9,23 @@ import store from './stores';
import './styles/index.scss';
import 'virtual:uno.css';
import 'element-plus/dist/index.css';
// SVG插件配置
import 'virtual:svg-icons-register';
// 创建 Vue 应用
const app = createApp(App);
// 安装插件
app.use(router);
app.use(ElMessage);
app.use(ElementPlusX);
// 注册ElementPlus所有图标
// 注册图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.use(store);
// 挂载 Vue 应用
// mount 完成说明应用初始化完毕,此时手动通知 loading 动画结束
app.mount('#app');

View File

@@ -1,47 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>.Net意社区</title>
<!-- 为了icp备案-->
<!-- <title>个人成果展示</title>-->
<link rel="stylesheet" href="/src/assets/loading.css" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5453339688995325"
crossorigin="anonymous"></script>
</head>
<body>
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
<title>.Net意社区</title>
<!-- 为了icp备案-->
<!-- <title>个人成果展示</title>-->
<link rel="stylesheet" href="/src/assets/loading.css" />
</head>
<!-- 引入聊天组件脚本 -->
<script async src="https://opendeep.wiki/koala-chat-widget.js"></script>
<script>
function loadChatWidget() {
KoalaChatWidget.init({
appId: 'ccnetcore',
title: 'ccnetcore',
theme: 'light', // 或 'dark'
// 其他可选配置...
onError: (error) => {
console.error('Chat widget error:', error);
},
onValidationFailed: (domain) => {
console.error('Domain validation failed:', domain);
}
});
}
// 页面加载完成后再加载聊天组件
window.addEventListener('load', loadChatWidget);
</script>
</html>
<body style="background: linear-gradient(135deg, #0a0a0a 0%, #0d1520 30%, #0a0a0a 70%, #0f1520 100%), linear-gradient(135deg, rgba(0, 255, 136, 0.03) 0%, rgba(0, 0, 0, 0.8) 50%, rgba(0, 255, 136, 0.02) 100%);">
<div id="Loading">
<div class="loader JS_on">
<span class="binary"></span>
<span class="binary"></span>
<span class="getting-there">意社区很大,你要等一下...</span>
</div>
</div>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
<!-- 引入聊天组件脚本 -->
<!-- <script async src="https://opendeep.wiki/koala-chat-widget.js"></script>
<script>
function loadChatWidget() {
KoalaChatWidget.init({
appId: 'ccnetcore',
title: 'ccnetcore',
theme: 'light', // 或 'dark'
// 其他可选配置...
onError: (error) => {
console.error('Chat widget error:', error);
},
onValidationFailed: (domain) => {
console.error('Domain validation failed:', domain);
}
});
}
// 页面加载完成后再加载聊天组件
window.addEventListener('load', loadChatWidget);
</script> -->
</html>

View File

@@ -38,7 +38,7 @@
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"copy-webpack-plugin": "^11.0.0",
"sass": "1.52.1",
"sass": "^1.89.2",
"unplugin-auto-import": "^0.15.0",
"unplugin-vue-components": "^0.24.0",
"vite": "^4.1.3",

View File

@@ -1,5 +1,3 @@
<script setup></script>
<template>
<el-config-provider :locale="locale">
<RouterView />

View File

@@ -1,2 +1,2 @@
<svg width="100%" height="100%" id="svg" viewBox="0 0 1440 390" xmlns="http://www.w3.org/2000/svg" class="transition duration-300 ease-in-out delay-150"><defs><linearGradient id="gradient" x1="52%" y1="0%" x2="48%" y2="100%"><stop offset="5%" stop-color="#0693e3"></stop><stop offset="95%" stop-color="#0693e3"></stop></linearGradient></defs><path d="M 0,400 L 0,100 C 80.95362418412918,103.28753005839917 161.90724836825837,106.57506011679834 224,114 C 286.09275163174163,121.42493988320166 329.3246307110958,132.9872895912058 390,125 C 450.6753692889042,117.0127104087942 528.7942287873583,89.47578151837855 594,89 C 659.2057712126417,88.52421848162145 711.4984541394712,115.10958433527998 789,108 C 866.5015458605288,100.89041566472002 969.2119546547576,60.08588114050154 1053,65 C 1136.7880453452424,69.91411885949846 1201.6537272414978,120.54689110271386 1263,134 C 1324.3462727585022,147.45310889728614 1382.173136379251,123.72655444864307 1440,100 L 1440,400 L 0,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="0.53" class="transition-all duration-300 ease-in-out delay-150 path-0"></path><defs><linearGradient id="gradient" x1="52%" y1="0%" x2="48%" y2="100%"><stop offset="5%" stop-color="#0693e3"></stop><stop offset="95%" stop-color="#0693e3"></stop></linearGradient></defs><path d="M 0,400 L 0,233 C 77.77464788732394,237.21916867055995 155.54929577464787,241.43833734111988 219,235 C 282.4507042253521,228.56166265888012 331.57746478873247,211.46581930608042 388,206 C 444.42253521126753,200.53418069391958 508.14084507042253,206.6983854345586 591,209 C 673.8591549295775,211.3016145654414 775.8591549295775,209.74063895568528 849,201 C 922.1408450704225,192.25936104431472 966.4225352112676,176.33905874270016 1035,195 C 1103.5774647887324,213.66094125729984 1196.450704225352,266.9031260735142 1268,279 C 1339.549295774648,291.0968739264858 1389.7746478873241,262.0484369632429 1440,233 L 1440,400 L 0,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="1" class="transition-all duration-300 ease-in-out delay-150 path-1" ></path>
<svg width="100%" height="100%" id="svg" viewBox="0 0 1440 390" xmlns="http://www.w3.org/2000/svg" class="transition duration-300 ease-in-out delay-150"><defs><linearGradient id="gradient" x1="52%" y1="0%" x2="48%" y2="100%"><stop offset="5%" stop-color="#00F984"></stop><stop offset="95%" stop-color="#00F984"></stop></linearGradient></defs><path d="M 0,400 L 0,100 C 80.95362418412918,103.28753005839917 161.90724836825837,106.57506011679834 224,114 C 286.09275163174163,121.42493988320166 329.3246307110958,132.9872895912058 390,125 C 450.6753692889042,117.0127104087942 528.7942287873583,89.47578151837855 594,89 C 659.2057712126417,88.52421848162145 711.4984541394712,115.10958433527998 789,108 C 866.5015458605288,100.89041566472002 969.2119546547576,60.08588114050154 1053,65 C 1136.7880453452424,69.91411885949846 1201.6537272414978,120.54689110271386 1263,134 C 1324.3462727585022,147.45310889728614 1382.173136379251,123.72655444864307 1440,100 L 1440,400 L 0,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="0.53" class="transition-all duration-300 ease-in-out delay-150 path-0"></path><defs><linearGradient id="gradient" x1="52%" y1="0%" x2="48%" y2="100%"><stop offset="5%" stop-color="#00F984"></stop><stop offset="95%" stop-color="#00F984"></stop></linearGradient></defs><path d="M 0,400 L 0,233 C 77.77464788732394,237.21916867055995 155.54929577464787,241.43833734111988 219,235 C 282.4507042253521,228.56166265888012 331.57746478873247,211.46581930608042 388,206 C 444.42253521126753,200.53418069391958 508.14084507042253,206.6983854345586 591,209 C 673.8591549295775,211.3016145654414 775.8591549295775,209.74063895568528 849,201 C 922.1408450704225,192.25936104431472 966.4225352112676,176.33905874270016 1035,195 C 1103.5774647887324,213.66094125729984 1196.450704225352,266.9031260735142 1268,279 C 1339.549295774648,291.0968739264858 1389.7746478873241,262.0484369632429 1440,233 L 1440,400 L 0,400 Z" stroke="none" stroke-width="0" fill="url(#gradient)" fill-opacity="1" class="transition-all duration-300 ease-in-out delay-150 path-1" ></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702540720819" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4776" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 490.666667c-19.989333 0-37.354667 3.2-52.074667 13.866666s-22.592 29.717333-22.592 44.8c0 19.136 9.066667 27.392 15.509334 34.133334 6.4 6.72 13.098667 11.797333 19.456 16.234666 12.672 8.917333 24.704 15.253333 24.704 15.253334a32 32 0 0 0 29.994666 0s12.032-6.336 24.704-15.253334c6.357333-4.437333 13.034667-9.514667 19.456-16.234666 6.442667-6.741333 15.509333-14.997333 15.509334-34.133334 0-15.082667-7.850667-34.133333-22.592-44.8-14.72-10.666667-32.085333-13.866667-52.074667-13.866666z m0.533333 32a32 32 0 0 0-32 32c0 14.464-3.754667 47.168 5.098667 80.213333 4.416 16.512 12.714667 34.304 28.458667 47.957333 15.722667 13.653333 37.930667 21.162667 62.442666 21.162667 52.650667 0 96-43.349333 96-96a32 32 0 0 0-32-32 32 32 0 0 0-32 32c0 18.048-13.930667 32-32 32-12.8 0-17.28-2.709333-20.522666-5.546667-3.285333-2.837333-6.314667-7.722667-8.533334-16.128-4.522667-16.789333-2.944-42.773333-2.944-63.658666a32 32 0 0 0-32-32z m-0.533333 0a32 32 0 0 0-32 32c0 20.906667 1.578667 46.869333-2.922667 63.658666-2.24 8.405333-5.269333 13.290667-8.533333 16.128-3.264 2.837333-7.744 5.546667-20.544 5.546667-18.048 0-32-13.952-32-32A32 32 0 0 0 384 576a32 32 0 0 0-32 32c0 52.650667 43.349333 96 96 96 24.533333 0 46.72-7.530667 62.464-21.162667 15.722667-13.653333 24.042667-31.445333 28.458667-47.957333 8.832-33.045333 5.077333-65.749333 5.077333-80.213333a32 32 0 0 0-32-32z m128-160a42.666667 42.666667 0 0 1-42.666667 42.666666 42.666667 42.666667 0 0 1-42.666666-42.666666 42.666667 42.666667 0 0 1 42.666666-42.666667 42.666667 42.666667 0 0 1 42.666667 42.666667z m-170.666667 0a42.666667 42.666667 0 0 1-42.666666 42.666666 42.666667 42.666667 0 0 1-42.666667-42.666666 42.666667 42.666667 0 0 1 42.666667-42.666667 42.666667 42.666667 0 0 1 42.666666 42.666667z m42.666667-202.666667c-233.173333 0-416 224.021333-416 448 0 112 48.704 200.746667 125.418667 257.664C298.133333 922.581333 400.768 949.333333 512 949.333333s213.866667-26.752 290.581333-83.669333S928 720 928 608c0-223.978667-182.826667-448-416-448z m0 64c190.976 0 352 195.605333 352 384 0 94.208-37.269333 160.085333-99.541333 206.293333C702.186667 860.48 612.842667 885.333333 512 885.333333c-100.842667 0-190.186667-24.853333-252.458667-71.04C197.269333 768.085333 160 702.208 160 608c0-188.394667 161.024-384 352-384z m203.52-147.2a32 32 0 0 0-41.237333 17.984l-42.666667 106.666667a32 32 0 0 0 17.834667 41.6 32 32 0 0 0 41.6-17.834667l27.648-69.184c14.613333 12.906667 31.146667 30.421333 45.504 54.677333 25.344 42.816 43.690667 100.48 36.010666 169.749334a32 32 0 0 0 28.245334 35.328 32 32 0 0 0 35.328-28.245334 336.469333 336.469333 0 0 0-44.501334-209.450666C787.925333 125.077333 751.36 90.581333 715.52 76.8z m-407.04 0c-35.84 13.781333-72.405333 48.277333-103.765333 101.290667a336.469333 336.469333 0 0 0-44.501334 209.450666 32 32 0 0 0 35.328 28.245334 32 32 0 0 0 28.245334-35.328c-7.68-69.269333 10.666667-126.933333 36.010666-169.749334a225.792 225.792 0 0 1 45.504-54.677333l27.648 69.184a32 32 0 0 0 41.6 17.834667 32 32 0 0 0 17.834667-41.6l-42.666667-106.666667A32 32 0 0 0 308.48 76.8z" p-id="4777" fill="#409eff"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1702540720819" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4776" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 490.666667c-19.989333 0-37.354667 3.2-52.074667 13.866666s-22.592 29.717333-22.592 44.8c0 19.136 9.066667 27.392 15.509334 34.133334 6.4 6.72 13.098667 11.797333 19.456 16.234666 12.672 8.917333 24.704 15.253333 24.704 15.253334a32 32 0 0 0 29.994666 0s12.032-6.336 24.704-15.253334c6.357333-4.437333 13.034667-9.514667 19.456-16.234666 6.442667-6.741333 15.509333-14.997333 15.509334-34.133334 0-15.082667-7.850667-34.133333-22.592-44.8-14.72-10.666667-32.085333-13.866667-52.074667-13.866666z m0.533333 32a32 32 0 0 0-32 32c0 14.464-3.754667 47.168 5.098667 80.213333 4.416 16.512 12.714667 34.304 28.458667 47.957333 15.722667 13.653333 37.930667 21.162667 62.442666 21.162667 52.650667 0 96-43.349333 96-96a32 32 0 0 0-32-32 32 32 0 0 0-32 32c0 18.048-13.930667 32-32 32-12.8 0-17.28-2.709333-20.522666-5.546667-3.285333-2.837333-6.314667-7.722667-8.533334-16.128-4.522667-16.789333-2.944-42.773333-2.944-63.658666a32 32 0 0 0-32-32z m-0.533333 0a32 32 0 0 0-32 32c0 20.906667 1.578667 46.869333-2.922667 63.658666-2.24 8.405333-5.269333 13.290667-8.533333 16.128-3.264 2.837333-7.744 5.546667-20.544 5.546667-18.048 0-32-13.952-32-32A32 32 0 0 0 384 576a32 32 0 0 0-32 32c0 52.650667 43.349333 96 96 96 24.533333 0 46.72-7.530667 62.464-21.162667 15.722667-13.653333 24.042667-31.445333 28.458667-47.957333 8.832-33.045333 5.077333-65.749333 5.077333-80.213333a32 32 0 0 0-32-32z m128-160a42.666667 42.666667 0 0 1-42.666667 42.666666 42.666667 42.666667 0 0 1-42.666666-42.666666 42.666667 42.666667 0 0 1 42.666666-42.666667 42.666667 42.666667 0 0 1 42.666667 42.666667z m-170.666667 0a42.666667 42.666667 0 0 1-42.666666 42.666666 42.666667 42.666667 0 0 1-42.666667-42.666666 42.666667 42.666667 0 0 1 42.666667-42.666667 42.666667 42.666667 0 0 1 42.666666 42.666667z m42.666667-202.666667c-233.173333 0-416 224.021333-416 448 0 112 48.704 200.746667 125.418667 257.664C298.133333 922.581333 400.768 949.333333 512 949.333333s213.866667-26.752 290.581333-83.669333S928 720 928 608c0-223.978667-182.826667-448-416-448z m0 64c190.976 0 352 195.605333 352 384 0 94.208-37.269333 160.085333-99.541333 206.293333C702.186667 860.48 612.842667 885.333333 512 885.333333c-100.842667 0-190.186667-24.853333-252.458667-71.04C197.269333 768.085333 160 702.208 160 608c0-188.394667 161.024-384 352-384z m203.52-147.2a32 32 0 0 0-41.237333 17.984l-42.666667 106.666667a32 32 0 0 0 17.834667 41.6 32 32 0 0 0 41.6-17.834667l27.648-69.184c14.613333 12.906667 31.146667 30.421333 45.504 54.677333 25.344 42.816 43.690667 100.48 36.010666 169.749334a32 32 0 0 0 28.245334 35.328 32 32 0 0 0 35.328-28.245334 336.469333 336.469333 0 0 0-44.501334-209.450666C787.925333 125.077333 751.36 90.581333 715.52 76.8z m-407.04 0c-35.84 13.781333-72.405333 48.277333-103.765333 101.290667a336.469333 336.469333 0 0 0-44.501334 209.450666 32 32 0 0 0 35.328 28.245334 32 32 0 0 0 28.245334-35.328c-7.68-69.269333 10.666667-126.933333 36.010666-169.749334a225.792 225.792 0 0 1 45.504-54.677333l27.648 69.184a32 32 0 0 0 41.6 17.834667 32 32 0 0 0 17.834667-41.6l-42.666667-106.666667A32 32 0 0 0 308.48 76.8z" p-id="4777" fill="#00DA72"></path></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -7,7 +7,7 @@
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
line-height: 1.5;
color: #24292e;
color: #fff;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 16px;
line-height: 1.5;

View File

@@ -33,7 +33,7 @@
bottom: 0;
left: -50%;
font-size: 20px;
color: #000;
color: #fff;
}
.loader .binary {

View File

@@ -0,0 +1,39 @@
/* Element Plus 主色调覆盖 */
html.dark {
--el-color-primary: #00DA72;
--el-color-primary-light-3: #40e394;
--el-color-primary-light-5: #66eaa6;
--el-color-primary-light-7: #8cf1b8;
--el-color-primary-light-8: #a6f5c9;
--el-color-primary-light-9: #bff8db;
--el-color-primary-dark-2: #00b05c;
--el-menu-hover-bg-color: none;
}
.el-tag {
--el-tag-border-radius: 0px;
}
.el-tag.el-tag--primary {
--el-tag-text-color: #000;
}
.el-button {
border-radius: 0px;
}
.el-card {
--el-card-bg-color: none !important;
border-radius: 0px !important;
}
.el-card:hover {
border-color: rgba(0, 255, 136, 0.4) !important;
}
:root {
--w-e-toolbar-bg-color: #0E131D !important;
--w-e-toolbar-color: #fff !important;
--w-e-textarea-bg-color: #0E131D !important;
}

View File

@@ -3,6 +3,7 @@
* http://cssreset.com
* 全局初始化样式
*/
html,
body,
div,
@@ -78,25 +79,30 @@ mark,
audio,
video,
input {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-weight: normal;
vertical-align: baseline;
outline: none;
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-weight: normal;
vertical-align: baseline;
outline: none;
}
body {
/* px-to-viewport-ignore-next */
min-width: 1024px; /* px-to-viewport-ignore */
}
html {
/* px-to-viewport-ignore-next */
min-width: 1024px; /* px-to-viewport-ignore */
/* px-to-viewport-ignore-next */
min-width: 1024px;
/* px-to-viewport-ignore */
}
html {
/* px-to-viewport-ignore-next */
min-width: 1024px;
/* px-to-viewport-ignore */
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
@@ -107,88 +113,99 @@ header,
menu,
nav,
section {
display: block;
display: block;
}
blockquote,
q {
quotes: none;
quotes: none;
}
blockquote::before,
blockquote::after,
q::before,
q::after {
content: none;
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
border-collapse: collapse;
border-spacing: 0;
}
/* custom */
li {
//list-style: none;
//list-style: none;
}
button {
outline: none;
cursor: pointer;
background-color: transparent;
border-style: none;
outline: none;
cursor: pointer;
background-color: transparent;
border-style: none;
}
.el-textarea__inner::placeholder {
color: #999 !important;
color: #999 !important;
}
.el-input__inner::placeholder {
color: #999 !important;
color: #999 !important;
}
input::-webkit-input-placeholder {
color: #999 !important;
color: #999 !important;
}
.el-input__inner::-webkit-input-placeholder {
color: #999 !important;
color: #999 !important;
}
input::placeholder {
color: #999 !important;
color: #999 !important;
}
textarea::placeholder {
color: #999 !important;
color: #999 !important;
}
* {
box-sizing: border-box;
box-sizing: border-box;
}
html,
body,
#app {
width: 100%;
height: 100%;
font-family: Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbo;
width: 100%;
// height: 100%;
font-family: Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbo;
}
/* //滚动条样式 */
::-webkit-scrollbar {
width: 10px;
height: 10px;
width: 10px;
height: 10px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
border-radius: 10px;
border-radius: 10px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #ccc;
border-radius: 10px;
background: #ccc;
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(249, 222, 222, 0.6);
}
background: rgba(249, 222, 222, 0.6);
}

View File

@@ -9,7 +9,7 @@
justify-content: center;
align-items: center;
height: 100%;
/*background-color: #272162;*/
background: linear-gradient(135deg, #0a0a0a 0%, #0d1520 30%, #0a0a0a 70%, #0f1520 100%), linear-gradient(135deg, rgba(0, 255, 136, 0.03) 0%, rgba(0, 0, 0, 0.8) 50%, rgba(0, 255, 136, 0.02) 100%);
}
.div-content
@@ -24,7 +24,8 @@
{
width: 414px;
height:522px;
background-color: #FFFFFF;
border: 1px solid rgba(0, 255, 136, 0.2);
/* background-color: #FFFFFF; */
}
.div-right
{
@@ -89,22 +90,20 @@ height: 25px;
}
.left-btn button
{
border: 1px solid #000;
border: 1px solid rgba(0, 255, 136, 0.2);
width: 100px;
height: 35px;
cursor: pointer;
}
.btn-login
{
background-color: #EF6562;
color: #FFFFFF;
color: #000000;
background: #00DB73;
}
.btn-reg
{
margin-left: 20px;
background-color: #FFFFFF;
}
.left-lable
{
@@ -197,8 +196,8 @@ align-items: flex-end;
{
width: 414px;
height:522px;
background-color: #FFFFFF;
/* background-color: #FFFFFF; */
border: 1px solid rgba(0, 255, 136, 0.2);
}
.div-right-register
{
@@ -218,4 +217,12 @@ align-items: flex-end;
{
width: 100%;
height: 100%;
}
}
.btn-login:hover {
box-shadow: 0 10px 25px rgba(0, 255, 136, 0.3);
}
.btn-reg:hover {
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.3);
}

View File

@@ -138,7 +138,7 @@
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;
color: #409EFF;
color: #00DA72;
margin-left: 10px;
}

View File

@@ -1,6 +1,6 @@
<template>
<el-button text @click="agree">
<el-icon v-if="data.isAgree" color="#409EFF">
<el-icon v-if="data.isAgree" color="#00DA72">
<CircleCheckFilled />
</el-icon>
<el-icon v-else color="#1E1E1E">

View File

@@ -19,7 +19,7 @@ overflow-x: hidden;
margin-bottom: 0;
overflow-x: hidden;
.header {
background-color: #409eff;
background-color: #00DA72;
color: white;
height: 30px;
display: flex;

View File

@@ -48,7 +48,6 @@ a:hover {
}
.botton-div {
background: transparent;
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
display: flex;
flex-wrap: wrap;

View File

@@ -44,11 +44,12 @@
<div class="item item-bottom">
<div class="tag-list">
<el-tag v-if="discuss.permissionRoleCodes.length>0" v-for="item in discuss.permissionRoleCodes" effect="dark" type="danger" :key="item">{{item}}</el-tag>
<el-tag class="tag-list-item" v-if="discuss.permissionRoleCodes.length>0" v-for="item in discuss.permissionRoleCodes" effect="dark" type="danger" :key="item">{{item}}</el-tag>
<el-tag v-if="discuss.title!=''&& discuss.lables.length===0">暂无标签</el-tag>
<el-tag class="tag-list-item" v-if="discuss.title!=''&& discuss.lables.length===0">暂无标签</el-tag>
<el-tag v-if="discuss.lables.length>0" v-for="item in discuss.lables" :key="item.id">{{item.name}}</el-tag>
<el-tag class="tag-list-item" v-if="discuss.lables.length>0" v-for="item in discuss.lables" :key="item.id">{{item.name}}</el-tag>
</div>
<el-space :size="10" :spacer="spacer">
<div class="item-description">
@@ -142,7 +143,8 @@ onMounted(() => {
</script>
<style scoped lang="scss">
.el-card {
border: 2px solid white;
border: 1px solid rgba(0, 255, 136, 0.2);
animation-delay: 0.4s;
}
.item-bottom {
@@ -177,8 +179,7 @@ onMounted(() => {
top: 10px;
right: 10px;
padding: 2px 15px;
border-radius: 5px;
color: rgb(8, 119, 229);
color: #00DA72;
background-color: #ecf5ff;
font-size: 14px;
.icon {
@@ -224,7 +225,7 @@ onMounted(() => {
right: -26px;
z-index: 1;
padding: 14px 22px 2px;
background-color: #ff9900;
background-color: #00DA72;
transform: rotate(45deg);
font-size: 12px;
color: #ffffff;
@@ -237,7 +238,7 @@ onMounted(() => {
left: 70%; /* 盒子中央位置 */
transform: translateX(-50%); /* 水平居中 */
padding: 5px 10px;
background-color: #ff9900;
background-color: #00DA72;
color: #ffffff;
font-size: 12px;
border-radius: 5px;
@@ -250,4 +251,10 @@ onMounted(() => {
opacity: 1; /* 鼠标悬浮时完全显示 */
}
}
.tag-list-item{
border: 0px;
color: #BCBCBC;
background: none;
}
</style>

View File

@@ -144,5 +144,28 @@ const change=(value,render)=>{
width: 100%;
}
.edit ::v-deep(.auto-textarea-input){
/* background: none !important; */
background: #1D1E1F !important;
color: #fff !important;
}
.edit ::v-deep(.content-input-wrapper)
{
background: #1D1E1F !important;
color: #fff !important;
}
.edit ::v-deep(.v-note-edit)
{
background: #1D1E1F !important;
color: #fff !important;
}
.edit ::v-deep(.v-note-op){
background: #1D1E1F !important;
color: #fff !important;
}
.edit ::v-deep(.v-show-content){
background: #1D1E1F !important;
color: #fff !important;
}
</style>

View File

@@ -55,7 +55,7 @@ const props = defineProps(["name", "introduction", "id", "isPublish"]);
right: -26px;
z-index: 1;
padding: 14px 22px 2px;
background-color: #ff9900;
background-color: #00DA72;
transform: rotate(45deg);
font-size: 12px;
color: #ffffff;
@@ -68,7 +68,7 @@ const props = defineProps(["name", "introduction", "id", "isPublish"]);
left: 70%; /* 盒子中央位置 */
transform: translateX(-50%); /* 水平居中 */
padding: 5px 10px;
background-color: #ff9900;
background-color: #00DA72;
color: #ffffff;
font-size: 12px;
border-radius: 5px;

View File

@@ -45,8 +45,8 @@ onMounted(async () => {
height: 2.6rem;
margin: 0 0.2rem;
text-align: center;
border-radius: 4px;
background-color: #fafafa;
/* background: rgba(0, 255, 136, 0.1); */
font-size: 14px;
color: #00ff88;
}
</style>

View File

@@ -27,6 +27,9 @@ const startAnimate=()=>{
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
centerX = canvas.width / 2;
centerY = canvas.height / 2;
drawStars();
});
createStars(props.number??1000);
@@ -44,10 +47,9 @@ function createStars(numStars) {
}
function drawStars() {
ctx.fillStyle = '#f0f2f5';
ctx.fillStyle = '#00DA72';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
stars.forEach(star => {
const k = 128 / star.z;
const px = star.x * k + centerX;
@@ -69,7 +71,7 @@ function drawStars() {
function animate() {
drawStars();
// requestAnimationFrame(animate);
// requestAnimationFrame(animate); // 注释掉动画循环以节省性能
}

View File

@@ -24,14 +24,14 @@
<span>
<a
style="color: #409eff; margin-left: 8px"
style="color: #00DA72; margin-left: 8px"
@click="$emit('create', node, data)"
v-if="isArticleUser && isAddArticle"
>
+
</a>
<a
style="color: #409eff; margin-left: 8px"
style="color: #00DA72; margin-left: 8px"
@click="$emit('update', node, data)"
v-if="isArticleUser && isEditArticle"
>

View File

@@ -1,5 +1,4 @@
<template>
<StarBackground :speed="0.2" :number="1000"/>
<div class="content-main">
<RouterView />
</div>
@@ -7,7 +6,6 @@
</template>
<script setup>
import StarBackground from "@/components/StarBackground.vue";
</script>
<style scoped>
.content-main {

View File

@@ -91,7 +91,7 @@
<div v-if="item.isRead" class="notice-msg" style="background-color: #f7f7f7;">
<div v-if="item.isRead" class="notice-msg" >
<span class="notice-time">[已读]通知时间 {{ dayjs(item.creationTime).format('YYYY年M月D日 HH时mm分ss秒') }}</span>
<div v-html="item.message"></div>
</div>

View File

@@ -18,6 +18,7 @@ onUnmounted(()=>{
.chat-body
{
height: 100%;
/* padding: 5%; */
display: flex;
justify-content: center;

View File

@@ -44,23 +44,23 @@ const handleScroll = () => {
.common {
&-layout {
width: 100%;
height: 100%;
// height: 100%;
}
&-container {
width: 100%;
height: 100%;
// height: 100%;
}
&-header {
width: 100%;
background-color: #fff;
background: #0A0B0C;
box-shadow: rgba(0, 0, 0, 0.1) -4px 9px 25px -6px;
height: 60px;
display: flex;
justify-content: center;
}
&-main {
height: calc(100% - 50px);
overflow: visible !important;
background: linear-gradient(135deg, #0a0a0a 0%, #0d1520 30%, #0a0a0a 70%, #0f1520 100%),linear-gradient(135deg, rgba(0, 255, 136, 0.03) 0%, rgba(0, 0, 0, 0.8) 50%, rgba(0, 255, 136, 0.02) 100%);
}
}
@@ -68,7 +68,6 @@ const handleScroll = () => {
margin: 0;
padding: 0;
min-height: 10rem;
//background-color: #f0f2f5;
}
.fixed {
@@ -79,3 +78,4 @@ const handleScroll = () => {
z-index: 99999;
}
</style>

View File

@@ -12,11 +12,9 @@
</div>
</div>
</div>
<StarBackground :speed="0.01" :number="4000"/>
</template>
<!-- <style src="@/assets/styles/login.scss" scoped></style> -->
<script setup>
import StarBackground from "@/components/StarBackground.vue";
import useConfigStore from "@/stores/config";
const configStore=useConfigStore();
const isIcp=import.meta.env.VITE_APP_ICP==="true";

View File

@@ -39,20 +39,19 @@ const handleScroll = () => {
}
};
</script>
<style scoped lang="scss">
.common {
&-layout {
width: 100%;
height: 100%;
// height: 100%;
}
&-container {
width: 100%;
height: 100%;
// height: 100%;
}
&-header {
width: 100%;
background-color: #fff;
background: #0A0B0C;
box-shadow: rgba(0, 0, 0, 0.1) -4px 9px 25px -6px;
height: 60px;
display: flex;
@@ -63,6 +62,7 @@ const handleScroll = () => {
justify-content: center;
width: 100%;
height: calc(100% - 50px);
background: linear-gradient(135deg, #0a0a0a 0%, #0d1520 30%, #0a0a0a 70%, #0f1520 100%),linear-gradient(135deg, rgba(0, 255, 136, 0.03) 0%, rgba(0, 0, 0, 0.8) 50%, rgba(0, 255, 136, 0.02) 100%);
}
}
@@ -70,7 +70,7 @@ const handleScroll = () => {
margin: 0;
padding: 0;
min-height: 10rem;
background-color: #f0f2f5;
//background-color: #f0f2f5;
}
.fixed {

View File

@@ -23,14 +23,14 @@ import ActivityNav from './ActivityNav.vue'
.menuList {
width: 20%;
height: 100%;
background-color: #fff;
border-radius: 5px;
background-color: #1D1E1F;
border: 1px solid rgba(0, 255, 136, 0.2);
}
.page {
width: 75%;
height: 100%;
background-color: #fff;
border-radius: 5px;
border: 1px solid rgba(0, 255, 136, 0.2);
background-color: #1D1E1F;
}
}
</style>

View File

@@ -5,7 +5,9 @@ import App from "./App.vue";
import router from "./router";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import "element-plus/dist/index.css";
import 'element-plus/theme-chalk/dark/css-vars.css'
import '@/assets/styles/element-override.css' //重写element
import "./assets/main.css";
import "@/assets/styles/index.scss"; // global css
@@ -14,19 +16,21 @@ import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import directive from "./directive"; // directive
import VueLuckyCanvas from '@lucky-canvas/vue'
import "./permission";
(async () => {
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
directive(app);
app.use(router);
app.use(VueLuckyCanvas);
await router.isReady();
app.mount("#app");
})();
(async() => {
const app = createApp(App);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
directive(app);
app.use(router);
app.use(VueLuckyCanvas);
await router.isReady();
app.mount("#app");
})();

View File

@@ -518,13 +518,13 @@ watch(
}
.left-div .el-col {
background-color: #ffffff;
background-color: #1D1E1F;
min-height: 12rem;
margin-bottom: 1rem;
}
.right-div .el-col {
background-color: #ffffff;
// background-color: #1D1E1F;
min-height: 10rem;
margin-bottom: 1rem;
}
@@ -548,7 +548,7 @@ watch(
h2 {
margin-bottom: 0.5em;
color: rgba(0, 0, 0, 0.85);
color: #fff;
font-weight: 600;
font-size: 30px;
line-height: 1.35;

View File

@@ -470,7 +470,8 @@ const getFile = async (e) => {
.body-div {
position: relative;
min-height: 1000px;
background-color: #fff;
/* background-color: #fff; */
border: 1px solid rgba(0, 255, 136, 0.2);
margin: 1.5rem;
padding: 1.5rem;

View File

@@ -503,7 +503,7 @@ const clickCopyEvent = async function (event) {
<template>
<div style="position: absolute; top: 0;left: 0;" v-show="isShowTipNumber>0">
<div style="position: absolute; top: 0;left: 0;z-index: 100;" v-show="isShowTipNumber>0">
<p>当前版本3.0.0</p>
<p>tip:官方学习交流群每次发送消息消耗 1 钱钱</p>
<p>tip:点击聊天窗口右上角"X"可退出</p>
@@ -802,7 +802,7 @@ ul {
.user-list {
height: calc(100% - 75px);
color: #000;
overflow-y: auto;
@@ -1182,7 +1182,7 @@ ul {
overflow-x: scroll;
.header {
background-color: #409eff;
background-color: #00DA72;
color: white;
height: 30px;
display: flex;

View File

@@ -57,7 +57,7 @@ const handleClick = (item) => {
<style scoped lang="scss">
.tabs {
display: flex;
background-color: #fff;
border: 1px solid rgba(0, 255, 136, 0.2);
padding: 1rem;
margin: 1rem 0rem;
color: #8c8c8c;
@@ -79,6 +79,6 @@ const handleClick = (item) => {
}
}
.active-tab {
color: #409eff;
color: #00DA72;
}
</style>

View File

@@ -225,7 +225,7 @@ const tabList = ref([
align-items: center;
}
.header {
background-color: #ffffff;
border: 1px solid rgba(0, 255, 136, 0.2);
padding: 1rem;
margin: 1rem 0rem;
}

View File

@@ -154,7 +154,7 @@ margin: 10px auto;">
<InfoCard header="简介" text="">
<template #content>
<div class="introduce">
没有什么能够阻挡人类对代码<span style="color: #1890ff">优雅</span>的追求
没有什么能够阻挡人类对代码<span style="color: #00DA72">优雅</span>的追求
</div>
</template>
</InfoCard>
@@ -215,7 +215,7 @@ margin: 10px auto;">
</template>
</el-col>
<el-col :span="24" style="background-color: #ffffff;">
<el-col :span="24" >
<BottomInfo/>
</el-col>
</el-row>
@@ -533,28 +533,25 @@ const onClickToWeChat = () => {
.left-div .el-col,
.right-div .el-col {
background-color: #ffffff;
margin-bottom: 1rem;
border-radius: 8px; /* 增加圆角 */
margin-bottom: 0.5rem;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05); /* 添加微妙阴影 */
transition: all 0.3s ease; /* 过渡效果 */
&:hover {
transform: translateY(-5px); /* 悬停时微抬升 */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); /* 悬停时增强阴影 */
// transform: translateY(-5px); /* 悬停时微抬升 */
// box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); /* 悬停时增强阴影 */
}
}
/* 简介卡片样式特别处理 */
.introduce {
color: rgba(0, 0, 0, 0.65); /* 更深的颜色提高对比度 */
font-size: 14px; /* 稍微增大字体 */
line-height: 1.6; /* 增加行高 */
padding: 15px 5px; /* 增加内边距 */
letter-spacing: 0.5px; /* 字间距 */
span {
color: #1890ff;
color: #00DA72;
font-weight: 600; /* 加粗 */
padding: 0 2px; /* 增加内边距 */
}
@@ -593,12 +590,11 @@ const onClickToWeChat = () => {
height: 100%;
position: relative;
background: url("@/assets/box/online_bg.svg") no-repeat;
background-color: #fff;
background-color: #080E14;
background-position: 0 30px;
background-size: 150% 100%;
border: 1px solid #409eff;
border-radius: 5px;
color: #409eff;
border: 1px solid rgba(0, 255, 136, 0.2);
color: #00DA72;
.content {
width: 100%;
@@ -634,9 +630,8 @@ const onClickToWeChat = () => {
font-size: 12px;
text-align: center;
border: 1px solid #d9ecff;
border-radius: 5px;
color: #409eff;
background-color: #ecf5ff;
color: #000;
background-color: #BFF8DB;
}
}
}
@@ -653,7 +648,6 @@ const onClickToWeChat = () => {
/* gap: 10px; */
/* padding: 15px; */
/* background-color: #f9f9f9; */
border-radius: 8px;
.el-col {
flex-direction: column;
@@ -661,7 +655,6 @@ const onClickToWeChat = () => {
display: flex;
cursor: pointer;
padding: 15px 0; /* 增加内边距 */
border-radius: 6px; /* 圆角 */
transition: all 0.3s ease;
.el-icon {
@@ -671,12 +664,12 @@ const onClickToWeChat = () => {
}
&:hover {
background-color: #ebf5ff; /* 更柔和的悬停色 */
color: #409eff; /* 文字颜色变化 */
// background-color: #ebf5ff; /* 更柔和的悬停色 */
color: #00DA72; /* 文字颜色变化 */
// border: 1px solid rgba(0, 255, 136, 0.2);
.el-icon {
color: #409eff; /* 图标颜色跟随变化 */
transform: scale(1.1); /* 图标微放大 */
color: #00DA72; /* 图标颜色跟随变化 */
// transform: scale(1.1); /* 图标微放大 */
}
}
}
@@ -686,7 +679,7 @@ const onClickToWeChat = () => {
.VisitsLineChart p {
display: flex;
justify-content: flex-end;
color: #409eff;
color: #fff;
cursor: pointer;
margin-top: 8px;
}
@@ -705,14 +698,14 @@ const onClickToWeChat = () => {
//走马灯,聊天室链接
.chat-hub {
background-color: #E6A23C;
// background-color: #00DA72;
color: #ffffff;
margin-bottom: 10px;
width: 100%;
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
border: 1px solid rgba(0, 255, 136, 0.2);
span {
color: red;
}
@@ -746,12 +739,10 @@ const onClickToWeChat = () => {
.scrollable-div::-webkit-scrollbar-track {
background: #f5f5f5;
border-radius: 10px;
}
.scrollable-div::-webkit-scrollbar-thumb {
background: linear-gradient(to bottom, #e0e0e0, #bdbdbd); /* 渐变色滚动条 */
border-radius: 10px;
border: 2px solid transparent;
background-clip: content-box;
}
@@ -764,9 +755,8 @@ const onClickToWeChat = () => {
.switch-span {
display: inline-block;
padding: 5px 12px;
background-color: #ecf5ff;
color: #409eff;
border-radius: 20px; /* 更圆润的形状 */
// background-color: #00DA72;
color: #fff;
font-size: 13px;
cursor: pointer;
transition: all 0.3s ease;
@@ -775,9 +765,10 @@ const onClickToWeChat = () => {
overflow: hidden;
&:hover {
background-color: #409eff;
border: 1px solid rgba(0, 255, 136, 0.2);
// background-color: #00DA72;
color: #fff !important;/* 悬浮时文字变为白色 */
transform: translateY(-2px);
// transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(64, 158, 255, 0.25);
}

View File

@@ -91,7 +91,7 @@ const userImageSrc = computed(() => {
.name {
cursor: pointer;
width: 100%;
color: #252933;
color: #ffffff;
margin-left: 5px;
white-space: nowrap;
overflow: hidden;
@@ -108,7 +108,7 @@ const userImageSrc = computed(() => {
justify-content: flex-start;
align-items: center;
font-size: 16px;
color: #1171ee;
color: #00DA72;
}
}
}

View File

@@ -87,7 +87,7 @@ const userImageSrc = computed(() => {
align-items: center;
.name {
width: 100px;
color: #252933;
color: #fff;
margin-left: 5px;
white-space: nowrap;
overflow: hidden;
@@ -107,7 +107,7 @@ const userImageSrc = computed(() => {
justify-content: flex-start;
align-items: center;
font-size: 16px;
color: #1171ee;
color: #00DA72;
}
}
}

View File

@@ -76,7 +76,7 @@ const handleClickTheme = (id) => {
font-size: 16px;
cursor: pointer;
width: 230px;
color: #252933;
color: #fff;
margin-left: 5px;
white-space: nowrap;
overflow: hidden;
@@ -96,7 +96,7 @@ const handleClickTheme = (id) => {
width: 30px;
margin-left: 10px;
cursor: pointer;
color: #252933;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -370,7 +370,7 @@
display: flex;
justify-content: flex-end;
align-items: center;
color: #409eff;
color: #00DA72;
}
.top {
height: 40px;

View File

@@ -12,6 +12,10 @@ const route = useRoute();
const registerFormRef = ref();
//验证码弹窗
const codeDialogVisible=ref(false);
//防止重复点击
const isSending=ref(false);
//防止重复注册
const isRegistering=ref(false);
// 获取图片验证码
const codeImageURL = computed(() => useUserStore().codeImageURL);
@@ -49,14 +53,26 @@ const registerRules = reactive({
});
const register = async (formEl) => {
if (!formEl) return;
// 防止重复点击
if (isRegistering.value) {
ElMessage({
message: "正在注册中,请稍候...",
type: "warning",
duration: 1000,
});
return;
}
await formEl.validate(async (valid) => {
if (valid) {
try {
if (registerForm.password !== passwordConfirm.value) {
ElMessage.error("两次密码输入不一致");
return;
}
isRegistering.value = true;
await registerFun(registerForm);
//注册成功返回登录
handleSignInNow();
@@ -66,6 +82,8 @@ const register = async (formEl) => {
type: "error",
duration: 2000,
});
} finally {
isRegistering.value = false;
}
}
});
@@ -102,38 +120,52 @@ const handleSignInNow=()=>{
router.push("/login");
}
const captcha = async () => {
// 防止重复点击
if (isSending.value) return;
// 立即关闭弹窗,防止用户连续点击
codeDialogVisible.value = false;
if (registerForm.email!==""&&phoneForm.code!=="")
{
phoneForm.email=registerForm.email;
const { data } = await getCodeEmail(phoneForm);
registerForm.uuid = data.uuid;
codeDialogVisible.value=false;
ElMessage({
message: `已向${registerForm.email}发送验证码,请注意查收`,
type: "success",
});
isDisabledCode.value = true;
let time = 60; //定义剩下的秒数
let timer = setInterval(function () {
if (time == 0) {
//清除定时器和复原按钮
clearInterval(timer);
codeInfo.value = "发送验证码";
time = 60; //这个10是重新开始
} else {
codeInfo.value = "剩余" + time + "秒";
time--;
}
}, 1000);
isSending.value = true;
try {
phoneForm.email=registerForm.email;
const { data } = await getCodeEmail(phoneForm);
registerForm.uuid = data.uuid;
ElMessage({
message: `已向${registerForm.email}发送验证码,请注意查收`,
type: "success",
});
isDisabledCode.value = true;
let time = 60; //定义剩下的秒数
let timer = setInterval(function () {
if (time == 0) {
//清除定时器和复原按钮
clearInterval(timer);
codeInfo.value = "发送验证码";
time = 60; //这个10是重新开始
isSending.value = false;
} else {
codeInfo.value = "剩余" + time + "秒";
time--;
}
}, 1000);
} catch (error) {
isSending.value = false;
ElMessage({
message: "发送失败,请重试",
type: "error",
});
}
}
else
{
ElMessage({
message: `请先输入手机号`,
message: `请先输入邮箱和图片验证码`,
type: "warning",
});
}
};
</script>
@@ -202,7 +234,7 @@ const captcha = async () => {
</div>
</el-form>
<div class="left-btn">
<button type="button" class="btn-login" @click="register(registerFormRef)">注册</button>
<button type="button" class="btn-login" :disabled="isRegistering" @click="register(registerFormRef)">{{ isRegistering ? '注册中...' : '注册' }}</button>
<button type="button" class="btn-reg" @click="handleSignInNow">前往登录</button>
</div>
</div>

View File

@@ -286,11 +286,13 @@ $userHeight: 220px;
$remarkHeight: $topHeight - $userHeight;
.div-top {
padding-top: 10px;
background-color: #ffffff;
border: 1px solid rgba(0, 255, 136, 0.2);
border-radius: 0;
background-color: #1D1E1F;
min-height: $topHeight;
margin-top: 30px;
font-size: 14px;
color: #555666;
color: #fff;
&-user {
display: flex;
width: 100%;
@@ -310,7 +312,7 @@ $remarkHeight: $topHeight - $userHeight;
padding-top: 5px;
padding-bottom: 5px;
&-left {
color:#222226;
color:#fff;
font-size: 23px;
font-weight: 800;
.el-tag{
@@ -327,7 +329,7 @@ $remarkHeight: $topHeight - $userHeight;
.user-remark span {
font-size: larger;
font-weight: bold;
color: black;
color: #fff;
}
p {
@@ -366,14 +368,15 @@ width: 80%;
&-left {
padding-top: 30px;
height: 280px;
background-color: #FFFFFF;
border: 1px solid rgba(0, 255, 136, 0.2);
}
&-right {
background-color: #f0f2f5;
background-color: #0C121A;
padding-left: 20px;
&-content {
width: 100%;
background-color: #FFFFFF;
background-color: #0C121A;
}
}
.el-menu

View File

@@ -104,7 +104,6 @@ const confirmBuy=async()=>{
margin-bottom: 40px;
margin-top: 20px;
padding: 20px;
background-color: #ffffff;
width: 60%;
.title {

View File

@@ -177,7 +177,6 @@ onUnmounted(() => {
.start-body {
height: 100%;
width: 100%;
background-color: #FCFCFC;
}
@@ -210,7 +209,7 @@ onUnmounted(() => {
height: 1800px;
padding: 48px;
background-color: #fff;
border-radius: 12px;
border-radius: 0px;
border: 0;
box-shadow: 0 0 1rem rgba(0, 0, 0, .08);
display: flex;
@@ -221,7 +220,7 @@ onUnmounted(() => {
&-right {
width: 0%;
background-color: #409EFF;
background-color: #00DA72;
}
@@ -238,7 +237,7 @@ onUnmounted(() => {
h4 {
font-size: 20px;
font-weight: 500;
color: #409EFF;
color: #00DA72;
margin-bottom: .25rem;
margin-top: 0;

View File

@@ -23,7 +23,7 @@ watch(()=>props.isSelect,(n,o)=>{
</template>
<style lang="scss" scoped>
.selected {
border: 2px solid #409EFF !important;
border: 2px solid #00DA72 !important;
background-color: #FFFFFF !important;
}
@@ -42,7 +42,7 @@ watch(()=>props.isSelect,(n,o)=>{
}
.box:hover {
border: 2px solid #409EFF;
border: 2px solid #00DA72;
}
</style>

View File

@@ -70,7 +70,7 @@ const copyText=async ()=>{
}
&-right{
button{
background-color:#409EFF;
background-color:#00DA72;
color: #fff !important;
height: 25px;
width: 50px;

View File

@@ -65,6 +65,6 @@ input[type=checkbox]:checked::after {
color: #fff;
font-size: 16px;
font-weight: bold;
background-color: #409EFF;
background-color: #00DA72;
}
</style>

View File

@@ -35,7 +35,7 @@ input {
width: 100%;
}
input:hover {
border: 2px solid #409EFF;
border: 2px solid #00DA72;
background-color:#FFFFFF; /* 鼠标悬停时的边框颜色 */
}
</style>