├── .gitattributes ├── .gitignore ├── .idea └── .idea.CoolNetBlog │ └── .idea │ ├── .gitignore │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── CommonObject ├── CommonObject.csproj ├── Constructs │ └── ValueResult.cs ├── Enums │ ├── DataBaseTypes.cs │ └── ValueCodes.cs └── Methods │ ├── BashExecute.cs │ ├── PathProvider.cs │ └── ValueCompute.cs ├── ComponentsServices ├── Base │ ├── Inter │ │ └── IDateBaseStorage.cs │ └── SugarDataBaseStorage.cs ├── ComponentsServices.csproj ├── Mail.MailKit │ └── MailSendHelper.cs ├── ORM.ConfigSugar │ └── SugarDbConfiged.cs └── configs.json ├── CoolNetBlog.sln ├── CoolNetBlog ├── .config │ └── dotnet-tools.json ├── Base │ ├── AdminEnterFilter.cs │ ├── BaseAdminController.cs │ └── BaseController.cs ├── Bll │ ├── BaseLogicBll.cs │ ├── CommentBll.cs │ ├── DetailLogicBll.cs │ ├── GossipBll.cs │ └── ThumbsUpBll.cs ├── Controllers │ ├── AdminAccess │ │ ├── AdminArticleController.cs │ │ ├── AdminController.cs │ │ ├── AdminFileController.cs │ │ ├── AdminGossipController.cs │ │ ├── AdminLeaveMessageController.cs │ │ ├── AdminLoveLookController.cs │ │ ├── AdminMenuController.cs │ │ └── AdminSiteSettingController.cs │ ├── Api │ │ └── Blog │ │ │ └── BlogApiController.cs │ ├── CommentController.cs │ ├── DetailController.cs │ ├── EpLinkHelperController.cs │ ├── GossipController.cs │ ├── HomeController.cs │ ├── ThumbsUpController.cs │ └── WarningPageController.cs ├── CoolNetBlog.csproj ├── Exception │ ├── BlogException.cs │ ├── DetailNotExistException.cs │ └── MenuNotExistException.cs ├── Models │ ├── AdminUser.cs │ ├── Article.cs │ ├── ArticleThumbUp.cs │ ├── Comment.cs │ ├── CommonThumbUp.cs │ ├── FilePath.cs │ ├── Gossip.cs │ ├── LoveLook.cs │ ├── Menu.cs │ ├── Reply.cs │ └── SiteSetting.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── ViewModels │ ├── Admin │ │ ├── ArticleViewModel.cs │ │ ├── DataBackViewModel.cs │ │ ├── FilePathViewModel.cs │ │ ├── GossipViewModel.cs │ │ ├── LeaveMessageViewModel.cs │ │ ├── LoginViewModel.cs │ │ ├── LoveLookViewModel.cs │ │ ├── MenuViewModel.cs │ │ ├── PassBaseViewModel.cs │ │ ├── ResetViewModel.cs │ │ └── SiteSettingViewModel.cs │ ├── CommentViewModel.cs │ ├── Detail │ │ ├── DetailArticleUnLockViewModel.cs │ │ └── DetailArticleViewModel.cs │ ├── Home │ │ ├── HomeArticleViewModel.cs │ │ ├── HomeLoveLookViewModel.cs │ │ ├── HomeMenuViewModel.cs │ │ ├── HomeSiteSettingViewModel.cs │ │ └── HomeViewModel.cs │ ├── ReplyViewModel.cs │ └── SelectList.cs ├── Views │ ├── AdminAccess │ │ ├── Admin │ │ │ ├── AdminHome.cshtml │ │ │ └── Login.cshtml │ │ ├── AdminArticle │ │ │ ├── ArticleAmManagement.cshtml │ │ │ ├── ArticleEdit.cshtml │ │ │ └── _ViewStart.cshtml │ │ ├── AdminFile │ │ │ └── FileAmManagement.cshtml │ │ ├── AdminGossip │ │ │ ├── GossipAmManagement.cshtml │ │ │ └── _ViewStart.cshtml │ │ ├── AdminLeaveMessage │ │ │ ├── LeaveNotPassAmManagement.cshtml │ │ │ ├── LeavePublicAmManagement.cshtml │ │ │ └── _ViewStart.cshtml │ │ ├── AdminLoveLook │ │ │ └── LoveLookAmManagement.cshtml │ │ ├── AdminMenu │ │ │ └── MenuAmManagement.cshtml │ │ ├── AdminSiteSetting │ │ │ └── EditSiteSetting.cshtml │ │ └── _ViewStart.cshtml │ ├── Detail │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── NotFound.cshtml │ │ ├── _AdminLayout.cshtml │ │ ├── _AdminLayout.cshtml.css │ │ ├── _AdminLeaveMessageLayout.cshtml │ │ ├── _AdminTextPostLayout.cshtml │ │ ├── _AdminTextPostLayout.cshtml.css │ │ ├── _Layout.cshtml │ │ ├── _Layout.cshtml.css │ │ └── _ValidationScriptsPartial.cshtml │ ├── WarningPage │ │ ├── Error.cshtml │ │ └── _ViewStart.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ ├── css │ └── site.css │ ├── favicon.ico │ ├── favicon.png │ ├── js │ ├── com.js │ ├── md5.min.js │ └── view │ │ ├── Detail │ │ └── Index.js │ │ ├── Home │ │ └── Index.js │ │ └── Shared │ │ └── _Layout.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-grid.rtl.css │ │ ├── bootstrap-grid.rtl.css.map │ │ ├── bootstrap-grid.rtl.min.css │ │ ├── bootstrap-grid.rtl.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap-reboot.rtl.css │ │ ├── bootstrap-reboot.rtl.css.map │ │ ├── bootstrap-reboot.rtl.min.css │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ ├── bootstrap-utilities.css │ │ ├── bootstrap-utilities.css.map │ │ ├── bootstrap-utilities.min.css │ │ ├── bootstrap-utilities.min.css.map │ │ ├── bootstrap-utilities.rtl.css │ │ ├── bootstrap-utilities.rtl.css.map │ │ ├── bootstrap-utilities.rtl.min.css │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── bootstrap.rtl.css │ │ ├── bootstrap.rtl.css.map │ │ ├── bootstrap.rtl.min.css │ │ └── bootstrap.rtl.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.esm.js │ │ ├── bootstrap.esm.js.map │ │ ├── bootstrap.esm.min.js │ │ ├── bootstrap.esm.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ ├── jquery │ ├── LICENSE.txt │ └── dist │ │ ├── jquery.js │ │ ├── jquery.min.js │ │ └── jquery.min.map │ └── uikit │ ├── css │ ├── uikit-rtl.css │ ├── uikit-rtl.min.css │ ├── uikit.css │ └── uikit.min.css │ └── js │ ├── uikit-icons.js │ ├── uikit-icons.min.js │ ├── uikit.js │ └── uikit.min.js ├── LICENSE ├── README-EN.md ├── README-IMGS.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.idea/.idea.CoolNetBlog/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /projectSettingsUpdater.xml 7 | /modules.xml 8 | /.idea.CoolNetBlog.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.CoolNetBlog/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/.idea.CoolNetBlog/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.CoolNetBlog/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CommonObject/CommonObject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CommonObject/Constructs/ValueResult.cs: -------------------------------------------------------------------------------- 1 | using CommonObject.Enums; 2 | 3 | namespace CommonObject.Constructs 4 | { 5 | public class ValueResult 6 | { 7 | public ValueCodes Code { get; set; } 8 | public string? TipMessage { get; set; } = ""; 9 | public string? HideMessage { get; set; } = ""; 10 | public dynamic DmData { get; set; } 11 | public object Data { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CommonObject/Enums/DataBaseTypes.cs: -------------------------------------------------------------------------------- 1 | namespace CommonObject.Enums 2 | { 3 | /// 4 | /// 数据库产品类型 5 | /// 6 | public enum DataBaseTypes 7 | { 8 | MySql, 9 | SqlServer, 10 | Sqlite, 11 | Oracle, 12 | PostgreSQL, 13 | } 14 | } -------------------------------------------------------------------------------- /CommonObject/Enums/ValueCodes.cs: -------------------------------------------------------------------------------- 1 | namespace CommonObject.Enums 2 | { 3 | public enum ValueCodes 4 | { 5 | Error, 6 | Success, 7 | UnKnow 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /CommonObject/Methods/BashExecute.cs: -------------------------------------------------------------------------------- 1 | using CommonObject.Constructs; 2 | using System.Diagnostics; 3 | using System.Text; 4 | 5 | namespace CommonObject.Methods 6 | { 7 | public class BashExecute 8 | { 9 | /// 10 | /// 执行bash command,接收结果 11 | /// 12 | /// 纯命令文本 13 | /// 是何操作系统 14 | /// 15 | public static ValueResult Bash(string cmdInput, bool unix=true) 16 | { 17 | ValueResult result = new ValueResult(); 18 | var escapedArgs = unix ? $"-c \"{cmdInput.Replace("\"", "\\\"")}\"" : "/C " + cmdInput; 19 | var process = new Process() 20 | { 21 | StartInfo = new ProcessStartInfo 22 | { 23 | FileName = unix ? "/bin/bash" : "cmd.exe", 24 | Arguments = escapedArgs, 25 | Verb = "RunAs", 26 | RedirectStandardOutput = true, 27 | RedirectStandardError = true, 28 | UseShellExecute = false, 29 | CreateNoWindow = true, 30 | } 31 | }; 32 | var errPut = new StringBuilder(); 33 | process.ErrorDataReceived += (sender, args) => errPut.AppendLine(args.Data); 34 | var output = ""; 35 | try 36 | { 37 | process.Start(); 38 | process.BeginErrorReadLine(); 39 | output = process.StandardOutput.ReadToEnd(); 40 | process.WaitForExit(); 41 | } 42 | catch (Exception e) 43 | { 44 | result.Code = Enums.ValueCodes.Error; 45 | result.HideMessage = "执行bash命令发生异常: " + e.Message; 46 | return result; 47 | } 48 | 49 | if (process.ExitCode != 0) 50 | { 51 | result.Code = Enums.ValueCodes.UnKnow; 52 | result.HideMessage = "执行bash命令得到未知结果,code:" + process.ExitCode + ";结果: " + errPut; 53 | return result; 54 | } 55 | result.Code = Enums.ValueCodes.Success; 56 | result.TipMessage = output; 57 | result.HideMessage = output; 58 | return result; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /CommonObject/Methods/PathProvider.cs: -------------------------------------------------------------------------------- 1 | namespace CommonObject.Methods 2 | { 3 | public class PathProvider 4 | { 5 | /// 6 | /// 验证是否字符串是合格的Url地址 7 | /// 8 | /// 9 | /// 10 | public static bool TryCreateUrl(string url) { 11 | Uri? uriResult; 12 | bool passUri = Uri.TryCreate(url, UriKind.Absolute, out uriResult) 13 | && (uriResult?.Scheme == Uri.UriSchemeHttp || uriResult?.Scheme == Uri.UriSchemeHttps); 14 | return passUri; 15 | } 16 | 17 | /// 18 | /// 复制文件夹到一个位置,原文件夹不存在则忽略 19 | /// 20 | /// 要复制的文件夹路径,不存在则不执行复制操作 21 | /// 目标路径 22 | public static void CopyDir(string srcPath, string disPath) 23 | { 24 | var srcDir = new DirectoryInfo(srcPath); 25 | var disDir = new DirectoryInfo(disPath); 26 | if (srcDir.Exists) 27 | { 28 | CopyDirCall(srcDir, disDir); 29 | } 30 | } 31 | 32 | /// 33 | /// 复制文件夹到指定目录,核心递归方法 34 | /// 35 | /// 要复制的目录 36 | /// 目录的目标同名路径 37 | private static void CopyDirCall(DirectoryInfo srcDir, DirectoryInfo disDir) 38 | { 39 | Directory.CreateDirectory(disDir.FullName); 40 | foreach (FileInfo fi in srcDir.GetFiles()) 41 | { 42 | fi.CopyTo(Path.Combine(disDir.FullName, fi.Name), true); 43 | } 44 | 45 | foreach (DirectoryInfo theSubD in srcDir.GetDirectories()) 46 | { 47 | DirectoryInfo theSameDisSubDir = disDir.CreateSubdirectory(theSubD.Name); 48 | CopyDirCall(theSubD, theSameDisSubDir); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /CommonObject/Methods/ValueCompute.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace CommonObject.Methods 5 | { 6 | /// 7 | /// 公共计算方法类 8 | /// 9 | public class ValueCompute 10 | { 11 | /// 12 | /// 返回一个加密了的MD5字符串表现形式(32个字符长度) 13 | /// 14 | /// 15 | /// 16 | public static string MakeMD5(string str) 17 | { 18 | using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) 19 | { 20 | byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(str); 21 | byte[] hashBytes = md5.ComputeHash(inputBytes); 22 | 23 | StringBuilder sb = new StringBuilder(); 24 | for (int i = 0; i < hashBytes.Length; i++) 25 | { 26 | sb.Append(hashBytes[i].ToString("X2")); 27 | } 28 | return sb.ToString(); 29 | } 30 | 31 | } 32 | 33 | public static string NewSaveString(string input) 34 | { 35 | return input.Replace("<", "").Replace(">", "") 36 | .Replace("javascript:", "javascript:", true, null) 37 | .Replace("delete", "删除", true, null) 38 | .Replace("update", "更新", true, null) 39 | .Replace("insert", "插入", true, null) 40 | .Replace("show", "显示", true, null) 41 | .Replace("select", "查询", true, null) 42 | .Replace("drop", "删除", true, null) 43 | .Replace("alter", "修改", true, null); 44 | } 45 | 46 | public static bool CheckNotNullAndWhiteValue(object newO) 47 | { 48 | if (newO is null) 49 | { 50 | return false; 51 | } 52 | var type = newO.GetType(); 53 | if (type.FullName.ToLower().EndsWith("string")) 54 | { 55 | if (string.IsNullOrWhiteSpace(newO.ToString())) 56 | { 57 | return false; 58 | } 59 | } 60 | foreach (var p in type.GetProperties()) 61 | { 62 | if (p != null) 63 | { 64 | if (p.GetValue(newO) == null) 65 | { 66 | return false; 67 | } 68 | if (string.IsNullOrWhiteSpace(p.GetValue(newO)?.ToString())) 69 | { 70 | return false; 71 | 72 | } 73 | } 74 | } 75 | return true; 76 | } 77 | public static void SetNotNullForObj(object newO) 78 | { 79 | try 80 | { 81 | var type = newO?.GetType(); 82 | 83 | if (type != null) 84 | { 85 | foreach (var p in type.GetProperties()) 86 | { 87 | if (p.GetValue(newO) == null) 88 | { 89 | if (p.GetType().IsValueType) 90 | { 91 | newO = 0; 92 | } 93 | else 94 | { 95 | p.SetValue(newO, ""); 96 | } 97 | } 98 | } 99 | } 100 | } 101 | catch (Exception) 102 | { 103 | } 104 | } 105 | 106 | public static bool CheckHtmlLabelContains(string? input) 107 | { 108 | if (string.IsNullOrWhiteSpace(input)) 109 | { 110 | return false; 111 | } 112 | Regex regex = new Regex(@"<\w+>", RegexOptions.IgnoreCase); 113 | var b = regex.IsMatch(input); 114 | if (b == false) 115 | { 116 | b = input.ToLower().Contains("javascript:"); 117 | } 118 | return b; 119 | } 120 | 121 | /// 122 | /// 改成.Replace("<", "《").Replace(">", "》")效果一样 123 | /// 124 | /// 125 | /// 126 | public static string ReplaceHtmlLabelContains(string? input) 127 | { 128 | if (string.IsNullOrWhiteSpace(input)) 129 | { 130 | return input; 131 | } 132 | //改为中文冒号,避免"javascript:"字符 133 | input = input.Replace("javascript:", "javascript:", true, null); 134 | //匹配以<开头以>结尾,中间是任意字符包含空白的标签元素,且?让它尽可能少匹配,这样不会只匹配最外层的一个 135 | Regex regex = new Regex(@"<(.|\s)*?>", RegexOptions.IgnoreCase); 136 | var matcheds = regex.Matches(input); 137 | if (matcheds.Count>0) 138 | { 139 | foreach (Match mh in matcheds) 140 | { 141 | var mhv = mh.Value; 142 | string? filterInput = input.Replace(mhv, $"《{mhv.Trim('<').Trim('>')}》"); 143 | input = filterInput; 144 | } 145 | } 146 | return input; 147 | } 148 | 149 | /// 150 | /// 字符串是否是邮箱形式 151 | /// 152 | /// 153 | public static bool IsEmail(string inpu) 154 | { 155 | Regex regex = new Regex(@"^[\w-]+([\.-]?\w+)*@[\w-]+([\.-]?\w+)*(\.\w{2,10})+$"); 156 | return regex.IsMatch(inpu); 157 | } 158 | 159 | /// 160 | /// GUID生成16位字符串 161 | /// 162 | /// 163 | public static string Guid16() 164 | { 165 | long i = 1; 166 | foreach (byte b in Guid.NewGuid().ToByteArray()) 167 | { 168 | i *= b + 1; 169 | } 170 | return string.Format("{0:x}", i - DateTime.Now.Ticks); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /ComponentsServices/Base/Inter/IDateBaseStorage.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace ComponentsServices.Base.Inter 4 | { 5 | public interface IDateBaseStorage where TEntity : class,new() 6 | { 7 | IList GetAllList(); 8 | Task> GetAllListAsync(); 9 | Task> GetTopCountListAsync(int tc, Expression> orderCol, SqlSugar.OrderByType type = SqlSugar.OrderByType.Asc); 10 | IList GetListByExp(Expression> p); 11 | Task> GetListByExpAsync(Expression> p); 12 | SqlSugar.ISugarQueryable GetListBuilder(); 13 | TEntity FindOneById(object id); 14 | Task FindOneByIdAsync(object id); 15 | TEntity FirstOrDefault(Expression> p); 16 | Task FirstOrDefaultAsync(Expression> p); 17 | int Insert(TEntity entity); 18 | Task InsertAsync(TEntity entity); 19 | int Update(TEntity entity); 20 | Task UpdateAsync(TEntity entity); 21 | Task UpdateByExpAsync(TEntity entity,Expression> p); 22 | Task UpdateBySomeColExpAsync(Expression> cols, Expression> p); 23 | Task UpdateByIgColsAsync(TEntity entity, params string[] igCols); 24 | int Delete(TEntity entity); 25 | Task DeleteAsync(TEntity entity); 26 | Task DeleteEntitiesAsync(Expression> p); 27 | Task CountAsync(); 28 | Task CountAsync(Expression> p); 29 | bool Any(Expression> p); 30 | Task AnyAsync(Expression> p); 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ComponentsServices/ComponentsServices.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | PreserveNewest 22 | true 23 | PreserveNewest 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /ComponentsServices/Mail.MailKit/MailSendHelper.cs: -------------------------------------------------------------------------------- 1 | using MailKit.Net.Smtp; 2 | using MimeKit; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ComponentsServices.Mail.MailKit 10 | { 11 | /// 12 | /// 发送邮件类 13 | /// 14 | public class MailSendHelper 15 | { 16 | private MimeMessage emailData; 17 | private string _host; 18 | private int _port; 19 | private bool _useSsl; 20 | /// 21 | /// 初始化Mail发送类,后续必须调用InputEmailServerAddr()指定邮箱服务器地址。 22 | /// 23 | public MailSendHelper() 24 | { 25 | emailData =new MimeMessage(); 26 | } 27 | 28 | /// 29 | /// 指定邮箱服务器地址初始化Mail发送类 30 | /// 31 | /// 32 | /// 33 | /// 34 | public MailSendHelper(string host, int port, bool useSsl=false) 35 | { 36 | emailData = new MimeMessage(); 37 | _host = host; 38 | _port = port; 39 | _useSsl = useSsl; 40 | } 41 | 42 | /// 43 | /// 指定邮箱服务器地址 44 | /// 45 | /// 46 | /// 47 | /// 48 | public void InputSmtpServerHost(string host, int port, bool useSsl=false) 49 | { 50 | _host = host; 51 | _port = port; 52 | _useSsl = useSsl; 53 | } 54 | 55 | /// 56 | /// 指定发送者的邮件地址 57 | /// 58 | /// 此次发送的发送人姓名 59 | /// 此次发送的发送人邮件地址 60 | public void InputYourEmail(string youName, string youEmail) 61 | { 62 | emailData.From.Clear(); 63 | emailData.From.Add(new MailboxAddress(youName, youEmail)); 64 | 65 | } 66 | 67 | /// 68 | /// 指定要接收对象的邮件地址 69 | /// 70 | /// 此次发送的接收人姓名 71 | /// 此次发送的接收人邮件地址 72 | public void InputFriendEmail(string friendName, string friendEmail) 73 | { 74 | emailData.To.Add(new MailboxAddress(friendName, friendEmail)); 75 | } 76 | 77 | /// 78 | /// 设置邮件内容 79 | /// 80 | /// 主题 81 | /// 邮件内容 82 | /// 是否内容文本是html格式的文本 83 | public void InputContent(string subject, string bodyContent, bool useHtmlText=false) 84 | { 85 | emailData.Subject = subject; 86 | var textType = useHtmlText ? "html" : "plain"; 87 | emailData.Body = new TextPart(textType) 88 | { 89 | Text = bodyContent 90 | }; 91 | } 92 | 93 | 94 | /// 95 | /// 尝试发送邮件 96 | /// 97 | public void Send() { 98 | using (var client = new SmtpClient()) 99 | { 100 | client.Connect(_host, _port, _useSsl); 101 | client.Send(emailData); 102 | client.Disconnect(true); 103 | } 104 | } 105 | 106 | /// 107 | /// 尝试发送邮件,使用发送人邮箱服务器的账户密码 108 | /// 109 | public void SendByAuthenticate(string emailUsername, string emailPassword) 110 | { 111 | using (var client = new SmtpClient()) 112 | { 113 | client.Connect(_host, _port, _useSsl); 114 | client.Authenticate(emailUsername, emailPassword); 115 | client.Send(emailData); 116 | client.Disconnect(true); 117 | } 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ComponentsServices/ORM.ConfigSugar/SugarDbConfiged.cs: -------------------------------------------------------------------------------- 1 | using CommonObject.Enums; 2 | using SqlSugar; 3 | using System.Text.Json; 4 | 5 | namespace ComponentsServices.ORM.ConfigSugar 6 | { 7 | public class SugarDbConfiged 8 | { 9 | /// 10 | /// 静态数据库连接字符串变量 注意:项目只使用单数据库模式。 11 | /// 多数据库切换,DbConnStr和DataBaseType不能是static,因为多个客户端连接都会使用最先定义的全局静态,导致数据连接错误 12 | /// 13 | private static string? DbConnStr; 14 | /// 15 | /// 静态数据库连接类型 注意:项目只使用单数据库模式。 16 | /// 多数据库切换,DbConnStr和DataBaseType不能是static,因为多个客户端连接都会使用最先定义的全局静态,导致数据连接错误 17 | /// 18 | private static DataBaseTypes DataBaseType; 19 | public SqlSugarScope DbHandler { get; set; } 20 | public SugarDbConfiged(DataBaseTypes dataBaseType = DataBaseTypes.MySql) 21 | { 22 | DataBaseType = dataBaseType; 23 | //创建数据库对象 24 | SqlSugarScope db = new SqlSugarScope(new ConnectionConfig() 25 | { 26 | ConnectionString = FindConnStr(), 27 | DbType = FindType(), 28 | IsAutoCloseConnection = true 29 | }); 30 | 31 | db.Aop.OnLogExecuting = (sql, pars) => 32 | { 33 | //Console.WriteLine(sql); 34 | }; 35 | this.DbHandler = db; 36 | //db.DbFirst.IsCreateAttribute().CreateClassFile(@"C:\Users\Public\M", "Models"); 37 | } 38 | 39 | private DbType FindType() 40 | { 41 | switch (DataBaseType) 42 | { 43 | case DataBaseTypes.MySql: 44 | return DbType.MySql; 45 | case DataBaseTypes.SqlServer: 46 | return DbType.SqlServer; 47 | default: 48 | throw new ArgumentNullException("DataBaseTypes not matched."); 49 | } 50 | } 51 | private static string FindConnStr() 52 | { 53 | // DbConnStr已在最初的连接时被设置 后续任何操作、任何客户端连接都不必再读 54 | if (string.IsNullOrWhiteSpace(DbConnStr)) 55 | { 56 | var jsonStr = File.ReadAllText($"configs.json"); 57 | var appSettings = JsonDocument.Parse(jsonStr, new JsonDocumentOptions { CommentHandling = JsonCommentHandling.Skip }); 58 | var needResult = appSettings.RootElement.GetProperty("DbConnStr"); 59 | bool isFinded; 60 | switch (DataBaseType) 61 | { 62 | case DataBaseTypes.MySql: 63 | isFinded = needResult.TryGetProperty("Default", out _); 64 | if (isFinded) 65 | { 66 | DbConnStr = needResult.GetProperty("Default").ToString(); 67 | } 68 | break; 69 | case DataBaseTypes.SqlServer: 70 | isFinded = needResult.TryGetProperty("SqlServer", out _); 71 | if (isFinded) 72 | { 73 | DbConnStr = needResult.GetProperty("Default").ToString(); 74 | } 75 | break; 76 | } 77 | } 78 | return DbConnStr == ""?throw new ArgumentException("connection string is empty."): DbConnStr ?? 79 | throw new ArgumentNullException("connection string is null."); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ComponentsServices/configs.json: -------------------------------------------------------------------------------- 1 | { 2 | "DbConnStr": { 3 | "Default": "123", 4 | "MySql": "123", 5 | "SqlServer": "" 6 | }, 7 | "Redis": {} 8 | } 9 | -------------------------------------------------------------------------------- /CoolNetBlog.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.32112.339 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoolNetBlog", "CoolNetBlog\CoolNetBlog.csproj", "{9138E48D-6404-4DBE-8DA4-5F42EDCC7EEC}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ComponentsServices", "ComponentsServices\ComponentsServices.csproj", "{833B99EC-80AD-4FDA-9361-45B76338732F}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonObject", "CommonObject\CommonObject.csproj", "{503CC776-7BA0-4E9D-A18A-C6AD4BB69536}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {9138E48D-6404-4DBE-8DA4-5F42EDCC7EEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {9138E48D-6404-4DBE-8DA4-5F42EDCC7EEC}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {9138E48D-6404-4DBE-8DA4-5F42EDCC7EEC}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {9138E48D-6404-4DBE-8DA4-5F42EDCC7EEC}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {833B99EC-80AD-4FDA-9361-45B76338732F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {833B99EC-80AD-4FDA-9361-45B76338732F}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {833B99EC-80AD-4FDA-9361-45B76338732F}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {833B99EC-80AD-4FDA-9361-45B76338732F}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {503CC776-7BA0-4E9D-A18A-C6AD4BB69536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {503CC776-7BA0-4E9D-A18A-C6AD4BB69536}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {503CC776-7BA0-4E9D-A18A-C6AD4BB69536}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {503CC776-7BA0-4E9D-A18A-C6AD4BB69536}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {547104F5-8DA4-4081-8486-9AE51877DCD4} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /CoolNetBlog/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-ef": { 6 | "version": "6.0.2", 7 | "commands": [ 8 | "dotnet-ef" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /CoolNetBlog/Base/AdminEnterFilter.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.Models; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.AspNetCore.Mvc.Filters; 5 | using Microsoft.Extensions.Primitives; 6 | 7 | namespace CoolNetBlog.Base 8 | { 9 | public class AdminEnterFilter : Attribute, IActionFilter 10 | { 11 | public void OnActionExecuted(ActionExecutedContext context) 12 | { 13 | } 14 | 15 | /// 16 | /// 权限过滤器,后台界面(不包括后台登陆入口)都需要判断权限 17 | /// 18 | /// 19 | public void OnActionExecuting(ActionExecutingContext context) 20 | { 21 | List adminExpCtrName = new List { 22 | "admin","adminmenu", "adminarticle", "adminsitesetting", "adminfile", "adminlovelook","adminleavemessage","admingossip" }; 23 | 24 | var controllerName = ((ControllerBase)context.Controller).ControllerContext.ActionDescriptor.ControllerName; 25 | var actionName = ((ControllerBase)context.Controller).ControllerContext.ActionDescriptor.ActionName; 26 | if (adminExpCtrName.Contains(controllerName.ToLower()) && (actionName.ToLower()!="login" && actionName.ToLower() != "reset")) 27 | { 28 | 29 | StringValues pt; 30 | context.HttpContext.Request.Cookies.TryGetValue("coolnetblogadminloginxiyuaneightfourone", out string? cv); 31 | context.HttpContext.Request.Query.TryGetValue("pt", out pt); 32 | //query是空,尝试从form表单中找,因为更新删除是post 33 | if (String.IsNullOrWhiteSpace(pt.FirstOrDefault())) 34 | { 35 | try 36 | { 37 | // 若get方式没有 而强制尝试从form中取,当前请求本是get不是post 就会取不到 捕获异常返回验证失败 38 | context.HttpContext.Request.Form.TryGetValue("PassToken", out pt); 39 | if (string.IsNullOrWhiteSpace(pt.FirstOrDefault())) 40 | { 41 | context.HttpContext.Request.Form.TryGetValue("pt", out pt); 42 | } 43 | } 44 | catch (Exception) 45 | { 46 | context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized); 47 | } 48 | } 49 | if (string.IsNullOrWhiteSpace(cv) || cv != BaseAdminController._currentCookieValue) 50 | context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized); 51 | 52 | var adminUserSet = new SugarDataBaseStorage(); 53 | var au = adminUserSet.FirstOrDefault(a => a.Token == pt); 54 | if (au is null) 55 | context.Result = new StatusCodeResult(StatusCodes.Status401Unauthorized); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CoolNetBlog/Base/BaseAdminController.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.ViewModels.Admin; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace CoolNetBlog.Base 6 | { 7 | public class BaseAdminController:Controller 8 | { 9 | protected static BaseSugar _bdb; 10 | 11 | public static PassBaseViewModel spassVm = new(); 12 | /// 13 | /// 当前cookie的值,登录后生成此随机值。后续后台的操作都会从客户端取cookie验证值是否是一开始登陆时设置的此值 14 | /// 15 | public static string? _currentCookieValue = null; 16 | 17 | public BaseAdminController() 18 | { 19 | if (_bdb is null) 20 | _bdb = new BaseSugar(); 21 | } 22 | /// 23 | /// 任何基础视图模型类可以继承自PassBaseViewModel类,以封装数据。
24 | /// 一些公共属性数据存储于PassBaseViewModel父类中,必要时调用此方法以自动封装属性 25 | ///
26 | /// 27 | /// 28 | protected PassBaseViewModel WrapMustNeedPassFields(PassBaseViewModel pvm) 29 | { 30 | pvm.AccountName = spassVm.AccountName; 31 | pvm.PassToken = spassVm.PassToken; 32 | pvm.Email = spassVm.Email; 33 | return pvm; 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CoolNetBlog/Base/BaseController.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.Bll; 3 | using CoolNetBlog.Models; 4 | using CoolNetBlog.ViewModels.Admin; 5 | using CoolNetBlog.ViewModels.Home; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace CoolNetBlog.Base 9 | { 10 | public class BaseController:Controller 11 | { 12 | public static BaseSugar bdb; 13 | // 14 | public BaseLogicBll _bll; 15 | // 主页全局所需展示数据 16 | public HomeViewModel _homeGlobalView; 17 | 18 | 19 | public BaseController() 20 | { 21 | if (bdb is null) 22 | bdb = new BaseSugar(); 23 | _bll = new BaseLogicBll(); 24 | _homeGlobalView = new HomeViewModel(); 25 | } 26 | 27 | /// 28 | /// 获取前台主页全局所需展示的数据 29 | /// 30 | protected async void WrapsGlobalHomeData() 31 | { 32 | // 处理菜单和其子菜单 33 | var allMenus = await bdb._dbHandler.SqlQueryable 34 | ("select * from Menu where IsShow=1 order by OrderNumber asc").ToListAsync(); 35 | if (allMenus.Any()) 36 | { 37 | // 先找出所有顶级菜单 38 | var pMenus = allMenus.Where(m => m.PId == 0).ToList(); 39 | allMenus.RemoveAll(m => m.PId == 0); 40 | // 迭代顶级菜单 搜索下级菜单 41 | _bll.DealSubMenu(pMenus, allMenus); 42 | // 赋值菜单数据 43 | _homeGlobalView.HomeMenusData = pMenus; 44 | } 45 | // 获取基本配置 46 | _homeGlobalView.HomeSiteSettingData = await bdb._dbHandler.Queryable().FirstAsync(); 47 | _homeGlobalView.HomeLoveLookData = await bdb._dbHandler.Queryable().ToListAsync(); 48 | 49 | // 判断是否没有显示任何一个侧边栏组件 50 | _homeGlobalView.IsNotShowAnyOneCom = !_homeGlobalView.HomeSiteSettingData.IsShowEdgeSearch 51 | && !_homeGlobalView.HomeSiteSettingData.IsShowLoveLook 52 | && !_homeGlobalView.HomeSiteSettingData.IsShowWishPicture 53 | && !_homeGlobalView.HomeSiteSettingData.IsShowGossip; 54 | } 55 | 56 | /// 57 | /// 处理所需文章数据,主页分页 或具体菜单下的文章 或关键词搜索 58 | /// 59 | /// 60 | /// 若有,具体菜单下的文章 菜单Id 61 | /// 若有,关键字搜索 关键字 62 | /// 63 | /// 64 | protected async Task DealFilterData(string? from, int? menuId, string? kw, int pageIndex, int onePageCount) 65 | { 66 | return await _bll.DealFilterData(_homeGlobalView, bdb, from, menuId, kw, pageIndex, onePageCount); 67 | } 68 | 69 | 70 | /// 71 | /// 处理分页逻辑 72 | /// 73 | /// 当前按条件过滤后的总数 74 | /// 75 | /// 76 | protected void ComputePage(int c,int pageIndex, int onePageCount) 77 | { 78 | _bll.ComputePage(_homeGlobalView, c,pageIndex, onePageCount); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CoolNetBlog/Bll/DetailLogicBll.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.BlogException; 3 | using CoolNetBlog.Models; 4 | using CoolNetBlog.ViewModels.Detail; 5 | 6 | namespace CoolNetBlog.Bll 7 | { 8 | /// 9 | /// 文章详细 单独业务处理类 10 | /// 11 | public class DetailLogicBll 12 | { 13 | private BaseSugar _baseSugar; 14 | private SugarDataBaseStorage _articleVmSet; 15 | private SugarDataBaseStorage _menuSet; 16 | private SugarDataBaseStorage _thumbUpSet; 17 | 18 | public DetailLogicBll() 19 | { 20 | _baseSugar = new BaseSugar(); 21 | _articleVmSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 22 | _menuSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 23 | _thumbUpSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 24 | } 25 | 26 | /// 27 | /// 获取当前的文章 28 | /// 29 | /// 30 | /// 31 | /// 32 | /// 33 | public async Task DealArticleEntityAsync(ViewModels.Home.HomeViewModel _homeGlobalView, int? articleId, string? custUri) 34 | { 35 | _homeGlobalView.DetailArticleData = null; 36 | // 要么传递文章Id,要么传递自定义文章uri,两个都是唯一的 37 | if (!String.IsNullOrWhiteSpace(custUri)) 38 | { 39 | _homeGlobalView.DetailArticleData = await _articleVmSet.FirstOrDefaultAsync(a => a.CustUri == custUri); 40 | } 41 | else if (articleId!=null) 42 | { 43 | _homeGlobalView.DetailArticleData = await _articleVmSet.FindOneByIdAsync(articleId); 44 | } 45 | 46 | // 没有此文章 或文章当前是草稿 47 | if (_homeGlobalView.DetailArticleData is null || _homeGlobalView.DetailArticleData.IsDraft) 48 | { 49 | // 找不到显示的文章 主动抛出异常捕获,前端显示提示文本 50 | _homeGlobalView.NotTips = "找不到文章(帖子)!返回重试一下,或者关键字重新搜搜吧?!"; 51 | throw new DetailNotExistException(); 52 | } 53 | if(!_homeGlobalView.DetailArticleData.IsSpecial) 54 | { 55 | _homeGlobalView.DetailArticleData.Ig_MenuName = (await _menuSet.FindOneByIdAsync(_homeGlobalView.DetailArticleData.MenuId)).Name; 56 | }else 57 | { 58 | _homeGlobalView.DetailArticleData.Ig_MenuName = "特殊内容"; 59 | } 60 | _homeGlobalView.Location = "Detail"; 61 | _homeGlobalView.LocationTip = "文章(帖子)"; 62 | // 将标签组成字符串列表 63 | if (!string.IsNullOrWhiteSpace(_homeGlobalView.DetailArticleData.Labels)) 64 | { 65 | var tmpLabelV = _homeGlobalView.DetailArticleData.Labels.Split(',', ',', ' ').ToList(); 66 | tmpLabelV.RemoveAll(a => string.IsNullOrWhiteSpace(a)); 67 | _homeGlobalView.DetailArticleData.LabelsList = tmpLabelV; 68 | } 69 | // 是加锁文章 隐藏内容主体 70 | _homeGlobalView.DetailArticleData.Content = _homeGlobalView.DetailArticleData.IsLock ? 71 | "" : _homeGlobalView.DetailArticleData.Content; 72 | // 处理文章点赞数 73 | // 获取文章Id,因为有可能是用文章自定义uri访问文章,此时方法参数articleId是null,而需要用id去文章点赞表找当前文章的点赞数 74 | articleId = _homeGlobalView.DetailArticleData.Id; 75 | var theAllArticleThumb = await _thumbUpSet.GetListByExpAsync(x => x.ArticleId == articleId); 76 | // 文章表态类型数量,文章点赞数ThumbUpStart;文章"有被笑到"数ThumbUpFun;文章"不敢苟同"数ThumbUpSilence 77 | int thumbUpStart,thumbUpFun, thumbUpSilence = 0; 78 | thumbUpStart = theAllArticleThumb.Where(x => x.Type == 1).Count(); 79 | thumbUpFun = theAllArticleThumb.Where(x => x.Type == 2).Count(); 80 | thumbUpSilence = theAllArticleThumb.Where(x => x.Type == 3).Count(); 81 | var thumbData = new Dictionary { 82 | { "ThumbUpStart", thumbUpStart }, 83 | { "ThumbUpFun", thumbUpFun }, 84 | { "ThumbUpSilence", thumbUpSilence } 85 | }; 86 | _homeGlobalView.DetailArticleData.ThumbUpNumbers = thumbData; 87 | _homeGlobalView.CurrentTitle = _homeGlobalView.DetailArticleData.IsShowTitle&& 88 | !string.IsNullOrWhiteSpace(_homeGlobalView.DetailArticleData.Title) ? 89 | _homeGlobalView.DetailArticleData.Title: "正阅读无题文章"+_homeGlobalView.DetailArticleData.Id; 90 | } 91 | 92 | 93 | 94 | /// 95 | /// 加锁文章解锁 96 | /// 97 | /// 98 | /// 99 | public DetailArticleUnLockViewModel DealArticleUnLock(DetailArticleUnLockViewModel data) 100 | { 101 | try 102 | { 103 | data.Code = 0; 104 | data.Content = ""; 105 | //SugarDataBaseStorage articleSet = new SugarDataBaseStorage(); 106 | var article = _articleVmSet.FindOneById(data.ArticleId); 107 | if (article is null) 108 | { 109 | return data; 110 | } 111 | // 若已经不是加锁文章或密码正确 112 | if ((!article.IsLock) || data.Password == article.LockPassword) 113 | { 114 | data.Content = article.Content; 115 | data.Code = 1; 116 | return data; 117 | } 118 | 119 | return data; 120 | 121 | } 122 | catch (Exception) 123 | { 124 | return data; 125 | } 126 | 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /CoolNetBlog/Bll/GossipBll.cs: -------------------------------------------------------------------------------- 1 | using CommonObject.Constructs; 2 | using CommonObject.Enums; 3 | using ComponentsServices.Base; 4 | using CoolNetBlog.Models; 5 | 6 | namespace CoolNetBlog.Bll 7 | { 8 | public class GossipBll 9 | { 10 | private readonly BaseSugar _baseSugar; 11 | private readonly SugarDataBaseStorage _gossipSet; 12 | private ValueResult _result; 13 | 14 | public GossipBll() 15 | { 16 | _result = new() 17 | { 18 | Code = ValueCodes.UnKnow 19 | }; 20 | _baseSugar = new BaseSugar(); 21 | _gossipSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 22 | 23 | } 24 | 25 | public async Task GetGossipsAsync(int index, int pageCount) 26 | { 27 | 28 | try 29 | { 30 | // 最新记录在前 一次加载取pageCount 31 | var gossips = await _gossipSet.GetListBuilder() 32 | .OrderBy(g=>g.AddTime, SqlSugar.OrderByType.Desc) 33 | .Skip((index - 1)* pageCount).Take(pageCount).ToListAsync(); 34 | _result.Data = gossips; 35 | _result.Code = ValueCodes.Success; 36 | } 37 | catch (Exception e) 38 | { 39 | _result.Code = ValueCodes.Error; 40 | _result.HideMessage = $"获取“闲言碎语”Gossip表数据失败,引发异常:{e.Message} {e.StackTrace}"; 41 | _result.TipMessage = "加载失败了,下滑在试试吧?!"; 42 | } 43 | return _result; 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CoolNetBlog/Bll/ThumbsUpBll.cs: -------------------------------------------------------------------------------- 1 | using CommonObject.Constructs; 2 | using CommonObject.Enums; 3 | using ComponentsServices.Base; 4 | using CoolNetBlog.Models; 5 | 6 | namespace CoolNetBlog.Bll 7 | { 8 | public class ThumbsUpBll 9 | { 10 | private readonly BaseSugar _baseSugar; 11 | private readonly SugarDataBaseStorage _articleSet; 12 | private readonly SugarDataBaseStorage _thumbUpSet; 13 | public ThumbsUpBll() 14 | { 15 | _baseSugar = new BaseSugar(); 16 | _articleSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 17 | _thumbUpSet = new SugarDataBaseStorage(_baseSugar._dbHandler); 18 | } 19 | 20 | public async Task DealThumbsUpArticleAsync(int articleId, int type, HttpContext httpContext) 21 | { 22 | ValueResult result = new ValueResult(); 23 | result.Code = ValueCodes.UnKnow; 24 | if (articleId<=0) 25 | { 26 | result.HideMessage = "文章Id小于等于0"; 27 | result.TipMessage = "该文章或许已不存在了,请返回首页或者再试一次吧?!"; 28 | return result; 29 | } 30 | var articleAble = await _articleSet.FindOneByIdAsync(articleId); 31 | if (articleAble is null) 32 | { 33 | result.HideMessage = "没有此文章Id"; 34 | result.TipMessage = "该文章或许已不存在了,请返回首页或者再试一次吧?!"; 35 | return result; 36 | } 37 | var cip = httpContext.Connection.RemoteIpAddress?.ToString(); 38 | if (string.IsNullOrWhiteSpace(cip)) 39 | { 40 | result.HideMessage = "文章点赞,获取不到客户端ip"; 41 | result.TipMessage = "表态失败了呢,你的好意我心领啦。"; 42 | return result; 43 | } 44 | var exd = await _thumbUpSet.AnyAsync(u => u.ArticleId == articleId && u.ClientIp == cip); 45 | if (exd) 46 | { 47 | result.TipMessage = "该文章你已经表过态啦!"; 48 | return result; 49 | } 50 | try 51 | { 52 | _articleSet.TransBegin(); 53 | await _thumbUpSet.InsertAsync(new ArticleThumbUp { ArticleId = articleId, 54 | ClientIp = cip,Type=type, UpTime = DateTime.Now}); 55 | _articleSet.TransCommit(); 56 | } 57 | catch (Exception e) 58 | { 59 | _articleSet.TransRoll(); 60 | result.Code = ValueCodes.Error; 61 | result.HideMessage = "点赞文章,执行插入数据报错:"+e.Message; 62 | result.TipMessage = "表态失败了呢,你的好意我心领啦。"; 63 | return result; 64 | } 65 | result.Code = ValueCodes.Success; 66 | if (type==1) 67 | { 68 | result.TipMessage = "~谢谢你的点赞!"; 69 | }else if (type == 2) 70 | { 71 | result.TipMessage = "~欢乐也是一时的享受。"; 72 | } 73 | else 74 | { 75 | //type == 3 不敢苟同 76 | result.TipMessage = "~有容乃大,谢谢你的表态。"; 77 | } 78 | var theAllArticleThumb = await _thumbUpSet.GetListByExpAsync(x => x.ArticleId == articleId); 79 | //文章表态类型数量,文章点赞数ThumbUpStart;文章"有被笑到"数ThumbUpFun;文章"不敢苟同"数ThumbUpSilence 80 | int thumbUpStart, thumbUpFun, thumbUpSilence = 0; 81 | thumbUpStart = theAllArticleThumb.Where(x => x.Type == 1).Count(); 82 | thumbUpFun = theAllArticleThumb.Where(x => x.Type == 2).Count(); 83 | thumbUpSilence = theAllArticleThumb.Where(x => x.Type == 3).Count(); 84 | // 封装返回给aiax重显当前最新点赞数据 85 | result.Data = new { ThumbUpStart=thumbUpStart, ThumbUpFun=thumbUpFun, ThumbUpSilence=thumbUpSilence }; 86 | return result; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/AdminAccess/AdminGossipController.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.Base; 3 | using CoolNetBlog.Models; 4 | using CoolNetBlog.ViewModels; 5 | using CoolNetBlog.ViewModels.Admin; 6 | using Microsoft.AspNetCore.Mvc; 7 | 8 | namespace CoolNetBlog.Controllers.Admin 9 | { 10 | 11 | /// 12 | /// 后台"闲言碎语"内容操作 13 | /// 14 | 15 | public class AdminGossipController : BaseAdminController 16 | { 17 | private static GossipViewModel sgvm = new(); 18 | private SugarDataBaseStorage _gossipSet; 19 | private SugarDataBaseStorage _filePathSet; 20 | 21 | public AdminGossipController():base() 22 | { 23 | BaseSugar baseSugar = new BaseSugar(); 24 | _gossipSet = new SugarDataBaseStorage(baseSugar._dbHandler); 25 | _filePathSet = new SugarDataBaseStorage(baseSugar._dbHandler); 26 | } 27 | 28 | /// 29 | /// 根据Id删除一个"闲言碎语" 30 | /// 31 | /// 32 | /// 有关键词搜索,带上关键词,删除成功后跳转带给首页过滤 33 | /// 34 | [HttpPost] 35 | public async Task Delete([FromForm] int id, [FromForm] string? kw) { 36 | if (id<1) 37 | { 38 | ModelState.AddModelError("", "删除闲言碎语失败:Id无效,重启浏览器再试吧?。"); 39 | return View("GossipAmManagement", sgvm); 40 | } 41 | Gossip delable; 42 | try 43 | { 44 | delable = await _gossipSet.FindOneByIdAsync(id); 45 | } 46 | catch (Exception) 47 | { 48 | ModelState.AddModelError("", "删除闲言碎语失败:没有此Id,刷新再试吧?。"); 49 | return View("GossipAmManagement", sgvm); 50 | } 51 | 52 | // 一并删除此“闲言碎语”的点赞数据 53 | SugarDataBaseStorage cThumbUpDelHelper = new SugarDataBaseStorage(_gossipSet._dbHandler); 54 | _gossipSet.TransBegin(); 55 | try 56 | { 57 | // 删除此条的所有点赞表态 58 | await cThumbUpDelHelper.DeleteEntitiesAsync(u => u.SourceId == id&&u.SourceType==1); 59 | 60 | // 删除此条实体 61 | await _gossipSet.DeleteAsync(delable); 62 | _gossipSet.TransCommit(); 63 | } 64 | catch (Exception e) 65 | { 66 | _gossipSet.TransRoll(); 67 | ModelState.AddModelError("", "删除失败:刷新再试吧?。"); 68 | return View("GossipAmManagement", sgvm); 69 | } 70 | 71 | return RedirectToAction("GossipAmManagement", "AdminGossip", new { pt = sgvm.PassToken,kw= kw }); 72 | 73 | } 74 | 75 | /// 76 | /// 创建 77 | /// 78 | /// 79 | /// 80 | [HttpPost] 81 | public async Task AddGossip([FromForm] GossipViewModel vm) 82 | { 83 | RemoveSomeValid(); 84 | ModelState.Clear(); 85 | // 新增,赋值当前实体编辑的数据 避免新增错误时返回视图后丢失 86 | sgvm.Content = vm.Content; 87 | sgvm.ImgUrl = vm.ImgUrl; 88 | sgvm.Type = vm.Type; 89 | if (vm.Type<=0) 90 | { 91 | ModelState.AddModelError("", "发表失败:请选择类型"); 92 | return View("GossipAmManagement", sgvm); 93 | } 94 | if (vm.Type==2&&string.IsNullOrWhiteSpace(vm.ImgUrl)) 95 | { 96 | // 内部图片下拉框没有 从第三方输入框的值去找 97 | vm.ImgUrl = HttpContext.Request.Form["UnofficialImg"]; 98 | if (string.IsNullOrWhiteSpace(vm.ImgUrl)) 99 | { 100 | ModelState.AddModelError("", "发表失败:带图片的内容请选定图片url地址"); 101 | return View("GossipAmManagement", sgvm); 102 | } 103 | } 104 | if (vm.Type == 1) 105 | { 106 | vm.ImgUrl = null; 107 | } 108 | if (string.IsNullOrWhiteSpace(vm.Content)|| vm.Content.Length>150) 109 | { 110 | ModelState.AddModelError("", "发表失败:内容字数无效"); 111 | return View("GossipAmManagement", sgvm); 112 | } 113 | Gossip editable = new Gossip(); 114 | editable.ImgUrl = vm.ImgUrl; 115 | editable.StarNumber = vm.StarNumber; 116 | editable.AddTime = DateTime.Now; 117 | editable.Content = vm.Content; 118 | editable.Type = vm.Type; 119 | try 120 | { 121 | _gossipSet.TransBegin(); 122 | await _gossipSet.InsertAsync(editable); 123 | _gossipSet.TransCommit(); 124 | } 125 | catch (Exception e) 126 | { 127 | _gossipSet.TransRoll(); 128 | ModelState.AddModelError("", "发表失败:已回滚,复制输入好的的内容,返回重新再试一遍?"); 129 | return View("GossipAmManagement", sgvm); 130 | } 131 | // 新增成功,直接重定向到第一页数据 132 | return RedirectToAction("GossipAmManagement", "AdminGossip", new { pt= sgvm.PassToken }); 133 | } 134 | 135 | 136 | /// 137 | /// 列表页面入口 138 | /// 139 | /// 140 | public async Task GossipAmManagement(string? pt, string? kw, int index=1, int pageCount = 30) 141 | { 142 | // 获取'闲言碎语'列表 143 | IList gossipes; 144 | if (!string.IsNullOrWhiteSpace(kw)) 145 | { 146 | gossipes = await _gossipSet.GetListBuilder().Where(a => 147 | (a.Id.ToString()==kw) || 148 | (a.Content != null && a.Content.Contains(kw))) 149 | .OrderBy(a => a.AddTime, SqlSugar.OrderByType.Desc) 150 | .Skip((index-1)* pageCount) 151 | .Take(pageCount).ToListAsync(); 152 | } 153 | else 154 | { 155 | gossipes = await _gossipSet.GetListBuilder() 156 | .OrderBy(a => a.AddTime, SqlSugar.OrderByType.Desc) 157 | .Skip((index - 1) * pageCount) 158 | .Take(pageCount).ToListAsync(); 159 | } 160 | 161 | sgvm = new GossipViewModel { GossipesOrg = gossipes }; 162 | 163 | sgvm.ImgRelPaths = await _filePathSet.GetListBuilder().Where(f => f.Type == "img") 164 | .OrderBy(f => f.UploadTime, SqlSugar.OrderByType.Desc).Take(20).ToListAsync(); 165 | 166 | // 处理分页按钮索引值 167 | sgvm.Keyword = kw; 168 | if (!sgvm.GossipesOrg.Any()) 169 | { 170 | // 当前页没有任何数据了 不改动分页索引值 171 | sgvm.NextIndex = index; 172 | sgvm.PreIndex = (index-1) <= 0? 1: index - 1; 173 | } 174 | else 175 | { 176 | if (index<=1) 177 | { 178 | sgvm.NextIndex = 2; 179 | sgvm.PreIndex = 1; 180 | } 181 | else 182 | { 183 | sgvm.NextIndex = index + 1; 184 | sgvm.PreIndex = index - 1; 185 | } 186 | } 187 | // 自动封装已有的数据 188 | sgvm = (GossipViewModel)WrapMustNeedPassFields(sgvm); 189 | return View(sgvm); 190 | } 191 | 192 | 193 | /// 194 | /// 移除有些场景下表单不需要验证的属性 195 | /// 196 | private void RemoveSomeValid() 197 | { 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/AdminAccess/AdminLoveLookController.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.Base; 3 | using CoolNetBlog.Models; 4 | using CoolNetBlog.ViewModels.Admin; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace CoolNetBlog.Controllers.AdminAccess 8 | { 9 | /// 10 | /// 管理员后台 链接显示"看看这些" 管理 11 | /// 12 | public class AdminLoveLookController : BaseAdminController 13 | { 14 | private readonly IWebHostEnvironment _environment; 15 | private static LoveLookViewModel slvm = new(); 16 | private readonly SugarDataBaseStorage _loveLookSet; 17 | 18 | public AdminLoveLookController(IWebHostEnvironment environment) :base() 19 | { 20 | this._environment = environment; 21 | _loveLookSet = new SugarDataBaseStorage(); 22 | 23 | } 24 | 25 | 26 | [HttpPost] 27 | public async Task Add(string? pt, LoveLookViewModel vm) 28 | { 29 | ModelState.Clear(); 30 | RemoveSomeValid(); 31 | slvm = (LoveLookViewModel)WrapMustNeedPassFields(slvm); 32 | if (vm is null || vm.Type<=0) 33 | { 34 | ModelState.AddModelError("", "添加失败,请返回重试一下吧!"); 35 | return View("LoveLookAmManagement", slvm); 36 | } 37 | if (string.IsNullOrWhiteSpace(vm.RelHref)) 38 | { 39 | ModelState.AddModelError("", "添加失败,要输入链接哦!"); 40 | return View("LoveLookAmManagement", slvm); 41 | } 42 | if (string.IsNullOrWhiteSpace(vm.LinkName)) 43 | { 44 | ModelState.AddModelError("", "添加失败,要输入链接名哦,在主页侧边栏中显示!"); 45 | return View("LoveLookAmManagement", slvm); 46 | } 47 | _loveLookSet.TransBegin(); 48 | try 49 | { 50 | LoveLook saveModel = new LoveLook 51 | { 52 | RelHref = vm.RelHref, 53 | AddedTime = DateTime.Now, 54 | LinkName = vm.LinkName, 55 | Type = vm.Type, 56 | }; 57 | if (saveModel.Type==1) 58 | { 59 | if (!saveModel.RelHref.ToLower().StartsWith("detail")) 60 | { 61 | ModelState.AddModelError("", "添加失败,内部文章链接格式错误,文章详细链接应该是 'detail' 开头的值(忽略单引号),请重新查看一下你的文章详情uri。"); 62 | return View("LoveLookAmManagement", slvm); 63 | }; 64 | // 组成 供点击访问 65 | saveModel.RelHref = Path.Combine("/", saveModel.RelHref); 66 | } 67 | 68 | if (saveModel.Type == 2) 69 | { 70 | // 检测链接文件是否存在 71 | var exLinkName = Path.Combine(_environment.WebRootPath, "epLinks", saveModel.RelHref); 72 | if (!System.IO.File.Exists(exLinkName)) 73 | { 74 | ModelState.AddModelError("", "添加失败,没有此上传过的链接文件,请先上传文件链接。"); 75 | return View("LoveLookAmManagement", slvm); 76 | } 77 | // 组成 供点击访问 epLinks链接文件夹+具体上传的链接文件 名 78 | saveModel.RelHref = "/epLinks/"+saveModel.RelHref.TrimStart('/'); 79 | } 80 | _loveLookSet.Insert(saveModel); 81 | _loveLookSet.TransCommit(); 82 | return RedirectToAction("LoveLookAmManagement", "AdminLoveLook", new { pt = slvm.PassToken }); 83 | } 84 | catch (Exception ex) 85 | { 86 | _loveLookSet.TransRoll(); 87 | ModelState.AddModelError("", "添加失败,请返回重试一下吧!"); 88 | return View("LoveLookAmManagement", slvm); 89 | } 90 | } 91 | 92 | 93 | 94 | 95 | [HttpPost("DeleteLoveLook")] 96 | public IActionResult DeleteLoveLook(string? pt,int id) 97 | { 98 | RemoveSomeValid(); 99 | try 100 | { 101 | _loveLookSet.Delete(_loveLookSet.FindOneById(id)); 102 | } 103 | catch (Exception) 104 | { 105 | ModelState.AddModelError("", "删除失败 请重试!"); 106 | return RedirectToAction("LoveLookAmManagement", "AdminLoveLook", new { pt = slvm.PassToken }); 107 | } 108 | // 删除成功 重定向刷新页面 109 | return RedirectToAction("LoveLookAmManagement", "AdminLoveLook", new { pt = slvm.PassToken }); 110 | } 111 | 112 | [HttpGet] 113 | public async Task LoveLookAmManagement(string? pt) 114 | { 115 | 116 | // 获取所有"看一看"显示的链接 117 | var data = (await _loveLookSet.GetAllListAsync()).OrderByDescending(a=>a.AddedTime).ToList(); 118 | 119 | slvm = new LoveLookViewModel { LoveLooksOrg = data }; 120 | // 自动封装已有的数据 121 | slvm = (LoveLookViewModel)WrapMustNeedPassFields(slvm); 122 | return View(slvm); 123 | } 124 | 125 | /// 126 | /// 移除有些场景下表单不需要验证的属性 127 | /// 128 | private void RemoveSomeValid() 129 | { 130 | ModelState.Remove("AccountName"); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/AdminAccess/AdminSiteSettingController.cs: -------------------------------------------------------------------------------- 1 | using ComponentsServices.Base; 2 | using CoolNetBlog.Base; 3 | using CoolNetBlog.Models; 4 | using CoolNetBlog.ViewModels.Admin; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace CoolNetBlog.Controllers.AdminAccess 8 | { 9 | /// 10 | /// 后台基本配置操作 11 | /// 12 | public class AdminSiteSettingController : BaseAdminController 13 | { 14 | private SugarDataBaseStorage _siteSettingSet; 15 | 16 | public AdminSiteSettingController() : base() 17 | { 18 | _siteSettingSet = new SugarDataBaseStorage(); 19 | } 20 | 21 | /// 22 | /// 移除有些场景下表单不需要验证的属性 23 | /// 24 | private void RemoveSomeValid() 25 | { 26 | ModelState.Remove("AccountName"); 27 | 28 | } 29 | 30 | /// 31 | /// 更新站点的基本配置 32 | /// 33 | /// 34 | /// 35 | [HttpPost] 36 | public async Task EditSiteSetting(SiteSettingViewModel vm) 37 | { 38 | RemoveSomeValid(); 39 | 40 | if (vm is null) 41 | { 42 | ModelState.AddModelError("", "更新失败:传递获取不到模型,重启浏览器再试吧?。"); 43 | return View(vm); 44 | } 45 | vm = (SiteSettingViewModel)WrapMustNeedPassFields(vm); 46 | if (vm.OnePageCount<=0) 47 | { 48 | ModelState.AddModelError("", "更新失败:主页列表展示文章的条数不能是0。"); 49 | return View(vm); 50 | } 51 | 52 | SiteSetting saveEntity = new SiteSetting 53 | { 54 | SiteName = vm.SiteName, 55 | IsShowSiteName = vm.IsShowSiteName, 56 | IsShowQutoes = vm.IsShowQutoes, 57 | Cban = vm.Cban, 58 | Domain = vm.Domain, 59 | FashionQuotes = vm.FashionQuotes, 60 | Host = vm.Host, 61 | TailContent = vm.TailContent, 62 | OnePageCount = vm.OnePageCount, 63 | LoginUriValue = vm.LoginUriValue, 64 | LoveLookTitle = vm.LoveLookTitle, 65 | IsShowEdgeSearch = vm.IsShowEdgeSearch, 66 | IsShowLoveLook = vm.IsShowLoveLook, 67 | IsShowGossip = vm.IsShowGossip, 68 | WishPictureRelPath = vm.WishPictureRelPath, 69 | IsShowWishPicture = vm.IsShowWishPicture, 70 | WishPictureName = vm.WishPictureName, 71 | IsOpenDetailThumb = vm.IsOpenDetailThumb, 72 | LeaveLimitCount = vm.LeaveLimitCount, 73 | IsShowLeaveHeadImg = vm.IsShowLeaveHeadImg, 74 | }; 75 | 76 | _siteSettingSet.TransBegin(); 77 | try 78 | { 79 | // 虽然是全删除 但也只有一个站点数据 80 | await _siteSettingSet._dbHandler.Deleteable().Where(s=>1==1).ExecuteCommandAsync(); 81 | await _siteSettingSet.InsertAsync(saveEntity); 82 | _siteSettingSet.TransCommit(); 83 | } 84 | catch (Exception) 85 | { 86 | _siteSettingSet.TransRoll(); 87 | ModelState.AddModelError("", "更新失败回滚,重启浏览器再试吧?。"); 88 | return View(vm); 89 | } 90 | return RedirectToAction("AdminHome", "Admin", new { pt = vm.PassToken }); 91 | 92 | } 93 | 94 | /// 95 | /// 站点的基本配置修改页面 96 | /// 97 | /// 98 | /// 99 | public async Task EditSiteSetting(string? pt) 100 | { 101 | var eny = await _siteSettingSet._dbHandler.Queryable().FirstAsync(); 102 | SiteSettingViewModel org = new SiteSettingViewModel 103 | { 104 | SiteName = eny.SiteName, 105 | IsShowSiteName = eny.IsShowSiteName, 106 | IsShowQutoes = eny.IsShowQutoes, 107 | Cban = eny.Cban, 108 | Domain = eny.Domain, 109 | FashionQuotes = eny.FashionQuotes, 110 | Host = eny.Host, 111 | OnePageCount = eny.OnePageCount, 112 | TailContent = eny.TailContent, 113 | LoginUriValue = eny.LoginUriValue, 114 | LoveLookTitle = eny.LoveLookTitle, 115 | IsShowEdgeSearch = eny.IsShowEdgeSearch, 116 | IsShowLoveLook = eny.IsShowLoveLook, 117 | IsShowGossip = eny.IsShowGossip, 118 | WishPictureRelPath = eny.WishPictureRelPath, 119 | IsShowWishPicture = eny.IsShowWishPicture, 120 | WishPictureName = eny.WishPictureName, 121 | IsOpenDetailThumb = eny.IsOpenDetailThumb, 122 | LeaveLimitCount = eny.LeaveLimitCount, 123 | IsShowLeaveHeadImg = eny.IsShowLeaveHeadImg, 124 | }; 125 | // 自动封装已有的数据 126 | org = (SiteSettingViewModel)WrapMustNeedPassFields(org); 127 | return View(org); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/Api/Blog/BlogApiController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Newtonsoft.Json; 3 | 4 | // For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 5 | 6 | namespace CoolNetBlog.Controllers.Api.Blog 7 | { 8 | public class BlogApiController : Controller 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/CommentController.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Bll; 2 | using CoolNetBlog.ViewModels; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace CoolNetBlog.Controllers 6 | { 7 | /// 8 | /// 评论和回复处理控制接口 9 | /// 10 | public class CommentController : Controller 11 | { 12 | private readonly CommentBll _commentBll; 13 | 14 | public CommentController() 15 | { 16 | _commentBll = new CommentBll(); 17 | } 18 | 19 | /// 20 | /// 发布评论 处理接口 21 | /// 22 | /// 23 | /// 24 | [HttpPost] 25 | public async Task Comment([FromBody] CommentViewModel data) 26 | { 27 | var res =await _commentBll.DealCommentPostAsync(data, HttpContext); 28 | return new JsonResult(res); 29 | } 30 | 31 | /// 32 | /// 评论回复 处理接口 33 | /// 34 | /// 35 | /// 36 | [HttpPost] 37 | public async Task Reply([FromBody] ReplyViewModel data) 38 | { 39 | var res = await _commentBll.DealReplyPostAsync(data, HttpContext); 40 | return new JsonResult(res); 41 | } 42 | 43 | /// 44 | /// 获取文章的评论数据 45 | /// 46 | /// 文章id 47 | /// 当前加载评论的索引(次数),默认为1,开头,每点击一次“加载评论”按钮,前端传递数据+1 48 | /// 49 | [HttpGet] 50 | public async Task GetArticleComments(int sourceId, int index=1) 51 | { 52 | var res = await _commentBll.GetArticleCommentsAsync(sourceId, index); 53 | return new JsonResult(res); 54 | } 55 | 56 | /// 57 | /// 获取评论的回复数据 58 | /// 59 | /// 评论id 60 | /// 当前加载回复的索引(次数),默认为1,开头,每点击一次“加载回复”按钮,前端传递数据+1 61 | /// 62 | [HttpGet] 63 | public async Task GetCommentReplys(int commentId, int index = 1) 64 | { 65 | var res = await _commentBll.GetCommentReplysAsync(commentId, index); 66 | return new JsonResult(res); 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/DetailController.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Base; 2 | using CoolNetBlog.Bll; 3 | using CoolNetBlog.ViewModels.Detail; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace CoolNetBlog.Controllers.Home 7 | { 8 | /// 9 | /// 文章帖子详细页面控制器 10 | /// 11 | public class DetailController : BaseController 12 | { 13 | private readonly ILogger _logger; 14 | private readonly DetailLogicBll _detailBll; 15 | 16 | public DetailController(ILogger logger) : base() 17 | { 18 | _logger = logger; 19 | _detailBll = new DetailLogicBll(); 20 | WrapsGlobalHomeData(); 21 | } 22 | [Route("{Controller}/")] 23 | [Route("{Controller}/{custUri}")] 24 | public async Task Index(int? articleId, string? custUri) 25 | { 26 | try 27 | { 28 | await _detailBll.DealArticleEntityAsync(_homeGlobalView, articleId, custUri); 29 | return View(_homeGlobalView); 30 | } 31 | catch (Exception e) 32 | { 33 | var tagExName = e.GetType().Name; 34 | if (tagExName.Contains("DetailNotExistException")) 35 | { 36 | // 捕获到异常DetailNotExistException,让前端显示内容。通常发生在文章已不存在时(草稿|删除|错误articleId),所以主动处理 37 | return View("NotFound", _homeGlobalView); 38 | } 39 | //... 40 | // 未经预料的异常,最终抛出 转给WarningPageController 41 | throw; 42 | } 43 | } 44 | 45 | /// 46 | /// 加锁文章解锁接口 47 | /// 48 | /// 49 | /// 50 | /// 指定路由为{Controller}/UnLock/ArticleUnLock,否则{Controller}/ArticleUnLock直接走Index方法自定义文章uri了 51 | [HttpPost] 52 | [Route("{Controller}/UnLock/ArticleUnLock")] 53 | public JsonResult ArticleUnLock([FromBody]DetailArticleUnLockViewModel data) 54 | { 55 | var res = _detailBll.DealArticleUnLock(data); 56 | return new JsonResult(res); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/EpLinkHelperController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace CoolNetBlog.Controllers 4 | { 5 | public class EpLinkHelperController : Controller 6 | { 7 | private readonly IWebHostEnvironment _environment; 8 | 9 | public EpLinkHelperController(IWebHostEnvironment environment) 10 | { 11 | this._environment = environment; 12 | } 13 | 14 | /// 15 | /// 这是忽略文件扩展名,将其跳转到wwwroot\epLinks\下具体文件名的html控制操作方法 16 | /// 17 | /// 18 | /// 19 | [Route("epLinks/{htmlFileName}")] 20 | public IActionResult Index(string htmlFileName) 21 | { 22 | if (String.IsNullOrWhiteSpace(htmlFileName)) 23 | { 24 | return new StatusCodeResult(StatusCodes.Status404NotFound); 25 | } 26 | var uri = $"/epLinks/{htmlFileName}.html"; 27 | if (!System.IO.File.Exists(Path.Combine(_environment.WebRootPath, "epLinks", htmlFileName+".html"))){ 28 | return new StatusCodeResult(StatusCodes.Status404NotFound); 29 | } 30 | return Redirect(uri); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/GossipController.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Bll; 2 | using CoolNetBlog.ViewModels; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace CoolNetBlog.Controllers 6 | { 7 | /// 8 | /// "闲言碎语"获取接口 9 | /// 10 | public class GossipController : Controller 11 | { 12 | private readonly GossipBll _gossipBll; 13 | 14 | public GossipController() 15 | { 16 | _gossipBll = new GossipBll(); 17 | } 18 | 19 | 20 | /// 21 | /// 获取当前加载的"闲言碎语"数据 22 | /// 23 | /// 24 | [HttpGet] 25 | public async Task GetGossips(int index=1, int pageCount = 10) 26 | { 27 | var res = await _gossipBll.GetGossipsAsync(index, pageCount); 28 | return new JsonResult(res); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Base; 2 | using CoolNetBlog.BlogException; 3 | using CoolNetBlog.Models; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Diagnostics; 6 | 7 | namespace CoolNetBlog.Controllers.Home 8 | { 9 | /// 10 | /// 主页前台展示控制 11 | /// 12 | public class HomeController : BaseController 13 | { 14 | private readonly ILogger _logger; 15 | 16 | public HomeController(ILogger logger):base() 17 | { 18 | _logger = logger; 19 | WrapsGlobalHomeData(); 20 | } 21 | 22 | /// 23 | /// 主页索引 展示具体分页数据 24 | /// 25 | /// 点击来源 菜单|外链 26 | /// 点击来源的具体值 菜单是菜单id .. 27 | /// 点击来源的具体值 搜索是关键字.. 28 | /// 分页码 29 | /// 每页文章条数 30 | /// 31 | public async Task Index(string? from, int? menuId,string? kw, int pageIndex=1, int onePageCount=5) 32 | { 33 | try 34 | { 35 | _homeGlobalView.CurrentTitle = _homeGlobalView.HomeSiteSettingData.SiteName; 36 | // 从配置里取设置的每页条数 37 | onePageCount = _homeGlobalView.HomeSiteSettingData.OnePageCount<=0? 38 | onePageCount: _homeGlobalView.HomeSiteSettingData.OnePageCount; 39 | // 跳到当前页 获取文章列表 40 | int c = await DealFilterData(from, menuId, kw, pageIndex, onePageCount); 41 | 42 | // 根据文章MenuId获取每篇文章所属菜单名 43 | foreach (var item in _homeGlobalView.HomeArticleViewModels) 44 | { 45 | item.Ig_MenuName = (await bdb._dbHandler.Queryable().FirstAsync(m => m.Id == item.MenuId))?.Name ?? "未知菜单"; 46 | } 47 | ComputePage(c, pageIndex, onePageCount); 48 | return View(_homeGlobalView); 49 | } 50 | catch (Exception e) 51 | { 52 | var tagExName = e.GetType().Name; 53 | if (tagExName.Contains("MenuNotExistException")) 54 | { 55 | // 捕获到异常MenuNotExistException,让前端显示内容。通常发生在用户故意在地址栏输入不存在的menuId,所以主动处理 56 | return View("NotFound", _homeGlobalView); 57 | } 58 | //... 59 | // 未经预料的异常,最终抛出 转给WarningPageController 60 | throw; 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/ThumbsUpController.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Bll; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CoolNetBlog.Controllers 5 | { 6 | /// 7 | /// 文章点赞控制 8 | /// 9 | public class ThumbsUpController : Controller 10 | { 11 | private readonly ThumbsUpBll _thumbsUpBll; 12 | 13 | public ThumbsUpController() 14 | { 15 | _thumbsUpBll = new ThumbsUpBll(); 16 | } 17 | 18 | [Route("{Controller}/ThumbsUp")] 19 | [HttpPost] 20 | public async Task ThumbsUpAsync([FromForm] int articleId, [FromForm] int type) 21 | { 22 | var res =await _thumbsUpBll.DealThumbsUpArticleAsync(articleId, type, HttpContext); 23 | return new JsonResult(res); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /CoolNetBlog/Controllers/WarningPageController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace CoolNetBlog.Controllers 4 | { 5 | public class WarningPageController : Controller 6 | { 7 | public IActionResult Error() 8 | { 9 | return View(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CoolNetBlog/CoolNetBlog.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | <_WebToolingArtifacts Remove="Properties\launchSettings.json" /> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | false 43 | 44 | 45 | -------------------------------------------------------------------------------- /CoolNetBlog/Exception/BlogException.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.BlogException 2 | { 3 | public class BlogException: Exception 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CoolNetBlog/Exception/DetailNotExistException.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.BlogException 2 | { 3 | public class DetailNotExistException : BlogException 4 | { 5 | public DetailNotExistException(string message) 6 | { 7 | Message = message; 8 | } 9 | public DetailNotExistException() 10 | { 11 | } 12 | 13 | public new string Message { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CoolNetBlog/Exception/MenuNotExistException.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.BlogException 2 | { 3 | public class MenuNotExistException : BlogException 4 | { 5 | public MenuNotExistException(string message) 6 | { 7 | Message = message; 8 | } 9 | public MenuNotExistException() 10 | { 11 | } 12 | 13 | public new string Message { get; set; } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/AdminUser.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | [SugarTable("AdminUser")] 6 | 7 | public class AdminUser 8 | { 9 | [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] 10 | public int Id { get; set; } 11 | public string AccountName { get; set; } 12 | public string Password { get; set; } 13 | /// 14 | /// Token=PassToken (from page passd) 15 | /// 16 | public string? Email { get; set; } 17 | public string Token { get; set; } 18 | public string? SmtpHost { get; set; } 19 | public string? EmailPassword { get; set; } 20 | public int? SmtpPort { get; set; } 21 | public bool? SmtpIsUseSsl { get; set; } 22 | 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/Article.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using SqlSugar; 5 | 6 | namespace CoolNetBlog.Models 7 | { 8 | /// 9 | /// 10 | /// 11 | [SugarTable("Article")] 12 | public partial class Article 13 | { 14 | public Article(){ 15 | 16 | 17 | } 18 | /// 19 | /// Desc:文章(帖子)自增主键 20 | /// Default: 21 | /// Nullable:False 22 | /// 23 | [SugarColumn(IsPrimaryKey=true,IsIdentity=true)] 24 | public int Id {get;set;} 25 | 26 | /// 27 | /// Desc:所属菜单Id 28 | /// Default: 29 | /// Nullable:False 30 | /// 31 | public int MenuId {get;set;} 32 | 33 | /// 34 | /// Desc:文章标题 35 | /// Default: 36 | /// Nullable:True 37 | /// 38 | public string Title {get;set;} 39 | 40 | /// 41 | /// Desc:文章内容 42 | /// Default: 43 | /// Nullable:False 44 | /// 45 | public string Content {get;set;} 46 | 47 | /// 48 | /// Desc:是否展示此文章的标题 49 | /// Default: 50 | /// Nullable:False 51 | /// 52 | public bool IsShowTitle {get;set;} 53 | 54 | /// 55 | /// Desc:文章自定义摘要描述 56 | /// Default: 57 | /// Nullable:False 58 | /// 59 | public string Abstract {get;set;} 60 | 61 | /// 62 | /// Desc:首次创建时间 63 | /// Default: 64 | /// Nullable:False 65 | /// 66 | public DateTime CreatedTime {get;set;} 67 | 68 | /// 69 | /// Desc:最后更新时间 70 | /// Default: 71 | /// Nullable:False 72 | /// 73 | public DateTime UpdateTime {get;set;} 74 | 75 | /// 76 | /// Desc:是否加锁文章,加锁文章需在页面解锁才显示主体内容 77 | /// Default:b'0' 78 | /// Nullable:False 79 | /// 80 | public bool IsLock {get;set;} 81 | 82 | /// 83 | /// Desc:加锁文章密码 84 | /// Default: 85 | /// Nullable:true 86 | /// 87 | public string LockPassword {get;set;} 88 | 89 | public bool IsDraft { get;set;} 90 | 91 | /// 92 | /// 是否是特殊文章,通常"关于""友链"等内容可以定义为特殊文章,特殊文章不能设为加锁且不会被列表显示和搜索到 93 | /// 94 | public bool IsSpecial { get; set; } 95 | 96 | /// 97 | /// Desc:标签 标签字符串:“xx,xx..”|"xx,xx.."|"xx xx.." 98 | /// Default: 99 | /// Nullable:true 100 | /// 101 | public string? Labels { get; set; } 102 | /// 103 | /// 自定义文章uri,唯一,可通过此字段或Id找寻文章 104 | /// Nullable:true 105 | /// 106 | public string? CustUri { get; set; } 107 | 108 | /// 109 | /// 评论类型,1直接公开,2经过审核,3不允许评论 110 | /// Nullable:false 111 | /// 112 | public int CommentType { get; set; } 113 | 114 | /// 115 | /// 文章所属菜单名 非字段 只用于显示 116 | /// 117 | [SugarColumn(IsIgnore=true)] 118 | public string Ig_MenuName { get; set; } 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/ArticleThumbUp.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | /// 6 | /// 文章专门点赞表 7 | /// (文章点赞使用此表 而"闲言碎语"组件等是用统一的点赞表CommonThumbUp) 8 | /// 9 | [SugarTable("ArticleThumbUp")] 10 | public class ArticleThumbUp 11 | { 12 | /// 13 | /// Desc:文章Id 14 | /// Default: 15 | /// Nullable:False 16 | /// 17 | [SugarColumn(IsPrimaryKey = true)] 18 | public int ArticleId { get; set; } 19 | 20 | /// 21 | /// Desc:客户端点赞文章的Ip 22 | /// Default: 23 | /// Nullable:False 24 | /// 25 | [SugarColumn(IsPrimaryKey = true)] 26 | public string ClientIp { get; set; } 27 | public string? ClientDevice { get; set; } 28 | public string? ClientBrowser { get; set; } 29 | public DateTime? UpTime { get; set; } 30 | /// 31 | /// 类型:1觉得很赞2有被笑到3不敢苟同 32 | /// 33 | public int? Type { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/Comment.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | /// 6 | /// 评论表 7 | /// 8 | [SugarTable("Comment")] 9 | public class Comment 10 | { 11 | /// 12 | /// Desc:Id 13 | /// Default: 14 | /// Nullable:False 15 | /// 16 | [SugarColumn(IsPrimaryKey = true)] 17 | public int Id { get; set; } 18 | 19 | /// 20 | /// Nullable:False 21 | /// 22 | public int SourceId { get; set; } 23 | /// 24 | /// Nullable:False 25 | /// 26 | public int SourceType { get; set; } 27 | /// 28 | /// Nullable:False 29 | /// 30 | public string Name { get; set; } 31 | /// 32 | /// Nullable:False 33 | /// 34 | public string Email { get; set; } 35 | /// 36 | /// Nullable:true 37 | /// 38 | public string? SiteUrl { get; set; } 39 | /// 40 | /// Nullable:False 41 | /// 42 | public string Content { get; set; } 43 | /// 44 | /// Nullable:true 45 | /// 46 | public bool? IsPassed { get; set; } 47 | /// 48 | /// Nullable:False 49 | /// 50 | public DateTime CommentTime { get; set; } 51 | /// 52 | /// 是管理员的 53 | /// 54 | public bool IsAdmin { get; set; } = false; 55 | 56 | 57 | /// 58 | /// Desc:评论者的Ip 59 | /// Default: 60 | /// Nullable:False 61 | /// 62 | public string ClientIp { get; set; } 63 | public string? ClientDevice { get; set; } 64 | public string? ClientBrowser { get; set; } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/CommonThumbUp.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | /// 6 | /// 统一点赞表 7 | /// 8 | [SugarTable("CommonThumbUp")] 9 | public class CommonThumbUp 10 | { 11 | /// 12 | /// Default:源Id 13 | /// Nullable:False 14 | /// 15 | [SugarColumn(IsPrimaryKey = true)] 16 | public int SourceId { get; set; } 17 | /// 18 | /// Default:源Id所属类型:1"闲言碎语"组件表,2.. 19 | /// (文章点赞不使用此表而是专门的文章点赞表ArticleThumbUp) 20 | /// Nullable:False 21 | /// 22 | [SugarColumn(IsPrimaryKey = true)] 23 | public int SourceType { get; set; } 24 | 25 | /// 26 | /// Desc:客户端点赞文章的Ip 27 | /// Default: 28 | /// Nullable:False 29 | /// 30 | [SugarColumn(IsPrimaryKey = true)] 31 | public string ClientIp { get; set; } 32 | public string? ClientDevice { get; set; } 33 | public string? ClientBrowser { get; set; } 34 | public DateTime? UpTime { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/FilePath.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | [SugarTable("FilePath")] 6 | 7 | public class FilePath 8 | { 9 | [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] 10 | public int Id { get; set; } 11 | public string FileRelPath { get; set; } 12 | public DateTime UploadTime { get; set; } 13 | public string? HelpName { get; set; } 14 | public string Type { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/Gossip.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using SqlSugar; 5 | 6 | namespace CoolNetBlog.Models 7 | { 8 | /// 9 | /// "闲言碎语"组件表 10 | /// 11 | [SugarTable("Gossip")] 12 | public partial class Gossip 13 | { 14 | public Gossip(){ 15 | 16 | 17 | } 18 | /// 19 | /// Desc:自增主键 20 | /// Default: 21 | /// Nullable:False 22 | /// 23 | [SugarColumn(IsPrimaryKey=true,IsIdentity=true)] 24 | public int Id {get;set;} 25 | 26 | /// 27 | /// Desc:内容类型。1纯文字2带图片的文字 28 | /// Default:1 29 | /// Nullable:False 30 | /// 31 | public int Type { get;set;} 32 | 33 | /// 34 | /// 2带图片的文字,图片url 35 | /// 36 | public string? ImgUrl { get;set;} 37 | /// 38 | /// Desc:内容 39 | /// Default: 40 | /// Nullable:False 41 | /// 42 | public string Content {get;set;} 43 | 44 | public DateTime AddTime {get;set;} 45 | 46 | public int StarNumber { get; set; } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/LoveLook.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | /// 6 | /// 7 | /// 8 | [SugarTable("LoveLook")] 9 | public partial class LoveLook 10 | { 11 | public LoveLook(){ 12 | 13 | 14 | } 15 | 16 | [SugarColumn(IsPrimaryKey=true,IsIdentity=true)] 17 | public int Id {get;set;} 18 | 19 | 20 | public DateTime AddedTime { get;set;} 21 | 22 | 23 | public string RelHref { get;set;} 24 | public string LinkName { get;set;} 25 | public int Type { get;set;} 26 | 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/Menu.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | [SugarTable("Menu")] 6 | public class Menu 7 | { 8 | [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] 9 | public int Id { get; set; } 10 | public string Name { get; set; } 11 | public string? Tips { get; set; } 12 | public int PId { get; set; } 13 | 14 | public bool IsHome { get; set; } 15 | public bool IsShow { get; set; } 16 | public int OrderNumber { get; set; } 17 | 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /CoolNetBlog/Models/Reply.cs: -------------------------------------------------------------------------------- 1 | using SqlSugar; 2 | 3 | namespace CoolNetBlog.Models 4 | { 5 | /// 6 | /// 回复评论表 7 | /// 8 | [SugarTable("Reply")] 9 | public class Reply 10 | { 11 | /// 12 | /// Desc:Id 13 | /// Default: 14 | /// Nullable:False 15 | /// 16 | [SugarColumn(IsPrimaryKey = true)] 17 | public int Id { get; set; } 18 | 19 | /// 20 | /// Nullable:False 21 | /// 22 | public int CommentId { get; set; } 23 | /// 24 | /// Nullable:False 25 | /// 26 | public string Name { get; set; } 27 | /// 28 | /// Nullable:False 29 | /// 30 | public string Email { get; set; } 31 | /// 32 | /// Nullable:true 33 | /// 34 | public string? SiteUrl { get; set; } 35 | /// 36 | /// Nullable:False 37 | /// 38 | public string Content { get; set; } 39 | /// 40 | /// Nullable:true 41 | /// 42 | public bool? IsPassed { get; set; } 43 | /// 44 | /// Nullable:False 45 | /// 46 | public DateTime ReplyTime { get; set; } 47 | /// 48 | /// 是管理员的 49 | /// 50 | public bool IsAdmin { get; set; } = false; 51 | 52 | /// 53 | /// Desc:回复者的Ip 54 | /// Default: 55 | /// Nullable:False 56 | /// 57 | public string ClientIp { get; set; } 58 | public string? ClientDevice { get; set; } 59 | public string? ClientBrowser { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /CoolNetBlog/Models/SiteSetting.cs: -------------------------------------------------------------------------------- 1 |  2 | using SqlSugar; 3 | 4 | namespace CoolNetBlog.Models 5 | { 6 | /// 7 | /// 8 | /// 9 | [SugarTable("SiteSetting")] 10 | public partial class SiteSetting 11 | { 12 | public SiteSetting(){ 13 | 14 | 15 | } 16 | /// 17 | /// Desc:站点名称 18 | /// Default: 19 | /// Nullable:True 20 | /// 21 | public string SiteName {get;set;} 22 | 23 | /// 24 | /// Desc:站点IP 25 | /// Default: 26 | /// Nullable:True 27 | /// 28 | public string Host {get;set;} 29 | 30 | /// 31 | /// Desc:站点域名 32 | /// Default: 33 | /// Nullable:True 34 | /// 35 | public string Domain {get;set;} 36 | 37 | /// 38 | /// Desc:个性签名 39 | /// Default: 40 | /// Nullable:True 41 | /// 42 | public string FashionQuotes {get;set;} 43 | 44 | /// 45 | /// Desc:是否显示站点名 46 | /// Default:b'1' 47 | /// Nullable:false 48 | /// 49 | public bool IsShowSiteName {get;set;} 50 | 51 | /// 52 | /// Desc:是否侧边栏也显示搜索框组件 53 | /// Default:b'1' 54 | /// Nullable:false 55 | /// 56 | public bool IsShowEdgeSearch { get;set;} 57 | 58 | /// 59 | /// Desc:是否显示侧边栏 "看看这些"组件 60 | /// Default:b'0' 61 | /// Nullable:false 62 | /// 63 | public bool IsShowLoveLook { get;set;} 64 | 65 | /// 66 | /// Desc:是否显示侧边栏 "闲言碎语"组件 67 | /// Default:b'0' 68 | /// Nullable:false 69 | /// 70 | public bool IsShowGossip { get; set; } 71 | 72 | /// 73 | /// Desc:是否显示个性签名 74 | /// Default:b'1' 75 | /// Nullable:false 76 | /// 77 | public bool IsShowQutoes {get;set;} 78 | 79 | /// 80 | /// Desc:备案号 81 | /// Default: 82 | /// Nullable:True 83 | /// 84 | public string Cban {get;set;} 85 | 86 | /// 87 | /// Desc:尾部文字内容 88 | /// Default: 89 | /// Nullable:True 90 | /// 91 | public string TailContent {get;set;} 92 | 93 | /// 94 | /// Desc:主页列表展示文章的条数 95 | /// Default:5 96 | /// Nullable:false 97 | /// 98 | public int OnePageCount { get;set;} 99 | /// 100 | /// 后台登录入口Value参数设置的值,若设置了值,必须验证正确的值显示登录界面 101 | /// 102 | public string LoginUriValue { get; set; } 103 | /// 104 | /// 显示链接("侧边栏看一看")自定义标题文本 105 | /// 106 | public string? LoveLookTitle { get; set; } 107 | /// 108 | /// 侧边栏"心愿图片"展示路径 在[文件图片]板块中设置心愿图片 109 | /// 110 | public string? WishPictureRelPath { get; set; } 111 | /// 112 | /// 是否展示"心愿图片" 113 | /// 114 | public bool IsShowWishPicture { get; set; } 115 | /// 116 | /// "心愿图片"标题祝福语 117 | /// 118 | public string? WishPictureName { get; set; } 119 | /// 120 | /// Desc:是否开启文章点赞表态功能 121 | /// Default:b'0' 122 | /// Nullable:false 123 | /// 124 | public bool IsOpenDetailThumb { get; set; } 125 | 126 | /// 127 | /// Desc:ip一日内允许的留言次数(包括回复、评论),0为无限制 128 | /// Default:b'0' 129 | /// Nullable:true 130 | /// 131 | public int? LeaveLimitCount { get; set; } 132 | 133 | /// 134 | /// Desc:是否在评论区展示留言者的头像,采用Cravatar源:https://cravatar.cn 135 | /// Default:b'0' 136 | /// Nullable:false 137 | /// 138 | public bool IsShowLeaveHeadImg { get; set; } 139 | 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /CoolNetBlog/Program.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSkyCaps/CoolNetBlog/bbecf641e1da92e360dc034ba6e8e696b5758cad/CoolNetBlog/Program.cs -------------------------------------------------------------------------------- /CoolNetBlog/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "CoolNetBlog": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Production" 8 | }, 9 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 10 | }, 11 | "iis": { 12 | "commandName": "Project", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Production" 16 | }, 17 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/ArticleViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | /// 6 | /// 文章操作模型视图类 7 | /// 8 | public class ArticleViewModel : PassBaseViewModel 9 | { 10 | //----⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇ POST表单属性 11 | public int Id { get; set; } 12 | public int MenuId { get; set; } 13 | public string Title { get; set; } 14 | public string Content { get; set; } 15 | public bool IsShowTitle { get; set; } 16 | public string Abstract { get; set; } 17 | public DateTime CreatedTime { get; set; } 18 | public DateTime UpdateTime { get; set; } 19 | public bool IsLock { get; set; } 20 | public string LockPassword { get; set; } 21 | public bool IsDraft { get; set; } 22 | public bool IsSpecial { get; set; } 23 | public string? Labels { get; set; } 24 | public string? CustUri { get; set; } 25 | public int CommentType { get; set; } 26 | /// 27 | /// 更新文章 是否更新更新时间 不勾选不会更新更新时间 28 | /// 29 | public bool UpTimeLine { get; set; } 30 | 31 | // -----⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆ 32 | 33 | /// 34 | /// 是否有至少一个菜单 没有避免点击新增文章按钮 35 | /// 36 | public bool HasAnyOneMenu { get; set; } 37 | 38 | /// 39 | /// 列表迭代显示的文章属性原值 40 | /// 41 | public IList
ArticlesOrg { get; set; } = new List
(); 42 | /// 43 | /// 用于设置文章的存放菜单 44 | /// 45 | public IList MenuSelectList { get; set; } = new List(); 46 | /// 47 | /// 当前文章关联的菜单实体 48 | /// 49 | public Menu RelatedMenu { get; set; } = new Menu(); 50 | 51 | /// 52 | /// 图片实际名 图片名称地址列表 供文章编辑时选择插入的图片 53 | /// 54 | public List ImgRelPaths { get; set; } = new List(); 55 | 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/DataBackViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.ViewModels.Admin 2 | { 3 | public class DataBackViewModel 4 | { 5 | public string DbUserName { get; set; } 6 | public string dbPassword { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/FilePathViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | public class FilePathViewModel : PassBaseViewModel 6 | { 7 | //----⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇ POST表单属性 8 | public int Id { get; set; } 9 | public string FileRelPath { get; set; } 10 | public string HelpName { get; set; } 11 | public string Type { get; set; } 12 | public DateTime UploadTime { get; set; } 13 | //-----⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆ 14 | /// 15 | /// 迭代显示FilePath列表原值 16 | /// 17 | public IList FileImgPathsOrg { get; set; } = new List(); 18 | public IList FileOtherPathsOrg { get; set; } = new List(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/GossipViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | public class GossipViewModel:PassBaseViewModel 6 | { 7 | //----⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇ POST表单属性 8 | public int Id { get; set; } 9 | 10 | public int Type { get; set; } 11 | 12 | public string? ImgUrl { get; set; } 13 | public string Content { get; set; } 14 | 15 | public DateTime AddTime { get; set; } 16 | public int StarNumber { get; set; } 17 | // -----⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆ 18 | 19 | public IList GossipesOrg { get; set; } = new List(); 20 | public string? Keyword { get; set; } 21 | public IList ImgRelPaths { get; set; } = new List(); 22 | 23 | public int Index { get; set; } = 1; 24 | public int NextIndex { get; set; } = 2; 25 | public int PreIndex { get; set; } = 1; 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/LeaveMessageViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | /// 6 | /// 留言管理(评论、回复)列表查询模型视图类 7 | /// 8 | public class LeaveMessageViewModel : PassBaseViewModel 9 | { 10 | /// 11 | /// 当前未审核的评论 12 | /// 13 | public IList? NotPassComments { get; set; } = new List(); 14 | /// 15 | /// 当前未审核的回复 16 | /// 17 | public IList? NotPassReplies { get; set; } = new List(); 18 | 19 | /// 20 | /// 当前已公开的评论 21 | /// 22 | public IList? PublicComments { get; set; } = new List(); 23 | /// 24 | /// 当前已公开的回复 25 | /// 26 | public IList? PublicReplies { get; set; } = new List(); 27 | 28 | } 29 | 30 | public class CommentCarryViewModel : Comment 31 | { 32 | [SqlSugar.SugarColumn(IsIgnore = true)] 33 | public Article? RelatedArticle { get; set; } = new Article(); 34 | 35 | [SqlSugar.SugarColumn(IsIgnore = true)] 36 | public string? RelatedArticleUrl { get; set; } = ""; 37 | 38 | } 39 | public class ReplyCarryViewModel:Reply 40 | { 41 | [SqlSugar.SugarColumn(IsIgnore = true)] 42 | public Article? RelatedArticle { get; set; } = new Article(); 43 | 44 | [SqlSugar.SugarColumn(IsIgnore = true)] 45 | public string? RelatedArticleUrl { get; set; }=""; 46 | 47 | [SqlSugar.SugarColumn(IsIgnore =true)] 48 | public Comment? RelatedComment { get; set; } = new Comment(); 49 | } 50 | 51 | /// 52 | /// 留言管理(评论、回复) 删除某个评论或回复 53 | /// 54 | public class DeleteOneMsgViewModel 55 | { 56 | //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ post表单属性---- 57 | public int Id { get; set; } 58 | public int DType { get; set; } 59 | public bool SendEmail { get; set; } 60 | public string Message { get; set; } 61 | // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 62 | } 63 | 64 | /// 65 | /// 留言管理(评论、回复) 审核公开某个评论或回复 66 | /// 67 | public class PassOneMsgViewModel 68 | { 69 | //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ post表单属性---- 70 | public int Id { get; set; } 71 | public int DType { get; set; } 72 | public bool SupplyReply { get; set; } 73 | public bool SendEmail { get; set; } 74 | public string Message { get; set; } 75 | // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | public class LoginViewModel:PassBaseViewModel 6 | { 7 | [Required(ErrorMessage = "密码是必填项。")] 8 | [DataType(DataType.Password)] 9 | [Display(Name = "密码:")] 10 | public string Password { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/LoveLookViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.ViewModels.Admin 2 | { 3 | public class LoveLookViewModel:PassBaseViewModel 4 | { 5 | public int Id { get; set; } 6 | public DateTime AddedTime { get; set; } 7 | public string RelHref { get; set; } 8 | public string LinkName { get; set; } 9 | public int Type { get; set; } 10 | //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑post表单属性 11 | 12 | public IList LoveLooksOrg { get; set; } = new List(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/MenuViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | /// 6 | /// 菜单操作模型视图类 7 | /// 8 | public class MenuViewModel:PassBaseViewModel 9 | { //----⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇⬇ POST表单属性 10 | public int Id { get; set; } 11 | public string Name { get; set; } 12 | public int PId { get; set; } 13 | 14 | public bool IsHome { get; set; } 15 | public bool IsShow { get; set; } 16 | public int OrderNumber { get; set; } 17 | public string? Tips { get; set; } 18 | // -----⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆⬆ 19 | 20 | /// 21 | /// 列表迭代显示的菜单属性原值 22 | /// 23 | public IList MenusOrg { get; set; } = new List(); 24 | /// 25 | /// 菜单下拉框选择列表选定的值 26 | /// 27 | public int MenuSelectedValue { get; set; } 28 | /// 29 | /// 菜单下拉框选择列表 30 | /// 31 | public List MenuSelectList { get; set; } = new List(); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/PassBaseViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace CoolNetBlog.ViewModels.Admin 5 | { 6 | public class PassBaseViewModel 7 | { 8 | [Required(ErrorMessage = "管理员昵称是必填项。")] 9 | [Display(Name = "管理员昵称:")] 10 | public string AccountName { get; set; } 11 | 12 | /// 13 | /// 其值等于当前AdminUser.Token,用于在页面中传递授权 14 | /// 15 | [HiddenInput(DisplayValue = false)] 16 | public string? PassToken { get; set; } 17 | [HiddenInput(DisplayValue = false)] 18 | public string? Email { get; set; } 19 | /// 20 | /// 当前某页面所需展示的公共封装数据 21 | /// 22 | public dynamic? CurrentData { get; set; } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/ResetViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace CoolNetBlog.ViewModels.Admin 4 | { 5 | public class ResetViewModel : PassBaseViewModel 6 | { 7 | public string OrgPassword { get; set; } = ""; 8 | public string OrgAccountName { get; set; } = ""; 9 | public string NewAccountName { get; set; } = ""; 10 | public string NewPassword { get; set; } = ""; 11 | public string NewPasswordRep { get; set; } = ""; 12 | public string? Email { get; set; } = ""; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Admin/SiteSettingViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.ViewModels.Admin 2 | { 3 | public class SiteSettingViewModel:PassBaseViewModel 4 | { 5 | public string SiteName { get; set; } 6 | public string Host { get; set; } 7 | public string Domain { get; set; } 8 | public string FashionQuotes { get; set; } 9 | public bool IsShowSiteName { get; set; } 10 | public bool IsShowQutoes { get; set; } 11 | public string Cban { get; set; } 12 | public string TailContent { get; set; } 13 | public int OnePageCount { get; set; } 14 | public string? LoveLookTitle { get; set; } 15 | public string LoginUriValue { get; set; } 16 | public bool IsShowEdgeSearch { get; set; } 17 | public bool IsShowLoveLook { get; set; } 18 | public bool IsShowWishPicture { get; set; } 19 | public string? WishPictureName { get; set; } 20 | public string? WishPictureRelPath { get; set; } 21 | public bool IsOpenDetailThumb { get; set; } 22 | public int? LeaveLimitCount { get; set; } 23 | public bool IsShowLeaveHeadImg { get; set; } 24 | public bool IsShowGossip { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/CommentViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | using SqlSugar; 3 | 4 | namespace CoolNetBlog.ViewModels 5 | { 6 | /// 7 | /// 评论视图模型 8 | /// 9 | public class CommentViewModel:Comment 10 | { 11 | /// 12 | /// 当前评论关联的回复 13 | /// 14 | [SugarColumn(IsIgnore =true)] 15 | public IList RelatedReplies { get; set; } = new List(); 16 | 17 | /// 18 | /// 当前评论关联的回复是否还有回复数据在下一页 19 | /// 20 | [SugarColumn(IsIgnore = true)] 21 | public bool HasReplyInNext { get; set; } = false; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Detail/DetailArticleUnLockViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.ViewModels.Detail 2 | { 3 | /// 4 | /// 博客站点 加锁文章需要解锁 传递得到的验证数据模型 5 | /// 6 | public class DetailArticleUnLockViewModel 7 | { 8 | public string Password { get; set; } 9 | public int ArticleId { get; set; } 10 | public string Content { get; set; } 11 | public int Code { get; set; } = -1; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Detail/DetailArticleViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | using CoolNetBlog.ViewModels.Home; 3 | using SqlSugar; 4 | 5 | namespace CoolNetBlog.ViewModels.Detail 6 | { 7 | /// 8 | /// 前台某篇具体文章内容 模型视图类 9 | /// 10 | public class DetailArticleViewModel:Article 11 | { 12 | /// 13 | /// 标签的字符串列表形式 文章详情用到 14 | /// 15 | [SugarColumn(IsIgnore = true)] 16 | public List LabelsList { get; set; } = new List(); 17 | 18 | /// 19 | /// 文章表态类型数量,文章点赞数ThumbUpStart;文章"有被笑到"数ThumbUpFun;文章"不敢苟同"数ThumbUpSilence 20 | /// 21 | //[SugarColumn(IsIgnore = true)] 22 | public Dictionary ThumbUpNumbers = new Dictionary(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Home/HomeArticleViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | using SqlSugar; 3 | 4 | namespace CoolNetBlog.ViewModels.Home 5 | { 6 | /// 7 | /// 前台文章 模型视图类 8 | /// 9 | public class HomeArticleViewModel:Article 10 | { 11 | /// 12 | /// 当前文章关联的菜单实体 非字段 只用于显示 13 | /// 6 | /// 前台主页所需链接显示("看看这些")组件 继承自实体模型 7 | /// 8 | public class HomeLoveLookViewModel: LoveLook 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Home/HomeMenuViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Home 4 | { 5 | /// 6 | /// 主页前台 菜单 模型视图类 7 | /// 8 | public class HomeMenuViewModel 9 | { 10 | public int Id { get; set; } 11 | public string Name { get; set; } 12 | public int PId { get; set; } 13 | 14 | public bool IsHome { get; set; } 15 | public bool IsShow { get; set; } 16 | public int OrderNumber { get; set; } 17 | public string? Tips { get; set; } 18 | 19 | 20 | /// 21 | /// 下级菜单 22 | /// 23 | public List Subs { get; set; } = new List(); 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Home/HomeSiteSettingViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels.Home 4 | { 5 | /// 6 | /// 前台主页所需全局展示基本配置数据 继承自实体模型 7 | /// 8 | public class HomeSiteSettingViewModel: SiteSetting 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/Home/HomeViewModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using CoolNetBlog.Models; 3 | using CoolNetBlog.ViewModels.Detail; 4 | 5 | namespace CoolNetBlog.ViewModels.Home 6 | { 7 | /// 8 | /// 主页所需展示视图模型 9 | /// 10 | public class HomeViewModel 11 | { 12 | /// 13 | /// 全局视图所需的全局菜单数据 14 | /// 15 | public List HomeMenusData { get; set; } = new List(); 16 | /// 17 | /// 全局视图所需的全局基本配置数据 18 | /// 19 | public HomeSiteSettingViewModel HomeSiteSettingData { get; set; } = new HomeSiteSettingViewModel(); 20 | /// 21 | /// 全局视图所需的全局链接显示("看看这些")数据 22 | /// 23 | public List HomeLoveLookData { get; set; } = new List(); 24 | /// 25 | /// 分页列表视图所需用到的文章列表的视图模型类 26 | /// 27 | public List HomeArticleViewModels { get; set; } = new List(); 28 | /// 29 | /// 显示当前浏览的菜单、搜索结果提示,但基本主页分页浏览不显示 30 | /// 31 | public string LocationTip { get; set; } = ""; 32 | public string Location { get; set; } = ""; 33 | /// 34 | /// 找不到文章的提示 35 | /// 36 | public string NotTips { get; set; } = ""; 37 | /// 38 | /// 当前网页需要显示的title 首页是站点名 文章是文章标题 文章不显示标题仍是站点名 39 | /// 40 | public string CurrentTitle { get; set; } = ""; 41 | 42 | /// 43 | /// 是否没有设置显示任何一个侧边栏组件 44 | /// 45 | public bool IsNotShowAnyOneCom { get; set; } = false; 46 | 47 | /// 48 | /// 分页计数 49 | /// 50 | public PageCompute PageCompute { get; set; } = new PageCompute(); 51 | 52 | 53 | /// 54 | /// 此为某篇文章详细的视图模型类 55 | /// 56 | public DetailArticleViewModel? DetailArticleData { get; set; } = new DetailArticleViewModel(); 57 | 58 | } 59 | public class PageCompute 60 | { 61 | public int PageIndex { get; set; } = 1; 62 | public int NextIndex { get; set; } = 1; 63 | public int PreIndex { get; set; } = 1; 64 | public bool ShowPreIndex { get; set; } = false; 65 | public bool ShowNextIndex { get; set; } = false; 66 | public int PagesChangeMax { get; set; } = 1; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/ReplyViewModel.cs: -------------------------------------------------------------------------------- 1 | using CoolNetBlog.Models; 2 | 3 | namespace CoolNetBlog.ViewModels 4 | { 5 | /// 6 | /// 评论回复 视图模型 7 | /// 8 | public class ReplyViewModel:Reply 9 | { 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /CoolNetBlog/ViewModels/SelectList.cs: -------------------------------------------------------------------------------- 1 | namespace CoolNetBlog.ViewModels 2 | { 3 | /// 4 | /// 下拉框视图模型 5 | /// 6 | public class SelectList 7 | { 8 | public int Value { get; set; } 9 | public string Text { get; set; } 10 | public dynamic CarryData { get; set; } 11 | public List Subs { get; set; } = new List(); 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/Admin/Login.cshtml: -------------------------------------------------------------------------------- 1 | @model LoginViewModel 2 |
3 |
4 |
5 |
6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 |
18 |

19 | 22 |

23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 39 |
40 |
41 | 42 | 43 |
44 |
45 | 46 | 47 |
48 |
49 | 50 | 51 |
52 | 53 |
54 |
55 |
-------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminArticle/ArticleAmManagement.cshtml: -------------------------------------------------------------------------------- 1 | @model ArticleViewModel 2 |
3 |
4 |
5 | @{ 6 | //新增文章,不传参数文章id 7 | // 若没有一个菜单 封锁住新增文章按钮 8 | if (Model.HasAnyOneMenu) 9 | { 10 |
11 | 发表文章 12 |
13 |
14 | 15 | }else 16 | { 17 |
18 | 19 | 20 |
21 |
22 | } 23 | } 24 | 25 |
26 |
27 |
28 | 29 | 30 | 31 |
32 |
33 |
34 | 35 |
36 | @foreach (var item in @Model.ArticlesOrg) 37 | { 38 | 39 | //点击链接编辑文章,传文章id 40 | 41 |
42 |
@(string.IsNullOrWhiteSpace(item.Title)?"-无题-":item.Title)
43 | @item.UpdateTime 44 |
45 |

@item.Abstract

46 | @(item.IsDraft?"草稿":"") 47 | @(item.IsLock?"加锁内容":"") 48 | @(item.IsSpecial?"特殊内容":"") 49 | 菜单:@item.Ig_MenuName 50 |
51 |

52 | 55 |

56 |
57 | @{ 58 | var scheme = Context.Request.Scheme+"://"; 59 | var currentHost = Context.Request.Host.Host; 60 | var port = Context.Request.Host.Port; 61 | var hostV = scheme+currentHost + (port == null ? "" : ":"+port); 62 | hostV = hostV.TrimEnd('/'); 63 | var alink = hostV+"/detail?articleId="+item.Id; 64 | } 65 |
66 |
67 | 68 |
69 |
70 |
71 | 72 | 73 | 74 | 75 |
76 |
77 |
78 |
79 | } 80 |
81 | 100 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminArticle/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_AdminTextPostLayout"; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminFile/FileAmManagement.cshtml: -------------------------------------------------------------------------------- 1 | @model FilePathViewModel 2 |
3 | @if (TempData?["Tips"]!=null) 4 | { 5 | @TempData?["Tips"]?.ToString() 6 | } 7 |
8 | 9 |
10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 23 | 24 | 25 |
26 | 27 |
28 | 29 |
30 | @foreach (var item in Model.FileImgPathsOrg) 31 | { 32 |
33 | @Model.HelpName 34 |
35 |
@item.HelpName
36 |

实际文件名:@item.FileRelPath

37 | 38 |

@item.UploadTime

39 |
40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | 51 |
52 |
53 |
54 | } 55 |
56 | 57 |
58 | 59 |
60 |
61 | 62 | 63 | 64 | 65 |
66 |
67 |
68 |
69 | 其余链接 70 |
71 |
    72 | @foreach (var item in Model.FileOtherPathsOrg) 73 | { 74 |
  • 实际链接名:@item.FileRelPath 75 |
    76 |
    77 | 78 | 79 | 80 | 81 | 82 |
    83 |
    84 |
  • 85 | } 86 |
87 |
-------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminGossip/GossipAmManagement.cshtml: -------------------------------------------------------------------------------- 1 | @model GossipViewModel 2 |
3 |
4 | 5 | 6 |
7 |
8 | 9 |
10 | 11 | 12 |
闲言碎语:简单的小文字,类似心情和朋友圈小吐槽,字数5~150字。
13 |
14 | 18 | 31 | 32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 | @if (!Model.GossipesOrg.Any()) 51 | { 52 | 53 | }else 54 | { 55 |
56 | @foreach (var item in @Model.GossipesOrg) 57 | { 58 | if (item.Type==1) 59 | { 60 |
61 |
62 |

内容:@(item.Content)

63 |

时间:@(item.AddTime)

64 |
65 | 66 | 67 | 68 | 69 | 70 |
71 |
72 |
73 | } 74 | else 75 | { 76 |
77 | 78 |
79 |

内容:@(item.Content)

80 |

时间:@(item.AddTime)

81 |
82 | 83 | 84 | 85 | 86 | 87 |
88 |
89 |
90 | } 91 | } 92 |
93 | } 94 | 100 | 121 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminGossip/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_AdminTextPostLayout"; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminLeaveMessage/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_AdminLeaveMessageLayout"; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/AdminLoveLook/LoveLookAmManagement.cshtml: -------------------------------------------------------------------------------- 1 | @model LoveLookViewModel 2 |
3 |
4 |

给侧边栏"看看这些"组件添加链接。
5 | 若是你的文章,请勾选"内部文章链接",然后输入文章的链接,
6 | 例如(忽略单引号,你可以点击你想添加的文章详情页来复制地址栏的值):
7 | 'detail?articleId=1'、
8 | 'detail/你自定义文章uri'
9 | 若是"上传的文件链接",请输入例如(忽略单引号,务必先通过[文件图片管理]入口上传文件链接):
10 | 'fun.html'、
11 | 'fun2.pdf'
12 | 若是"外部链接",请输入完整的外部链接有效地址,例如(忽略单引号):
13 | 'www.baidu.com'、
14 | 'https://cn.bing.com/translator'
15 |

16 |
17 |
18 |
19 |
20 | 21 | 24 |
25 |
26 | 27 | 30 |
31 |
32 | 33 | 36 |
37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 | 45 | 46 |
47 | 48 |
49 |
50 | 51 |
52 |
53 | 54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 显示的链接("侧边栏 看看这些") 62 |
63 |
    64 | @{ 65 | var scheme = Context.Request.Scheme+"://"; 66 | var currentHost = Context.Request.Host.Host; 67 | var port = Context.Request.Host.Port; 68 | var hostV = scheme+currentHost + (port == null ? "" : ":"+port); 69 | hostV = hostV.TrimEnd('/'); 70 | } 71 | @foreach (var item in Model.LoveLooksOrg) 72 | { 73 |
  • 链接:@item.RelHref 74 |
    类型:@(item.Type==1?"内部文章链接":item.Type==2?"上传的文件链接":"外部链接")
    75 |
    添加时间:@item.AddedTime
    76 | @if (item.Type==3) 77 | { 78 | // 若是3外部链接类型 不必加host 因为外部是完整地址 79 | // target="_blank"在新窗口打开链接 因为当前窗口打开会导致异步Layout页面错误 80 | 81 | 82 | }else{ 83 | 84 | } 85 |
    86 |
    87 | 88 | 89 | 90 |
    91 |
    92 |
  • 93 | } 94 |
95 |
96 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/AdminAccess/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_AdminLayout"; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model HomeViewModel 2 | 3 | 4 | 5 | 6 | @foreach (var item in Model?.HomeArticleViewModels) 7 | { 8 |
9 |
10 | @if (item.IsShowTitle) 11 | { 12 |

@item.Title

13 | } 14 |

摘要@(item.Abstract)...

15 | @if (!string.IsNullOrWhiteSpace(@item.CustUri)) 16 | { 17 | // 若自定义uri不为空 指定uri获取文章,否则,用文章Id 18 | 阅读更多 19 | }else{ 20 | 阅读更多 21 | } 22 |
23 | 29 |
30 | } 31 | 32 |
33 | 50 |
51 | 52 | 68 | 69 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/NotFound.cshtml: -------------------------------------------------------------------------------- 1 | @model HomeViewModel 2 | @{ 3 | // 此局部视图 NotFound 显示特殊提示文本(如不存在的菜单..)继承_Layout视图 4 | if (!String.IsNullOrWhiteSpace(@Model?.NotTips)) 5 | { 6 | 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_AdminLayout.cshtml: -------------------------------------------------------------------------------- 1 | @model PassBaseViewModel 2 | 3 | 4 | 5 | 6 | 7 | 后台-光之使者 8 | @**@ 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | @{ 22 | // 23 | // 24 | // 25 | // 26 | // 27 | // 28 | // 29 | // 30 | // 31 | } 32 | 33 | @* 34 | 35 | 38 | *@ 39 | 40 | @await RenderSectionAsync("Scripts", required: false) 41 | 44 | 45 | 46 |
47 |
48 |

后台管理员 ~喵咪呼啦~

49 | 52 |
53 | @{ 54 | //若是在登陆界面,不具有PassToken,此样式不会显示。否则后台主页具有token,因为验证通过了 55 | if (!String.IsNullOrWhiteSpace(@Model?.PassToken)) 56 | { 57 |
58 |

大侠你好, @Model?.AccountName

59 |
60 | } 61 | } 62 | @if (!string.IsNullOrWhiteSpace(@Model?.PassToken)) 63 | { 64 | // 登录页没有PassToken 成功登陆后才有 不显示"返回后台面板" 65 |
66 | 面板 67 | 站点 68 | 登出 69 |
70 | } 71 |
72 |
73 |
74 |
75 | @RenderBody() 76 |
77 |
78 | 79 |
80 |
@@MyHeartWillGoOn 🐱后台猫の尾巴
81 |
82 | 83 | 84 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_AdminLayout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_AdminLeaveMessageLayout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | // 后台留言管理面板的Layout 内容跟_AdminLayout一样 就是为了让评论管理视图、回复管理视图共享一个关键词删除搜索组件 3 | } 4 | @model PassBaseViewModel 5 | 6 | 7 | 8 | 9 | 10 | 后台-光之使者 11 | @**@ 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | @* 25 | *@ 28 | 29 | @await RenderSectionAsync("Scripts", required: false) 30 | 33 | 34 | 35 |
36 |
37 |

后台管理员 ~喵咪呼啦~

38 | 41 |
42 | @{ 43 | //若是在登陆界面,不具有PassToken,此样式不会显示。否则后台主页具有token,因为验证通过了 44 | if (!String.IsNullOrWhiteSpace(@Model?.PassToken)) 45 | { 46 |
47 |

大侠你好, @Model?.AccountName

48 |
49 | } 50 | } 51 | @if (!string.IsNullOrWhiteSpace(@Model?.PassToken)) 52 | { 53 | // 登录页没有PassToken 成功登陆后才有 不显示"返回后台面板" 54 |
55 | 面板 56 | 站点 57 | 登出 58 |
59 | } 60 |
61 |
62 |
63 |
64 | @RenderBody() 65 | 66 | @{ 67 | //内容跟_AdminLayout一样 就是为了让评论管理视图、回复管理视图共享一个关键词删除搜索组件 68 | } 69 |
70 |
71 | 72 | 73 | 74 |
75 |
76 |
77 | 78 |
79 |
@@MyHeartWillGoOn 🐱后台猫の尾巴
80 |
81 | 82 | 83 | 84 | 157 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_AdminTextPostLayout.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | // 此Layout用于后台文章、后台"闲言碎语"共享,与_AdminLayout相同,多了共享的js函数。 3 | // 后台文章、后台"闲言碎语"都可以选择插入图片,此功能调用的逻辑一致。 4 | } 5 | @model PassBaseViewModel 6 | 7 | 8 | 9 | 10 | 11 | 后台-光之使者 12 | @**@ 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @* 26 | *@ 29 | 30 | @await RenderSectionAsync("Scripts", required: false) 31 | 34 | 35 | 36 |
37 |
38 |

后台管理员 ~喵咪呼啦~

39 | 42 |
43 | @{ 44 | //若是在登陆界面,不具有PassToken,此样式不会显示。否则后台主页具有token,因为验证通过了 45 | if (!String.IsNullOrWhiteSpace(@Model?.PassToken)) 46 | { 47 |
48 |

大侠你好, @Model?.AccountName

49 |
50 | } 51 | } 52 | @if (!string.IsNullOrWhiteSpace(@Model?.PassToken)) 53 | { 54 | // 登录页没有PassToken 成功登陆后才有 不显示"返回后台面板" 55 |
56 | 面板 57 | 站点 58 | 登出 59 |
60 | } 61 |
62 |
63 |
64 |
65 | @RenderBody() 66 |
67 |
68 | 69 |
70 |
@@MyHeartWillGoOn 🐱后台猫の尾巴
71 |
72 | 73 | 74 | 115 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_AdminTextPostLayout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_Layout.cshtml.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | a { 11 | color: #0077cc; 12 | } 13 | 14 | .btn-primary { 15 | color: #fff; 16 | background-color: #1b6ec2; 17 | border-color: #1861ac; 18 | } 19 | 20 | .nav-pills .nav-link.active, .nav-pills .show > .nav-link { 21 | color: #fff; 22 | background-color: #1b6ec2; 23 | border-color: #1861ac; 24 | } 25 | 26 | .border-top { 27 | border-top: 1px solid #e5e5e5; 28 | } 29 | .border-bottom { 30 | border-bottom: 1px solid #e5e5e5; 31 | } 32 | 33 | .box-shadow { 34 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 35 | } 36 | 37 | button.accept-policy { 38 | font-size: 1rem; 39 | line-height: inherit; 40 | } 41 | 42 | .footer { 43 | position: absolute; 44 | bottom: 0; 45 | width: 100%; 46 | white-space: nowrap; 47 | line-height: 60px; 48 | } 49 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/WarningPage/Error.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | @**@ 9 | 10 | 11 | 12 | 15 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/WarningPage/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = ""; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using CoolNetBlog 2 | @using CoolNetBlog.Models 3 | @using CoolNetBlog.ViewModels 4 | @using CoolNetBlog.ViewModels.Admin 5 | @using CoolNetBlog.ViewModels.Home 6 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -------------------------------------------------------------------------------- /CoolNetBlog/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /CoolNetBlog/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /CoolNetBlog/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 14px; 3 | } 4 | 5 | @media (min-width: 768px) { 6 | html { 7 | font-size: 16px; 8 | } 9 | } 10 | 11 | html { 12 | position: relative; 13 | min-height: 100%; 14 | } 15 | 16 | body { 17 | margin-bottom: 60px; 18 | } -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSkyCaps/CoolNetBlog/bbecf641e1da92e360dc034ba6e8e696b5758cad/CoolNetBlog/wwwroot/favicon.ico -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlueSkyCaps/CoolNetBlog/bbecf641e1da92e360dc034ba6e8e696b5758cad/CoolNetBlog/wwwroot/favicon.png -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/js/com.js: -------------------------------------------------------------------------------- 1 | // 把日期对象转为通用字符串形式 2 | function Gb_GetFmtDateStr(dateTime) { 3 | var date = new Date(dateTime); 4 | var year = date.getFullYear(); 5 | var month = date.getMonth() + 1; 6 | var day = date.getDate(); 7 | var hours = date.getHours(); 8 | var minutes = date.getMinutes(); 9 | var seconds = date.getSeconds(); 10 | var fmtTimeStr = year + "-" + (month > 9 ? month : "0" + month) + "-" 11 | + (day > 9 ? day : "0" + day) + " " 12 | + (hours > 9 ? hours : "0" + hours) + ":" 13 | + (minutes > 9 ? minutes : "0" + minutes) + ":" 14 | + (seconds > 9 ? seconds : "0" + seconds); 15 | return fmtTimeStr; 16 | } 17 | 18 | // 把日期对象转为"xx年|月|周|天|时|分|秒前"的字符串形式 19 | function Gb_GetFlowTimeStr(dateTime) { 20 | // 格式化时间字符串 21 | var fmtTimeStr = Gb_GetFmtDateStr(dateTime); 22 | var second = 1000; 23 | var minute = second * 60; 24 | var hour = minute * 60; 25 | var day = hour * 24; 26 | var week = day * 7; 27 | var month = day * 30; 28 | var year = month * 12; 29 | // 当前时间的时间戳 精确为毫秒 因此second以1000为基数 30 | var nowStamp = new Date().getTime(); 31 | // 指定时间的时间戳 32 | var thefmtTimeStamp = Date.parse(fmtTimeStr); 33 | var deStamp = nowStamp - thefmtTimeStamp; 34 | 35 | var flowTimeStr = ""; 36 | if (deStamp < 0) { 37 | flowTimeStr = "时间来自未来"; 38 | } else if (deStamp / year >= 1) { 39 | flowTimeStr = parseInt(deStamp / year) + "年前"; 40 | } else if (deStamp / month >= 1) { 41 | flowTimeStr = parseInt(deStamp / month) + "月前"; 42 | } else if (deStamp / week >= 1) { 43 | flowTimeStr = parseInt(deStamp / week) + "周前"; 44 | } else if (deStamp / day >= 1) { 45 | flowTimeStr = parseInt(deStamp / day) + "天前"; 46 | } else if (deStamp / hour >= 1) { 47 | flowTimeStr = parseInt(deStamp / hour) + "小时前"; 48 | } else if (deStamp / minute >= 1) { 49 | flowTimeStr = parseInt(deStamp / minute) + "分钟前"; 50 | } else if (deStamp / second >= 1) { 51 | flowTimeStr = parseInt(deStamp / second) + "秒前"; 52 | } 53 | return flowTimeStr; 54 | } 55 | 56 | function Gb_IsWhiteSpaceOrNull(v) { 57 | if (v === null || v === undefined || v === "undefined") 58 | return true; 59 | if (v.replace(/(^\s*)|(\s*$)/g, "") === "") 60 | return true; 61 | return false; 62 | } 63 | 64 | String.prototype.format = function () { 65 | if (arguments.length == 0) return this; 66 | for (var s = this, i = 0; i < arguments.length; i++) 67 | s = s.replace(new RegExp("\\{" + i + "\\}", "g"), arguments[i]); 68 | return s; 69 | }; 70 | 71 | String.prototype.isWhiteSpace = function () { 72 | if (this.replace(/(^\s*)|(\s*$)/g, "")==="") 73 | return true; 74 | return false; 75 | }; 76 | 77 | String.prototype.trimF = function (p) { 78 | if (p==="l"||p==="L") 79 | return this.replace(/(^\s*)/g, ""); 80 | if (p === "r" || p === "R") 81 | return this.replace(/(\s*$)/g, ""); 82 | return this.replace(/(^\s*)|(\s*$)/g, ""); 83 | }; 84 | 85 | /** 86 | * bootstrap.Popover组件 统一按钮定时弹出提示文本 87 | * @param {any} idStr 对应按钮元素id 88 | * @param {any} tipMsg 要弹出显示的提示文本 89 | * @param {any} dis 是否时间到禁用指定按钮 90 | * @param {any} lazyText 定时到时是否改变按钮的文本 91 | */ 92 | function Gb_PopoverShow(idStr, tipMsg, dis = false, lazyText = "") { 93 | $('#' + idStr).attr('data-bs-content', tipMsg); 94 | var popover = new bootstrap.Popover($('#' + idStr)); 95 | popover.show(); 96 | setTimeout(function () { 97 | popover.dispose(); 98 | if (lazyText !== "") { 99 | $('#' + idStr).text(lazyText); 100 | } 101 | if (dis) { 102 | $('#' + idStr).attr("disabled", "disabled"); 103 | } 104 | }, 3000); 105 | } 106 | 107 | /** 108 | * UIkit.notification组件 通知信息 109 | * @param {any} message 文本 110 | * @param {any} status 状态样式 111 | * @param {any} pos 显示位置 112 | */ 113 | function Gb_NotifShow(message = "", status = "success", pos = 'top-center') { 114 | UIkit.notification({ 115 | message: message, 116 | status: status, 117 | pos: pos, 118 | timeout: 3000 119 | }); 120 | } 121 | 122 | function Gb_isEmail(input) { 123 | return (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,10})+$/.test(input)); 124 | } 125 | 126 | function gb_debounceBase(func, immediate, wait) { 127 | var timeout; 128 | return function () { 129 | var context = this, args = arguments; 130 | var later = function () { 131 | timeout = null; 132 | if (!immediate) func.apply(context, args); 133 | }; 134 | var callNow = immediate && !timeout; 135 | clearTimeout(timeout); 136 | timeout = setTimeout(later, wait); 137 | if (callNow) func.apply(context, args); 138 | }; 139 | }; 140 | 141 | function Gb_Debounce(func, immediate, wait = 250) { 142 | (gb_debounceBase(func, immediate, wait))(); 143 | } -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/js/md5.min.js: -------------------------------------------------------------------------------- 1 | !function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d((u=d(d(t,n),d(e,u)))<>>32-o,r)}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function c(n,t){var r,e,o,u;n[t>>5]|=128<>>9<<4)]=t;for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h>5]>>>e%32&255);return t}function a(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e>5]|=(255&n.charCodeAt(e/8))<>>4&15)+r.charAt(15&t);return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return i(c(a(n=r(n)),8*n.length))}function u(n,t){return function(n,t){var r,e=a(n),o=[],u=[];for(o[15]=u[15]=void 0,16 code { 220 | color: inherit; 221 | } 222 | 223 | kbd { 224 | padding: 0.2rem 0.4rem; 225 | font-size: 0.875em; 226 | color: #fff; 227 | background-color: #212529; 228 | border-radius: 0.2rem; 229 | } 230 | kbd kbd { 231 | padding: 0; 232 | font-size: 1em; 233 | font-weight: 700; 234 | } 235 | 236 | figure { 237 | margin: 0 0 1rem; 238 | } 239 | 240 | img, 241 | svg { 242 | vertical-align: middle; 243 | } 244 | 245 | table { 246 | caption-side: bottom; 247 | border-collapse: collapse; 248 | } 249 | 250 | caption { 251 | padding-top: 0.5rem; 252 | padding-bottom: 0.5rem; 253 | color: #6c757d; 254 | text-align: left; 255 | } 256 | 257 | th { 258 | text-align: inherit; 259 | text-align: -webkit-match-parent; 260 | } 261 | 262 | thead, 263 | tbody, 264 | tfoot, 265 | tr, 266 | td, 267 | th { 268 | border-color: inherit; 269 | border-style: solid; 270 | border-width: 0; 271 | } 272 | 273 | label { 274 | display: inline-block; 275 | } 276 | 277 | button { 278 | border-radius: 0; 279 | } 280 | 281 | button:focus:not(:focus-visible) { 282 | outline: 0; 283 | } 284 | 285 | input, 286 | button, 287 | select, 288 | optgroup, 289 | textarea { 290 | margin: 0; 291 | font-family: inherit; 292 | font-size: inherit; 293 | line-height: inherit; 294 | } 295 | 296 | button, 297 | select { 298 | text-transform: none; 299 | } 300 | 301 | [role=button] { 302 | cursor: pointer; 303 | } 304 | 305 | select { 306 | word-wrap: normal; 307 | } 308 | select:disabled { 309 | opacity: 1; 310 | } 311 | 312 | [list]::-webkit-calendar-picker-indicator { 313 | display: none; 314 | } 315 | 316 | button, 317 | [type=button], 318 | [type=reset], 319 | [type=submit] { 320 | -webkit-appearance: button; 321 | } 322 | button:not(:disabled), 323 | [type=button]:not(:disabled), 324 | [type=reset]:not(:disabled), 325 | [type=submit]:not(:disabled) { 326 | cursor: pointer; 327 | } 328 | 329 | ::-moz-focus-inner { 330 | padding: 0; 331 | border-style: none; 332 | } 333 | 334 | textarea { 335 | resize: vertical; 336 | } 337 | 338 | fieldset { 339 | min-width: 0; 340 | padding: 0; 341 | margin: 0; 342 | border: 0; 343 | } 344 | 345 | legend { 346 | float: left; 347 | width: 100%; 348 | padding: 0; 349 | margin-bottom: 0.5rem; 350 | font-size: calc(1.275rem + 0.3vw); 351 | line-height: inherit; 352 | } 353 | @media (min-width: 1200px) { 354 | legend { 355 | font-size: 1.5rem; 356 | } 357 | } 358 | legend + * { 359 | clear: left; 360 | } 361 | 362 | ::-webkit-datetime-edit-fields-wrapper, 363 | ::-webkit-datetime-edit-text, 364 | ::-webkit-datetime-edit-minute, 365 | ::-webkit-datetime-edit-hour-field, 366 | ::-webkit-datetime-edit-day-field, 367 | ::-webkit-datetime-edit-month-field, 368 | ::-webkit-datetime-edit-year-field { 369 | padding: 0; 370 | } 371 | 372 | ::-webkit-inner-spin-button { 373 | height: auto; 374 | } 375 | 376 | [type=search] { 377 | outline-offset: -2px; 378 | -webkit-appearance: textfield; 379 | } 380 | 381 | /* rtl:raw: 382 | [type="tel"], 383 | [type="url"], 384 | [type="email"], 385 | [type="number"] { 386 | direction: ltr; 387 | } 388 | */ 389 | ::-webkit-search-decoration { 390 | -webkit-appearance: none; 391 | } 392 | 393 | ::-webkit-color-swatch-wrapper { 394 | padding: 0; 395 | } 396 | 397 | ::file-selector-button { 398 | font: inherit; 399 | } 400 | 401 | ::-webkit-file-upload-button { 402 | font: inherit; 403 | -webkit-appearance: button; 404 | } 405 | 406 | output { 407 | display: inline-block; 408 | } 409 | 410 | iframe { 411 | border: 0; 412 | } 413 | 414 | summary { 415 | display: list-item; 416 | cursor: pointer; 417 | } 418 | 419 | progress { 420 | vertical-align: baseline; 421 | } 422 | 423 | [hidden] { 424 | display: none !important; 425 | } 426 | 427 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | @media (prefers-reduced-motion: no-preference) { 15 | :root { 16 | scroll-behavior: smooth; 17 | } 18 | } 19 | 20 | body { 21 | margin: 0; 22 | font-family: var(--bs-body-font-family); 23 | font-size: var(--bs-body-font-size); 24 | font-weight: var(--bs-body-font-weight); 25 | line-height: var(--bs-body-line-height); 26 | color: var(--bs-body-color); 27 | text-align: var(--bs-body-text-align); 28 | background-color: var(--bs-body-bg); 29 | -webkit-text-size-adjust: 100%; 30 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 31 | } 32 | 33 | hr { 34 | margin: 1rem 0; 35 | color: inherit; 36 | background-color: currentColor; 37 | border: 0; 38 | opacity: 0.25; 39 | } 40 | 41 | hr:not([size]) { 42 | height: 1px; 43 | } 44 | 45 | h6, h5, h4, h3, h2, h1 { 46 | margin-top: 0; 47 | margin-bottom: 0.5rem; 48 | font-weight: 500; 49 | line-height: 1.2; 50 | } 51 | 52 | h1 { 53 | font-size: calc(1.375rem + 1.5vw); 54 | } 55 | @media (min-width: 1200px) { 56 | h1 { 57 | font-size: 2.5rem; 58 | } 59 | } 60 | 61 | h2 { 62 | font-size: calc(1.325rem + 0.9vw); 63 | } 64 | @media (min-width: 1200px) { 65 | h2 { 66 | font-size: 2rem; 67 | } 68 | } 69 | 70 | h3 { 71 | font-size: calc(1.3rem + 0.6vw); 72 | } 73 | @media (min-width: 1200px) { 74 | h3 { 75 | font-size: 1.75rem; 76 | } 77 | } 78 | 79 | h4 { 80 | font-size: calc(1.275rem + 0.3vw); 81 | } 82 | @media (min-width: 1200px) { 83 | h4 { 84 | font-size: 1.5rem; 85 | } 86 | } 87 | 88 | h5 { 89 | font-size: 1.25rem; 90 | } 91 | 92 | h6 { 93 | font-size: 1rem; 94 | } 95 | 96 | p { 97 | margin-top: 0; 98 | margin-bottom: 1rem; 99 | } 100 | 101 | abbr[title], 102 | abbr[data-bs-original-title] { 103 | -webkit-text-decoration: underline dotted; 104 | text-decoration: underline dotted; 105 | cursor: help; 106 | -webkit-text-decoration-skip-ink: none; 107 | text-decoration-skip-ink: none; 108 | } 109 | 110 | address { 111 | margin-bottom: 1rem; 112 | font-style: normal; 113 | line-height: inherit; 114 | } 115 | 116 | ol, 117 | ul { 118 | padding-right: 2rem; 119 | } 120 | 121 | ol, 122 | ul, 123 | dl { 124 | margin-top: 0; 125 | margin-bottom: 1rem; 126 | } 127 | 128 | ol ol, 129 | ul ul, 130 | ol ul, 131 | ul ol { 132 | margin-bottom: 0; 133 | } 134 | 135 | dt { 136 | font-weight: 700; 137 | } 138 | 139 | dd { 140 | margin-bottom: 0.5rem; 141 | margin-right: 0; 142 | } 143 | 144 | blockquote { 145 | margin: 0 0 1rem; 146 | } 147 | 148 | b, 149 | strong { 150 | font-weight: bolder; 151 | } 152 | 153 | small { 154 | font-size: 0.875em; 155 | } 156 | 157 | mark { 158 | padding: 0.2em; 159 | background-color: #fcf8e3; 160 | } 161 | 162 | sub, 163 | sup { 164 | position: relative; 165 | font-size: 0.75em; 166 | line-height: 0; 167 | vertical-align: baseline; 168 | } 169 | 170 | sub { 171 | bottom: -0.25em; 172 | } 173 | 174 | sup { 175 | top: -0.5em; 176 | } 177 | 178 | a { 179 | color: #0d6efd; 180 | text-decoration: underline; 181 | } 182 | a:hover { 183 | color: #0a58ca; 184 | } 185 | 186 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 187 | color: inherit; 188 | text-decoration: none; 189 | } 190 | 191 | pre, 192 | code, 193 | kbd, 194 | samp { 195 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 196 | font-size: 1em; 197 | direction: ltr ; 198 | unicode-bidi: bidi-override; 199 | } 200 | 201 | pre { 202 | display: block; 203 | margin-top: 0; 204 | margin-bottom: 1rem; 205 | overflow: auto; 206 | font-size: 0.875em; 207 | } 208 | pre code { 209 | font-size: inherit; 210 | color: inherit; 211 | word-break: normal; 212 | } 213 | 214 | code { 215 | font-size: 0.875em; 216 | color: #d63384; 217 | word-wrap: break-word; 218 | } 219 | a > code { 220 | color: inherit; 221 | } 222 | 223 | kbd { 224 | padding: 0.2rem 0.4rem; 225 | font-size: 0.875em; 226 | color: #fff; 227 | background-color: #212529; 228 | border-radius: 0.2rem; 229 | } 230 | kbd kbd { 231 | padding: 0; 232 | font-size: 1em; 233 | font-weight: 700; 234 | } 235 | 236 | figure { 237 | margin: 0 0 1rem; 238 | } 239 | 240 | img, 241 | svg { 242 | vertical-align: middle; 243 | } 244 | 245 | table { 246 | caption-side: bottom; 247 | border-collapse: collapse; 248 | } 249 | 250 | caption { 251 | padding-top: 0.5rem; 252 | padding-bottom: 0.5rem; 253 | color: #6c757d; 254 | text-align: right; 255 | } 256 | 257 | th { 258 | text-align: inherit; 259 | text-align: -webkit-match-parent; 260 | } 261 | 262 | thead, 263 | tbody, 264 | tfoot, 265 | tr, 266 | td, 267 | th { 268 | border-color: inherit; 269 | border-style: solid; 270 | border-width: 0; 271 | } 272 | 273 | label { 274 | display: inline-block; 275 | } 276 | 277 | button { 278 | border-radius: 0; 279 | } 280 | 281 | button:focus:not(:focus-visible) { 282 | outline: 0; 283 | } 284 | 285 | input, 286 | button, 287 | select, 288 | optgroup, 289 | textarea { 290 | margin: 0; 291 | font-family: inherit; 292 | font-size: inherit; 293 | line-height: inherit; 294 | } 295 | 296 | button, 297 | select { 298 | text-transform: none; 299 | } 300 | 301 | [role=button] { 302 | cursor: pointer; 303 | } 304 | 305 | select { 306 | word-wrap: normal; 307 | } 308 | select:disabled { 309 | opacity: 1; 310 | } 311 | 312 | [list]::-webkit-calendar-picker-indicator { 313 | display: none; 314 | } 315 | 316 | button, 317 | [type=button], 318 | [type=reset], 319 | [type=submit] { 320 | -webkit-appearance: button; 321 | } 322 | button:not(:disabled), 323 | [type=button]:not(:disabled), 324 | [type=reset]:not(:disabled), 325 | [type=submit]:not(:disabled) { 326 | cursor: pointer; 327 | } 328 | 329 | ::-moz-focus-inner { 330 | padding: 0; 331 | border-style: none; 332 | } 333 | 334 | textarea { 335 | resize: vertical; 336 | } 337 | 338 | fieldset { 339 | min-width: 0; 340 | padding: 0; 341 | margin: 0; 342 | border: 0; 343 | } 344 | 345 | legend { 346 | float: right; 347 | width: 100%; 348 | padding: 0; 349 | margin-bottom: 0.5rem; 350 | font-size: calc(1.275rem + 0.3vw); 351 | line-height: inherit; 352 | } 353 | @media (min-width: 1200px) { 354 | legend { 355 | font-size: 1.5rem; 356 | } 357 | } 358 | legend + * { 359 | clear: right; 360 | } 361 | 362 | ::-webkit-datetime-edit-fields-wrapper, 363 | ::-webkit-datetime-edit-text, 364 | ::-webkit-datetime-edit-minute, 365 | ::-webkit-datetime-edit-hour-field, 366 | ::-webkit-datetime-edit-day-field, 367 | ::-webkit-datetime-edit-month-field, 368 | ::-webkit-datetime-edit-year-field { 369 | padding: 0; 370 | } 371 | 372 | ::-webkit-inner-spin-button { 373 | height: auto; 374 | } 375 | 376 | [type=search] { 377 | outline-offset: -2px; 378 | -webkit-appearance: textfield; 379 | } 380 | 381 | [type="tel"], 382 | [type="url"], 383 | [type="email"], 384 | [type="number"] { 385 | direction: ltr; 386 | } 387 | ::-webkit-search-decoration { 388 | -webkit-appearance: none; 389 | } 390 | 391 | ::-webkit-color-swatch-wrapper { 392 | padding: 0; 393 | } 394 | 395 | ::file-selector-button { 396 | font: inherit; 397 | } 398 | 399 | ::-webkit-file-upload-button { 400 | font: inherit; 401 | -webkit-appearance: button; 402 | } 403 | 404 | output { 405 | display: inline-block; 406 | } 407 | 408 | iframe { 409 | border: 0; 410 | } 411 | 412 | summary { 413 | display: list-item; 414 | cursor: pointer; 415 | } 416 | 417 | progress { 418 | vertical-align: baseline; 419 | } 420 | 421 | [hidden] { 422 | display: none !important; 423 | } 424 | /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.rtl.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.0 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */ -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | // Unobtrusive validation support library for jQuery and jQuery Validate 2 | // Copyright (c) .NET Foundation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 4 | // @version v3.2.11 5 | !function(a){"function"==typeof define&&define.amd?define("jquery.validate.unobtrusive",["jquery-validation"],a):"object"==typeof module&&module.exports?module.exports=a(require("jquery-validation")):jQuery.validator.unobtrusive=a(jQuery)}(function(a){function e(a,e,n){a.rules[e]=n,a.message&&(a.messages[e]=a.message)}function n(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function t(a){return a.replace(/([!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function r(a){return a.substr(0,a.lastIndexOf(".")+1)}function i(a,e){return 0===a.indexOf("*.")&&(a=a.replace("*.",e)),a}function o(e,n){var r=a(this).find("[data-valmsg-for='"+t(n[0].name)+"']"),i=r.attr("data-valmsg-replace"),o=i?a.parseJSON(i)!==!1:null;r.removeClass("field-validation-valid").addClass("field-validation-error"),e.data("unobtrusiveContainer",r),o?(r.empty(),e.removeClass("input-validation-error").appendTo(r)):e.hide()}function d(e,n){var t=a(this).find("[data-valmsg-summary=true]"),r=t.find("ul");r&&r.length&&n.errorList.length&&(r.empty(),t.addClass("validation-summary-errors").removeClass("validation-summary-valid"),a.each(n.errorList,function(){a("
  • ").html(this.message).appendTo(r)}))}function s(e){var n=e.data("unobtrusiveContainer");if(n){var t=n.attr("data-valmsg-replace"),r=t?a.parseJSON(t):null;n.addClass("field-validation-valid").removeClass("field-validation-error"),e.removeData("unobtrusiveContainer"),r&&n.empty()}}function l(e){var n=a(this),t="__jquery_unobtrusive_validation_form_reset";if(!n.data(t)){n.data(t,!0);try{n.data("validator").resetForm()}finally{n.removeData(t)}n.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors"),n.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}}function u(e){var n=a(e),t=n.data(v),r=a.proxy(l,e),i=f.unobtrusive.options||{},u=function(n,t){var r=i[n];r&&a.isFunction(r)&&r.apply(e,t)};return t||(t={options:{errorClass:i.errorClass||"input-validation-error",errorElement:i.errorElement||"span",errorPlacement:function(){o.apply(e,arguments),u("errorPlacement",arguments)},invalidHandler:function(){d.apply(e,arguments),u("invalidHandler",arguments)},messages:{},rules:{},success:function(){s.apply(e,arguments),u("success",arguments)}},attachValidation:function(){n.off("reset."+v,r).on("reset."+v,r).validate(this.options)},validate:function(){return n.validate(),n.valid()}},n.data(v,t)),t}var m,f=a.validator,v="unobtrusiveValidation";return f.unobtrusive={adapters:[],parseElement:function(e,n){var t,r,i,o=a(e),d=o.parents("form")[0];d&&(t=u(d),t.options.rules[e.name]=r={},t.options.messages[e.name]=i={},a.each(this.adapters,function(){var n="data-val-"+this.name,t=o.attr(n),s={};void 0!==t&&(n+="-",a.each(this.params,function(){s[this]=o.attr(n+this)}),this.adapt({element:e,form:d,message:t,params:s,rules:r,messages:i}))}),a.extend(r,{__dummy__:!0}),n||t.attachValidation())},parse:function(e){var n=a(e),t=n.parents().addBack().filter("form").add(n.find("form")).has("[data-val=true]");n.find("[data-val=true]").each(function(){f.unobtrusive.parseElement(this,!0)}),t.each(function(){var a=u(this);a&&a.attachValidation()})}},m=f.unobtrusive.adapters,m.add=function(a,e,n){return n||(n=e,e=[]),this.push({name:a,params:e,adapt:n}),this},m.addBool=function(a,n){return this.add(a,function(t){e(t,n||a,!0)})},m.addMinMax=function(a,n,t,r,i,o){return this.add(a,[i||"min",o||"max"],function(a){var i=a.params.min,o=a.params.max;i&&o?e(a,r,[i,o]):i?e(a,n,i):o&&e(a,t,o)})},m.addSingleVal=function(a,n,t){return this.add(a,[n||"val"],function(r){e(r,t||a,r.params[n])})},f.addMethod("__dummy__",function(a,e,n){return!0}),f.addMethod("regex",function(a,e,n){var t;return!!this.optional(e)||(t=new RegExp(n).exec(a),t&&0===t.index&&t[0].length===a.length)}),f.addMethod("nonalphamin",function(a,e,n){var t;return n&&(t=a.match(/\W/g),t=t&&t.length>=n),t}),f.methods.extension?(m.addSingleVal("accept","mimtype"),m.addSingleVal("extension","extension")):m.addSingleVal("extension","extension","accept"),m.addSingleVal("regex","pattern"),m.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url"),m.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range"),m.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength"),m.add("equalto",["other"],function(n){var o=r(n.element.name),d=n.params.other,s=i(d,o),l=a(n.form).find(":input").filter("[name='"+t(s)+"']")[0];e(n,"equalTo",l)}),m.add("required",function(a){"INPUT"===a.element.tagName.toUpperCase()&&"CHECKBOX"===a.element.type.toUpperCase()||e(a,"required",!0)}),m.add("remote",["url","type","additionalfields"],function(o){var d={url:o.params.url,type:o.params.type||"GET",data:{}},s=r(o.element.name);a.each(n(o.params.additionalfields||o.element.name),function(e,n){var r=i(n,s);d.data[r]=function(){var e=a(o.form).find(":input").filter("[name='"+t(r)+"']");return e.is(":checkbox")?e.filter(":checked").val()||e.filter(":hidden").val()||"":e.is(":radio")?e.filter(":checked").val()||"":e.val()}}),e(o,"remote",d)}),m.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&e(a,"minlength",a.params.min),a.params.nonalphamin&&e(a,"nonalphamin",a.params.nonalphamin),a.params.regex&&e(a,"regex",a.params.regex)}),m.add("fileextensions",["extensions"],function(a){e(a,"extension",a.params.extensions)}),a(function(){f.unobtrusive.parse(document)}),f.unobtrusive}); 6 | -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CoolNetBlog/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | # CoolNetBlog 2 | 3 | ## A lightweight, concise, and fast blogging framework using asp.net core 6. 4 | 5 | [![ASP.NET Core 6](https://shields.io/badge/-Asp_Net_Core_6-blue)](https://asp.net/) 6 | [![Bootstrap](https://img.shields.io/badge/Bootstrap-v5.1-blue)](https://getbootstrap.com/) 7 | [![Uikit](https://img.shields.io/badge/Uikit-v3.10.1-blue)](https://getuikit.com) 8 | [![jQuery](https://img.shields.io/badge/jQuery-v3.5.1-blue)](https://jquery.com/) 9 | 10 | ### Basic features: 11 | 1. Support mobile and pc style. Menus can be set, keyword search, custom site tail and title can be customized. 12 | 2. Article management: There are standard articles, draft articles, privacy articles (password lock) and special articles. 13 | 3. admin can set the comment type of the article: 1 Public 2 Moderation 3 Do not allow comments 14 | 5. The background can review the message, can directly reply to the messager, and choose to send an email to the messager. 15 | 6. Upload images and files: Insert images into articles; extend custom file links and html pages. 16 | 7. Configure widgets: "Personality Picture", Custom Link List, "Small Text". 17 | 8. Turn on or off article likes. 18 | 9. Limit the number of messages a visitor can leave in a single day 19 | 10. Integrated Cravatar avatar service, allowing the avatar of the commenter to be displayed or turned off in the comment area 20 | ...... 21 | 22 | 23 | ### Run|development project : 24 | #### IDE:.net core 6 sdk and asp.net core 6,visual studio 2022 25 | By default, this project uses mysql8.0 for development, and sqlserver requires you to verify the development by yourself. 26 | 27 | 1. Select a Release version and download the code, such as v2.0.1 28 | 2. Create the file 'configs.json' in the 'CoolNetBlog' directory and copy the json content: 29 | ``` 30 | { 31 | "DbConnStr": { 32 | //here your database connection string 33 | "Default": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 34 | "MySql": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 35 | "SqlServer": "" 36 | }, 37 | "Redis": {} 38 | } 39 | ``` 40 | You can then use git to make configs.json an ignored file. 41 | 42 | 3. Add coolNetBlog database (version v2.0.1, similar to the rest): 43 | 44 | Use navicat and other database management tools to connect to the mysql server, run sql statements, and the sql file is the "v201_dump_CoolNetBlog.sql" under directory. 45 | 46 | Another way is that the commands can be executed sequentially in the terminal: 47 | ``` 48 | mysql -u{name} -p 49 | CREATE DATABASE if not exists CoolNetBlog CHARACTER SET utf8 COLLATE utf8_general_ci; 50 | exit; 51 | mysql -u{name} -p{password} CoolNetBlog < v201_dump_CoolNetBlog.sql 52 | ``` 53 | This completes the addition of the CoolNetBlog database. 54 | 55 | 4. Visual studio starts the project. If the database address is a remote server, make sure that the address and password are entered correctly, that the server has port 3306 open, and that the mysql version of the remote connection method is configured correctly. 56 | 57 | 5. Background admin page is {site}/admin/login, default username is bluesky and password is 12345678 58 | 59 | ### Deploy to the server : 60 | #### Ubuntu & Nginx 61 | #### CentOS & Apache 62 | #### Windows & IIS 63 | 64 | See the Microsoft MSDN documentation for details. 65 | [Host and deploy ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-6.0"部署Linux|windows") 66 | 67 | -------------------------------------------------------------------------------- /README-IMGS.md: -------------------------------------------------------------------------------- 1 | # CoolNetBlog 2 | 3 | ## A lightweight, concise, and fast blogging framework using asp.net core 6. 4 | 使用 asp.net core 6 的轻量级、简洁、快速的博客框架。 5 | 6 | ### 使用介绍视频: 7 | [点击:观看基本使用介绍视频](https://www.bilibili.com/video/BV19S4y1F7zA?share_source=copy_web "好玩:独自开发的极简博客框架~全手写部署Linux|windows") 8 | 9 | ### Screenshot show 截图展示: 10 | ![PC首页](https://s2.loli.net/2022/02/27/zhpayQPRT3OF6xw.png) 11 | ![PC文章页](https://s2.loli.net/2022/02/27/quPsDoGChXUyJWQ.png) 12 | ![移动端首页和文章页](https://s2.loli.net/2022/02/27/Jvqc1wPImfloSnu.png) 13 | ![后台面版](https://s2.loli.net/2022/02/27/sE6Otn5rgNUcbLJ.png) 14 | 15 | ### Basic features 基本功能(v1.9.9,当前版本): 16 | Powerd By asp.net core 6 mvc,default use mysql8 17 | Support mobile and pc style, you can set menus, keyword search, customize site tail and title. 18 | Article management: Drafts, menu categorization, article locking, article text style, labeling and searching, article images (large, original, thumbnail). 19 | Upload images and files: Insert images into articles; extend custom file links and html pages. 20 | Configuration widgets: external links sidebar, wish-possible images, custom link lists. 21 | Turn on or disable article likes. 22 | 1. 支持移动端和pc端样式,可以设置菜单,关键词搜索,自定义站点尾巴和标题。 23 | 2. 文章管理:草稿、菜单归类、文章加锁、文章文字样式、设置标签且搜索、文章图片(大图、原始图、缩略图)。 24 | 3. 上传图片和文件:插入图片到文章;扩展自定义文件链接和html页面。 25 | 4. 配置小组件:外部链接侧边栏、心愿图片、自定义链接列表。 26 | 5. 开启或取消文章点赞功能。 27 | 28 | ### Subsequent features 后续功能(v2.0.1,下一版本) 29 | Post comment messages, which can be turned on and off according to the article. 30 | Article editing is optimized, and text edits can be previewed. 31 | ...... 32 | - 扩充帖子评论留言功能,随时启用。 33 | - 文章编辑优化,文本编辑可预览(不引用第三方富文本插件,暂无此想法)。 34 | - ........ 35 | 36 | ### Run|development project 运行|开发项目: 37 | #### 环境配置:.net core 6 sdk and asp.net core 6,visual studio 2022 38 | By default, this project uses mysql for development, and sqlserver requires you to verify the development by yourself. 39 | 本项目默认使用mysql进行开发完成,sqlserver等需自行验证开发 40 | 1. Select a Release version(Lasted) of the repository and download the repository code, such as v1.9.9 or v9.9.9|选择仓库的某个Release版本(Lasted),下载仓库代码,如v1.9.9 or v9.9.9。 41 | 2. Create the file 'configs.json' in the 'CoolNetBlog' directory and copy the json content|在CoolNetBlog\目录下创建文件configs.json,并复制一下json内容: 42 | ``` 43 | { 44 | "DbConnStr": { 45 | //here your database connection string 46 | "Default": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 47 | "MySql": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 48 | "SqlServer": "" 49 | }, 50 | "Redis": {} 51 | } 52 | ``` 53 | You can then use git to make configs.json an ignored file.|然后可以使用git将configs.json设为忽略项 54 | 55 | 3. Add coolNetBlog database (version v1.9.9, similar to the rest)|添加CoolNetBlog数据库(版本v1.9.9,其余类似): 56 | 57 | Use navicat and other database management tools to connect to the mysql server, run sql statements, and the sql file is the "v199_dump_CoolNetBlog.sql" under this repository.|使用navicat等数据库管理工具连接mysql服务器,运行sql语句,sql文件是本仓库下的"v199_dump_CoolNetBlog.sql"。 58 | 59 | Another way is that the commands can be executed sequentially in the terminal|或者在终端依次执行命令: 60 | ``` 61 | mysql -u用户名 -p密码 62 | CREATE DATABASE if not exists CoolNetBlog CHARACTER SET utf8 COLLATE utf8_general_ci; 63 | exit; 64 | mysql -u用户名 -p密码 CoolNetBlog < v199_dump_CoolNetBlog.sql 65 | ``` 66 | This completes the addition of the CoolNetBlog database.| 这样便添加CoolNetBlog数据库完毕。 67 | 68 | 4. Visual studio starts the project. If the database address is a remote server, make sure that the address and password are entered correctly, that the server has port 3306 open, and that the mysql version of the remote connection method is configured correctly.|visual studio启动项目。若数据库地址是远程服务器,确保地址和密码输入正确且服务器开启3306端口、mysql版本远程连接方式配置正确。 69 | 70 | 5. Background admin page(后台页) is /admin/login, default username(默认账户) is bluesky and password(密码) is 12345678 71 | 72 | ### Deploy to the server 部署至服务器: 73 | #### Ubuntu & Nginx 74 | #### CentOS & Apache 75 | #### Windows & IIS 76 | 参见微软msdn文档,有详细介绍。 77 | See the Microsoft MSDN documentation for details. 78 | [Host and deploy ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-6.0"部署Linux|windows") 79 | 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoolNetBlog 2 | 3 | ## 使用 asp.net core 6 的轻量级、简约、快速的博客框架。 4 | 5 | [![ASP.NET Core 6](https://shields.io/badge/-Asp_Net_Core_6-blue)](https://asp.net/) 6 | [![Bootstrap](https://img.shields.io/badge/Bootstrap-v5.1-blue)](https://getbootstrap.com/) 7 | [![Uikit](https://img.shields.io/badge/Uikit-v3.10.1-blue)](https://getuikit.com) 8 | [![jQuery](https://img.shields.io/badge/jQuery-v3.5.1-blue)](https://jquery.com/) 9 | 10 | 11 | ### 目前功能: 12 | 1. 支持移动端和pc端样式。可以设置菜单,关键词搜索,自定义站点尾巴和标题。 13 | 2. 文章管理:有标准文章、草稿文章、隐私文章(密码加锁)和特殊文章(如"友链"等),文章文字样式(包含图片插入)、设置标签。 14 | 4. 可以设置文章的评论类型:1公开2审核3不允许评论 15 | 5. 可以审核留言,通过与不通过,也可以直接回复留言者,并且选择抄送邮件给留言者。 16 | 6. 上传图片和文件:插入图片到文章;扩展自定义文件链接和html页面。 17 | 7. 配置小组件:心愿图片、自定义链接列表、“闲言碎语”。 18 | 8. 开启或取消文章点赞功能。 19 | 9. 允许限制访客单日内的留言次数 20 | 10. 集成Cravatar头像服务,允许在评论区显示或关闭留言者的头像 21 | ...... 22 | 23 | ### 运行|开发项目: 24 | #### 环境配置:.net core 6 sdk and asp.net core 6,visual studio 2022 25 | 本项目默认使用mysql8.0进行开发完成,但采用Sqlsugar ORM框架,只需更改configs.json的连接字符串以使用sqlServer等,但非mysql数据库需要自行验证开发。 26 | 1. 选择某个Release版本,下载代码,如v2.0.1。 27 | 2. 在CoolNetBlog\目录下创建文件configs.json,并复制以下json内容: 28 | ``` 29 | { 30 | "DbConnStr": { 31 | //here your database connection string 32 | "Default": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 33 | "MySql": "server=your_ip;uid=your_username;pwd=your_mysql_password;database=CoolNetBlog", 34 | "SqlServer": "" 35 | }, 36 | "Redis": {} 37 | } 38 | ``` 39 | 然后可以使用git将configs.json设为忽略项 40 | 41 | 3. 添加CoolNetBlog数据库(版本v2.0.1,其余类似): 42 | 43 | 使用navicat等数据库管理工具连接mysql服务器,运行sql语句,sql文件是本仓库下的"v201_dump_CoolNetBlog.sql"。 44 | 45 | 或者在终端依次执行命令: 46 | ``` 47 | mysql -u用户名 -p密码 48 | CREATE DATABASE if not exists CoolNetBlog CHARACTER SET utf8 COLLATE utf8_general_ci; 49 | exit; 50 | mysql -u用户名 -p密码 CoolNetBlog < v201_dump_CoolNetBlog.sql 51 | ``` 52 | 这样便添加CoolNetBlog数据库完毕。 53 | 54 | 4. visual studio启动项目。若数据库地址是远程服务器,确保地址和密码输入正确且服务器开启3306端口、mysql版本远程连接方式配置正确。 55 | 56 | 5. 后台页是 站点/admin/login, 默认账户是bluesky默认密码12345678 57 | 58 | ### 部署至服务器: 59 | #### Ubuntu & Nginx 60 | #### CentOS & Apache 61 | #### Windows & IIS 62 | 参见微软msdn文档,有详细介绍。 63 | [Host and deploy ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/?view=aspnetcore-6.0"部署Linux|windows") 64 | --------------------------------------------------------------------------------