mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-04-16 22:26:37 +08:00
Compare commits
22 Commits
digital-co
...
TenantConf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45736dfce9 | ||
|
|
753b5b0a26 | ||
|
|
1fd4f2754a | ||
|
|
bedee3391e | ||
|
|
176a672e86 | ||
|
|
9da6bcde41 | ||
|
|
3f8daa1d17 | ||
|
|
ed873da3b6 | ||
|
|
c0dfa83828 | ||
|
|
db08688968 | ||
|
|
400a146a48 | ||
|
|
a645264da7 | ||
|
|
09d19d876f | ||
|
|
373877cfcf | ||
|
|
9e143c0a75 | ||
|
|
37b16e8395 | ||
|
|
9c94953e0e | ||
|
|
2c48b8f881 | ||
|
|
4c4b78dda7 | ||
|
|
4ba9a7917f | ||
|
|
5d286ebc9e | ||
|
|
e69bbb46b3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -263,6 +263,7 @@ src/Acme.BookStore.Blazor.Server.Tiered/Logs/*
|
|||||||
|
|
||||||
# Use abp install-libs to restore.
|
# Use abp install-libs to restore.
|
||||||
**/wwwroot/libs/*
|
**/wwwroot/libs/*
|
||||||
|
public
|
||||||
dist
|
dist
|
||||||
.vscode
|
.vscode
|
||||||
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
|
/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.Development.json
|
||||||
|
|||||||
37
README-Docker.md
Normal file
37
README-Docker.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# 🍉Docker 构建说明
|
||||||
|
|
||||||
|
## 🍊后端
|
||||||
|
执行目录:Yi\Yi.Abp.Net8
|
||||||
|
|
||||||
|
#### 🍊启动
|
||||||
|
D:/code/csharp/source/Yi/Yi.Bbs.Vue3/yi-bbs.conf 为我的配置文件,内部带了默认的配置文件,根据自己配置进行更改
|
||||||
|
|
||||||
|
//不带配置文件
|
||||||
|
docker run -d --name yi.admin -p 19001:19001 jiftcc/yi.admin:1.0.0
|
||||||
|
|
||||||
|
//带配置文件
|
||||||
|
docker run -d --name yi.admin -p 19001:19001 -v D:/code/csharp/source/Yi/Yi.Abp.Net8/src/Yi.Abp.Web/appsettings.json:/app/appsettings.json jiftcc/yi.admin:1.0.0
|
||||||
|
|
||||||
|
|
||||||
|
#### 🍊完整代码编译
|
||||||
|
docker build -t jiftcc/yi.admin:1.0.0 -f Dockerfile .
|
||||||
|
|
||||||
|
#### 🍊快速产物编译
|
||||||
|
docker build -t jiftcc/yi.admin:1.0.0 -f DockerfileFast .
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
## 🍇前端
|
||||||
|
执行目录:Yi\Yi.Bbs.Vue3
|
||||||
|
|
||||||
|
#### 🍇启动
|
||||||
|
D:/code/csharp/source/Yi/Yi.Bbs.Vue3/yi-bbs.conf 为我的conf配置目录,默认反向代理到ccnetcore.com,根据自己后端地址进行修改配置
|
||||||
|
|
||||||
|
docker run -d --name yi.bbs -p 18001:18001 -v D:/code/csharp/source/Yi/Yi.Bbs.Vue3/yi-bbs.conf:/etc/nginx/conf.d/yi-bbs.conf jiftcc/yi.bbs:1.0.0
|
||||||
|
|
||||||
|
#### 🍇完整代码编译
|
||||||
|
docker build -t jiftcc/yi.bbs:1.0.0 -f Dockerfile .
|
||||||
|
|
||||||
|
#### 🍇快速产物编译
|
||||||
|
docker build -t jiftcc/yi.bbs:1.0.0 -f DockerfileFast .
|
||||||
|
|
||||||
12
README-en.md
12
README-en.md
@@ -35,6 +35,18 @@ A Comprehensive Solution, Ultimately Just Another Wheel.
|
|||||||
- Yi.RuoYi.Vue3:RuoYi JS Backend Frontend
|
- Yi.RuoYi.Vue3:RuoYi JS Backend Frontend
|
||||||
|
|
||||||
****
|
****
|
||||||
|
## 🍉 docker
|
||||||
|
|
||||||
|
Full content:README-Docker.md
|
||||||
|
|
||||||
|
backend:`docker run -d --name yi.admin -p 19001:19001 jiftcc/yi.admin:last`
|
||||||
|
|
||||||
|
bbs frontend:`docker run -d --name yi.bbs -p 18001:18001 -v /home/Yi/Yi.Bbs.Vue3/yi-bbs.conf:/etc/nginx/conf.d/yi-bbs.conf jiftcc/yi.bbs:last`
|
||||||
|
|
||||||
|
> In addition, we provide Docker build operation, and we hope that you can build your own image through this method
|
||||||
|
|
||||||
|
****
|
||||||
|
|
||||||
|
|
||||||
## 🍊 Official website and demo link:
|
## 🍊 Official website and demo link:
|
||||||
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -41,6 +41,17 @@ Yi框架-一套与SqlSugar一样爽的.Net8开源框架。
|
|||||||
- Yi.Pure.Vue3:Pure ts后台前端
|
- Yi.Pure.Vue3:Pure ts后台前端
|
||||||
- Yi.RuoYi.Vue3:RuoYi js后台前端
|
- Yi.RuoYi.Vue3:RuoYi js后台前端
|
||||||
|
|
||||||
|
****
|
||||||
|
## 🍉 docker 一键启动
|
||||||
|
|
||||||
|
完整内容在:README-Docker.md
|
||||||
|
|
||||||
|
后端:`docker run -d --name yi.admin -p 19001:19001 jiftcc/yi.admin:last`
|
||||||
|
|
||||||
|
bbs前端:`docker run -d --name yi.bbs -p 18001:18001 -v /home/Yi/Yi.Bbs.Vue3/yi-bbs.conf:/etc/nginx/conf.d/yi-bbs.conf jiftcc/yi.bbs:last`
|
||||||
|
|
||||||
|
> 另外我们提供docker的build操作,我们更希望你能通过此种方式二开构建属于自己的镜像
|
||||||
|
|
||||||
****
|
****
|
||||||
|
|
||||||
## 🍊 官网及演示地址:
|
## 🍊 官网及演示地址:
|
||||||
@@ -60,6 +71,7 @@ Pure后台演示地址:https://ccnetcore.com:1001 (用户cc、密码123456
|
|||||||
- [x] 完全支持微服务架构
|
- [x] 完全支持微服务架构
|
||||||
|
|
||||||
****
|
****
|
||||||
|
|
||||||
## 🍇 详细到爆炸的Yi框架教程导航:
|
## 🍇 详细到爆炸的Yi框架教程导航:
|
||||||
|
|
||||||
1. [框架快速开始教程](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(已完成)
|
1. [框架快速开始教程](https://ccnetcore.com/article/aaa00329-7f35-d3fe-d258-3a0f8380b742)(已完成)
|
||||||
|
|||||||
@@ -27,4 +27,7 @@ README.md
|
|||||||
!.git/HEAD
|
!.git/HEAD
|
||||||
!.git/config
|
!.git/config
|
||||||
!.git/packed-refs
|
!.git/packed-refs
|
||||||
!.git/refs/heads/**
|
!.git/refs/heads/**
|
||||||
|
appsettings.Development.json
|
||||||
|
appsettings.Production.json
|
||||||
|
appsettings.Staging.json
|
||||||
22
Yi.Abp.Net8/Dockerfile
Normal file
22
Yi.Abp.Net8/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER root
|
||||||
|
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 19001
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /main
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/main/src/Yi.Abp.Web"
|
||||||
|
RUN dotnet restore "Yi.Abp.Web.csproj"
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
WORKDIR "/main/src/Yi.Abp.Web"
|
||||||
|
RUN dotnet publish "Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "Yi.Abp.Web.dll"]
|
||||||
11
Yi.Abp.Net8/DockerfileFast
Normal file
11
Yi.Abp.Net8/DockerfileFast
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER root
|
||||||
|
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
RUN echo "Asia/Shanghai" > /etc/timezone
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 19001
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY ["./publish","."]
|
||||||
|
ENTRYPOINT ["dotnet", "Yi.Abp.Web.dll"]
|
||||||
@@ -36,6 +36,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
version.props = version.props
|
version.props = version.props
|
||||||
publish.bat = publish.bat
|
publish.bat = publish.bat
|
||||||
publish_Demo.bat = publish_Demo.bat
|
publish_Demo.bat = publish_Demo.bat
|
||||||
|
Dockerfile = Dockerfile
|
||||||
|
DockerfileFast = DockerfileFast
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore.Abstractions", "framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj", "{FD6D6860-3753-4747-8A26-977E4A3001F9}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.SqlSugarCore.Abstractions", "framework\Yi.Framework.SqlSugarCore.Abstractions\Yi.Framework.SqlSugarCore.Abstractions.csproj", "{FD6D6860-3753-4747-8A26-977E4A3001F9}"
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
using Hangfire;
|
using System.Linq.Expressions;
|
||||||
|
using Hangfire;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Volo.Abp.BackgroundWorkers;
|
using Volo.Abp.BackgroundWorkers;
|
||||||
using Volo.Abp.BackgroundWorkers.Hangfire;
|
using Volo.Abp.BackgroundWorkers.Hangfire;
|
||||||
|
using Volo.Abp.DynamicProxy;
|
||||||
|
|
||||||
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
namespace Yi.Framework.BackgroundWorkers.Hangfire;
|
||||||
|
|
||||||
@@ -19,11 +22,28 @@ public class YiFrameworkBackgroundWorkersHangfireModule : AbpModule
|
|||||||
var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
|
var backgroundWorkerManager = context.ServiceProvider.GetRequiredService<IBackgroundWorkerManager>();
|
||||||
var works = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
|
var works = context.ServiceProvider.GetServices<IHangfireBackgroundWorker>();
|
||||||
|
|
||||||
|
var configuration = context.ServiceProvider.GetRequiredService<IConfiguration>();
|
||||||
|
//【特殊,为了兼容内存模式,由于内存模式任务,不能使用队列】
|
||||||
|
bool.TryParse(configuration["Redis:IsEnabled"], out var redisEnabled);
|
||||||
foreach (var work in works)
|
foreach (var work in works)
|
||||||
{
|
{
|
||||||
//如果为空,默认使用服务器本地utc时间
|
//如果为空,默认使用服务器本地上海时间
|
||||||
work.TimeZone ??= TimeZoneInfo.Local;
|
work.TimeZone = TimeZoneInfo.Local;
|
||||||
await backgroundWorkerManager.AddAsync(work);
|
if (redisEnabled)
|
||||||
|
{
|
||||||
|
await backgroundWorkerManager.AddAsync(work);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object unProxyWorker = ProxyHelper.UnProxy((object)work);
|
||||||
|
RecurringJob.AddOrUpdate(work.RecurringJobId,
|
||||||
|
(Expression<Func<Task>>)(() =>
|
||||||
|
((IHangfireBackgroundWorker)unProxyWorker).DoWorkAsync(default(CancellationToken))),
|
||||||
|
work.CronExpression, new RecurringJobOptions()
|
||||||
|
{
|
||||||
|
TimeZone = work.TimeZone
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="SqlSugarCoreNoDrive" Version="$(SqlSugarVersion)" />
|
<!-- <PackageReference Include="SqlSugarCoreNoDrive" Version="$(SqlSugarVersion)" />-->
|
||||||
|
|
||||||
|
<PackageReference Include="SqlSugarCore" Version="$(SqlSugarVersion)" />
|
||||||
|
|
||||||
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />
|
<PackageReference Include="Volo.Abp.Ddd.Domain" Version="$(AbpVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,12 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
|
|||||||
|
|
||||||
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
|
if (entityInfo.PropertyName.Equals(nameof(IAuditedObject.LastModificationTime)))
|
||||||
{
|
{
|
||||||
if (!DateTime.MinValue.Equals(oldValue))
|
//最后更新时间,已经是最小值,忽略
|
||||||
|
if (DateTime.MinValue.Equals(oldValue))
|
||||||
|
{
|
||||||
|
entityInfo.SetValue(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
entityInfo.SetValue(DateTime.Now);
|
entityInfo.SetValue(DateTime.Now);
|
||||||
}
|
}
|
||||||
@@ -70,7 +75,12 @@ public class DefaultSqlSugarDbContext : SqlSugarDbContext
|
|||||||
{
|
{
|
||||||
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
|
if (typeof(Guid?) == entityInfo.EntityColumnInfo.PropertyInfo.PropertyType)
|
||||||
{
|
{
|
||||||
if (CurrentUser.Id != null)
|
//最后更新者,已经是空guid,忽略
|
||||||
|
if (Guid.Empty.Equals(oldValue))
|
||||||
|
{
|
||||||
|
entityInfo.SetValue(null);
|
||||||
|
}
|
||||||
|
else if (CurrentUser.Id != null)
|
||||||
{
|
{
|
||||||
entityInfo.SetValue(CurrentUser.Id);
|
entityInfo.SetValue(CurrentUser.Id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
|
|
||||||
private IAbpLazyServiceProvider LazyServiceProvider { get; }
|
private IAbpLazyServiceProvider LazyServiceProvider { get; }
|
||||||
|
|
||||||
|
private TenantConfigurationWrapper TenantConfigurationWrapper=> LazyServiceProvider.LazyGetRequiredService<TenantConfigurationWrapper>();
|
||||||
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
|
private ICurrentTenant CurrentTenant => LazyServiceProvider.LazyGetRequiredService<ICurrentTenant>();
|
||||||
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
|
||||||
|
private DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
||||||
|
|
||||||
private ISerializeService SerializeService => LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
|
private ISerializeService SerializeService => LazyServiceProvider.LazyGetRequiredService<ISerializeService>();
|
||||||
|
|
||||||
@@ -36,19 +38,13 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
{
|
{
|
||||||
LazyServiceProvider = lazyServiceProvider;
|
LazyServiceProvider = lazyServiceProvider;
|
||||||
|
|
||||||
var connectionString = GetCurrentConnectionString();
|
var tenantConfiguration= AsyncHelper.RunSync(async () =>await TenantConfigurationWrapper.GetAsync());
|
||||||
|
|
||||||
var connectionConfig =BuildConnectionConfig(action: options =>
|
var connectionConfig =BuildConnectionConfig(action: options =>
|
||||||
{
|
{
|
||||||
options.ConnectionString = connectionString;
|
options.ConnectionString =tenantConfiguration.GetCurrentConnectionString();
|
||||||
options.DbType = GetCurrentDbType();
|
options.DbType = GetCurrentDbType(tenantConfiguration.GetCurrentConnectionName());
|
||||||
});
|
});
|
||||||
// var connectionConfig = ConnectionConfigCache.GetOrAdd(connectionString, (_) =>
|
|
||||||
// BuildConnectionConfig(action: options =>
|
|
||||||
// {
|
|
||||||
// options.ConnectionString = connectionString;
|
|
||||||
// options.DbType = GetCurrentDbType();
|
|
||||||
// }));
|
|
||||||
SqlSugarClient = new SqlSugarClient(connectionConfig);
|
SqlSugarClient = new SqlSugarClient(connectionConfig);
|
||||||
//生命周期,以下都可以直接使用sqlsugardb了
|
//生命周期,以下都可以直接使用sqlsugardb了
|
||||||
|
|
||||||
@@ -188,41 +184,18 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
|
|
||||||
return connectionConfig;
|
return connectionConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
protected virtual DbType GetCurrentDbType(string tenantName)
|
||||||
/// db切换多库支持
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected virtual string GetCurrentConnectionString()
|
|
||||||
{
|
{
|
||||||
var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService<IConnectionStringResolver>();
|
if (tenantName == ConnectionStrings.DefaultConnectionStringName)
|
||||||
var connectionString =
|
|
||||||
AsyncHelper.RunSync(() => connectionStringResolver.ResolveAsync());
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(connectionString))
|
|
||||||
{
|
{
|
||||||
Check.NotNull(Options.Url, "dbUrl未配置");
|
return Options.DbType!.Value;
|
||||||
}
|
}
|
||||||
|
var dbTypeFromTenantName = GetDbTypeFromTenantName(tenantName);
|
||||||
return connectionString!;
|
return dbTypeFromTenantName!.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual DbType GetCurrentDbType()
|
//根据租户name进行匹配db类型: Test@Sqlite,[form:AI]
|
||||||
{
|
|
||||||
if (CurrentTenant.Name is not null)
|
|
||||||
{
|
|
||||||
var dbTypeFromTenantName = GetDbTypeFromTenantName(CurrentTenant.Name);
|
|
||||||
if (dbTypeFromTenantName is not null)
|
|
||||||
{
|
|
||||||
return dbTypeFromTenantName.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Check.NotNull(Options.DbType, "默认DbType未配置!");
|
|
||||||
return Options.DbType!.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//根据租户name进行匹配db类型: Test_Sqlite,[来自AI]
|
|
||||||
private DbType? GetDbTypeFromTenantName(string name)
|
private DbType? GetDbTypeFromTenantName(string name)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
@@ -230,25 +203,26 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查找下划线的位置
|
// 查找@符号的位置
|
||||||
int underscoreIndex = name.LastIndexOf('_');
|
int atIndex = name.LastIndexOf('@');
|
||||||
|
|
||||||
if (underscoreIndex == -1 || underscoreIndex == name.Length - 1)
|
if (atIndex == -1 || atIndex == name.Length - 1)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取 枚举 部分
|
// 提取 枚举 部分
|
||||||
string enumString = name.Substring(underscoreIndex + 1);
|
string enumString = name.Substring(atIndex + 1);
|
||||||
|
|
||||||
// 尝试将 尾缀 转换为枚举
|
// 尝试将 尾缀 转换为枚举
|
||||||
if (Enum.TryParse<DbType>(enumString, out DbType result))
|
if (Enum.TryParse<DbType>(enumString, out DbType result))
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// 条件不满足时返回 null
|
{
|
||||||
return null;
|
throw new ArgumentException($"数据库{name}db类型错误或不支持:无法匹配{enumString}数据库类型");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void BackupDataBase()
|
public virtual void BackupDataBase()
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Volo.Abp.Data;
|
||||||
|
using Volo.Abp.DependencyInjection;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
|
|
||||||
|
namespace Yi.Framework.SqlSugarCore;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 租户配置
|
||||||
|
/// </summary>
|
||||||
|
public class TenantConfigurationWrapper : ITransientDependency
|
||||||
|
{
|
||||||
|
private readonly IAbpLazyServiceProvider _serviceProvider;
|
||||||
|
private ICurrentTenant CurrentTenant => _serviceProvider.LazyGetRequiredService<ICurrentTenant>();
|
||||||
|
private ITenantStore TenantStore => _serviceProvider.LazyGetRequiredService<ITenantStore>();
|
||||||
|
private DbConnOptions DbConnOptions => _serviceProvider.LazyGetRequiredService<IOptions<DbConnOptions>>().Value;
|
||||||
|
|
||||||
|
public TenantConfigurationWrapper(IAbpLazyServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取租户信息
|
||||||
|
/// [from:ai]
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<TenantConfiguration?> GetAsync()
|
||||||
|
{
|
||||||
|
//未开启多租户
|
||||||
|
if (!DbConnOptions.EnabledSaasMultiTenancy)
|
||||||
|
{
|
||||||
|
return await TenantStore.FindAsync(ConnectionStrings.DefaultConnectionStringName);
|
||||||
|
}
|
||||||
|
|
||||||
|
TenantConfiguration? tenantConfiguration = null;
|
||||||
|
|
||||||
|
if (CurrentTenant.Id is not null)
|
||||||
|
{
|
||||||
|
tenantConfiguration = await TenantStore.FindAsync(CurrentTenant.Id.Value);
|
||||||
|
if (tenantConfiguration == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"未找到租户信息,租户Id:{CurrentTenant.Id}");
|
||||||
|
}
|
||||||
|
return tenantConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(CurrentTenant.Name))
|
||||||
|
{
|
||||||
|
tenantConfiguration = await TenantStore.FindAsync(CurrentTenant.Name);
|
||||||
|
if (tenantConfiguration == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"未找到租户信息,租户名称:{CurrentTenant.Name}");
|
||||||
|
}
|
||||||
|
return tenantConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await TenantStore.FindAsync(ConnectionStrings.DefaultConnectionStringName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前连接字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GetCurrentConnectionStringAsync()
|
||||||
|
{
|
||||||
|
return (await GetAsync()).ConnectionStrings.Default!;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前连接名
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GetCurrentConnectionNameAsync()
|
||||||
|
{
|
||||||
|
return (await GetAsync()).Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TenantConfigurationExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前连接字符串
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetCurrentConnectionString(this TenantConfiguration tenantConfiguration)
|
||||||
|
{
|
||||||
|
return tenantConfiguration.ConnectionStrings.Default!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前连接名
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetCurrentConnectionName(this TenantConfiguration tenantConfiguration)
|
||||||
|
{
|
||||||
|
return tenantConfiguration.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
|||||||
{
|
{
|
||||||
public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; }
|
public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; }
|
||||||
public IServiceProvider ServiceProvider { get; set; }
|
public IServiceProvider ServiceProvider { get; set; }
|
||||||
|
|
||||||
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
|
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
|
||||||
|
protected readonly TenantConfigurationWrapper _tenantConfigurationWrapper;
|
||||||
protected readonly IUnitOfWorkManager UnitOfWorkManager;
|
protected readonly IUnitOfWorkManager UnitOfWorkManager;
|
||||||
protected readonly IConnectionStringResolver ConnectionStringResolver;
|
protected readonly IConnectionStringResolver ConnectionStringResolver;
|
||||||
protected readonly ICancellationTokenProvider CancellationTokenProvider;
|
protected readonly ICancellationTokenProvider CancellationTokenProvider;
|
||||||
@@ -26,26 +26,25 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
|||||||
IUnitOfWorkManager unitOfWorkManager,
|
IUnitOfWorkManager unitOfWorkManager,
|
||||||
IConnectionStringResolver connectionStringResolver,
|
IConnectionStringResolver connectionStringResolver,
|
||||||
ICancellationTokenProvider cancellationTokenProvider,
|
ICancellationTokenProvider cancellationTokenProvider,
|
||||||
ICurrentTenant currentTenant
|
ICurrentTenant currentTenant, TenantConfigurationWrapper tenantConfigurationWrapper)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
UnitOfWorkManager = unitOfWorkManager;
|
UnitOfWorkManager = unitOfWorkManager;
|
||||||
ConnectionStringResolver = connectionStringResolver;
|
ConnectionStringResolver = connectionStringResolver;
|
||||||
CancellationTokenProvider = cancellationTokenProvider;
|
CancellationTokenProvider = cancellationTokenProvider;
|
||||||
CurrentTenant = currentTenant;
|
CurrentTenant = currentTenant;
|
||||||
|
_tenantConfigurationWrapper = tenantConfigurationWrapper;
|
||||||
Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance;
|
Logger = NullLogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>>.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task<TDbContext> GetDbContextAsync()
|
public virtual async Task<TDbContext> GetDbContextAsync()
|
||||||
{
|
{
|
||||||
|
|
||||||
var connectionStringName = ConnectionStrings.DefaultConnectionStringName;
|
|
||||||
|
|
||||||
//获取当前连接字符串,未多租户时,默认为空
|
//获取当前连接字符串,未多租户时,默认为空
|
||||||
var connectionString = await ResolveConnectionStringAsync(connectionStringName);
|
var tenantConfiguration= await _tenantConfigurationWrapper.GetAsync();
|
||||||
|
//由于sqlsugar的特殊性,没有db区分,不再使用连接字符串解析器
|
||||||
|
var connectionStringName = tenantConfiguration.GetCurrentConnectionName();
|
||||||
|
var connectionString = tenantConfiguration.GetCurrentConnectionString();
|
||||||
var dbContextKey = $"{this.GetType().Name}_{connectionString}";
|
var dbContextKey = $"{this.GetType().Name}_{connectionString}";
|
||||||
|
|
||||||
|
|
||||||
var unitOfWork = UnitOfWorkManager.Current;
|
var unitOfWork = UnitOfWorkManager.Current;
|
||||||
if (unitOfWork == null )
|
if (unitOfWork == null )
|
||||||
{
|
{
|
||||||
@@ -67,8 +66,6 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
|||||||
databaseApi = new SqlSugarDatabaseApi(
|
databaseApi = new SqlSugarDatabaseApi(
|
||||||
await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
|
await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString)
|
||||||
);
|
);
|
||||||
|
|
||||||
//await Console.Out.WriteLineAsync(">>>----------------实例化了db"+ ((SqlSugarDatabaseApi)databaseApi).DbContext.SqlSugarClient.ContextID.ToString());
|
|
||||||
//创建的db加入到当前工作单元中
|
//创建的db加入到当前工作单元中
|
||||||
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
|
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
|
||||||
|
|
||||||
@@ -98,7 +95,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
|||||||
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
|
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
|
||||||
{
|
{
|
||||||
//事务key
|
//事务key
|
||||||
var transactionApiKey = $"SqlsugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";
|
var transactionApiKey = $"SqlSugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";
|
||||||
|
|
||||||
//尝试查找事务
|
//尝试查找事务
|
||||||
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;
|
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;
|
||||||
@@ -123,20 +120,5 @@ namespace Yi.Framework.SqlSugarCore.Uow
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected virtual async Task<string> ResolveConnectionStringAsync(string connectionStringName)
|
|
||||||
{
|
|
||||||
if (typeof(TDbContext).IsDefined(typeof(IgnoreMultiTenancyAttribute), false))
|
|
||||||
{
|
|
||||||
using (CurrentTenant.Change(null))
|
|
||||||
{
|
|
||||||
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await ConnectionStringResolver.ResolveAsync(connectionStringName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ using Volo.Abp.Data;
|
|||||||
using Volo.Abp.Domain;
|
using Volo.Abp.Domain;
|
||||||
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.Domain.Repositories;
|
||||||
using Volo.Abp.Guids;
|
using Volo.Abp.Guids;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
|
using Volo.Abp.MultiTenancy.ConfigurationStore;
|
||||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
using Yi.Framework.SqlSugarCore.Repositories;
|
using Yi.Framework.SqlSugarCore.Repositories;
|
||||||
using Yi.Framework.SqlSugarCore.Uow;
|
using Yi.Framework.SqlSugarCore.Uow;
|
||||||
@@ -51,7 +53,7 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
options.DefaultSequentialGuidType = guidType;
|
options.DefaultSequentialGuidType = guidType;
|
||||||
});
|
});
|
||||||
|
|
||||||
service.TryAddScoped<ISqlSugarDbContext, SqlSugarDbContextFactory>();
|
service.TryAddTransient<ISqlSugarDbContext, SqlSugarDbContextFactory>();
|
||||||
|
|
||||||
//不开放sqlsugarClient
|
//不开放sqlsugarClient
|
||||||
//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlsugarDbContext>().SqlSugarClient);
|
//service.AddTransient<ISqlSugarClient>(x => x.GetRequiredService<ISqlsugarDbContext>().SqlSugarClient);
|
||||||
@@ -69,7 +71,25 @@ namespace Yi.Framework.SqlSugarCore
|
|||||||
var dbConfig = section.Get<DbConnOptions>();
|
var dbConfig = section.Get<DbConnOptions>();
|
||||||
//将默认db传递给abp连接字符串模块
|
//将默认db传递给abp连接字符串模块
|
||||||
Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });
|
Configure<AbpDbConnectionOptions>(x => { x.ConnectionStrings.Default = dbConfig.Url; });
|
||||||
|
//配置abp默认租户,对接abp模块
|
||||||
|
Configure<AbpDefaultTenantStoreOptions>(x => {
|
||||||
|
var tenantList = x.Tenants.ToList();
|
||||||
|
foreach(var tenant in tenantList)
|
||||||
|
{
|
||||||
|
tenant.NormalizedName = tenant.Name.Contains("@") ?
|
||||||
|
tenant.Name.Substring(0, tenant.Name.LastIndexOf("@")) :
|
||||||
|
tenant.Name;
|
||||||
|
}
|
||||||
|
tenantList.Insert(0, new TenantConfiguration
|
||||||
|
{
|
||||||
|
Id = Guid.Empty,
|
||||||
|
Name = $"{ConnectionStrings.DefaultConnectionStringName}",
|
||||||
|
NormalizedName = ConnectionStrings.DefaultConnectionStringName,
|
||||||
|
ConnectionStrings = new ConnectionStrings() { { ConnectionStrings.DefaultConnectionStringName, dbConfig.Url } },
|
||||||
|
IsActive = true
|
||||||
|
});
|
||||||
|
x.Tenants = tenantList.ToArray();
|
||||||
|
});
|
||||||
context.Services.AddYiDbContext<DefaultSqlSugarDbContext>();
|
context.Services.AddYiDbContext<DefaultSqlSugarDbContext>();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ public class AccessLogCacheJob : HangfireBackgroundWorkerBase
|
|||||||
{
|
{
|
||||||
_localEventBus = localEventBus;
|
_localEventBus = localEventBus;
|
||||||
RecurringJobId = "访问日志写入缓存";
|
RecurringJobId = "访问日志写入缓存";
|
||||||
//每10秒执行一次,将本地缓存转入redis,防止丢数据
|
//每分钟执行一次,将本地缓存转入redis,防止丢数据
|
||||||
CronExpression = "*/10 * * * * *";
|
CronExpression = "0 * * * * ?";
|
||||||
//
|
//
|
||||||
// JobDetail = JobBuilder.Create<AccessLogCacheJob>().WithIdentity(nameof(AccessLogCacheJob))
|
// JobDetail = JobBuilder.Create<AccessLogCacheJob>().WithIdentity(nameof(AccessLogCacheJob))
|
||||||
// .Build();
|
// .Build();
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ public class AccessLogStoreJob : HangfireBackgroundWorkerBase
|
|||||||
|
|
||||||
|
|
||||||
RecurringJobId = "访问日志写入数据库";
|
RecurringJobId = "访问日志写入数据库";
|
||||||
//每分钟执行一次
|
//每小时执行一次
|
||||||
CronExpression = "0 * * * * ?";
|
CronExpression = "0 0 * * * ?";
|
||||||
// JobDetail = JobBuilder.Create<AccessLogStoreJob>().WithIdentity(nameof(AccessLogStoreJob))
|
// JobDetail = JobBuilder.Create<AccessLogStoreJob>().WithIdentity(nameof(AccessLogStoreJob))
|
||||||
// .Build();
|
// .Build();
|
||||||
// //每分钟执行一次
|
// //每分钟执行一次
|
||||||
|
|||||||
@@ -136,8 +136,6 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
|||||||
{ DiscussId = output.Id, OldSeeNum = output.SeeNum });
|
{ DiscussId = output.Id, OldSeeNum = output.SeeNum });
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查询
|
/// 查询
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -157,7 +155,11 @@ namespace Yi.Framework.Bbs.Application.Services.Forum
|
|||||||
.WhereIF(input.UserName is not null, (discuss, user) => user.UserName == input.UserName!)
|
.WhereIF(input.UserName is not null, (discuss, user) => user.UserName == input.UserName!)
|
||||||
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
|
.LeftJoin<BbsUserExtraInfoEntity>((discuss, user, info) => user.Id == info.UserId)
|
||||||
.OrderByDescending(discuss => discuss.OrderNum)
|
.OrderByDescending(discuss => discuss.OrderNum)
|
||||||
.OrderByIF(input.Type == QueryDiscussTypeEnum.New, discuss => discuss.CreationTime, OrderByType.Desc)
|
//已提示杰哥新增表达式
|
||||||
|
// .OrderByIF(input.Type == QueryDiscussTypeEnum.New,
|
||||||
|
// @"COALESCE(discuss.LastModificationTime, discuss.CreationTime) DESC")
|
||||||
|
//采用上方写法
|
||||||
|
.OrderByIF(input.Type == QueryDiscussTypeEnum.New,discuss=>SqlFunc.Coalesce(discuss.LastModificationTime,discuss.CreationTime),OrderByType.Desc)
|
||||||
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
|
.OrderByIF(input.Type == QueryDiscussTypeEnum.Host, discuss => discuss.SeeNum, OrderByType.Desc)
|
||||||
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
|
.OrderByIF(input.Type == QueryDiscussTypeEnum.Suggest, discuss => discuss.AgreeNum, OrderByType.Desc)
|
||||||
.Select((discuss, user, info) => new DiscussGetListOutputDto
|
.Select((discuss, user, info) => new DiscussGetListOutputDto
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ namespace Yi.Framework.Bbs.Domain.Entities.Forum
|
|||||||
PlateId = plateId;
|
PlateId = plateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddSeeNumber()
|
||||||
|
{
|
||||||
|
this.SeeNum += 1;
|
||||||
|
//设置最小值,不更新
|
||||||
|
this.LastModificationTime = DateTime.MinValue;
|
||||||
|
//设置空值,不更新
|
||||||
|
this.LastModifierId = Guid.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
[SugarColumn(ColumnName = "Id", IsPrimaryKey = true)]
|
||||||
public override Guid Id { get; protected set; }
|
public override Guid Id { get; protected set; }
|
||||||
public string? Title { get; set; }
|
public string? Title { get; set; }
|
||||||
|
|||||||
@@ -8,28 +8,24 @@ using Volo.Abp.Domain.Repositories;
|
|||||||
using Volo.Abp.EventBus;
|
using Volo.Abp.EventBus;
|
||||||
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
||||||
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
using Yi.Framework.Bbs.Domain.Shared.Etos;
|
||||||
|
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||||
|
|
||||||
namespace Yi.Framework.Bbs.Domain.EventHandlers
|
namespace Yi.Framework.Bbs.Domain.EventHandlers
|
||||||
{
|
{
|
||||||
public class SeeDiscussEventHandler : ILocalEventHandler<SeeDiscussEventArgs>, ITransientDependency
|
public class SeeDiscussEventHandler : ILocalEventHandler<SeeDiscussEventArgs>, ITransientDependency
|
||||||
{
|
{
|
||||||
private IRepository<DiscussAggregateRoot, Guid> _repository;
|
private ISqlSugarRepository<DiscussAggregateRoot, Guid> _repository;
|
||||||
public SeeDiscussEventHandler(IRepository<DiscussAggregateRoot, Guid> repository)
|
|
||||||
|
public SeeDiscussEventHandler(ISqlSugarRepository<DiscussAggregateRoot, Guid> repository)
|
||||||
{
|
{
|
||||||
_repository = repository;
|
_repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleEventAsync(SeeDiscussEventArgs eventData)
|
public async Task HandleEventAsync(SeeDiscussEventArgs eventData)
|
||||||
{
|
{
|
||||||
var entity = await _repository.GetAsync(eventData.DiscussId);
|
await _repository._Db.Updateable<DiscussAggregateRoot>()
|
||||||
if (entity is not null)
|
.SetColumns(x => new DiscussAggregateRoot { SeeNum = x.SeeNum + 1 })
|
||||||
{
|
.Where(x => x.Id == eventData.DiscussId).ExecuteCommandAsync();
|
||||||
entity.SeeNum += 1;
|
|
||||||
await _repository.UpdateAsync(entity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Mapster;
|
using Mapster;
|
||||||
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
using Volo.Abp.Caching;
|
using Volo.Abp.Caching;
|
||||||
using Volo.Abp.DependencyInjection;
|
using Volo.Abp.DependencyInjection;
|
||||||
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
using Yi.Framework.Bbs.Domain.Entities.Forum;
|
||||||
@@ -28,10 +29,13 @@ public class DiscussLableRepository : SqlSugarRepository<DiscussLableAggregateRo
|
|||||||
public async Task<Dictionary<Guid, DiscussLableCacheItem>> GetDiscussLableCacheMapAsync()
|
public async Task<Dictionary<Guid, DiscussLableCacheItem>> GetDiscussLableCacheMapAsync()
|
||||||
{
|
{
|
||||||
var cahce = await _lableCache.GetOrAddAsync(DiscussLableConst.DiscussLableCacheKey, async () =>
|
var cahce = await _lableCache.GetOrAddAsync(DiscussLableConst.DiscussLableCacheKey, async () =>
|
||||||
{
|
{
|
||||||
var entities = await _DbQueryable.ToListAsync();
|
var entities = await _DbQueryable.ToListAsync();
|
||||||
return entities.Adapt<List<DiscussLableCacheItem>>();
|
return entities.Adapt<List<DiscussLableCacheItem>>();
|
||||||
});
|
}, () =>
|
||||||
|
new DistributedCacheEntryOptions()
|
||||||
|
{ AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) }
|
||||||
|
);
|
||||||
return cahce.ToDictionary(x => x.Id);
|
return cahce.ToDictionary(x => x.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ namespace Yi.Framework.ChatHub.Domain.Managers
|
|||||||
{
|
{
|
||||||
if (result.Successful)
|
if (result.Successful)
|
||||||
{
|
{
|
||||||
yield return result.Choices.FirstOrDefault()?.Message.Content ?? string.Empty;
|
yield return result.Choices.FirstOrDefault()?.Message.Content ?? null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -99,10 +99,14 @@ public class FileManager : DomainService, IFileManager
|
|||||||
this.LoggerFactory.CreateLogger<FileManager>().LogInformation(exception, exception.Message);
|
this.LoggerFactory.CreateLogger<FileManager>().LogInformation(exception, exception.Message);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
this.LoggerFactory.CreateLogger<FileManager>().LogError(exception, exception.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
{
|
{
|
||||||
//如果失败了,直接复制一份到缩略图上即可
|
//如果失败了,直接复制一份到缩略图上即可
|
||||||
compressImageStream = fileStream;
|
compressImageStream = fileStream;
|
||||||
this.LoggerFactory.CreateLogger<FileManager>().LogError(exception, exception.Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,8 @@ public class YiMultiTenantConnectionStringResolver : DefaultConnectionStringReso
|
|||||||
: Options.ConnectionStrings.Default!;
|
: Options.ConnectionStrings.Default!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Requesting specific connection string...
|
//Requesting specific connection string...
|
||||||
var connString = tenant.ConnectionStrings?.FirstOrDefault().Value;
|
var connString = tenant.ConnectionStrings?.GetOrDefault(connectionStringName);
|
||||||
if (!connString.IsNullOrWhiteSpace())
|
if (!connString.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
//Found for the tenant
|
//Found for the tenant
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Volo.Abp.Application.Services;
|
using Volo.Abp.Application.Services;
|
||||||
using Volo.Abp.DistributedLocking;
|
using Volo.Abp.DistributedLocking;
|
||||||
|
using Volo.Abp.Domain.Repositories;
|
||||||
|
using Volo.Abp.MultiTenancy;
|
||||||
using Volo.Abp.Settings;
|
using Volo.Abp.Settings;
|
||||||
using Volo.Abp.Uow;
|
using Volo.Abp.Uow;
|
||||||
using Yi.Framework.Bbs.Application.Contracts.Dtos.Banner;
|
using Yi.Framework.Bbs.Application.Contracts.Dtos.Banner;
|
||||||
@@ -215,5 +217,53 @@ namespace Yi.Abp.Application.Services
|
|||||||
});
|
});
|
||||||
return $"加锁结果:{number},不加锁结果:{number2}";
|
return $"加锁结果:{number},不加锁结果:{number2}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ICurrentTenant CurrentTenant { get; set; }
|
||||||
|
public IRepository<BannerAggregateRoot> repository { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 多租户
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> GetMultiTenantAsync()
|
||||||
|
{
|
||||||
|
using (var uow=UnitOfWorkManager.Begin())
|
||||||
|
{
|
||||||
|
//此处会实例化一个db,连接默认库
|
||||||
|
var defautTenantData1= await repository.GetListAsync();
|
||||||
|
using (CurrentTenant.Change(null,"Default"))
|
||||||
|
{
|
||||||
|
var defautTenantData2= await repository.GetListAsync();
|
||||||
|
await repository.InsertAsync(new BannerAggregateRoot
|
||||||
|
{
|
||||||
|
Name = "default",
|
||||||
|
});
|
||||||
|
var defautTenantData3= await repository.GetListAsync(x=>x.Name=="default");
|
||||||
|
}
|
||||||
|
//此处会实例化一个新的db连接MES
|
||||||
|
using (CurrentTenant.Change(null,"Mes"))
|
||||||
|
{
|
||||||
|
var otherTenantData1= await repository.GetListAsync();
|
||||||
|
await repository.InsertAsync(new BannerAggregateRoot
|
||||||
|
{
|
||||||
|
Name = "Mes1",
|
||||||
|
});
|
||||||
|
var otherTenantData2= await repository.GetListAsync(x=>x.Name=="Mes1");
|
||||||
|
}
|
||||||
|
//此处会复用Mesdb,不会实例化新的db
|
||||||
|
using (CurrentTenant.Change(Guid.Parse("33333333-3d72-4339-9adc-845151f8ada0")))
|
||||||
|
{
|
||||||
|
var otherTenantData1= await repository.GetListAsync();
|
||||||
|
await repository.InsertAsync(new BannerAggregateRoot
|
||||||
|
{
|
||||||
|
Name = "Mes2",
|
||||||
|
});
|
||||||
|
var otherTenantData2= await repository.GetListAsync(x=>x.Name=="Mes2");
|
||||||
|
}
|
||||||
|
//此处会将多库进行一起提交,前面的操作有报错,全部回滚
|
||||||
|
await uow.CompleteAsync();
|
||||||
|
return "根据租户切换不同的数据库,并管理db实例连接,涉及多库事务统一到最后提交";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
|
||||||
USER app
|
|
||||||
WORKDIR /app
|
|
||||||
EXPOSE 8080
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
COPY ./common.props ./
|
|
||||||
COPY ["src/Yi.Abp.Web/Yi.Abp.Web.csproj", "src/Yi.Abp.Web/"]
|
|
||||||
COPY ["framework/Yi.Framework.AspNetCore.Authentication.OAuth/Yi.Framework.AspNetCore.Authentication.OAuth.csproj", "framework/Yi.Framework.AspNetCore.Authentication.OAuth/"]
|
|
||||||
COPY ["framework/Yi.Framework.AspNetCore/Yi.Framework.AspNetCore.csproj", "framework/Yi.Framework.AspNetCore/"]
|
|
||||||
COPY ["framework/Yi.Framework.Core/Yi.Framework.Core.csproj", "framework/Yi.Framework.Core/"]
|
|
||||||
COPY ["src/Yi.Abp.Application/Yi.Abp.Application.csproj", "src/Yi.Abp.Application/"]
|
|
||||||
COPY ["framework/Yi.Framework.Ddd.Application/Yi.Framework.Ddd.Application.csproj", "framework/Yi.Framework.Ddd.Application/"]
|
|
||||||
COPY ["framework/Yi.Framework.Ddd.Application.Contracts/Yi.Framework.Ddd.Application.Contracts.csproj", "framework/Yi.Framework.Ddd.Application.Contracts/"]
|
|
||||||
COPY ["module/bbs/Yi.Framework.Bbs.Application/Yi.Framework.Bbs.Application.csproj", "module/bbs/Yi.Framework.Bbs.Application/"]
|
|
||||||
COPY ["module/rbac/Yi.Framework.Rbac.Application/Yi.Framework.Rbac.Application.csproj", "module/rbac/Yi.Framework.Rbac.Application/"]
|
|
||||||
COPY ["module/rbac/Yi.Framework.Rbac.Application.Contracts/Yi.Framework.Rbac.Application.Contracts.csproj", "module/rbac/Yi.Framework.Rbac.Application.Contracts/"]
|
|
||||||
COPY ["module/rbac/Yi.Framework.Rbac.Domain.Shared/Yi.Framework.Rbac.Domain.Shared.csproj", "module/rbac/Yi.Framework.Rbac.Domain.Shared/"]
|
|
||||||
COPY ["framework/Yi.Framework.Mapster/Yi.Framework.Mapster.csproj", "framework/Yi.Framework.Mapster/"]
|
|
||||||
COPY ["framework/Yi.Framework.SqlSugarCore.Abstractions/Yi.Framework.SqlSugarCore.Abstractions.csproj", "framework/Yi.Framework.SqlSugarCore.Abstractions/"]
|
|
||||||
COPY ["module/rbac/Yi.Framework.Rbac.Domain/Yi.Framework.Rbac.Domain.csproj", "module/rbac/Yi.Framework.Rbac.Domain/"]
|
|
||||||
COPY ["module/bbs/Yi.Framework.Bbs.Application.Contracts/Yi.Framework.Bbs.Application.Contracts.csproj", "module/bbs/Yi.Framework.Bbs.Application.Contracts/"]
|
|
||||||
COPY ["module/bbs/Yi.Framework.Bbs.Domain.Shared/Yi.Framework.Bbs.Domain.Shared.csproj", "module/bbs/Yi.Framework.Bbs.Domain.Shared/"]
|
|
||||||
COPY ["module/bbs/Yi.Framework.Bbs.Domain/Yi.Framework.Bbs.Domain.csproj", "module/bbs/Yi.Framework.Bbs.Domain/"]
|
|
||||||
COPY ["src/Yi.Abp.Application.Contracts/Yi.Abp.Application.Contracts.csproj", "src/Yi.Abp.Application.Contracts/"]
|
|
||||||
COPY ["src/Yi.Abp.Domain.Shared/Yi.Abp.Domain.Shared.csproj", "src/Yi.Abp.Domain.Shared/"]
|
|
||||||
COPY ["src/Yi.Abp.Domain/Yi.Abp.Domain.csproj", "src/Yi.Abp.Domain/"]
|
|
||||||
COPY ["src/Yi.Abp.SqlSugarCore/Yi.Abp.SqlSugarCore.csproj", "src/Yi.Abp.SqlSugarCore/"]
|
|
||||||
COPY ["framework/Yi.Framework.SqlSugarCore/Yi.Framework.SqlSugarCore.csproj", "framework/Yi.Framework.SqlSugarCore/"]
|
|
||||||
COPY ["module/bbs/Yi.Framework.Bbs.SqlSugarCore/Yi.Framework.Bbs.SqlSugarCore.csproj", "module/bbs/Yi.Framework.Bbs.SqlSugarCore/"]
|
|
||||||
COPY ["module/rbac/Yi.Framework.Rbac.SqlSugarCore/Yi.Framework.Rbac.SqlSugarCore.csproj", "module/rbac/Yi.Framework.Rbac.SqlSugarCore/"]
|
|
||||||
RUN dotnet restore "./src/Yi.Abp.Web/./Yi.Abp.Web.csproj"
|
|
||||||
COPY . .
|
|
||||||
WORKDIR "/src/src/Yi.Abp.Web"
|
|
||||||
RUN dotnet build "./Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
RUN dotnet publish "./Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
|
||||||
|
|
||||||
FROM base AS final
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=publish /app/publish .
|
|
||||||
ENTRYPOINT ["dotnet", "Yi.Abp.Web.dll"]
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Docker 构建说明
|
|
||||||
|
|
||||||
## 执行命令
|
|
||||||
|
|
||||||
```shell
|
|
||||||
# 在Yi.Abp.Net8 目录下执行
|
|
||||||
docker build -t admin-server:${BUILD_NUMBER} -f ./src/Yi.Abp.Web/Dockerfile .
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意
|
|
||||||
|
|
||||||
NuGet 源国内访问有时候会报错,可以考虑切换成华为源,加上参数
|
|
||||||
|
|
||||||
```shell
|
|
||||||
RUN dotnet restore --source https://repo.huaweicloud.com/repository/nuget/v3/index.json "./src/Yi.Abp.Web/./Yi.Abp.Web.csproj"
|
|
||||||
|
|
||||||
RUN dotnet build --source https://repo.huaweicloud.com/repository/nuget/v3/index.json "./Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
|
||||||
|
|
||||||
RUN dotnet publish --source https://repo.huaweicloud.com/repository/nuget/v3/index.json "./Yi.Abp.Web.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
|
||||||
|
|
||||||
```
|
|
||||||
@@ -188,10 +188,10 @@ namespace Yi.Abp.Web
|
|||||||
|
|
||||||
//配置Hangfire定时任务存储,开启redis后,优先使用redis
|
//配置Hangfire定时任务存储,开启redis后,优先使用redis
|
||||||
var redisConfiguration = configuration["Redis:Configuration"];
|
var redisConfiguration = configuration["Redis:Configuration"];
|
||||||
var redisEnabled = configuration["Redis:IsEnabled"];
|
|
||||||
context.Services.AddHangfire(config=>
|
context.Services.AddHangfire(config=>
|
||||||
{
|
{
|
||||||
if (redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled))
|
bool.TryParse( configuration["Redis:IsEnabled"], out var redisEnabled);
|
||||||
|
if (redisEnabled)
|
||||||
{
|
{
|
||||||
config.UseRedisStorage(
|
config.UseRedisStorage(
|
||||||
ConnectionMultiplexer.Connect(redisConfiguration),
|
ConnectionMultiplexer.Connect(redisConfiguration),
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
{
|
{
|
||||||
|
//多租户,支持多库,DbConnOptions会自动创建到默认租户,支持配置文件方式+数据库方式,AbpDefaultTenantStoreOptions
|
||||||
|
// "Tenants": [
|
||||||
|
// {
|
||||||
|
// "Id": "33333333-3d72-4339-9adc-845151f8ada0",
|
||||||
|
// "Name": "Mes@MySql",
|
||||||
|
// "ConnectionStrings": {
|
||||||
|
// "Default": "DataSource=mes-dev.db"
|
||||||
|
// },
|
||||||
|
// "IsActive": false
|
||||||
|
// }
|
||||||
|
// ],
|
||||||
|
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
//"Default": "Information",
|
//"Default": "Information",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AbpVersion>8.2.0</AbpVersion>
|
<AbpVersion>8.3.4</AbpVersion>
|
||||||
<SqlSugarVersion>5.1.4.166</SqlSugarVersion>
|
<SqlSugarVersion>5.1.4.176-preview16</SqlSugarVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
33
Yi.Bbs.Vue3/.dockerignore
Normal file
33
Yi.Bbs.Vue3/.dockerignore
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
||||||
|
appsettings.Development.json
|
||||||
|
appsettings.Production.json
|
||||||
|
appsettings.Staging.json
|
||||||
13
Yi.Bbs.Vue3/Dockerfile
Normal file
13
Yi.Bbs.Vue3/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM nginx:stable-alpine3.20-perl as base
|
||||||
|
EXPOSE 18001
|
||||||
|
|
||||||
|
FROM node:20.18 as publish
|
||||||
|
WORKDIR /main
|
||||||
|
COPY . .
|
||||||
|
RUN npm i
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /main/dist/ .
|
||||||
|
ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||||||
8
Yi.Bbs.Vue3/DockerfileFast
Normal file
8
Yi.Bbs.Vue3/DockerfileFast
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
FROM nginx as base
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
EXPOSE 18001
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY ["./dist/","."]
|
||||||
|
ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"i": "^0.3.7",
|
"i": "^0.3.7",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"marked": "^4.2.12",
|
"marked": "^4.2.12",
|
||||||
|
"marked-katex-extension": "^5.1.4",
|
||||||
"mavon-editor": "^3.0.0",
|
"mavon-editor": "^3.0.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"path-to-regexp": "^6.2.1",
|
"path-to-regexp": "^6.2.1",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, defineProps } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { compile } from "path-to-regexp";
|
import { compile } from "path-to-regexp";
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, reactive, ref, defineProps } from "vue";
|
import { onMounted, reactive, ref } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { getListByDiscussId, add, del } from "@/apis/commentApi.js";
|
import { getListByDiscussId, add, del } from "@/apis/commentApi.js";
|
||||||
import AvatarInfo from "./AvatarInfo.vue";
|
import AvatarInfo from "./AvatarInfo.vue";
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const onClickText=()=>{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-card class="box-card" shadow="never" :body-style="{padding: isPadding===false?'0px 20px':'20px 20px'}">
|
<el-card class="box-card" shadow="never" :body-style="{padding: props.isPadding===false?'0px 0px':'20px 20px'}">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>{{ props.header }}</span>
|
<span>{{ props.header }}</span>
|
||||||
@@ -37,7 +37,8 @@ const onClickText=()=>{
|
|||||||
.el-divider {
|
.el-divider {
|
||||||
margin: 0.2rem 0;
|
margin: 0.2rem 0;
|
||||||
}
|
}
|
||||||
.VisitsLineChart /deep/ .el-card__body{
|
|
||||||
|
::v-deep(.VisitsLineChart .el-card__body){
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
.box-card-info {
|
.box-card-info {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineProps } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isBorder: {
|
isBorder: {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- @node-click="handleNodeClick"-->
|
||||||
<el-tree
|
<el-tree
|
||||||
empty-text="无子文章"
|
empty-text="无子文章"
|
||||||
:data="props.data == '' ? [] : props.data"
|
:data="props.data == '' ? [] : props.data"
|
||||||
:props="defaultProps"
|
:props="defaultProps"
|
||||||
@node-click="handleNodeClick"
|
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
node-key="id"
|
node-key="id"
|
||||||
:default-expand-all="true"
|
:default-expand-all="true"
|
||||||
@@ -18,7 +19,7 @@
|
|||||||
:content="data.name"
|
:content="data.name"
|
||||||
placement="right"
|
placement="right"
|
||||||
>
|
>
|
||||||
<span class="title-name">{{ data.name }}</span>
|
<span @click="handleNodeClick(data)" class="title-name">{{ data.name }}</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
||||||
<span>
|
<span>
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
删除
|
删除
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
@@ -79,7 +81,7 @@ const { isHasPermission: isRemoveArticle } = getPermission("bbs:article:del");
|
|||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.custom-tree-node {
|
.custom-tree-node {
|
||||||
width: 100%;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="UserInfoCard">
|
<script setup name="UserInfoCard">
|
||||||
import { computed, defineProps } from "vue";
|
import { computed } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import UserLimitTag from "../UserLimitTag.vue";
|
import UserLimitTag from "../UserLimitTag.vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
|
|
||||||
<el-menu-item index="2" @click="enterStart"
|
<el-menu-item index="2" @click="enterStart"
|
||||||
>开始</el-menu-item>
|
>开始</el-menu-item>
|
||||||
<el-menu-item index="3" @click="enterWatermelon" style="color: red;font-weight: bolder;font-size: large;"
|
<el-menu-item index="3" @click="enterBook" style="color: red;font-weight: bolder;font-size: large;"
|
||||||
>知识宝典</el-menu-item>
|
>面试宝典</el-menu-item>
|
||||||
<el-menu-item index="4" @click="enterShop"
|
<el-menu-item index="4" @click="enterShop"
|
||||||
>商城</el-menu-item>
|
>商城</el-menu-item>
|
||||||
<!-- <el-sub-menu index="4">-->
|
<!-- <el-sub-menu index="4">-->
|
||||||
@@ -233,9 +233,8 @@ const enterStart = () => {
|
|||||||
router.push("/start");
|
router.push("/start");
|
||||||
}
|
}
|
||||||
|
|
||||||
const enterWatermelon=()=>{
|
const enterBook=()=>{
|
||||||
// router.push("/dc");
|
router.push("/book");
|
||||||
alert("即将上线,敬请期待!")
|
|
||||||
}
|
}
|
||||||
const enterShop=()=>{
|
const enterShop=()=>{
|
||||||
router.push("/shop");
|
router.push("/shop");
|
||||||
|
|||||||
@@ -45,6 +45,10 @@
|
|||||||
<el-icon><Trophy /></el-icon>
|
<el-icon><Trophy /></el-icon>
|
||||||
<span>数字藏品</span>
|
<span>数字藏品</span>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
<el-menu-item index="9" :route="{ path: '/book' }">
|
||||||
|
<el-icon><Memo /></el-icon>
|
||||||
|
<span>面试宝典</span>
|
||||||
|
</el-menu-item>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -136,6 +136,14 @@ const router = createRouter({
|
|||||||
title: "数字藏品",
|
title: "数字藏品",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "book",
|
||||||
|
path: "/book",
|
||||||
|
component: () => import("../views/book/Index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "面试宝典",
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,82 +4,84 @@
|
|||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-row class="art-info-left">
|
<el-row class="art-info-left">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<InfoCard header="文章信息" text="展开" hideDivider="true">
|
<InfoCard header="文章信息" text="展开" hideDivider="true" :isPadding="false" style="padding: 10px">
|
||||||
<template #content>
|
<template #content>
|
||||||
<el-button
|
<el-button
|
||||||
style="width: 100%; margin-bottom: 0.8rem"
|
style="width: 100%; margin-bottom: 0.8rem"
|
||||||
@click="loadDiscuss(true)"
|
@click="loadDiscuss(true)"
|
||||||
>主题首页</el-button
|
>主题首页
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="isAddArticle && isArticleUser"
|
v-if="isAddArticle && isArticleUser"
|
||||||
@click="addArticle('00000000-0000-0000-0000-000000000000')"
|
@click="addArticle('00000000-0000-0000-0000-000000000000')"
|
||||||
type="primary"
|
type="primary"
|
||||||
style="width: 100%; margin-bottom: 0.8rem; margin-left: 0"
|
style="width: 100%; margin-bottom: 0.8rem; margin-left: 0"
|
||||||
>添加子文章</el-button
|
>添加子文章
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
<!--目录在这里 -->
|
<!--目录在这里 -->
|
||||||
<el-scrollbar style="min-height: 410px">
|
<el-scrollbar style="height:600px;overflow-y: auto;">
|
||||||
<TreeArticleInfo
|
<TreeArticleInfo
|
||||||
:data="articleData"
|
:data="articleData"
|
||||||
@remove="delArticle"
|
@remove="delArticle"
|
||||||
@update="updateArticle"
|
@update="updateArticle"
|
||||||
@create="addNextArticle"
|
@create="addNextArticle"
|
||||||
@handleNodeClick="handleNodeClick"
|
@handleNodeClick="handleNodeClick"
|
||||||
:currentNodeKey="currentNodeKey"
|
:currentNodeKey="currentNodeKey"
|
||||||
:isArticleUser="isArticleUser"
|
:isArticleUser="isArticleUser"
|
||||||
/>
|
/>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
</InfoCard>
|
</InfoCard>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<InfoCard :items="authorList" :isPadding="false" header="作者分享" height="410" text="更多">
|
<InfoCard :items="authorList" :isPadding="false" header="作者分享" height="410" text="更多" style="padding:0 20px">
|
||||||
<template #item="temp">
|
<template #item="temp">
|
||||||
<ThemeData :themeData="temp"/>
|
<ThemeData :themeData="temp"/>
|
||||||
</template>
|
</template>
|
||||||
</InfoCard>
|
</InfoCard>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- <el-col :span="24">-->
|
<!-- <el-col :span="24">-->
|
||||||
<!-- <InfoCard :items="items" header="内容推荐" text="更多">-->
|
<!-- <InfoCard :items="items" header="内容推荐" text="更多">-->
|
||||||
<!-- <template #item="temp">-->
|
<!-- <template #item="temp">-->
|
||||||
<!-- <AvatarInfo />-->
|
<!-- <AvatarInfo />-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </InfoCard>-->
|
<!-- </InfoCard>-->
|
||||||
<!-- </el-col>-->
|
<!-- </el-col>-->
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="14">
|
<el-col :span="14">
|
||||||
<el-row class="left-div">
|
<el-row class="left-div">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<Breadcrumb :breadcrumbsList="breadcrumbsList" class="breadcrumb" />
|
<Breadcrumb :breadcrumbsList="breadcrumbsList" class="breadcrumb"/>
|
||||||
<!-- {{ discuss.user }} -->
|
<!-- {{ discuss.user }} -->
|
||||||
<AvatarInfo
|
<AvatarInfo
|
||||||
:size="50"
|
:size="50"
|
||||||
:showWatching="true"
|
:showWatching="true"
|
||||||
:time="discuss.creationTime"
|
:time="discuss.creationTime"
|
||||||
:userInfo="discuss.user"
|
:userInfo="discuss.user"
|
||||||
></AvatarInfo>
|
></AvatarInfo>
|
||||||
<!-- :userInfo="{nick:'qwe'} -->
|
<!-- :userInfo="{nick:'qwe'} -->
|
||||||
|
|
||||||
<h2>{{ discuss.title }}</h2>
|
<h2>{{ discuss.title }}</h2>
|
||||||
<h5 class="subtitle">{{discuss.introduction }}</h5>
|
<h5 class="subtitle">{{ discuss.introduction }}</h5>
|
||||||
<el-image
|
<el-image
|
||||||
:preview-src-list="[getUrl(discuss.cover)]"
|
:preview-src-list="[getUrl(discuss.cover)]"
|
||||||
v-if="discuss.cover"
|
v-if="discuss.cover"
|
||||||
:src="getUrl(discuss.cover)"
|
:src="getUrl(discuss.cover)"
|
||||||
style="width: 150px; height: 150px"
|
style="width: 150px; height: 150px"
|
||||||
/>
|
/>
|
||||||
<el-divider />
|
<el-divider/>
|
||||||
<ArticleContentInfo
|
<ArticleContentInfo
|
||||||
:code="discuss.content ?? ''"
|
:code="discuss.content ?? ''"
|
||||||
></ArticleContentInfo>
|
></ArticleContentInfo>
|
||||||
|
|
||||||
<el-divider class="tab-divider" />
|
<el-divider class="tab-divider"/>
|
||||||
|
|
||||||
<el-space :size="10" :spacer="spacer">
|
<el-space :size="10" :spacer="spacer">
|
||||||
<AgreeInfo :data="discuss" />
|
<AgreeInfo :data="discuss"/>
|
||||||
<el-button icon="Star" text> 0</el-button>
|
<el-button icon="Star" text> 0</el-button>
|
||||||
<el-button icon="Share" text> 分享</el-button>
|
<el-button icon="Share" text> 分享</el-button>
|
||||||
<el-button icon="Operation" text> 操作</el-button>
|
<el-button icon="Operation" text> 操作</el-button>
|
||||||
@@ -87,38 +89,40 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="24" class="comment">
|
<el-col :span="24" class="comment">
|
||||||
<CommentInfo :isComment="isDisabledCreateComment" />
|
<CommentInfo :isComment="isDisabledCreateComment"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<BottomInfo />
|
<BottomInfo/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<el-row class="right-div">
|
<el-row class="right-div">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<InfoCard
|
<InfoCard
|
||||||
class="art-info-right"
|
class="art-info-right"
|
||||||
header="主题信息"
|
header="主题信息"
|
||||||
text="更多"
|
text="更多"
|
||||||
hideDivider="true"
|
hideDivider="true"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div>
|
<div>
|
||||||
<ul class="art-info-ul">
|
<ul class="art-info-ul">
|
||||||
<li>
|
<li>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="default"
|
size="default"
|
||||||
v-if="isEditTheme && isArticleUser"
|
v-if="isEditTheme && isArticleUser"
|
||||||
@click="updateHander(route.params.discussId)"
|
@click="updateHander(route.params.discussId)"
|
||||||
>编辑</el-button
|
>编辑
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
<el-button
|
<el-button
|
||||||
style="margin-left: 1rem"
|
style="margin-left: 1rem"
|
||||||
type="danger"
|
type="danger"
|
||||||
v-if="isRemoveTheme && isArticleUser"
|
v-if="isRemoveTheme && isArticleUser"
|
||||||
@click="delHander(route.params.discussId)"
|
@click="delHander(route.params.discussId)"
|
||||||
>删除</el-button
|
>删除
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li>分类: <span>主题</span></li>
|
<li>分类: <span>主题</span></li>
|
||||||
@@ -135,18 +139,19 @@
|
|||||||
<template #content>
|
<template #content>
|
||||||
<div>
|
<div>
|
||||||
<el-empty
|
<el-empty
|
||||||
:image-size="100"
|
:image-size="100"
|
||||||
style="padding: 20px 0"
|
style="padding: 20px 0"
|
||||||
v-if="catalogueData.length == 0"
|
v-if="catalogueData.length == 0"
|
||||||
description="无目录"
|
description="无目录"
|
||||||
/>
|
/>
|
||||||
<ul v-else class="art-info-ul">
|
<ul v-else class="art-info-ul">
|
||||||
<li v-for="(item, i) in catalogueData" :key="i">
|
<li v-for="(item, i) in catalogueData" :key="i">
|
||||||
<el-button
|
<el-button
|
||||||
style="width: 100%; justify-content: left"
|
style="width: 100%; justify-content: left"
|
||||||
type="primary"
|
type="primary"
|
||||||
text
|
text
|
||||||
>{{ `${i + 1} : ${item}` }}</el-button
|
>{{ `${i + 1} : ${item}` }}
|
||||||
|
</el-button
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -155,62 +160,62 @@
|
|||||||
</InfoCard>
|
</InfoCard>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<InfoCard :items="themeList" :isPadding="false" header="推荐主题" text="更多" height="500">
|
<InfoCard :items="themeList" :isPadding="false" header="推荐主题" text="更多" height="500" style="padding:0 20px">
|
||||||
<template #item="temp">
|
<template #item="temp">
|
||||||
<ThemeData :themeData="temp"/>
|
<ThemeData :themeData="temp"/>
|
||||||
</template>
|
</template>
|
||||||
</InfoCard>
|
</InfoCard>
|
||||||
<!-- <InfoCard :items="items" header="其他" text="更多">-->
|
<!-- <InfoCard :items="items" header="其他" text="更多">-->
|
||||||
<!-- <template #item="temp">-->
|
<!-- <template #item="temp">-->
|
||||||
<!-- <AvatarInfo />-->
|
<!-- <AvatarInfo />-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </InfoCard>-->
|
<!-- </InfoCard>-->
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- <el-col :span="24">-->
|
<!-- <el-col :span="24">-->
|
||||||
<!-- <InfoCard :items="items" header="其他" text="更多">-->
|
<!-- <InfoCard :items="items" header="其他" text="更多">-->
|
||||||
<!-- <template #item="temp">-->
|
<!-- <template #item="temp">-->
|
||||||
<!-- <AvatarInfo />-->
|
<!-- <AvatarInfo />-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
<!-- </InfoCard>-->
|
<!-- </InfoCard>-->
|
||||||
<!-- </el-col>-->
|
<!-- </el-col>-->
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { h, ref, onMounted, watch, computed } from "vue";
|
import {h, ref, onMounted, watch, computed} from "vue";
|
||||||
import AvatarInfo from "@/components/AvatarInfo.vue";
|
import AvatarInfo from "@/components/AvatarInfo.vue";
|
||||||
import InfoCard from "@/components/InfoCard.vue";
|
import InfoCard from "@/components/InfoCard.vue";
|
||||||
import ArticleContentInfo from "@/components/ArticleContentInfo.vue";
|
import ArticleContentInfo from "@/components/ArticleContentInfo.vue";
|
||||||
import CommentInfo from "@/components/CommentInfo.vue";
|
import CommentInfo from "@/components/CommentInfo.vue";
|
||||||
import BottomInfo from "@/components/BottomInfo.vue";
|
import BottomInfo from "@/components/BottomInfo.vue";
|
||||||
import TreeArticleInfo from "@/components/TreeArticleInfo.vue";
|
import TreeArticleInfo from "@/components/TreeArticleInfo.vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import {useRoute, useRouter} from "vue-router";
|
||||||
import AgreeInfo from "@/components/AgreeInfo.vue";
|
import AgreeInfo from "@/components/AgreeInfo.vue";
|
||||||
import { get as discussGet, del as discussDel } from "@/apis/discussApi.js";
|
import {get as discussGet, del as discussDel} from "@/apis/discussApi.js";
|
||||||
import {
|
import {
|
||||||
all as articleall,
|
all as articleall,
|
||||||
del as articleDel,
|
del as articleDel,
|
||||||
get as articleGet,
|
get as articleGet,
|
||||||
} from "@/apis/articleApi.js";
|
} from "@/apis/articleApi.js";
|
||||||
import Breadcrumb from "@/components/Breadcrumb/index.vue";
|
import Breadcrumb from "@/components/Breadcrumb/index.vue";
|
||||||
import { getPermission } from "@/utils/auth";
|
import {getPermission} from "@/utils/auth";
|
||||||
import useUserStore from "@/stores/user.js";
|
import useUserStore from "@/stores/user.js";
|
||||||
import ThemeData from "@/views/home/components/RecommendTheme/index.vue";
|
import ThemeData from "@/views/home/components/RecommendTheme/index.vue";
|
||||||
import {getRecommendedTopic,getAuthorTopic} from "@/apis/analyseApi";
|
import {getRecommendedTopic, getAuthorTopic} from "@/apis/analyseApi";
|
||||||
//数据定义
|
//数据定义
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const spacer = h(ElDivider, { direction: "vertical" });
|
const spacer = h(ElDivider, {direction: "vertical"});
|
||||||
//子文章数据
|
//子文章数据
|
||||||
const articleData = ref([]);
|
const articleData = ref([]);
|
||||||
//主题内容
|
//主题内容
|
||||||
const discuss = ref({});
|
const discuss = ref({});
|
||||||
//推荐主题
|
//推荐主题
|
||||||
const themeList=ref([]);
|
const themeList = ref([]);
|
||||||
//作者主题
|
//作者主题
|
||||||
const authorList=ref([]);
|
const authorList = ref([]);
|
||||||
//封面url
|
//封面url
|
||||||
const getUrl = (str) => {
|
const getUrl = (str) => {
|
||||||
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`;
|
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`;
|
||||||
@@ -225,10 +230,10 @@ const catalogueData = ref([]);
|
|||||||
const breadcrumbsList = ref([]);
|
const breadcrumbsList = ref([]);
|
||||||
const resultRouters = ["index", "discuss", "themeCover"];
|
const resultRouters = ["index", "discuss", "themeCover"];
|
||||||
breadcrumbsList.value = route.matched[0].children
|
breadcrumbsList.value = route.matched[0].children
|
||||||
.filter((item) => resultRouters.includes(item.name))
|
.filter((item) => resultRouters.includes(item.name))
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
return resultRouters.indexOf(a.name) - resultRouters.indexOf(b.name);
|
return resultRouters.indexOf(a.name) - resultRouters.indexOf(b.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当前文章名称
|
// 当前文章名称
|
||||||
const currentArticle = ref("");
|
const currentArticle = ref("");
|
||||||
@@ -242,9 +247,9 @@ const loadArticleData = async () => {
|
|||||||
//主题初始化
|
//主题初始化
|
||||||
const isDisabledCreateComment = ref(false);
|
const isDisabledCreateComment = ref(false);
|
||||||
const isArticleUser = ref(false);
|
const isArticleUser = ref(false);
|
||||||
const { isHasPermission: isAddArticle } = getPermission("bbs:article:add");
|
const {isHasPermission: isAddArticle} = getPermission("bbs:article:add");
|
||||||
const { isHasPermission: isEditTheme } = getPermission("bbs:discuss:update");
|
const {isHasPermission: isEditTheme} = getPermission("bbs:discuss:update");
|
||||||
const { isHasPermission: isRemoveTheme } = getPermission("bbs:discuss:del");
|
const {isHasPermission: isRemoveTheme} = getPermission("bbs:discuss:del");
|
||||||
const loadDiscuss = async (isRewrite) => {
|
const loadDiscuss = async (isRewrite) => {
|
||||||
if (isRewrite) {
|
if (isRewrite) {
|
||||||
//跳转路由
|
//跳转路由
|
||||||
@@ -357,9 +362,7 @@ const handleNodeClick = async (data) => {
|
|||||||
|
|
||||||
router.push(`/article/${route.params.discussId}/${data.id}`);
|
router.push(`/article/${route.params.discussId}/${data.id}`);
|
||||||
|
|
||||||
const response = await articleGet(data.id);
|
|
||||||
discuss.value.content = response.data.content;
|
|
||||||
ContentHander();
|
|
||||||
};
|
};
|
||||||
//删除子文章
|
//删除子文章
|
||||||
const delArticle = (node, data) => {
|
const delArticle = (node, data) => {
|
||||||
@@ -377,11 +380,11 @@ const delArticle = (node, data) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const loadThemeData =async () => {
|
const loadThemeData = async () => {
|
||||||
const {data: themeData} = await getRecommendedTopic();
|
const {data: themeData} = await getRecommendedTopic();
|
||||||
themeList.value = themeData;
|
themeList.value = themeData;
|
||||||
}
|
}
|
||||||
const loadAuthorData=async () => {
|
const loadAuthorData = async () => {
|
||||||
const {data: authorData} = await getAuthorTopic(discuss.value.user.id);
|
const {data: authorData} = await getAuthorTopic(discuss.value.user.id);
|
||||||
authorList.value = authorData;
|
authorList.value = authorData;
|
||||||
}
|
}
|
||||||
@@ -394,39 +397,53 @@ onMounted(async () => {
|
|||||||
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => currentArticle.value,
|
() => currentArticle.value,
|
||||||
(val) => {
|
(val) => {
|
||||||
if (val !== "") {
|
if (val !== "") {
|
||||||
breadcrumbsList.value[3] = {
|
breadcrumbsList.value[3] = {
|
||||||
name: "article",
|
name: "article",
|
||||||
path: "/article/:discussId",
|
path: "/article/:discussId",
|
||||||
meta: {
|
meta: {
|
||||||
title: currentArticle.value,
|
title: currentArticle.value,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
watch(
|
watch(
|
||||||
() => route.params.articleId,
|
() => route.params,
|
||||||
async (val) => {
|
async (val) => {
|
||||||
if (val === "") {
|
if (val.articleId !=="")
|
||||||
discuss.value = (await discussGet(route.params.discussId)).data;
|
{
|
||||||
|
const response = await articleGet(route.params.articleId);
|
||||||
|
discuss.value.content = response.data.content;
|
||||||
|
ContentHander();
|
||||||
|
}
|
||||||
|
else if (val.discussId !== "") {
|
||||||
|
discuss.value = (await discussGet(route.params.discussId)).data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
//路由发送变化,重新加载
|
||||||
|
// watch(() => route.params, async () => {
|
||||||
|
// await loadDiscuss();
|
||||||
|
// await loadArticleData();
|
||||||
|
// await loadAuthorData();
|
||||||
|
// await loadThemeData();
|
||||||
|
// }
|
||||||
|
// )
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
||||||
.subtitle
|
.subtitle {
|
||||||
{
|
color: #999AAA;
|
||||||
color:#999AAA;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-box {
|
.article-box {
|
||||||
width: 1500px;
|
width: 1500px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
min-height: 40rem;
|
min-height: 40rem;
|
||||||
}
|
}
|
||||||
|
|||||||
53
Yi.Bbs.Vue3/src/views/book/Index.vue
Normal file
53
Yi.Bbs.Vue3/src/views/book/Index.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
|
||||||
|
<script setup>
|
||||||
|
import BookCard from './components/BookCard.vue'
|
||||||
|
import {getList} from '@/apis/discussApi'
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import {useRouter} from "vue-router";
|
||||||
|
const discussList=ref([]);
|
||||||
|
|
||||||
|
//面试宝典板块id
|
||||||
|
const bookPlateId="d940818d-90ec-9dbe-b7af-3a0f935dac0a";
|
||||||
|
onMounted(async ()=>{
|
||||||
|
const {data}=await getList({plateId:bookPlateId, skipCount:1,maxResultCount:100});
|
||||||
|
discussList.value=data.items;
|
||||||
|
})
|
||||||
|
const router = useRouter();
|
||||||
|
const enterDiscuss = (id) => {
|
||||||
|
router.push(`/article/${id}`);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="body">
|
||||||
|
<p class="title">面试宝典</p>
|
||||||
|
<div class="content">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col @click="enterDiscuss(discussInfo.id)" v-for="discussInfo in discussList" :xs="12" :sm="8" :md="8" :lg="6" :xl="6">
|
||||||
|
<BookCard :discuss="discussInfo"/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.el-col{
|
||||||
|
cursor: pointer; /* 显示手型光标 */
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.body{
|
||||||
|
width: 1200px;
|
||||||
|
.title{
|
||||||
|
margin-top: 0.5em;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.2667;
|
||||||
|
color: rgba(0, 0, 0, 0.88);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
71
Yi.Bbs.Vue3/src/views/book/components/BookCard.vue
Normal file
71
Yi.Bbs.Vue3/src/views/book/components/BookCard.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps([
|
||||||
|
"discuss",
|
||||||
|
]);
|
||||||
|
const discuss=ref(props.discuss)
|
||||||
|
onMounted(()=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
const getUrl = (str) => {
|
||||||
|
return `${import.meta.env.VITE_APP_BASEAPI}/file/${str}`;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="left-logo">
|
||||||
|
<el-image style="width: 40px; height: 40px"
|
||||||
|
:src="getUrl(discuss.cover)"
|
||||||
|
fit="fill">
|
||||||
|
<template #error>
|
||||||
|
<el-icon size="40px" color="#F0F2F5"><Picture /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="right-content">
|
||||||
|
|
||||||
|
<div class="content-top">
|
||||||
|
{{discuss.title}}
|
||||||
|
</div>
|
||||||
|
<div class="content-bottom">
|
||||||
|
{{discuss.introduction}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.card-body {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
.left-logo{
|
||||||
|
|
||||||
|
}
|
||||||
|
.right-content{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
.content-top{
|
||||||
|
color: rgba(0, 0, 0, 0.88);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.content-bottom{
|
||||||
|
font-size: 14px;
|
||||||
|
color: rgba(0, 0, 0, 0.45);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -18,6 +18,7 @@ import {getUrl} from '@/utils/icon'
|
|||||||
|
|
||||||
//markdown ai显示
|
//markdown ai显示
|
||||||
import {marked} from 'marked';
|
import {marked} from 'marked';
|
||||||
|
import markedKatex from "marked-katex-extension";
|
||||||
import '@/assets/atom-one-dark.css';
|
import '@/assets/atom-one-dark.css';
|
||||||
import '@/assets/github-markdown.css';
|
import '@/assets/github-markdown.css';
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
@@ -39,11 +40,11 @@ const currentInputValue = ref("");
|
|||||||
//临时存储的输入框,根据用户id及组name、all组为key,data为value
|
//临时存储的输入框,根据用户id及组name、all组为key,data为value
|
||||||
const inputListDataStore = ref([
|
const inputListDataStore = ref([
|
||||||
{key: "all", name: "官方学习交流群", titleName: "官方学习交流群", logo: "yilogo.png", value: ""},
|
{key: "all", name: "官方学习交流群", titleName: "官方学习交流群", logo: "yilogo.png", value: ""},
|
||||||
{key: "ai@deepseek-chat", name: "DeepSeek聊天", titleName: "DeepSeek-聊天模式", logo: "deepSeekAi.png", value: ""},
|
{key: "ai@deepseek-chat", name: "DeepSeek聊天", titleName: "满血!DeepSeek-聊天模式", logo: "deepSeekAi.png", value: ""},
|
||||||
{
|
{
|
||||||
key: "ai@DeepSeek-R1",
|
key: "ai@deepseek-ai/deepseek-r1",
|
||||||
name: "DeepSeek思索",
|
name: "DeepSeek思索",
|
||||||
titleName: "DeepSeek-思索模式",
|
titleName: "满血!DeepSeek-思索模式",
|
||||||
logo: "deepSeekAi.png",
|
logo: "deepSeekAi.png",
|
||||||
value: ""
|
value: ""
|
||||||
},
|
},
|
||||||
@@ -66,6 +67,25 @@ onMounted(async () => {
|
|||||||
onclickClose();
|
onclickClose();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
marked.use(markedKatex({
|
||||||
|
throwOnError: false,
|
||||||
|
nonStandard: true
|
||||||
|
}));
|
||||||
|
marked.setOptions({
|
||||||
|
renderer: new marked.Renderer(),
|
||||||
|
highlight: function (code, language) {
|
||||||
|
return codeHandler(code, language);
|
||||||
|
},
|
||||||
|
pedantic: false,
|
||||||
|
gfm: true,//允许 Git Hub标准的markdown
|
||||||
|
tables: true,//支持表格
|
||||||
|
breaks: true,
|
||||||
|
sanitize: false,
|
||||||
|
smartypants: false,
|
||||||
|
xhtml: false,
|
||||||
|
smartLists: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
//all的聊天消息
|
//all的聊天消息
|
||||||
chatStore.setMsgList((await getChatAccountMessageList()).data);
|
chatStore.setMsgList((await getChatAccountMessageList()).data);
|
||||||
//在线用户列表
|
//在线用户列表
|
||||||
@@ -117,7 +137,7 @@ const currentMsgContext = computed(() => {
|
|||||||
});
|
});
|
||||||
//获取聊天内容的头像
|
//获取聊天内容的头像
|
||||||
const getChatUrl = (url, position) => {
|
const getChatUrl = (url, position) => {
|
||||||
if (position === "left" && (selectIsAi()||selectIsAll())) {
|
if (position === "left" && selectIsAi()) {
|
||||||
return imageSrc(inputListDataStore.value.find(x=>x.key===currentSelectUser.value).logo)
|
return imageSrc(inputListDataStore.value.find(x=>x.key===currentSelectUser.value).logo)
|
||||||
}
|
}
|
||||||
return getUrl(url);
|
return getUrl(url);
|
||||||
@@ -125,7 +145,7 @@ const getChatUrl = (url, position) => {
|
|||||||
//当前聊天框显示的名称
|
//当前聊天框显示的名称
|
||||||
const currentHeaderName = computed(() => {
|
const currentHeaderName = computed(() => {
|
||||||
if (selectIsAll()||selectIsAi()) {
|
if (selectIsAll()||selectIsAi()) {
|
||||||
return inputListDataStore.value.find(x=>x.key===currentSelectUser.value).name;
|
return inputListDataStore.value.find(x=>x.key===currentSelectUser.value).titleName;
|
||||||
} else {
|
} else {
|
||||||
return currentSelectUser.value.userName;
|
return currentSelectUser.value.userName;
|
||||||
}
|
}
|
||||||
@@ -333,27 +353,14 @@ const clearAiMsg = () => {
|
|||||||
|
|
||||||
//转换markdown
|
//转换markdown
|
||||||
const toMarkDownHtml = (text) => {
|
const toMarkDownHtml = (text) => {
|
||||||
marked.setOptions({
|
//处理数学公式
|
||||||
renderer: new marked.Renderer(),
|
let soureMd=text.replace(/\\\[/g, '$').replace(/\\\]/g, '$');
|
||||||
highlight: function (code, language) {
|
|
||||||
return codeHandler(code, language);
|
|
||||||
},
|
|
||||||
pedantic: false,
|
|
||||||
gfm: true,//允许 Git Hub标准的markdown
|
|
||||||
tables: true,//支持表格
|
|
||||||
breaks: true,
|
|
||||||
sanitize: false,
|
|
||||||
smartypants: false,
|
|
||||||
xhtml: false,
|
|
||||||
smartLists: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//需要注意代码块样式
|
//需要注意代码块样式
|
||||||
const soureHtml = marked(text);
|
let soureHtml = marked(soureMd);
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
addCopyEvent();
|
addCopyEvent();
|
||||||
})
|
})
|
||||||
return soureHtml;
|
return soureHtml;
|
||||||
}
|
}
|
||||||
//code部分处理、高亮
|
//code部分处理、高亮
|
||||||
const codeHandler = (code, language) => {
|
const codeHandler = (code, language) => {
|
||||||
@@ -421,7 +428,7 @@ const clickCopyEvent = async function (event) {
|
|||||||
const spanId = event.target.id;
|
const spanId = event.target.id;
|
||||||
console.log(codeCopyDic, "codeCopyDic")
|
console.log(codeCopyDic, "codeCopyDic")
|
||||||
console.log(spanId, "spanId")
|
console.log(spanId, "spanId")
|
||||||
await navigator.clipboard.writeText(codeCopyDic.filter(x => x.id === spanId)[0].code);
|
await navigator.clipboard.writeText(codeCopyDic.filter(x => x.id == spanId)[0].code);
|
||||||
ElMessage({
|
ElMessage({
|
||||||
message: "代码块复制成功",
|
message: "代码块复制成功",
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -433,7 +440,7 @@ const clickCopyEvent = async function (event) {
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div style="position: absolute; top: 0;left: 0;" v-show="isShowTipNumber>0">
|
<div style="position: absolute; top: 0;left: 0;" v-show="isShowTipNumber>0">
|
||||||
<p>当前版本:2.0.0</p>
|
<p>当前版本:2.1.0</p>
|
||||||
<p>tip:官方学习交流群每次发送消息消耗 1 钱钱</p>
|
<p>tip:官方学习交流群每次发送消息消耗 1 钱钱</p>
|
||||||
<p>tip:点击聊天窗口右上角“X”可退出</p>
|
<p>tip:点击聊天窗口右上角“X”可退出</p>
|
||||||
<p>tip:多人同时在聊天室时,左侧可显示其他成员</p>
|
<p>tip:多人同时在聊天室时,左侧可显示其他成员</p>
|
||||||
@@ -1122,17 +1129,39 @@ ul {
|
|||||||
color: red;
|
color: red;
|
||||||
cursor: pointer; /* 设置鼠标悬浮为手型 */
|
cursor: pointer; /* 设置鼠标悬浮为手型 */
|
||||||
}
|
}
|
||||||
|
::v-deep(.katex-html)
|
||||||
|
{
|
||||||
|
color: #7B7C7C;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
::v-deep(.nav-ul) {
|
::v-deep(.nav-ul) {
|
||||||
border-right: 1px solid #FFFFFF;
|
border-right: 1px solid #FFFFFF;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
margin-left: 0 !important;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
|
|
||||||
.nav-li {
|
.nav-li {
|
||||||
margin: 1.0px 0;
|
margin: 1.0px 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.content-msg-common ::v-deep(ul){
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.content-msg-common ::v-deep(ol){
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
::v-deep(.katex){
|
||||||
|
margin: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, defineProps, defineEmits } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ margin: 10px auto;">
|
|||||||
<el-col v-if="!isIcp" :span="24">
|
<el-col v-if="!isIcp" :span="24">
|
||||||
<template v-if="isPointFinished">
|
<template v-if="isPointFinished">
|
||||||
<InfoCard :isPadding="false" :items="pointList" header="财富排行榜" text="查看我的位置" height="410"
|
<InfoCard :isPadding="false" :items="pointList" header="财富排行榜" text="查看我的位置" height="410"
|
||||||
|
style="padding:0 20px"
|
||||||
@onClickText="onClickMoneyTop">
|
@onClickText="onClickMoneyTop">
|
||||||
<template #item="temp">
|
<template #item="temp">
|
||||||
<PointsRanking :pointsData="temp"/>
|
<PointsRanking :pointsData="temp"/>
|
||||||
@@ -179,7 +180,8 @@ margin: 10px auto;">
|
|||||||
|
|
||||||
<el-col v-if="!isIcp" :span="24">
|
<el-col v-if="!isIcp" :span="24">
|
||||||
<template v-if="isFriendFinished">
|
<template v-if="isFriendFinished">
|
||||||
<InfoCard :isPadding="false" :items="friendList" header="推荐好友" text="更多" height="400">
|
<InfoCard :isPadding="false" :items="friendList" header="推荐好友" text="更多" height="400"
|
||||||
|
style="padding:0 20px">
|
||||||
<template #item="temp">
|
<template #item="temp">
|
||||||
<RecommendFriend :friendData="temp"/>
|
<RecommendFriend :friendData="temp"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -195,7 +197,9 @@ margin: 10px auto;">
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col v-if="!isIcp" :span="24">
|
<el-col v-if="!isIcp" :span="24">
|
||||||
<template v-if="isThemeFinished">
|
<template v-if="isThemeFinished">
|
||||||
<InfoCard :isPadding="false" :items="themeList" header="推荐主题" text="更多" height="400">
|
<InfoCard :isPadding="false" :items="themeList" header="推荐主题" text="更多" height="400"
|
||||||
|
style="padding:0 20px"
|
||||||
|
>
|
||||||
<template #item="temp">
|
<template #item="temp">
|
||||||
<ThemeData :themeData="temp"/>
|
<ThemeData :themeData="temp"/>
|
||||||
</template>
|
</template>
|
||||||
@@ -307,10 +311,10 @@ const activeList = [
|
|||||||
{name: "排行榜", path: "/money", icon: "Money"},
|
{name: "排行榜", path: "/money", icon: "Money"},
|
||||||
{name: "开始", path: "/start", icon: "Position"},
|
{name: "开始", path: "/start", icon: "Position"},
|
||||||
{name: "聊天室", path: "/chat", icon: "ChatRound"},
|
{name: "聊天室", path: "/chat", icon: "ChatRound"},
|
||||||
|
|
||||||
|
|
||||||
{name: "商城", path: "/shop", icon: "ShoppingCart"},
|
{name: "商城", path: "/shop", icon: "ShoppingCart"},
|
||||||
{name: "数字藏品", path: "/dc", icon: "Trophy"},
|
{name: "数字藏品", path: "/dc", icon: "Trophy"},
|
||||||
|
{name: "面试宝典", path: "/book", icon: "Memo"},
|
||||||
// {name: "小程序", path: "/", icon: "Position"},
|
// {name: "小程序", path: "/", icon: "Position"},
|
||||||
// {name: "公众号", path: "/", icon: "ChatRound"},
|
// {name: "公众号", path: "/", icon: "ChatRound"},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="AccessLogChart">
|
<script setup name="AccessLogChart">
|
||||||
import { ref, defineEmits, defineProps, defineExpose } from "vue";
|
import { ref } from "vue";
|
||||||
import useEcharts from "@/hooks/useEcharts";
|
import useEcharts from "@/hooks/useEcharts";
|
||||||
import { accessLogEchartsConfig } from "../../hooks/accessLogEchartsConfig";
|
import { accessLogEchartsConfig } from "../../hooks/accessLogEchartsConfig";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="PointsRanking">
|
<script setup name="PointsRanking">
|
||||||
import { defineProps, computed } from "vue";
|
import { computed } from "vue";
|
||||||
import UserInfoCard from "@/components/UserInfoCard/index.vue";
|
import UserInfoCard from "@/components/UserInfoCard/index.vue";
|
||||||
import UserLimitTag from "@/components/UserLimitTag.vue";
|
import UserLimitTag from "@/components/UserLimitTag.vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="RecommendFriend">
|
<script setup name="RecommendFriend">
|
||||||
import { defineProps, computed } from "vue";
|
import { computed } from "vue";
|
||||||
import UserInfoCard from "@/components/UserInfoCard/index.vue";
|
import UserInfoCard from "@/components/UserInfoCard/index.vue";
|
||||||
import UserLimitTag from "@/components/UserLimitTag.vue";
|
import UserLimitTag from "@/components/UserLimitTag.vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="RecommendFriend">
|
<script setup name="RecommendFriend">
|
||||||
import { defineProps, ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -57,7 +57,6 @@ const seeNumLength = ref(props.themeData.seeNum.toString().length);
|
|||||||
|
|
||||||
const handleClickTheme = (id) => {
|
const handleClickTheme = (id) => {
|
||||||
router.push(`/article/${id}`);
|
router.push(`/article/${id}`);
|
||||||
router.go(0);
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="VisitsLineChart">
|
<script setup name="VisitsLineChart">
|
||||||
import { ref, defineEmits, defineProps, defineExpose } from "vue";
|
import { ref } from "vue";
|
||||||
import useEcharts from "@/hooks/useEcharts";
|
import useEcharts from "@/hooks/useEcharts";
|
||||||
import { statisticsEcharts } from "../../hooks/echartsConfig";
|
import { statisticsEcharts } from "../../hooks/echartsConfig";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
28
Yi.Bbs.Vue3/yi-bbs.conf
Normal file
28
Yi.Bbs.Vue3/yi-bbs.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
server {
|
||||||
|
client_header_buffer_size 10k;
|
||||||
|
large_client_header_buffers 40 40k;
|
||||||
|
listen 18001;
|
||||||
|
server_name _;
|
||||||
|
client_max_body_size 100m;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
location /{
|
||||||
|
root /app;
|
||||||
|
index index.html;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
location /prod-api/ {
|
||||||
|
proxy_pass http://ccnetcore.com:19001/api/app/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /prod-ws/ {
|
||||||
|
proxy_pass http://ccnetcore.com:19001/hub/;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user