mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-02 15:50:54 +08:00
Compare commits
29 Commits
ai-hub-dev
...
bbs-sharpd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e73678c788 | ||
|
|
09a2f91cbf | ||
|
|
5b024e9443 | ||
|
|
225932eff1 | ||
|
|
3e647ef14d | ||
|
|
7cb3aea2e6 | ||
|
|
7f4b8f1c8a | ||
|
|
17f9ac6d54 | ||
|
|
3f8e6e48c0 | ||
|
|
bda4fdf69d | ||
|
|
5c85ed13fd | ||
|
|
1986901031 | ||
|
|
e1d3ec21e5 | ||
|
|
f45283dade | ||
|
|
31c44d8df7 | ||
|
|
bf443963c8 | ||
|
|
a0eb234539 | ||
|
|
b6d670c240 | ||
|
|
b5fb2c42c6 | ||
|
|
d72cc529ba | ||
|
|
660bd00cae | ||
|
|
b5489711ec | ||
|
|
76717c4f8a | ||
|
|
3d53d0bcd6 | ||
|
|
c7c9428b68 | ||
|
|
991a970d6a | ||
|
|
cbe93b9f7e | ||
|
|
5d7217b775 | ||
|
|
d6836b8bcf |
@@ -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}
|
||||
4:Do not include ``` markdown format in the output, display it directly in plain text
|
||||
5:The 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-4,gpt-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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
"ComputedRef": true,
|
||||
"DirectiveBinding": true,
|
||||
"EffectScope": true,
|
||||
"ElMessage": true,
|
||||
"ElMessageBox": true,
|
||||
"ExtractDefaultPropTypes": true,
|
||||
"ExtractPropTypes": true,
|
||||
"ExtractPublicPropTypes": true,
|
||||
|
||||
@@ -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无限制使用,直连,AI,claude ,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无限制使用,直连,AI,claude ,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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts"></script>
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-view />
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>
|
||||
@@ -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",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
<script setup></script>
|
||||
|
||||
<template>
|
||||
<el-config-provider :locale="locale">
|
||||
<RouterView />
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -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;
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
bottom: 0;
|
||||
left: -50%;
|
||||
font-size: 20px;
|
||||
color: #000;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.loader .binary {
|
||||
|
||||
39
Yi.Bbs.Vue3/src/assets/styles/element-override.css
Normal file
39
Yi.Bbs.Vue3/src/assets/styles/element-override.css
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -138,7 +138,7 @@
|
||||
/** 表格更多操作下拉样式 */
|
||||
.el-table .el-dropdown-link {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
color: #00DA72;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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); // 注释掉动画循环以节省性能
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -18,6 +18,7 @@ onUnmounted(()=>{
|
||||
.chat-body
|
||||
{
|
||||
height: 100%;
|
||||
|
||||
/* padding: 5%; */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
})();
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
color: #409eff;
|
||||
color: #00DA72;
|
||||
}
|
||||
.top {
|
||||
height: 40px;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -104,7 +104,6 @@ const confirmBuy=async()=>{
|
||||
margin-bottom: 40px;
|
||||
margin-top: 20px;
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
width: 60%;
|
||||
|
||||
.title {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -70,7 +70,7 @@ const copyText=async ()=>{
|
||||
}
|
||||
&-right{
|
||||
button{
|
||||
background-color:#409EFF;
|
||||
background-color:#00DA72;
|
||||
color: #fff !important;
|
||||
height: 25px;
|
||||
width: 50px;
|
||||
|
||||
@@ -65,6 +65,6 @@ input[type=checkbox]:checked::after {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
background-color: #409EFF;
|
||||
background-color: #00DA72;
|
||||
}
|
||||
</style>
|
||||
@@ -35,7 +35,7 @@ input {
|
||||
width: 100%;
|
||||
}
|
||||
input:hover {
|
||||
border: 2px solid #409EFF;
|
||||
border: 2px solid #00DA72;
|
||||
background-color:#FFFFFF; /* 鼠标悬停时的边框颜色 */
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user