Ver código fonte

1.搜索优化;
2.防火墙规则优化;
3.广告商竞价模块;
4.强类型html;
5.一些前端样式调整

懒得勤快 6 anos atrás
pai
commit
7714467da0
73 arquivos alterados com 1990 adições e 2165 exclusões
  1. 38 1521
      database/mysql/myblogs.sql
  2. 14 5
      src/Masuit.MyBlogs.Core/Common/CommonHelper.cs
  3. 34 33
      src/Masuit.MyBlogs.Core/Common/ImagebedClient.cs
  4. 0 5
      src/Masuit.MyBlogs.Core/Configs/AppConfig.cs
  5. 4 0
      src/Masuit.MyBlogs.Core/Configs/MappingProfile.cs
  6. 114 0
      src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs
  7. 3 1
      src/Masuit.MyBlogs.Core/Controllers/BaseController.cs
  8. 1 1
      src/Masuit.MyBlogs.Core/Controllers/DonateController.cs
  9. 5 8
      src/Masuit.MyBlogs.Core/Controllers/HomeController.cs
  10. 3 3
      src/Masuit.MyBlogs.Core/Controllers/MiscController.cs
  11. 4 0
      src/Masuit.MyBlogs.Core/Controllers/PostController.cs
  12. 1 1
      src/Masuit.MyBlogs.Core/Controllers/SearchController.cs
  13. 11 20
      src/Masuit.MyBlogs.Core/Extensions/FirewallAttribute.cs
  14. 3 4
      src/Masuit.MyBlogs.Core/Infrastructure/DataContext.cs
  15. 1 2
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs
  16. 4 4
      src/Masuit.MyBlogs.Core/Infrastructure/Repository/Repositories.cs
  17. 175 0
      src/Masuit.MyBlogs.Core/Infrastructure/Services/AdvertisementService.cs
  18. 43 0
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IAdvertisementService.cs
  19. 0 1
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IServices.cs
  20. 47 17
      src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs
  21. 0 6
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Services.cs
  22. 3 3
      src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj
  23. 53 0
      src/Masuit.MyBlogs.Core/Models/DTO/AdvertisementInputDto.cs
  24. 78 0
      src/Masuit.MyBlogs.Core/Models/Entity/Advertisement.cs
  25. 1 0
      src/Masuit.MyBlogs.Core/Models/Entity/Category.cs
  26. 9 9
      src/Masuit.MyBlogs.Core/Models/Entity/Donate.cs
  27. 19 0
      src/Masuit.MyBlogs.Core/Models/Enum/AdvertiseType.cs
  28. 69 0
      src/Masuit.MyBlogs.Core/Models/ViewModel/AdvertisementViewModel.cs
  29. 11 1
      src/Masuit.MyBlogs.Core/Models/ViewModel/IndexPageViewModel.cs
  30. 0 1
      src/Masuit.MyBlogs.Core/Startup.cs
  31. 1 1
      src/Masuit.MyBlogs.Core/Views/Error/AccessDeny.cshtml
  32. 1 1
      src/Masuit.MyBlogs.Core/Views/Error/ComingSoon.cshtml
  33. 1 1
      src/Masuit.MyBlogs.Core/Views/Error/TempDeny.cshtml
  34. 2 4
      src/Masuit.MyBlogs.Core/Views/Home/Index.cshtml
  35. 7 7
      src/Masuit.MyBlogs.Core/Views/Misc/Donate.cshtml
  36. 12 12
      src/Masuit.MyBlogs.Core/Views/Misc/Donate_Admin.cshtml
  37. 1 1
      src/Masuit.MyBlogs.Core/Views/Notice/Details.cshtml
  38. 2 2
      src/Masuit.MyBlogs.Core/Views/Notice/Index_Admin.cshtml
  39. 3 3
      src/Masuit.MyBlogs.Core/Views/Post/All.cshtml
  40. 60 2
      src/Masuit.MyBlogs.Core/Views/Post/CompareVersion.cshtml
  41. 31 3
      src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml
  42. 32 2
      src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml
  43. 6 0
      src/Masuit.MyBlogs.Core/Views/Post/History.cshtml
  44. 31 4
      src/Masuit.MyBlogs.Core/Views/Post/HistoryVersion.cshtml
  45. 30 1
      src/Masuit.MyBlogs.Core/Views/Post/HistoryVersion_Admin.cshtml
  46. 14 7
      src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml
  47. 29 0
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListAdvertisement.cshtml
  48. 2 2
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml
  49. 2 2
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml
  50. 9 2
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial.cshtml
  51. 21 8
      src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial_Admin.cshtml
  52. 19 21
      src/Masuit.MyBlogs.Core/Views/Shared/_Aside.cshtml
  53. 2 2
      src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml
  54. 12 0
      src/Masuit.MyBlogs.Core/bundleconfig.json
  55. 1 2
      src/Masuit.MyBlogs.Core/wwwroot/Assets/banner/bootstrap-touch-slider.css
  56. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/Assets/banner/bootstrap-touch-slider.min.css
  57. 4 2
      src/Masuit.MyBlogs.Core/wwwroot/Assets/jquery.tocify/jquery.tocify.css
  58. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/Assets/jquery.tocify/jquery.tocify.min.css
  59. 5 0
      src/Masuit.MyBlogs.Core/wwwroot/Content/common/articlestyle.css
  60. 2 2
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.js
  61. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.min.js
  62. 384 291
      src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/scripts.js
  63. 8 5
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/app/route.config.js
  64. 5 5
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/donate.js
  65. 224 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.js
  66. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.min.js
  67. 0 111
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.js
  68. 0 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.min.js
  69. 5 3
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/template/sidebar-left.html
  70. 2 2
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/donate.html
  71. 269 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/partner.html
  72. 1 6
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/postlist.html
  73. 1 1
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/system/home.html

Diferenças do arquivo suprimidas por serem muito extensas
+ 38 - 1521
database/mysql/myblogs.sql


+ 14 - 5
src/Masuit.MyBlogs.Core/Common/CommonHelper.cs

@@ -144,15 +144,24 @@ namespace Masuit.MyBlogs.Core.Common
                 return false;
             }
 
-            var denyed = DenyIP.Contains(ip) || DenyIPRange.AsParallel().Any(kv => kv.Key.StartsWith(ip.Split('.')[0]) && ip.IpAddressInRange(kv.Key, kv.Value));
+            return DenyIP.Contains(ip) || DenyIPRange.AsParallel().Any(kv => kv.Key.StartsWith(ip.Split('.')[0]) && ip.IpAddressInRange(kv.Key, kv.Value));
+        }
+
+        /// <summary>
+        /// 是否是禁区
+        /// </summary>
+        /// <param name="ip"></param>
+        /// <returns></returns>
+        public static bool IsInDenyArea(this string ip)
+        {
             if (SystemSettings.GetOrAdd("EnableDenyArea", "false") == "false")
             {
-                return denyed;
+                var pos = GetIPLocation(ip);
+                var denyAreas = SystemSettings.GetOrAdd("DenyArea", "").Split(',', ',');
+                return pos.Contains(denyAreas) || denyAreas.Intersect(pos.Split("|")).Any();
             }
 
-            var pos = GetIPLocation(ip);
-            var denyAreas = SystemSettings.GetOrAdd("DenyArea", "").Split(',', ',');
-            return denyed || pos.Contains(denyAreas) || denyAreas.Intersect(pos.Split("|")).Any();
+            return false;
         }
 
         public static string GetIPLocation(this IPAddress ip) => GetIPLocation(ip.MapToIPv4().ToString());

+ 34 - 33
src/Masuit.MyBlogs.Core/Common/ImagebedClient.cs

@@ -45,11 +45,6 @@ namespace Masuit.MyBlogs.Core.Common
         /// <returns></returns>
         public async Task<(string url, bool success)> UploadImage(Stream stream, string file)
         {
-            if (AppConfig.GiteeConfig.Enabled && stream.Length < AppConfig.GiteeConfig.FileLimitSize)
-            {
-                return await UploadGitee(stream, file);
-            }
-
             if (AppConfig.GitlabConfigs.Any())
             {
                 return await UploadGitlab(stream, file);
@@ -63,30 +58,6 @@ namespace Masuit.MyBlogs.Core.Common
             return await UploadSmms(stream, file);
         }
 
-        /// <summary>
-        /// 码云图床
-        /// </summary>
-        /// <param name="stream"></param>
-        /// <param name="file"></param>
-        /// <returns></returns>
-        public async Task<(string url, bool success)> UploadGitee(Stream stream, string file)
-        {
-            string base64String = Convert.ToBase64String(stream.ToByteArray());
-            string path = $"{DateTime.Now:yyyyMMdd}/{Path.GetFileName(file)}";
-            using var resp = await _httpClient.PostAsJsonAsync(AppConfig.GiteeConfig.ApiUrl + HttpUtility.UrlEncode(path), new
-            {
-                access_token = AppConfig.GiteeConfig.AccessToken,
-                content = base64String,
-                message = "上传一张图片"
-            });
-            if (resp.IsSuccessStatusCode || (await resp.Content.ReadAsStringAsync()).Contains("already exists"))
-            {
-                return (AppConfig.GiteeConfig.RawUrl + path, true);
-            }
-
-            return AppConfig.AliOssConfig.Enabled ? await UploadOss(stream, file) : await UploadSmms(stream, file);
-        }
-
         /// <summary>
         /// gitlab图床
         /// </summary>
@@ -101,9 +72,14 @@ namespace Masuit.MyBlogs.Core.Common
                 return AppConfig.AliOssConfig.Enabled ? await UploadOss(stream, file) : await UploadSmms(stream, file);
             }
 
+            if (gitlab.ApiUrl.Contains("gitee.com"))
+            {
+                return await UploadGitee(gitlab, stream, file);
+            }
+
             string base64String = Convert.ToBase64String(stream.ToByteArray());
+            string path = $"{DateTime.Now:yyyy/MM/dd}/{SnowFlake.NewId + Path.GetExtension(file)}";
             _httpClient.DefaultRequestHeaders.Add("PRIVATE-TOKEN", gitlab.AccessToken);
-            string path = $"{DateTime.Now:yyyyMMdd}/{Path.GetFileName(file)}";
             using var resp = await _httpClient.PostAsJsonAsync(gitlab.ApiUrl.Contains("/v3/") ? gitlab.ApiUrl : gitlab.ApiUrl + HttpUtility.UrlEncode(path), new
             {
                 file_path = path,
@@ -123,6 +99,31 @@ namespace Masuit.MyBlogs.Core.Common
             return AppConfig.AliOssConfig.Enabled ? await UploadOss(stream, file) : await UploadSmms(stream, file);
         }
 
+        /// <summary>
+        /// 码云图床
+        /// </summary>
+        /// <param name="config"></param>
+        /// <param name="stream"></param>
+        /// <param name="file"></param>
+        /// <returns></returns>
+        private async Task<(string url, bool success)> UploadGitee(GitlabConfig config, Stream stream, string file)
+        {
+            string base64String = Convert.ToBase64String(stream.ToByteArray());
+            string path = $"{DateTime.Now:yyyy/MM/dd}/{Path.GetFileName(file)}";
+            using var resp = await _httpClient.PostAsJsonAsync(config.ApiUrl + HttpUtility.UrlEncode(path), new
+            {
+                access_token = config.AccessToken,
+                content = base64String,
+                message = "上传一张图片"
+            });
+            if (resp.IsSuccessStatusCode || (await resp.Content.ReadAsStringAsync()).Contains("already exists"))
+            {
+                return (config.RawUrl + path, true);
+            }
+
+            return AppConfig.AliOssConfig.Enabled ? await UploadOss(stream, file) : await UploadSmms(stream, file);
+        }
+
         /// <summary>
         /// 阿里云Oss图床
         /// </summary>
@@ -132,13 +133,13 @@ namespace Masuit.MyBlogs.Core.Common
         public async Task<(string url, bool success)> UploadOss(Stream stream, string file)
         {
             var objectName = DateTime.Now.ToString("yyyyMMdd") + "/" + SnowFlake.NewId + Path.GetExtension(file);
-            var result = Policy.Handle<Exception>().Retry(5, (e, i) =>
+            var policy = Policy.Handle<Exception>().Retry(5, (e, i) =>
             {
                 Console.ForegroundColor = ConsoleColor.Red;
                 Console.WriteLine(e.Message);
                 Console.ResetColor();
-            }).Execute(() => OssClient.PutObject(AppConfig.AliOssConfig.BucketName, objectName, stream));
-            return result.HttpStatusCode == HttpStatusCode.OK ? (AppConfig.AliOssConfig.BucketDomain + "/" + objectName, true) : await UploadSmms(stream, file);
+            });
+            return policy.Wrap(Policy<(string url, bool success)>.Handle<Exception>().Fallback(() => UploadSmms(stream, file).Result)).Execute(() => OssClient.PutObject(AppConfig.AliOssConfig.BucketName, objectName, stream).HttpStatusCode == HttpStatusCode.OK ? (AppConfig.AliOssConfig.BucketDomain + "/" + objectName, true) : UploadSmms(stream, file).Result);
         }
 
         /// <summary>

+ 0 - 5
src/Masuit.MyBlogs.Core/Configs/AppConfig.cs

@@ -31,10 +31,5 @@ namespace Masuit.MyBlogs.Core.Configs
         /// gitlab图床配置
         /// </summary>
         public static List<GitlabConfig> GitlabConfigs { get; set; } = new List<GitlabConfig>();
-
-        /// <summary>
-        /// 码云图床配置
-        /// </summary>
-        public static GitlabConfig GiteeConfig { get; set; } = new GitlabConfig();
     }
 }

+ 4 - 0
src/Masuit.MyBlogs.Core/Configs/MappingProfile.cs

@@ -4,6 +4,7 @@ using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools.Systems;
+using System;
 using System.Linq;
 
 namespace Masuit.MyBlogs.Core.Configs
@@ -83,6 +84,9 @@ namespace Masuit.MyBlogs.Core.Configs
             CreateMap<PostMergeRequest, PostMergeRequestOutputDto>().ForMember(p => p.PostTitle, e => e.MapFrom(r => r.Post.Title));
             CreateMap<PostMergeRequest, Post>().ForMember(p => p.Id, e => e.Ignore()).ForMember(p => p.Status, e => e.Ignore()).ReverseMap();
             CreateMap<Post, PostMergeRequestOutputDto>().ReverseMap();
+
+            CreateMap<Advertisement, AdvertisementViewModel>().ForMember(a => a.CategoryName, e => e.MapFrom(a => a.Category.Name));
+            CreateMap<AdvertisementInputDto, Advertisement>().ForMember(a => a.CategoryId, e => e.MapFrom(d => string.IsNullOrEmpty(d.CategoryId) ? null : d.CategoryId)).ForMember(a => a.Status, e => e.Ignore()).ForMember(a => a.UpdateTime, e => e.MapFrom(a => DateTime.Now));
         }
     }
 }

+ 114 - 0
src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs

@@ -0,0 +1,114 @@
+using AutoMapper.QueryableExtensions;
+using Masuit.LuceneEFCore.SearchEngine.Linq;
+using Masuit.MyBlogs.Core.Common;
+using Masuit.MyBlogs.Core.Extensions;
+using Masuit.MyBlogs.Core.Models.DTO;
+using Masuit.MyBlogs.Core.Models.Entity;
+using Masuit.MyBlogs.Core.Models.Enum;
+using Masuit.MyBlogs.Core.Models.ViewModel;
+using Masuit.Tools.Core.Net;
+using Microsoft.AspNetCore.Mvc;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+
+namespace Masuit.MyBlogs.Core.Controllers
+{
+    [Route("ads/[action]")]
+    public class AdvertisementController : BaseController
+    {
+        /// <summary>
+        /// 前往
+        /// </summary>
+        /// <param name="id">广告id</param>
+        /// <returns></returns>
+        [HttpGet("{id:int}")]
+        public async Task<IActionResult> Redirect(int id)
+        {
+            var ad = AdsService.GetById(id) ?? throw new NotFoundException("广告不存在");
+            if (!HttpContext.Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("ads" + id)))
+            {
+                HttpContext.Session.Set("ads" + id, id.ToString());
+                ad.ViewCount++;
+                await AdsService.SaveChangesAsync();
+            }
+
+            return RedirectPermanent(ad.Url);
+        }
+
+        /// <summary>
+        /// 获取文章分页
+        /// </summary>
+        /// <returns></returns>
+        [Authority]
+        public ActionResult GetPageData([Range(1, int.MaxValue, ErrorMessage = "页数必须大于0")]int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")]int size = 10, string kw = "")
+        {
+            Expression<Func<Advertisement, bool>> where = p => true;
+            if (!string.IsNullOrEmpty(kw))
+            {
+                where = where.And(p => p.Title.Contains(kw) || p.Description.Contains(kw));
+            }
+
+            var query = AdsService.GetQuery(where);
+            var total = query.Count();
+            var list = query.OrderByDescending(p => p.Price).ThenByDescending(a => a.Weight).Skip((page - 1) * size).Take(size).ProjectTo<AdvertisementViewModel>(MapperConfig).ToList();
+            var pageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
+            return PageResult(list, pageCount, total);
+        }
+
+        /// <summary>
+        /// 保存banner
+        /// </summary>
+        /// <param name="model"></param>
+        /// <returns></returns>
+        [HttpPost, Authority]
+        public async Task<IActionResult> Save(AdvertisementInputDto model)
+        {
+            model.CategoryId = model.CategoryId?.Replace("null", "");
+            var entity = AdsService.GetById(model.Id);
+            if (entity != null)
+            {
+                Mapper.Map(model, entity);
+                bool b1 = await AdsService.SaveChangesAsync() > 0;
+                return ResultData(null, b1, b1 ? "修改成功" : "修改失败");
+            }
+
+            bool b = AdsService.AddEntitySaved(model.Mapper<Advertisement>()) != null;
+            return ResultData(null, b, b ? "添加成功" : "添加失败");
+        }
+
+        /// <summary>
+        /// 删除banner
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        [HttpPost("{id}"), HttpGet("{id}"), Authority]
+        public IActionResult Delete(int id)
+        {
+            bool b = AdsService.DeleteByIdSaved(id);
+            return ResultData(null, b, b ? "删除成功" : "删除失败");
+        }
+
+
+        /// <summary>
+        /// 禁用或开启文章评论
+        /// </summary>
+        /// <param name="id">文章id</param>
+        /// <returns></returns>
+        [Authority, HttpPost("{id}")]
+        public ActionResult ChangeState(int id)
+        {
+            var ad = AdsService.GetById(id);
+            if (ad != null)
+            {
+                ad.Status = ad.Status == Status.Available ? Status.Unavailable : Status.Available;
+                return ResultData(null, AdsService.SaveChanges() > 0, ad.Status == Status.Available ? $"【{ad.Title}】已上架!" : $"【{ad.Title}】已下架!");
+            }
+
+            return ResultData(null, false, "广告不存在");
+        }
+
+    }
+}

+ 3 - 1
src/Masuit.MyBlogs.Core/Controllers/BaseController.cs

@@ -39,6 +39,8 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         public ILinksService LinksService { get; set; }
 
+        public IAdvertisementService AdsService { get; set; }
+
         public UserInfoOutputDto CurrentUser => HttpContext.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
 
         public IMapper Mapper { get; set; }
@@ -143,7 +145,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             ViewBag.menus = MenuService.GetQueryFromCache<MenuOutputDto>(m => m.Status == Status.Available).OrderBy(m => m.Sort).ToList(); //菜单
             var model = new PageFootViewModel //页脚
             {
-                Links = LinksService.GetQueryFromCache<LinksOutputDto>(l => l.Status == Status.Available).OrderBy(l => l.Recommend).ThenByDescending(l => l.Weight).ThenByDescending(l => new Random().Next()).Take(40).ToList()
+                Links = LinksService.GetQueryFromCache<LinksOutputDto>(l => l.Status == Status.Available).OrderByDescending(l => l.Recommend).ThenByDescending(l => l.Weight).ThenByDescending(l => new Random().Next()).Take(40).ToList()
             };
             ViewBag.Footer = model;
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/DonateController.cs

@@ -7,7 +7,7 @@ using System.Linq;
 namespace Masuit.MyBlogs.Core.Controllers
 {
     /// <summary>
-    /// 捐赠管理
+    /// 打赏管理
     /// </summary>
     public class DonateController : AdminController
     {

+ 5 - 8
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -41,11 +41,6 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// </summary>
         public IFastShareService FastShareService { get; set; }
 
-        /// <summary>
-        /// banner
-        /// </summary>
-        public IBannerService BannerService { get; set; }
-
         /// <summary>
         /// 首页
         /// </summary>
@@ -54,7 +49,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         public ActionResult Index()
         {
             ViewBag.Total = PostService.Count(p => p.Status == Status.Pended || CurrentUser.IsAdmin);
-            var banners = BannerService.GetAllFromCache().OrderBy(b => new Random().Next()).ToList();
+            var banners = AdsService.GetsByWeightedPrice(8, AdvertiseType.Banner).OrderBy(a => Guid.NewGuid()).ToList();
             var fastShares = FastShareService.GetAllFromCache(s => s.Sort).ToList();
             ViewBag.FastShare = fastShares;
             var viewModel = GetIndexPageViewModel(1, 15, OrderBy.ModifyDate, CurrentUser);
@@ -171,7 +166,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         /// <returns></returns>
         private IndexPageViewModel GetIndexPageViewModel(int page, int size, OrderBy? orderBy, UserInfoOutputDto user)
         {
-            IQueryable<PostOutputDto> postsQuery = PostService.GetQuery<PostOutputDto>(p => (p.Status == Status.Pended || user.IsAdmin)); //准备文章的查询
+            var postsQuery = PostService.GetQuery<PostOutputDto>(p => (p.Status == Status.Pended || user.IsAdmin)); //准备文章的查询
             var notices = NoticeService.GetPagesFromCache<DateTime, NoticeOutputDto>(1, 5, out int _, n => (n.Status == Status.Display || user.IsAdmin), n => n.ModifyDate, false).ToList(); //加载前5条公告
             var cats = CategoryService.GetQueryFromCache<string, CategoryOutputDto>(c => c.Status == Status.Available, c => c.Name).ToList(); //加载分类目录
             var hotSearches = RedisHelper.Get<List<KeywordsRankOutputDto>>("SearchRank:Week").Take(10).ToList(); //热词统计
@@ -232,7 +227,9 @@ namespace Masuit.MyBlogs.Core.Controllers
                 Posts = posts,
                 Tags = newdic,
                 Top6Post = hot6Post,
-                PostsQueryable = postsQuery
+                PostsQueryable = postsQuery,
+                SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar),
+                ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList)
             };
         }
     }

+ 3 - 3
src/Masuit.MyBlogs.Core/Controllers/MiscController.cs

@@ -27,7 +27,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         public IMiscService MiscService { get; set; }
 
         /// <summary>
-        /// 捐赠
+        /// 打赏
         /// </summary>
         public IDonateService DonateService { get; set; }
 
@@ -47,7 +47,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         }
 
         /// <summary>
-        /// 捐赠
+        /// 打赏
         /// </summary>
         /// <returns></returns>
         [Route("donate")]
@@ -57,7 +57,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         }
 
         /// <summary>
-        /// 捐赠列表
+        /// 打赏列表
         /// </summary>
         /// <param name="page"></param>
         /// <param name="size"></param>

+ 4 - 0
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -70,6 +70,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 ViewData["keywords"] = post.Content.Contains(kw) ? $"['{kw}']" : SearchEngine.LuceneIndexSearcher.CutKeywords(kw).ToJsonString();
             }
 
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
             if (CurrentUser.IsAdmin)
             {
                 return View("Details_Admin", post);
@@ -99,6 +100,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var list = PostHistoryVersionService.GetPages(page, size, out int total, v => v.PostId == id, v => v.ModifyDate, false).ToList();
             ViewBag.Total = total;
             ViewBag.PageCount = Math.Ceiling(total * 1.0 / size).ToInt32();
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
             return View(list);
         }
 
@@ -114,6 +116,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var post = PostHistoryVersionService.Get(v => v.Id == hid) ?? throw new NotFoundException("文章未找到");
             ViewBag.Next = PostHistoryVersionService.Get(p => p.PostId == id && p.ModifyDate > post.ModifyDate, p => p.ModifyDate);
             ViewBag.Prev = PostHistoryVersionService.Get(p => p.PostId == id && p.ModifyDate < post.ModifyDate, p => p.ModifyDate, false);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
             return CurrentUser.IsAdmin ? View("HistoryVersion_Admin", post) : View(post);
         }
 
@@ -135,6 +138,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var diffOutput = diff.Build();
             right.Content = Regex.Replace(Regex.Replace(diffOutput, "<ins.+?</ins>", string.Empty), @"<\w+></\w+>", string.Empty);
             left.Content = Regex.Replace(Regex.Replace(diffOutput, "<del.+?</del>", string.Empty), @"<\w+></\w+>", string.Empty);
+            ViewBag.Ads = AdsService.GetsByWeightedPrice(2, AdvertiseType.InPage, main.CategoryId);
             return View(new[] { main, left, right });
         }
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/SearchController.cs

@@ -42,7 +42,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             ViewBag.Keyword = wd;
             string ip = HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
             string key = "Search:" + ip;
-            if (CacheManager.Exists(key))
+            if (CacheManager.Exists(key) && CacheManager.Get(key) != wd)
             {
                 var hotSearches = RedisHelper.Get<List<KeywordsRankOutputDto>>("SearchRank:Week").Take(10).ToList();
                 ViewBag.hotSearches = hotSearches;

+ 11 - 20
src/Masuit.MyBlogs.Core/Extensions/FirewallAttribute.cs

@@ -12,6 +12,7 @@ using Microsoft.Net.Http.Headers;
 using System;
 using System.Linq;
 using System.Text;
+using System.Text.RegularExpressions;
 using System.Web;
 
 namespace Masuit.MyBlogs.Core.Extensions
@@ -23,31 +24,29 @@ namespace Masuit.MyBlogs.Core.Extensions
         /// <inheritdoc />
         public override void OnActionExecuting(ActionExecutingContext context)
         {
-            if (context.Filters.Any(m => m.ToString().Contains(nameof(AllowAccessFirewallAttribute))))
-            {
-                return;
-            }
-
             var request = context.HttpContext.Request;
-            var httpMethod = request.Method;
-            if (httpMethod.Equals("OPTIONS", StringComparison.InvariantCultureIgnoreCase) || httpMethod.Equals("HEAD", StringComparison.InvariantCultureIgnoreCase))
+            if (context.Filters.Any(m => m.ToString().Contains(nameof(AllowAccessFirewallAttribute))) || request.Cookies["Email"].MDString3(AppConfig.BaiduAK).Equals(request.Cookies["FullAccessToken"]))
             {
                 return;
             }
 
-            if (request.Cookies["Email"].MDString3(AppConfig.BaiduAK).Equals(request.Cookies["FullAccessToken"]))
+            var ip = context.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
+            var sessionToken = context.HttpContext.Session.Get<string>("FullAccessViewToken");
+            if (ip.IsDenyIpAddress() && string.IsNullOrEmpty(sessionToken))
             {
+                AccessDeny(context, ip, request);
+                context.Result = new BadRequestObjectResult("您当前所在的网络环境不支持访问本站!");
                 return;
             }
 
-            var ip = context.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString();
-            if (ip.IsDenyIpAddress() && string.IsNullOrEmpty(context.HttpContext.Session.Get<string>("FullAccessViewToken")))
+            if (ip.IsInDenyArea() && string.IsNullOrEmpty(sessionToken))
             {
                 AccessDeny(context, ip, request);
+                context.Result = new RedirectToActionResult("AccessDeny", "Error", null);
                 return;
             }
 
-            if (request.IsRobot())
+            if (Regex.IsMatch(request.Method, "OPTIONS|HEAD", RegexOptions.IgnoreCase) || request.IsRobot())
             {
                 return;
             }
@@ -63,14 +62,7 @@ namespace Masuit.MyBlogs.Core.Extensions
             if (times > limit * 1.2)
             {
                 CacheManager.Expire("Frequency:" + ip, ExpirationMode.Sliding, TimeSpan.FromMinutes(CommonHelper.SystemSettings.GetOrAdd("BanIPTimespan", "10").ToInt32()));
-                var path = HttpUtility.UrlDecode(request.Path + request.QueryString, Encoding.UTF8);
-                BackgroundJob.Enqueue(() => HangfireBackJob.InterceptLog(new IpIntercepter()
-                {
-                    IP = ip,
-                    RequestUrl = HttpUtility.UrlDecode(request.Scheme + "://" + request.Host + path),
-                    Time = DateTime.Now,
-                    UserAgent = request.Headers[HeaderNames.UserAgent]
-                }));
+                AccessDeny(context, ip, request);
             }
 
             context.Result = new RedirectResult("/tempdeny");
@@ -86,7 +78,6 @@ namespace Masuit.MyBlogs.Core.Extensions
                 Time = DateTime.Now,
                 UserAgent = request.Headers[HeaderNames.UserAgent]
             }));
-            context.Result = new RedirectToActionResult("AccessDeny", "Error", null);
         }
     }
 }

+ 3 - 4
src/Masuit.MyBlogs.Core/Infrastructure/DataContext.cs

@@ -24,14 +24,12 @@ namespace Masuit.MyBlogs.Core.Infrastructure
             base.OnModelCreating(modelBuilder);
             modelBuilder.Entity<Category>().HasMany(e => e.Post).WithOne(e => e.Category).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Category>().HasMany(e => e.PostHistoryVersion).WithOne(e => e.Category).OnDelete(DeleteBehavior.Cascade);
-            modelBuilder.Entity<Post>().Property(e => e.Email).IsUnicode();
-            modelBuilder.Entity<Post>().Property(e => e.Label).IsUnicode();
+            modelBuilder.Entity<Category>().HasMany(e => e.Advertisements).WithOne(e => e.Category).IsRequired(false).HasForeignKey(a => a.CategoryId).OnDelete(DeleteBehavior.SetNull);
             modelBuilder.Entity<Post>().HasMany(e => e.Comment).WithOne(e => e.Post).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Post>().HasMany(e => e.PostHistoryVersion).WithOne(e => e.Post).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Post>().HasMany(e => e.Seminar).WithOne(s => s.Post).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<Post>().HasMany(e => e.PostMergeRequests).WithOne(s => s.Post).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<PostHistoryVersion>().HasMany(e => e.Seminar).WithOne(s => s.PostHistoryVersion);
-            modelBuilder.Entity<SearchDetails>().Property(e => e.KeyWords).IsUnicode();
             modelBuilder.Entity<UserInfo>().HasMany(e => e.LoginRecord).WithOne(e => e.UserInfo).OnDelete(DeleteBehavior.Cascade);
             modelBuilder.Entity<SeminarPost>().HasKey(s => new
             {
@@ -78,8 +76,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure
         public virtual DbSet<SeminarPostHistoryVersion> SeminarPostHistoryVersions { get; set; }
         public virtual DbSet<InternalMessage> InternalMessage { get; set; }
         public virtual DbSet<FastShare> FastShare { get; set; }
-        public virtual DbSet<Banner> Banner { get; set; }
+
         public virtual DbSet<PostMergeRequest> PostMergeRequests { get; set; }
+        public virtual DbSet<Advertisement> Advertisements { get; set; }
     }
 
     /// <summary>

+ 1 - 2
src/Masuit.MyBlogs.Core/Infrastructure/Repository/Interface/IBaseRepository.cs

@@ -507,7 +507,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository.Interface
     public partial interface IUserInfoRepository : IBaseRepository<UserInfo> { }
     public partial interface ISeminarPostRepository : IBaseRepository<SeminarPost> { }
     public partial interface ISeminarPostHistoryVersionRepository : IBaseRepository<SeminarPostHistoryVersion> { }
-    public partial interface IBannerRepository : IBaseRepository<Banner> { }
     public partial interface IPostMergeRequestRepository : IBaseRepository<PostMergeRequest> { }
-
+    public partial interface IAdvertisementRepository : IBaseRepository<Advertisement> { }
 }

+ 4 - 4
src/Masuit.MyBlogs.Core/Infrastructure/Repository/Repositories.cs

@@ -269,27 +269,27 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Repository
         }
     }
 
-    public partial class BannerRepository : BaseRepository<Banner>, IBannerRepository
+    public partial class PostMergeRequestRepository : BaseRepository<PostMergeRequest>, IPostMergeRequestRepository
     {
         /// <summary>
         /// 添加实体
         /// </summary>
         /// <param name="t">需要添加的实体</param>
         /// <returns>添加成功</returns>
-        public override Banner AddEntity(Banner t)
+        public override PostMergeRequest AddEntity(PostMergeRequest t)
         {
             DataContext.Add(t);
             return t;
         }
     }
-    public partial class PostMergeRequestRepository : BaseRepository<PostMergeRequest>, IPostMergeRequestRepository
+    public partial class AdvertisementRepository : BaseRepository<Advertisement>, IAdvertisementRepository
     {
         /// <summary>
         /// 添加实体
         /// </summary>
         /// <param name="t">需要添加的实体</param>
         /// <returns>添加成功</returns>
-        public override PostMergeRequest AddEntity(PostMergeRequest t)
+        public override Advertisement AddEntity(Advertisement t)
         {
             DataContext.Add(t);
             return t;

+ 175 - 0
src/Masuit.MyBlogs.Core/Infrastructure/Services/AdvertisementService.cs

@@ -0,0 +1,175 @@
+using Masuit.LuceneEFCore.SearchEngine.Interfaces;
+using Masuit.LuceneEFCore.SearchEngine.Linq;
+using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
+using Masuit.MyBlogs.Core.Infrastructure.Services.Interface;
+using Masuit.MyBlogs.Core.Models.Entity;
+using Masuit.MyBlogs.Core.Models.Enum;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Masuit.MyBlogs.Core.Infrastructure.Services
+{
+    public partial class AdvertisementService : BaseService<Advertisement>, IAdvertisementService
+    {
+        public AdvertisementService(IBaseRepository<Advertisement> repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher) : base(repository, searchEngine, searcher)
+        {
+        }
+
+        /// <summary>
+        /// 按权重随机筛选一个元素
+        /// </summary>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        public Advertisement GetByWeightedRandom(AdvertiseType type, int? cid = null)
+        {
+            return GetsByWeightedRandom(1, type, cid).FirstOrDefault();
+        }
+
+        /// <summary>
+        /// 按权重随机筛选一个元素
+        /// </summary>
+        /// <param name="count">数量</param>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        public List<Advertisement> GetsByWeightedRandom(int count, AdvertiseType type, int? cid = null)
+        {
+            Expression<Func<Advertisement, bool>> where = a => a.Types.Contains(type.ToString("D")) && a.Status == Status.Available;
+            if (cid.HasValue)
+            {
+                where = where.And(a => a.CategoryId == cid || a.CategoryId == null);
+            }
+
+            return GetRandomWeightList(GetQueryFromCache(where).ToList(), count);
+        }
+
+        /// <summary>
+        /// 按价格随机筛选一个元素
+        /// </summary>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        public Advertisement GetByWeightedPrice(AdvertiseType type, int? cid = null)
+        {
+            return GetsByWeightedPrice(1, type, cid).FirstOrDefault();
+        }
+
+        /// <summary>
+        /// 按价格随机筛选一个元素
+        /// </summary>
+        /// <param name="count">数量</param>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        public List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, int? cid = null)
+        {
+            Expression<Func<Advertisement, bool>> where = a => a.Types.Contains(type.ToString("D")) && a.Status == Status.Available;
+            if (cid.HasValue)
+            {
+                where = where.And(a => a.CategoryId == cid || a.CategoryId == null);
+            }
+
+            var list = GetQueryFromCache(@where).ToList();
+            return GetRandomPriceList(list, count);
+        }
+
+        /// <summary>
+        /// 产生一个带权重的随机数
+        /// </summary>
+        /// <param name="list"></param>
+        /// <returns></returns>
+        private int GenerateWeightedRandom(HashSet<int> list)
+        {
+            var num = list.FirstOrDefault(i => new Random().Next(1, list.Sum()) - i <= 0);
+            if (list.Any() && list.All(i => i != num))
+            {
+                num = GenerateWeightedRandom(list);
+            }
+
+            return num;
+        }
+
+        /// <summary>
+        /// 算法:
+        /// 1.每个广告项权重+1命名为w,防止为0情况。
+        /// 2.计算出总权重n。
+        /// 3.每个广告项权重w加上从0到(n-1)的一个随机数(即总权重以内的随机数),得到新的权重排序值s。
+        /// 4.根据得到新的权重排序值s进行排序,取前面s最大几个。
+        /// </summary>
+        /// <param name="list">原始列表</param>
+        /// <param name="count">随机抽取条数</param>
+        /// <returns></returns>
+        private List<Advertisement> GetRandomWeightList(List<Advertisement> list, int count)
+        {
+            if (list.Count <= count || count <= 0)
+            {
+                return list;
+            }
+
+            //随机赋值权重
+            var wlist = list.Select(t => t.Weight + 1 + new Random(GetRandomSeed()).Next(0, list.Sum(t => t.Weight + 1))).Select((w, i) => new KeyValuePair<int, int>(i, w)).ToList(); //第一个int为list下标索引、第一个int为权重排序值
+
+            //排序
+            wlist.Sort((kvp1, kvp2) => kvp2.Value - kvp1.Value);
+
+            //根据实际情况取排在最前面的几个
+            var newList = new List<Advertisement>();
+            for (int i = 0; i < count; i++)
+            {
+                newList.Add(list[wlist[i].Key]);
+            }
+
+            //随机法则
+            return newList;
+        }
+
+        /// <summary>
+        /// 算法:
+        /// 1.每个广告项权重+1命名为w,防止为0情况。
+        /// 2.计算出总权重n。
+        /// 3.每个广告项权重w加上从0到(n-1)的一个随机数(即总权重以内的随机数),得到新的权重排序值s。
+        /// 4.根据得到新的权重排序值s进行排序,取前面s最大几个。
+        /// </summary>
+        /// <param name="list">原始列表</param>
+        /// <param name="count">随机抽取条数</param>
+        /// <returns></returns>
+        private List<Advertisement> GetRandomPriceList(List<Advertisement> list, int count)
+        {
+            if (list.Count <= count || count <= 0)
+            {
+                return list;
+            }
+
+            //随机赋值权重
+            var wlist = list.Select(t => t.Price + 1 + new Random(GetRandomSeed()).Next(0, list.Sum(t => (int)t.Price + 1))).Select((w, i) => new KeyValuePair<int, decimal>(i, w)).ToList(); //第一个int为list下标索引、第一个int为权重排序值
+
+            //排序
+            wlist.Sort((kvp1, kvp2) => (int)(kvp2.Value - kvp1.Value));
+
+            //根据实际情况取排在最前面的几个
+            var newList = new List<Advertisement>();
+            for (int i = 0; i < count; i++)
+            {
+                newList.Add(list[wlist[i].Key]);
+            }
+
+            //随机法则
+            return newList;
+        }
+
+        /// <summary>
+        /// 随机种子值
+        /// </summary>
+        /// <returns></returns>
+        private static int GetRandomSeed()
+        {
+            byte[] bytes = new byte[4];
+            System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
+            rng.GetBytes(bytes);
+            return BitConverter.ToInt32(bytes, 0);
+        }
+    }
+}

+ 43 - 0
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IAdvertisementService.cs

@@ -0,0 +1,43 @@
+using Masuit.MyBlogs.Core.Models.Entity;
+using Masuit.MyBlogs.Core.Models.Enum;
+using System.Collections.Generic;
+
+namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
+{
+    public partial interface IAdvertisementService : IBaseService<Advertisement>
+    {
+        /// <summary>
+        /// 按权重随机筛选一个元素
+        /// </summary>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        Advertisement GetByWeightedRandom(AdvertiseType type, int? cid = null);
+
+        /// <summary>
+        /// 按权重随机筛选多个元素
+        /// </summary>
+        /// <param name="count">数量</param>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        List<Advertisement> GetsByWeightedRandom(int count, AdvertiseType type, int? cid = null);
+
+        /// <summary>
+        /// 按价格随机筛选一个元素
+        /// </summary>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        Advertisement GetByWeightedPrice(AdvertiseType type, int? cid = null);
+
+        /// <summary>
+        /// 按价格随机筛选多个元素
+        /// </summary>
+        /// <param name="count">数量</param>
+        /// <param name="type">广告类型</param>
+        /// <param name="cid">分类id</param>
+        /// <returns></returns>
+        List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, int? cid = null);
+    }
+}

+ 0 - 1
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IServices.cs

@@ -28,7 +28,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 
     public partial interface ISeminarPostService : IBaseService<SeminarPost> { }
     public partial interface ISeminarPostHistoryVersionService : IBaseService<SeminarPostHistoryVersion> { }
-    public partial interface IBannerService : IBaseService<Banner> { }
     public partial interface IPostMergeRequestService : IBaseService<PostMergeRequest> { }
 
 }

+ 47 - 17
src/Masuit.MyBlogs.Core/Infrastructure/Services/PostService.cs

@@ -1,4 +1,6 @@
-using Masuit.LuceneEFCore.SearchEngine;
+using CacheManager.Core;
+using Masuit.LuceneEFCore.SearchEngine;
+using Masuit.LuceneEFCore.SearchEngine.Extensions;
 using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Masuit.MyBlogs.Core.Common;
 using Masuit.MyBlogs.Core.Infrastructure.Repository.Interface;
@@ -7,7 +9,6 @@ using Masuit.MyBlogs.Core.Models.DTO;
 using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.Extensions.Caching.Memory;
 using PanGu;
 using PanGu.HighLight;
 using System;
@@ -20,28 +21,66 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
 {
     public partial class PostService : BaseService<Post>, IPostService
     {
-        private readonly IMemoryCache _memoryCache;
+        private readonly ICacheManager<SearchResult<PostOutputDto>> _cacheManager;
 
-        public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, IMemoryCache memoryCache) : base(repository, searchEngine, searcher)
+        public PostService(IPostRepository repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher, ICacheManager<SearchResult<PostOutputDto>> cacheManager) : base(repository, searchEngine, searcher)
         {
-            _memoryCache = memoryCache;
+            _cacheManager = cacheManager;
         }
 
         public SearchResult<PostOutputDto> SearchPage(int page, int size, string keyword)
         {
             var cacheKey = $"search:{keyword}:{page}:{size}";
-            if (_memoryCache.TryGetValue<SearchResult<PostOutputDto>>(cacheKey, out var value))
+            if (_cacheManager.Exists(cacheKey))
             {
-                return value;
+                return _cacheManager.Get(cacheKey);
             }
 
             var searchResult = _searchEngine.ScoredSearch<Post>(BuildSearchOptions(page, size, keyword));
-            var posts = searchResult.Results.Select(p => p.Entity.Mapper<PostOutputDto>()).Where(p => p.Status == Status.Pended).ToList();
+            var entities = searchResult.Results.Where(s => s.Entity.Status == Status.Pended).ToList();
+            var ids = entities.Select(s => s.Entity.Id).ToArray();
+            var dic = GetQuery<PostOutputDto>(p => ids.Contains(p.Id)).ToDictionary(p => p.Id);
+            var posts = entities.Select(s =>
+            {
+                var item = s.Entity.Mapper<PostOutputDto>();
+                if (dic.ContainsKey(item.Id))
+                {
+                    item.CategoryName = dic[item.Id].CategoryName;
+                    item.ModifyDate = dic[item.Id].ModifyDate;
+                    item.CommentCount = dic[item.Id].CommentCount;
+                    item.TotalViewCount = dic[item.Id].TotalViewCount;
+                }
+
+                return item;
+            }).ToList();
             var simpleHtmlFormatter = new SimpleHTMLFormatter("<span style='color:red;background-color:yellow;font-size: 1.1em;font-weight:700;'>", "</span>");
             var highlighter = new Highlighter(simpleHtmlFormatter, new Segment()) { FragmentSize = 200 };
             var keywords = _searcher.CutKeywords(keyword);
+            HighlightSegment(posts, keywords, highlighter);
+
+            var result = new SearchResult<PostOutputDto>()
+            {
+                Results = posts,
+                Elapsed = searchResult.Elapsed,
+                Total = searchResult.TotalHits
+            };
+
+            _cacheManager.Add(cacheKey, result);
+            _cacheManager.Expire(cacheKey, TimeSpan.FromHours(1));
+            return result;
+        }
+
+        /// <summary>
+        /// 高亮截取处理
+        /// </summary>
+        /// <param name="posts"></param>
+        /// <param name="keywords"></param>
+        /// <param name="highlighter"></param>
+        private static void HighlightSegment(List<PostOutputDto> posts, List<string> keywords, Highlighter highlighter)
+        {
             foreach (var p in posts)
             {
+                p.Content = p.Content.RemoveHtmlTag();
                 foreach (var s in keywords)
                 {
                     string frag;
@@ -69,15 +108,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
                     p.Content = p.Content.Substring(0, 200);
                 }
             }
-
-            var result = new SearchResult<PostOutputDto>()
-            {
-                Results = posts,
-                Elapsed = searchResult.Elapsed,
-                Total = searchResult.TotalHits
-            };
-
-            return _memoryCache.Set(cacheKey, result, TimeSpan.FromHours(1));
         }
 
         private static SearchOptions BuildSearchOptions(int page, int size, string keyword)

+ 0 - 6
src/Masuit.MyBlogs.Core/Infrastructure/Services/Services.cs

@@ -102,12 +102,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
         {
         }
     }
-    public partial class BannerService : BaseService<Banner>, IBannerService
-    {
-        public BannerService(IBaseRepository<Banner> repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher) : base(repository, searchEngine, searcher)
-        {
-        }
-    }
     public partial class PostMergeRequestService : BaseService<PostMergeRequest>, IPostMergeRequestService
     {
         public PostMergeRequestService(IBaseRepository<PostMergeRequest> repository, ISearchEngine<DataContext> searchEngine, ILuceneIndexSearcher searcher) : base(repository, searchEngine, searcher)

+ 3 - 3
src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj

@@ -56,7 +56,7 @@
     <PackageReference Include="CacheManager.Serialization.Json" Version="2.0.0-beta-1629" />
     <PackageReference Include="CSRedisCore" Version="3.1.10" />
     <PackageReference Include="EFSecondLevelCache.Core" Version="2.8.1" />
-    <PackageReference Include="Hangfire" Version="1.7.6" />
+    <PackageReference Include="Hangfire" Version="1.7.7" />
     <PackageReference Include="Hangfire.Autofac" Version="2.3.1" />
     <PackageReference Include="Hangfire.MemoryStorage" Version="1.6.3" />
     <PackageReference Include="Hangfire.Redis.StackExchange" Version="1.8.0" />
@@ -70,8 +70,8 @@
     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
     <PackageReference Include="WilderMinds.RssSyndication" Version="1.6.0" />
     <PackageReference Include="WinInsider.System.Net.Http.Formatting" Version="1.0.5" />
-    <PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="3.0.9" />
-    <PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="3.0.14" />
+    <PackageReference Include="Z.EntityFramework.Extensions.EFCore" Version="3.0.12" />
+    <PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="3.0.17" />
     <PackageReference Include="Z.ExtensionMethods" Version="2.1.1" />
   </ItemGroup>
 

+ 53 - 0
src/Masuit.MyBlogs.Core/Models/DTO/AdvertisementInputDto.cs

@@ -0,0 +1,53 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace Masuit.MyBlogs.Core.Models.DTO
+{
+    public class AdvertisementInputDto : BaseDto
+    {
+        /// <summary>
+        /// 标题
+        /// </summary>
+        [Required(ErrorMessage = "标题不能为空"), MinLength(10, ErrorMessage = "标题建议设置为10-128字"), MaxLength(128, ErrorMessage = "标题建议设置为10-128字")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 宣传图片
+        /// </summary>
+        public string ImageUrl { get; set; }
+
+        /// <summary>
+        /// 小宣传图片
+        /// </summary>
+        public string ThumbImgUrl { get; set; }
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        [Required(ErrorMessage = "描述文字不能为空"), MinLength(50, ErrorMessage = "标题建议设置为50-1000字"), MaxLength(1000, ErrorMessage = "标题建议设置为50-1000字")]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// 广告url
+        /// </summary>
+        [Required(ErrorMessage = "推广链接不能为空")]
+        public string Url { get; set; }
+
+        /// <summary>
+        /// 权重
+        /// </summary>
+        public int Weight { get; set; }
+
+        /// <summary>
+        /// 价格
+        /// </summary>
+        public decimal Price { get; set; }
+
+        /// <summary>
+        /// 广告类型
+        /// </summary>
+        [Required(ErrorMessage = "类型不能为空,至少需要选择一个类型")]
+        public string Types { get; set; }
+
+        public string CategoryId { get; set; }
+    }
+}

+ 78 - 0
src/Masuit.MyBlogs.Core/Models/Entity/Advertisement.cs

@@ -0,0 +1,78 @@
+using Masuit.MyBlogs.Core.Models.Enum;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Masuit.MyBlogs.Core.Models.Entity
+{
+    [Table("Advertisement")]
+    public class Advertisement : BaseEntity
+    {
+        public Advertisement()
+        {
+            Status = Status.Available;
+        }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        [Required(ErrorMessage = "标题不能为空"), MinLength(10, ErrorMessage = "标题建议设置为10-128字"), MaxLength(128, ErrorMessage = "标题建议设置为10-128字")]
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 宣传图片
+        /// </summary>
+        public string ImageUrl { get; set; }
+
+        /// <summary>
+        /// 小宣传图片
+        /// </summary>
+        public string ThumbImgUrl { get; set; }
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        [Required(ErrorMessage = "描述文字不能为空"), MinLength(50, ErrorMessage = "标题建议设置为50-1000字"), MaxLength(1000, ErrorMessage = "标题建议设置为50-1000字")]
+        public string Description { get; set; }
+
+        /// <summary>
+        /// 广告url
+        /// </summary>
+        [Required(ErrorMessage = "推广链接不能为空")]
+        public string Url { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime CreateTime { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public DateTime UpdateTime { get; set; }
+
+        /// <summary>
+        /// 权重
+        /// </summary>
+        public int Weight { get; set; }
+
+        /// <summary>
+        /// 价格
+        /// </summary>
+        public decimal Price { get; set; }
+
+        /// <summary>
+        /// 广告类型
+        /// </summary>
+        [Required(ErrorMessage = "类型不能为空,至少需要选择一个类型")]
+        public string Types { get; set; }
+
+        /// <summary>
+        /// 访问次数
+        /// </summary>
+        public int ViewCount { get; set; }
+
+        public int? CategoryId { get; set; }
+        public virtual Category Category { get; set; }
+    }
+}

+ 1 - 0
src/Masuit.MyBlogs.Core/Models/Entity/Category.cs

@@ -29,5 +29,6 @@ namespace Masuit.MyBlogs.Core.Models.Entity
 
         public virtual ICollection<Post> Post { get; set; }
         public virtual ICollection<PostHistoryVersion> PostHistoryVersion { get; set; }
+        public virtual ICollection<Advertisement> Advertisements { get; set; }
     }
 }

+ 9 - 9
src/Masuit.MyBlogs.Core/Models/Entity/Donate.cs

@@ -4,48 +4,48 @@ using System.ComponentModel.DataAnnotations.Schema;
 namespace Masuit.MyBlogs.Core.Models.Entity
 {
     /// <summary>
-    /// 捐赠
+    /// 打赏
     /// </summary>
     [Table("Donate")]
     public class Donate : BaseEntity
     {
         /// <summary>
-        /// 捐赠
+        /// 打赏
         /// </summary>
         public string NickName { get; set; }
 
         /// <summary>
-        /// 捐赠人邮箱
+        /// 打赏人邮箱
         /// </summary>
         public string Email { get; set; }
 
         /// <summary>
-        /// 捐赠人的QQ或微信
+        /// 打赏人的QQ或微信
         /// </summary>
         public string QQorWechat { get; set; }
 
         /// <summary>
-        /// 捐赠人的显式邮箱
+        /// 打赏人的显式邮箱
         /// </summary>
         public string EmailDisplay { get; set; }
 
         /// <summary>
-        /// 捐赠人的显式QQ或微信
+        /// 打赏人的显式QQ或微信
         /// </summary>
         public string QQorWechatDisplay { get; set; }
 
         /// <summary>
-        /// 捐赠金额
+        /// 打赏金额
         /// </summary>
         public string Amount { get; set; }
 
         /// <summary>
-        /// 捐赠途径
+        /// 打赏途径
         /// </summary>
         public string Via { get; set; }
 
         /// <summary>
-        /// 捐赠时间
+        /// 打赏时间
         /// </summary>
         public DateTime DonateTime { get; set; }
     }

+ 19 - 0
src/Masuit.MyBlogs.Core/Models/Enum/AdvertiseType.cs

@@ -0,0 +1,19 @@
+using System.ComponentModel;
+
+namespace Masuit.MyBlogs.Core.Models.Enum
+{
+    /// <summary>
+    /// 广告类型
+    /// </summary>
+    public enum AdvertiseType
+    {
+        [Description("首页头图")]
+        Banner = 1,
+        [Description("文章列表")]
+        PostList = 2,
+        [Description("边栏")]
+        SideBar = 3,
+        [Description("内页")]
+        InPage = 4
+    }
+}

+ 69 - 0
src/Masuit.MyBlogs.Core/Models/ViewModel/AdvertisementViewModel.cs

@@ -0,0 +1,69 @@
+using Masuit.MyBlogs.Core.Models.Entity;
+using System;
+
+namespace Masuit.MyBlogs.Core.Models.ViewModel
+{
+    /// <summary>
+    /// 
+    /// </summary>
+    public class AdvertisementViewModel : BaseEntity
+    {
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 宣传图片
+        /// </summary>
+        public string ImageUrl { get; set; }
+
+        /// <summary>
+        /// 小宣传图片
+        /// </summary>
+        public string ThumbImgUrl { get; set; }
+
+        /// <summary>
+        /// 描述
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// 广告url
+        /// </summary>
+        public string Url { get; set; }
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime CreateTime { get; set; }
+
+        /// <summary>
+        /// 更新时间
+        /// </summary>
+        public DateTime UpdateTime { get; set; }
+
+        /// <summary>
+        /// 权重
+        /// </summary>
+        public int Weight { get; set; }
+
+        /// <summary>
+        /// 价格
+        /// </summary>
+        public decimal Price { get; set; }
+
+        /// <summary>
+        /// 广告类型
+        /// </summary>
+        public string Types { get; set; }
+
+        /// <summary>
+        /// 访问次数
+        /// </summary>
+        public int ViewCount { get; set; }
+
+        public int? CategoryId { get; set; }
+        public string CategoryName { get; set; }
+    }
+}

+ 11 - 1
src/Masuit.MyBlogs.Core/Models/ViewModel/IndexPageViewModel.cs

@@ -48,6 +48,16 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// <summary>
         /// banner文章
         /// </summary>
-        public List<Banner> Banner { get; set; }
+        public List<Advertisement> Banner { get; set; }
+
+        /// <summary>
+        /// 边栏广告
+        /// </summary>
+        public List<Advertisement> SidebarAds { get; set; }
+
+        /// <summary>
+        /// 列表内广告
+        /// </summary>
+        public Advertisement ListAdvertisement { get; set; }
     }
 }

+ 0 - 1
src/Masuit.MyBlogs.Core/Startup.cs

@@ -66,7 +66,6 @@ namespace Masuit.MyBlogs.Core
             AppConfig.BaiduAK = configuration[nameof(AppConfig.BaiduAK)];
             AppConfig.Redis = configuration[nameof(AppConfig.Redis)];
             configuration.Bind("Imgbed:AliyunOSS", AppConfig.AliOssConfig);
-            configuration.Bind("Imgbed:Gitee", AppConfig.GiteeConfig);
             configuration.Bind("Imgbed:Gitlabs", AppConfig.GitlabConfigs);
         }
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Error/AccessDeny.cshtml

@@ -50,7 +50,7 @@
 <body id="main-scrollbar" data-scrollbar>
     <div class="container">
         <header id="header" class="page-header text-center">
-            <h1><a href="/">Oops!</a></h1>
+            <h1>Oops!</h1>
         </header>
         <div id="container">
             <h3 class="margintop20">

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Error/ComingSoon.cshtml

@@ -63,7 +63,7 @@
 <body id="main-scrollbar" data-scrollbar>
     <div class="container">
         <header id="header" class="page-header text-center">
-            <h1><a href="/">Oops!</a></h1>
+            <h1>Oops!</h1>
         </header>
         <div id="container">
             <img class="img img-responsive img-thumbnail" width="100%" src="https://gitee.com/masuit_admin/imgbed/raw/master/20190608/ComingSoon.jpg" alt="@ViewBag.Keywords" />

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Error/TempDeny.cshtml

@@ -49,7 +49,7 @@
 <body id="main-scrollbar" data-scrollbar>
     <div class="container">
         <header id="header" class="page-header text-center">
-            <h1><a href="/">Oops!</a></h1>
+            <h1>Oops!</h1>
         </header>
         <div id="container">
             <h3 class="margintop20">

+ 2 - 4
src/Masuit.MyBlogs.Core/Views/Home/Index.cshtml

@@ -2,9 +2,7 @@
 @using Masuit.MyBlogs.Core.Models.DTO
 @using Masuit.MyBlogs.Core.Models.Entity
 @using Masuit.MyBlogs.Core.Models.ViewModel
-@using Masuit.MyBlogs.Core.Extensions
 @using Masuit.Tools.Core.Net
-@using Masuit.Tools.Win32
 @using Microsoft.AspNetCore.Http
 @model Masuit.MyBlogs.Core.Models.ViewModel.IndexPageViewModel
 @{
@@ -50,7 +48,7 @@
                             <div class="slide-text @style[r.Next(style.Length)]">
                                 <h2 data-animation="animated @ani[r.Next(ani.Length)]">@p.Title</h2>
                                 <p data-animation="animated @ani[r.Next(ani.Length)]">@p.Description</p>
-                                <a href="@p.Url" target="_blank" class="btn btn-default" data-animation="animated @ani[r.Next(ani.Length)]">查看详情</a>
+                                <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@p.Id" target="_blank" class="btn btn-default" data-animation="animated @ani[r.Next(ani.Length)]">查看详情</a>
                             </div>
                         </div>
                     </div>
@@ -73,7 +71,7 @@
 @if (Model.Notices.Any())
 {
     <div class="container notice">
-        <a href="/n/@(Model.Notices.FirstOrDefault()?.Id)">
+        <a asp-controller="Notice" asp-action="Details" asp-route-id="@(Model.Notices.FirstOrDefault()?.Id)">
             <h3 class="size18 text-red text-center">网站最新公告</h3>
         </a>
         @Html.Raw(Model.Notices.FirstOrDefault()?.Content.Replace("img src=", $"img title='{CommonHelper.SystemSettings["Title"]}' alt='{CommonHelper.SystemSettings["Title"]}' data-original="))

+ 7 - 7
src/Masuit.MyBlogs.Core/Views/Misc/Donate.cshtml

@@ -2,7 +2,7 @@
 @model List<Masuit.MyBlogs.Core.Models.Entity.Donate>
 
 @{
-    ViewBag.Title = "网站捐赠";
+    ViewBag.Title = "网站打赏";
     Layout = "~/Views/Shared/_Layout.cshtml";
     Random r = new Random();
 }
@@ -68,7 +68,7 @@
             </div>
             <img class="img-responsive img-thumbnail" style="width: 100%" src="@CommonHelper.SystemSettings["DonateWechat"]" />
             <div class="protected">
-                <span class="text-red size20">注意:支付宝和微信无法看见支付人的名称,所以捐赠时最好附上完整个人信息,否则无法将您加入到赞助名单中。</span>
+                <span class="text-red size20">注意:支付宝和微信无法看见支付人的名称,所以打赏时最好附上完整个人信息,否则无法将您加入到赞助名单中。</span>
             </div>
         </div>
         <div class="col-md-3">
@@ -103,10 +103,10 @@
             本网站由博主一个人打造,开发和运营均为博主本人,如果您是真心喜欢本博客,您可以对博主表示一下感谢,以支持后期发布更多好资源,5毛也好、1元也罢,都是你们的心意。网站运营也需要成本,有你的援助,一切会更好。
         </p>
         <p class="text-red size20">
-            捐赠时最好能够附上你的完整信息,包括:您的昵称或真名、邮箱地址、QQ或微信、金额等;因为不能保证能够完全的清楚捐赠者的基本信息,如果你完成了捐赠,推荐你在网站留言板备注一下,或者QQ私信告知你是谁。
+            打赏时最好能够附上你的完整信息,包括:您的昵称或真名、邮箱地址、QQ或微信、金额等;因为不能保证能够完全的清楚打赏者的基本信息,如果你完成了打赏,推荐你在网站留言板备注一下,或者QQ私信告知你是谁。
         </p>
         <p>
-            您可以尽您所能地捐赠,如果您临时改变主意,您可以在事后一个月之内联系作者申请退款!
+            您可以尽您所能地打赏,如果您临时改变主意,您可以在事后一个月之内联系作者申请退款!
         </p>
         <p>
             本站的宗旨是:互联网分享精神,乐于发现,勤于分享;
@@ -119,12 +119,12 @@
 <div class="container-fluid" ng-app="myApp" ng-controller="home as list">
     <div class="page-header margin-clear">
         <h2 class="size24">
-            捐赠名单(排名不分先后):
+            打赏名单(排名不分先后):
         </h2>
     </div>
     <table ng-cloak ng-table="list.tableParams" class="table table-bordered table-hover table-condensed margin-clear" ng-form="list.tableForm" disable-filter="list.isAdding" tracked-table="list.tableTracker" style="margin: 0">
         <tr ng-repeat="row in $data" ng-form="rowForm" tracked-table-row="row">
-            <td title="'捐赠时间'" sortable="'DonateTime'">
+            <td title="'打赏时间'" sortable="'DonateTime'">
                 {{row.DonateTime|date:'yyyy-MM-dd'}}
             </td>
             <td title="'昵称'" sortable="'NickName'">
@@ -139,7 +139,7 @@
             <td title="'金额'" sortable="'Amount'">
                 {{row.Amount}}
             </td>
-            <td title="'捐赠方式'" sortable="'Via'">
+            <td title="'打赏方式'" sortable="'Via'">
                 {{row.Via}}
             </td>
         </tr>

+ 12 - 12
src/Masuit.MyBlogs.Core/Views/Misc/Donate_Admin.cshtml

@@ -2,7 +2,7 @@
 @model List<Masuit.MyBlogs.Core.Models.Entity.Donate>
 
 @{
-    ViewBag.Title = "网站捐赠";
+    ViewBag.Title = "网站打赏";
     Layout = "~/Views/Shared/_Layout.cshtml";
     Random r = new Random();
 }
@@ -105,10 +105,10 @@
             本网站由博主一个人打造,开发和运营均为博主本人,如果您是真心喜欢本博客,您可以对博主表示一下感谢,以支持后期发布更多好资源,5毛也好、1元也罢,都是你们的心意。网站运营也需要成本,有你的援助,一切会更好。
         </p>
         <p class="text-red size20">
-            捐赠时最好能够附上你的完整信息,包括:您的昵称或真名、邮箱地址、QQ或微信、金额等;因为不能保证能够完全的清楚捐赠者的基本信息,如果你完成了捐赠,推荐你在网站留言板备注一下,或者QQ私信告知你是谁。
+            打赏时最好能够附上你的完整信息,包括:您的昵称或真名、邮箱地址、QQ或微信、金额等;因为不能保证能够完全的清楚打赏者的基本信息,如果你完成了打赏,推荐你在网站留言板备注一下,或者QQ私信告知你是谁。
         </p>
         <p>
-            您可以尽您所能地捐赠,如果您临时改变主意,您可以在事后一个月之内联系作者申请退款!
+            您可以尽您所能地打赏,如果您临时改变主意,您可以在事后一个月之内联系作者申请退款!
         </p>
         <p>
             本站的宗旨是:互联网分享精神,乐于发现,勤于分享;
@@ -121,13 +121,13 @@
 <div class="container-fluid" ng-app="myApp" ng-controller="home as list">
     <div class="page-header margin-clear">
         <h2 class="size24" style="display: inline">
-            捐赠名单(排名不分先后):
+            打赏名单(排名不分先后):
         </h2>
-        <button class="btn btn-info pull-right" ng-click="save()">添加捐赠</button>
+        <button class="btn btn-info pull-right" ng-click="save()">添加打赏</button>
     </div>
     <table ng-table="list.tableParams" class="table table-bordered table-hover table-condensed margin-clear" ng-form="list.tableForm" disable-filter="list.isAdding" tracked-table="list.tableTracker">
         <tr ng-repeat="row in $data" ng-form="rowForm" tracked-table-row="row">
-            <td title="'捐赠时间'" filter="{DonateTime: 'text'}" sortable="'DonateTime'">
+            <td title="'打赏时间'" filter="{DonateTime: 'text'}" sortable="'DonateTime'">
                 {{row.DonateTime|date:'yyyy-MM-dd'}}
             </td>
             <td title="'昵称'" filter="{NickName: 'text'}" sortable="'NickName'">
@@ -136,7 +136,7 @@
             <td title="'金额'" filter="{Amount: 'number'}" sortable="'Amount'">
                 {{row.Amount}}
             </td>
-            <td title="'捐赠方式'" filter="{Via: 'text'}" sortable="'Via'">
+            <td title="'打赏方式'" filter="{Via: 'text'}" sortable="'Via'">
                 {{row.Via}}
             </td>
             <td title="'Email'" filter="{Email: 'text'}" sortable="'Email'">
@@ -204,7 +204,7 @@
         };
         self.del = function (row) {
             swal({
-                title: "确认删除这条捐赠记录吗?",
+                title: "确认删除这条打赏记录吗?",
                 text: row.NickName,
                 showCancelButton: true,
                 confirmButtonColor: "#DD6B55",
@@ -249,12 +249,12 @@
                 };
             }
             swal({
-                title: '添加捐赠记录',
+                title: '添加打赏记录',
                 html:
                     '<div class="input-group"><span class="input-group-addon">昵称: </span><input type="text" id="name" class="form-control input-lg" placeholder="请输入昵称" value="' + row.NickName + '"></div>' +
-                    '<div class="input-group"><span class="input-group-addon">捐赠时间: </span><input id="date" type="text" class="form-control input-lg date datainp dateicon" readonly placeholder="请输入捐赠时间" value="' + row.DonateTime + '"></div>	' +
-                    '<div class="input-group"><span class="input-group-addon">捐赠金额: </span><input id="amount" type="text" class="form-control input-lg" placeholder="请输入金额" value="' + row.Amount + '"></div>' +
-                    '<div class="input-group"><span class="input-group-addon">捐赠方式: </span><input id="via" type="text" class="form-control input-lg" placeholder="请输入捐赠方式" value="' + row.Via + '"></div>' +
+                    '<div class="input-group"><span class="input-group-addon">打赏时间: </span><input id="date" type="text" class="form-control input-lg date datainp dateicon" readonly placeholder="请输入打赏时间" value="' + row.DonateTime + '"></div>	' +
+                    '<div class="input-group"><span class="input-group-addon">打赏金额: </span><input id="amount" type="text" class="form-control input-lg" placeholder="请输入金额" value="' + row.Amount + '"></div>' +
+                    '<div class="input-group"><span class="input-group-addon">打赏方式: </span><input id="via" type="text" class="form-control input-lg" placeholder="请输入打赏方式" value="' + row.Via + '"></div>' +
                     '<div class="input-group"><span class="input-group-addon">Email: </span><input type="email" id="email" class="form-control input-lg" placeholder="请输入Email" value="' + row.Email + '"></div>' +
                     '<div class="input-group"><span class="input-group-addon">QQ或微信: </span><input type="text" id="qq" class="form-control input-lg" placeholder="请输入QQ或微信" value="' + row.QQorWechat + '"></div>' +
                     '<div class="input-group"><span class="input-group-addon">显示Email: </span><input type="text" id="demail" class="form-control input-lg" placeholder="请输入显示Email" value="' + row.EmailDisplay + '"></div>' +

+ 1 - 1
src/Masuit.MyBlogs.Core/Views/Notice/Details.cshtml

@@ -57,7 +57,7 @@
                             UserInfoOutputDto user = Context.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo) ?? new UserInfoOutputDto();
                             if (user.IsAdmin)
                             {
-                                <a class="btn btn-primary" href="/dashboard#/notice/[email protected]" target="_blank">修改</a>
+                                <a class="btn btn-primary" asp-controller="Dashboard" asp-action="Index" asp-fragment="/notice/[email protected]" target="_blank">修改</a>
                             }
                         }
                     </div>

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Notice/Index_Admin.cshtml

@@ -12,7 +12,7 @@
     @{
         await Html.RenderPartialAsync("_Pagination");
     }
-    <a href="/dashboard#/notice/index" class="btn btn-info">发布新公告</a>
+    <a asp-controller="Dashboard" asp-action="Index" asp-fragment="/notice/index" class="btn btn-info">发布新公告</a>
     <div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
         @foreach (NoticeOutputDto n in Model)
         {
@@ -28,7 +28,7 @@
                     <div class="panel-body">
                         <div class="btn-group">
                             <a class="btn btn-info" onclick="del(@n.Id,'@n.Title')">删除</a>
-                            <a class="btn btn-danger" href="/dashboard#/notice/[email protected]" target="_blank">修改</a>
+                            <a class="btn btn-danger" asp-controller="Dashboard" asp-action="Index" asp-fragment="/notice/[email protected]" target="_blank">修改</a>
                         </div>
                         @Html.Raw(n.Content)
                     </div>

+ 3 - 3
src/Masuit.MyBlogs.Core/Views/Post/All.cshtml

@@ -109,7 +109,7 @@
                     @{
                         foreach (var g in cats)
                         {
-                            <a href="/cat/@g.Id" class="label label-@colors[r.Next(colors.Length)]">@g.Name (@(g.Count)篇)</a>
+                            <a asp-controller="Home" asp-action="Category" asp-route-id="@g.Id" class="label label-@colors[r.Next(colors.Length)]">@g.Name (@(g.Count)篇)</a>
                         }
                     }
                 </p>
@@ -119,7 +119,7 @@
                     @{
                         foreach (var g in tags)
                         {
-                            <a href="/tag/@HttpUtility.UrlEncode(g.Key)" class="label label-@colors[r.Next(colors.Length)]">@g.Key (@g.Count()篇)</a>
+                            <a asp-controller="Home" asp-action="Tag" asp-route-id="@g.Key" class="label label-@colors[r.Next(colors.Length)]">@g.Key (@g.Count()篇)</a>
                         }
                     }
                 </p>
@@ -129,7 +129,7 @@
                     @{
                         foreach (var g in seminars)
                         {
-                            <a href="/c/@g.Id" class="label label-@colors[r.Next(colors.Length)]">@g.Name (@(g.Count)篇)</a>
+                            <a asp-controller="Seminar" asp-action="Index" asp-route-id="@g.Id" class="label label-@colors[r.Next(colors.Length)]">@g.Name (@(g.Count)篇)</a>
                         }
                     }
                 </p>

+ 60 - 2
src/Masuit.MyBlogs.Core/Views/Post/CompareVersion.cshtml

@@ -1,10 +1,12 @@
 @using System.Web
 @using Masuit.MyBlogs.Core.Common
+@using Masuit.MyBlogs.Core.Models.Entity
 @model Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion[]
 @{
     ViewBag.Title = Model[0].Title + "版本对比";
     Layout = "~/Views/Shared/_Layout.cshtml";
     string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
+    List<Advertisement> ads = ViewBag.Ads;
 }
 <style>
     ins {
@@ -67,7 +69,7 @@
                                                         {
                                                             if (!string.IsNullOrEmpty(s))
                                                             {
-                                                                <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                                <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                                     <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                                 </a>
                                                             }
@@ -82,6 +84,34 @@
                                     @Html.Raw(Model[1].Content.ReplaceImgAttribute())
                                 </article>
                             </section>
+                            @if (ads.Any())
+                            {
+                                <section class="protected">
+                                    <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ads[0].Id" target="_blank">
+                                        <h3>
+                                            @ads[0].Title
+                                            <span class="text-red">[推广]</span>
+                                        </h3>
+
+                                        <div class="row padding-bot10">
+                                            @{
+                                                string imgSrc = ads[0].ThumbImgUrl;
+                                                if (!string.IsNullOrEmpty(imgSrc))
+                                                {
+                                                    <div class="col-md-3">
+                                                        <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ads[0].Title" title="@ads[0].Title ">
+                                                    </div>
+                                                }
+                                            }
+                                            <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                                <p>
+                                                    @ads[0].Description
+                                                </p>
+                                            </div>
+                                        </div>
+                                    </a>
+                                </section>
+                            }
                         </main>
                     </div>
                 </div>
@@ -125,7 +155,7 @@
                                                         {
                                                             if (!string.IsNullOrEmpty(s))
                                                             {
-                                                                <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                                <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                                     <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                                 </a>
                                                             }
@@ -140,6 +170,34 @@
                                     @Html.Raw(Model[2].Content.ReplaceImgAttribute())
                                 </article>
                             </section>
+                            @if (ads.Count > 1)
+                            {
+                                <section class="protected">
+                                    <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ads[1].Id" target="_blank">
+                                        <h3>
+                                            @ads[1].Title
+                                            <span class="text-red">[推广]</span>
+                                        </h3>
+
+                                        <div class="row padding-bot10">
+                                            @{
+                                                string imgSrc = ads[1].ThumbImgUrl;
+                                                if (!string.IsNullOrEmpty(imgSrc))
+                                                {
+                                                    <div class="col-md-3">
+                                                        <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ads[1].Title" title="@ads[1].Title ">
+                                                    </div>
+                                                }
+                                            }
+                                            <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                                <p>
+                                                    @ads[1].Description
+                                                </p>
+                                            </div>
+                                        </div>
+                                    </a>
+                                </section>
+                            }
                         </main>
                     </div>
                 </div>

+ 31 - 3
src/Masuit.MyBlogs.Core/Views/Post/Details.cshtml

@@ -1,5 +1,4 @@
-@using System.Text.RegularExpressions
-@using System.Web
+@using System.Web
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.MyBlogs.Core.Configs
 @using Masuit.MyBlogs.Core.Models.Entity
@@ -13,6 +12,7 @@
     Layout = "~/Views/Shared/_Layout.cshtml";
     string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
     string cid = string.IsNullOrEmpty(Context.Request.Query["cid"]) ? "0" : Context.Request.Query["cid"].ToString();
+    Advertisement ad = ViewBag.Ads;
 }
 <link href="~/Assets/layui/css/layui.css" rel="stylesheet" />
 <link href="~/Assets/jquery.tocify/jquery.tocify.min.css" rel="stylesheet" />
@@ -58,7 +58,7 @@
                                                 {
                                                     if (!string.IsNullOrEmpty(s))
                                                     {
-                                                        <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                         </a>
                                                     }
@@ -131,6 +131,34 @@
                         <div id="cyReward" role="cylabs" data-use="reward"></div>
                     </section>
                 </main>
+                @if (ad != null)
+                {
+                    <section class="protected">
+                        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ad.Id" target="_blank">
+                            <h3>
+                                @ad.Title
+                                <span class="text-red">[推广]</span>
+                            </h3>
+
+                            <div class="row padding-bot10">
+                                @{
+                                    string imgSrc = ad.ThumbImgUrl;
+                                    if (!string.IsNullOrEmpty(imgSrc))
+                                    {
+                                        <div class="col-md-3">
+                                            <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ad.Title" title="@ad.Title ">
+                                        </div>
+                                    }
+                                }
+                                <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                    <p>
+                                        @ad.Description
+                                    </p>
+                                </div>
+                            </div>
+                        </a>
+                    </section>
+                }
                 <section class="wow row padding-top40 padding-bot20 animated fadeIn">
                     <div class="col-xs-6">
                         <div class="btn-group">

+ 32 - 2
src/Masuit.MyBlogs.Core/Views/Post/Details_Admin.cshtml

@@ -1,6 +1,7 @@
 @using System.Web
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.MyBlogs.Core.Models.DTO
+@using Masuit.MyBlogs.Core.Models.Entity
 @using Masuit.MyBlogs.Core.Models.Enum
 @using Masuit.MyBlogs.Core.Models.ViewModel
 @using Masuit.Tools.Core.Net
@@ -12,6 +13,7 @@
     string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
     UserInfoOutputDto user = Context.Session.Get<UserInfoOutputDto>(SessionKey.UserInfo);
     string cid = string.IsNullOrEmpty(Context.Request.Query["cid"]) ? "0" : Context.Request.Query["cid"].ToString();
+    Advertisement ad = ViewBag.Ads;
 }
 <style>
     #gooey-h {
@@ -66,7 +68,7 @@
                                                 {
                                                     if (!string.IsNullOrEmpty(s))
                                                     {
-                                                        <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                         </a>
                                                     }
@@ -108,6 +110,34 @@
                         <div id="cyReward" role="cylabs" data-use="reward"></div>
                     </section>
                 </main>
+                @if (ad != null)
+                {
+                    <section class="protected">
+                        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ad.Id" target="_blank">
+                            <h3>
+                                @ad.Title
+                                <span class="text-red">[推广]</span>
+                            </h3>
+
+                            <div class="row padding-bot10">
+                                @{
+                                    string imgSrc = ad.ThumbImgUrl;
+                                    if (!string.IsNullOrEmpty(imgSrc))
+                                    {
+                                        <div class="col-md-3">
+                                            <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ad.Title" title="@ad.Title ">
+                                        </div>
+                                    }
+                                }
+                                <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                    <p>
+                                        @ad.Description
+                                    </p>
+                                </div>
+                            </div>
+                        </a>
+                    </section>
+                }
                 <section class="wow fadeIn row padding-top40 padding-bot20">
                     <div class="col-xs-6">
                         <div class="btn-group">
@@ -281,7 +311,7 @@
             <a class="gooey-menu-item" id="pass"> <i class="icon icon-checkmark"></i></a>
         }
     }
-    <a href="/dashboard#/post/[email protected]" class="gooey-menu-item" target="_blank"> <i class="icon icon-pencil"></i></a>
+    <a asp-controller="Dashboard" asp-action="Index" asp-fragment="/post/[email protected]" class="gooey-menu-item" target="_blank"> <i class="icon icon-pencil"></i></a>
     <a class="gooey-menu-item" id="del"> <i class="icon icon-bin"></i></a>
 </nav>
 <script src="https://cdn.staticfile.org/jqueryui/1.12.1/jquery-ui.min.js"></script>

+ 6 - 0
src/Masuit.MyBlogs.Core/Views/Post/History.cshtml

@@ -1,9 +1,11 @@
 @using Masuit.MyBlogs.Core.Models.DTO
+@using Masuit.MyBlogs.Core.Models.Entity
 @model List<Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion>
 @{
     var post = (PostOutputDto)ViewBag.Primary;
     ViewBag.Title = post.Title + "的历史版本";
     Layout = "~/Views/Shared/_Layout.cshtml";
+    Advertisement ad = ViewBag.Ads;
 }
 <div class="container" style="min-height: 70vh">
     <ol class="cd-breadcrumb triangle">
@@ -61,6 +63,10 @@
             await Html.RenderPartialAsync("_Pagination");
         }
     </div>
+    @if (ad != null)
+    {
+        await Html.RenderPartialAsync("_ArticleListAdvertisement", ad);
+    }
 </div>
 <script>
     $(function() {

+ 31 - 4
src/Masuit.MyBlogs.Core/Views/Post/HistoryVersion.cshtml

@@ -1,14 +1,13 @@
-@using System.Text.RegularExpressions
-@using System.Web
+@using System.Web
 @using Masuit.MyBlogs.Core.Common
 @using Masuit.MyBlogs.Core.Models.Entity
 @using Masuit.Tools.Core.Net
-@using Masuit.Tools.Win32
 @model Masuit.MyBlogs.Core.Models.Entity.PostHistoryVersion
 @{
     ViewBag.Title = Model.Post.Title + "于" + Model.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss") + "的历史版本:" + Model.Title;
     Layout = "~/Views/Shared/_Layout.cshtml";
     string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
+    Advertisement ad = ViewBag.Ads;
 }
 <link href="~/Assets/jquery.tocify/jquery.tocify.min.css" rel="stylesheet" />
 <link href="~/Assets/UEditor/third-party/SyntaxHighlighter/styles/shCore.min.css" rel="stylesheet" />
@@ -58,7 +57,7 @@
                                                 {
                                                     if (!string.IsNullOrEmpty(s))
                                                     {
-                                                        <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                         </a>
                                                     }
@@ -149,6 +148,34 @@
                         }
                     </div>
                 </section>
+                @if (ad != null)
+                {
+                    <section class="protected">
+                        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ad.Id" target="_blank">
+                            <h3>
+                                @ad.Title
+                                <span class="text-red">[推广]</span>
+                            </h3>
+
+                            <div class="row padding-bot10">
+                                @{
+                                    string imgSrc = ad.ThumbImgUrl;
+                                    if (!string.IsNullOrEmpty(imgSrc))
+                                    {
+                                        <div class="col-md-3">
+                                            <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ad.Title" title="@ad.Title ">
+                                        </div>
+                                    }
+                                }
+                                <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                    <p>
+                                        @ad.Description
+                                    </p>
+                                </div>
+                            </div>
+                        </a>
+                    </section>
+                }
             </div>
         </div>
     </div>

+ 30 - 1
src/Masuit.MyBlogs.Core/Views/Post/HistoryVersion_Admin.cshtml

@@ -5,6 +5,7 @@
     ViewBag.Title = Model.Post.Title + "于" + Model.ModifyDate.ToString("yyyy-MM-dd HH:mm:ss") + "的历史版本:" + Model.Title;
     Layout = "~/Views/Shared/_Layout.cshtml";
     string[] colors = { "success", "info", "primary", "warning", "danger", "default", "primary" };
+    Advertisement ad = ViewBag.Ads;
 }
 <link href="~/Assets/jquery.tocify/jquery.tocify.min.css" rel="stylesheet" />
 <link href="~/Assets/UEditor/third-party/SyntaxHighlighter/styles/shCore.min.css" rel="stylesheet" />
@@ -58,7 +59,7 @@
                                                 {
                                                     if (!string.IsNullOrEmpty(s))
                                                     {
-                                                        <a href="/tag/@HttpUtility.UrlEncode(s)">
+                                                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s">
                                                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                                                         </a>
                                                     }
@@ -112,6 +113,34 @@
                         }
                     </div>
                 </section>
+                @if (ad != null)
+                {
+                    <section class="protected">
+                        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ad.Id" target="_blank">
+                            <h3>
+                                @ad.Title
+                                <span class="text-red">[推广]</span>
+                            </h3>
+
+                            <div class="row padding-bot10">
+                                @{
+                                    string imgSrc = ad.ThumbImgUrl;
+                                    if (!string.IsNullOrEmpty(imgSrc))
+                                    {
+                                        <div class="col-md-3">
+                                            <img class="img-thumbnail img-responsive thumb" data-original="@imgSrc" alt="@ad.Title" title="@ad.Title ">
+                                        </div>
+                                    }
+                                }
+                                <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                                    <p>
+                                        @ad.Description
+                                    </p>
+                                </div>
+                            </div>
+                        </a>
+                    </section>
+                }
             </div>
         </div>
     </div>

+ 14 - 7
src/Masuit.MyBlogs.Core/Views/Search/Search.cshtml

@@ -1,11 +1,9 @@
 @using System.Web
-@using AngleSharp.Network.Default
 @using Masuit.MyBlogs.Core.Models.DTO
 @using Masuit.Tools
 @model IList<Masuit.MyBlogs.Core.Models.DTO.PostOutputDto>
 @{
     ViewBag.Title = "站内搜索:" + ViewBag.Keyword;
-    var elapsed = ViewBag.Elapsed;
     Layout = "~/Views/Shared/_Layout.cshtml";
     List<KeywordsRankOutputDto> hotSearches = ViewBag.hotSearches;
 }
@@ -21,8 +19,8 @@
                     <div class="ibox-content">
                         @if (!string.IsNullOrEmpty(ViewBag.Keyword))
                         {
-                            <h3>为您找到约 @ViewBag.Total 条<span class="text-navy">“@ViewBag.Keyword”</span>相关结果。</h3>
-                            <small>搜索用时(@elapsed 毫秒)</small>
+                            <h3>为您找到约 @ViewBag.Total 条<span class="text-navy">“@ViewBag.Keyword”</span>匹配或相关的搜索结果。</h3>
+                            <small>搜索用时(@ViewBag.Elapsed 毫秒)</small>
                         }
                         <div class="search-form">
                             <form action="/s" method="get">
@@ -50,14 +48,23 @@
                         {
                             foreach (PostOutputDto p in Model)
                             {
-                                <div class="border padding-10">
+                                <div class="border padding-10 padding-bot5">
                                     <div class="search-result">
                                         <h2 class="size20">
                                             <a asp-controller="Post" asp-action="Details" asp-route-id="@p.Id" asp-route-kw="@ViewBag.Keyword" target="_blank">@Html.Raw(p.Title)</a>
                                         </h2>
-                                        <label class="label label-info">@Html.Raw(p.Author)</label>
-                                        @Html.ActionLink(Context.Request.Scheme + "://" + Context.Request.Host + "/" + p.Id, "Details", "Post", new { id = p.Id }, new { @class = "search-link", target = "_blank" })
+                                        <div>
+                                            作者:<label class="label label-info">@Html.Raw(p.Author)</label> | 发表时间:@p.ModifyDate.ToString("yyyy-MM-dd HH:mm") | 分类:@Html.ActionLink(p.CategoryName, "Category", "Home", new { id = p.CategoryId }, null)
+                                        </div>
                                         <p class="size14">@Html.Raw(p.Content)</p>
+                                        <div class="row">
+                                            <div class="col-md-6">
+                                                @Html.ActionLink(Context.Request.Scheme + "://" + Context.Request.Host + "/" + p.Id, "Details", "Post", new { id = p.Id }, new { @class = "search-link", target = "_blank" })
+                                            </div>
+                                            <div class="col-md-6 pull-right text-right">
+                                                @p.CommentCount 评论 | @p.TotalViewCount 浏览
+                                            </div>
+                                        </div>
                                     </div>
                                 </div>
                                 <div class="hr-line-dashed"></div>

+ 29 - 0
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListAdvertisement.cshtml

@@ -0,0 +1,29 @@
+@model Masuit.MyBlogs.Core.Models.Entity.Advertisement
+<div class="ibox">
+    <div class="ibox-content">
+        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@Model.Id" target="_blank">
+            <h4>
+                <i class="text-red size18">[荐]</i>
+                @Model.Title
+            </h4>
+        </a>
+        <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@Model.Id" target="_blank">
+            <div class="row padding-bot10">
+                @{
+                    string imgSrc = Model.ThumbImgUrl;
+                    if (!string.IsNullOrEmpty(imgSrc))
+                    {
+                        <div class="col-md-3">
+                            <img class="img-thumbnail img-responsive thumb" original="@imgSrc" alt="@Model.Title" title="@Model.Title">
+                        </div>
+                    }
+                }
+                <div class="col-md-@(string.IsNullOrEmpty(imgSrc) ? 12 : 9)">
+                    <p>
+                        @Model.Description
+                    </p>
+                </div>
+            </div>
+        </a>
+    </div>
+</div>

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem.cshtml

@@ -7,7 +7,7 @@
 @model Masuit.MyBlogs.Core.Models.DTO.PostOutputDto
 <div class="ibox wow fadeIn">
     <div class="ibox-content">
-        <a href="/@Model.Id" target="_blank">
+        <a asp-controller="Post" asp-action="Details" asp-route-id="@Model.Id" target="_blank">
             <h4>
                 @if (Model.IsFixedTop)
                 {
@@ -49,7 +49,7 @@
                 <div class="col-md-6">
                     @foreach (string s in Model.Label.Split(',', ','))
                     {
-                        <a href="/tag/@HttpUtility.UrlEncode(s)" target="_blank">
+                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s" target="_blank">
                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                         </a>
                     }

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListItem_Admin.cshtml

@@ -7,7 +7,7 @@
 @model Masuit.MyBlogs.Core.Models.DTO.PostOutputDto
 <div class="ibox wow fadeIn">
     <div class="ibox-content">
-        <a href="/@Model.Id" target="_blank">
+        <a asp-controller="Post" asp-action="Details" asp-route-id="@Model.Id" target="_blank">
             <h4>
                 @if (Model.IsFixedTop)
                 {
@@ -51,7 +51,7 @@
                 <div class="col-md-5">
                     @foreach (string s in Model.Label.Split(',', ','))
                     {
-                        <a href="/tag/@HttpUtility.UrlEncode(s)" target="_blank">
+                        <a asp-controller="Home" asp-action="Tag" asp-route-id="@s" target="_blank">
                             <span class="label label-@colors[new Random().Next() % colors.Length]">@s</span>
                         </a>
                     }

+ 9 - 2
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial.cshtml

@@ -40,14 +40,21 @@
                 </div>
             </div>
 
-            foreach (PostOutputDto post in Model.Posts)
+            var rnd = new Random().Next(Model.Posts.Count);
+            for (var index = 0; index < Model.Posts.Count; index++)
             {
+                if (rnd == index && Model.ListAdvertisement != null)
+                {
+                    await Html.RenderPartialAsync("_ArticleListAdvertisement", Model.ListAdvertisement);
+                }
+
+                var post = Model.Posts[index];
                 await Html.RenderPartialAsync("_ArticleListItem", post);
             }
         }
         else
         {
-            List<PostOutputDto> list = Model.PostsQueryable.OrderBy(p => Guid.NewGuid()).Skip(0).Take(5).ToList();
+            var list = Model.PostsQueryable.OrderBy(p => Guid.NewGuid()).Skip(0).Take(5).ToList();
             if (list.Any())
             {
                 <div class="page-header">

+ 21 - 8
src/Masuit.MyBlogs.Core/Views/Shared/_ArticleListPartial_Admin.cshtml

@@ -2,7 +2,8 @@
 @model Masuit.MyBlogs.Core.Models.ViewModel.IndexPageViewModel
 <div class="col-md-9 box-left">
     <div class="wrapper-content  animated fadeIn blog">
-        @if(Model.Posts.Any()) {
+        @if (Model.Posts.Any())
+        {
             <div class="orderby">
                 <div class="row">
                     <div class="col-md-9 text-center">
@@ -10,7 +11,7 @@
                     </div>
                     <div class="col-md-3">
                         <div class="btn-group pull-right">
-                            <a class="btn btn-info" href="/dashboard#/writeblog" target="_blank">
+                            <a class="btn btn-info" asp-controller="Dashboard" asp-action="Index" asp-fragment="/writeblog" target="_blank">
                                 <i class="icon icon-pencil"></i>
                                 写文章
                             </a>
@@ -43,16 +44,28 @@
                 </div>
             </div>
 
-            foreach(PostOutputDto post in Model.Posts) {
+            var rnd = new Random().Next(Model.Posts.Count);
+            for (var index = 0; index < Model.Posts.Count; index++)
+            {
+                if (rnd == index && Model.ListAdvertisement != null)
+                {
+                    await Html.RenderPartialAsync("_ArticleListAdvertisement", Model.ListAdvertisement);
+                }
+
+                var post = Model.Posts[index];
                 await Html.RenderPartialAsync("_ArticleListItem_Admin", post);
             }
-        } else {
-            List<PostOutputDto> list = Model.PostsQueryable.OrderBy(p => Guid.NewGuid()).Skip(0).Take(5).ToList();
-            if(list.Any()) {
+        }
+        else
+        {
+            var list = Model.PostsQueryable.OrderBy(p => Guid.NewGuid()).Skip(0).Take(5).ToList();
+            if (list.Any())
+            {
                 <div class="page-header">
-                    <h3>Oops!抱歉~没有找到相关的文章或资源!如果您有相关的结果,您可以 @Html.ActionLink("点击这里", "Publish", "Post", null, new {@class = "btn btn-info btn-lg"}) 投稿哦!以下是一些随机推荐:</h3>
+                    <h3>Oops!抱歉~没有找到相关的文章或资源!如果您有相关的结果,您可以 @Html.ActionLink("点击这里", "Publish", "Post", null, new { @class = "btn btn-info btn-lg" }) 投稿哦!以下是一些随机推荐:</h3>
                 </div>
-                foreach(PostOutputDto post in list) {
+                foreach (PostOutputDto post in list)
+                {
                     await Html.RenderPartialAsync("_ArticleListItem_Admin", post);
                 }
             }

+ 19 - 21
src/Masuit.MyBlogs.Core/Views/Shared/_Aside.cshtml

@@ -71,6 +71,21 @@
             </div>
         </article>
     </section>
+    @foreach (var ad in Model.SidebarAds)
+    {
+        <section>
+            <article class="panel panel-danger">
+                <div class="panel-heading">
+                    @ad.Title
+                </div>
+                <div class="panel-body">
+                    <a asp-controller="Advertisement" asp-action="Redirect" asp-route-id="@ad.Id" target="_blank">
+                        <img class="img img-responsive" src="@ad.ThumbImgUrl" alt="@ad.Title" title="@ad.Title" />
+                    </a>
+                </div>
+            </article>
+        </section>
+    }
 
     <section class="wow animated fadeIn">
         <div class="leaderboard-box">
@@ -80,7 +95,7 @@
                     @foreach (PostOutputDto post in Model.Top6Post)
                     {
                         <li>
-                            <a href="/@post.Id">
+                            <a asp-controller="Post" asp-action="Details" asp-route-id="@post.Id">
                                 <mark>@(post.Title.Length >= 20 ? post.Title.Substring(0, 20) + "..." : post.Title) </mark>
                                 <small style="color:#00ffff"> @post.TotalViewCount</small>
                             </a>
@@ -106,27 +121,10 @@
 				c0.33-0.865,0.479-1.723,0.545-2.327c0.207,0.021,0.416,0.033,0.627,0.033c0.211,0,0.42-0.013,0.627-0.033
 				C13.195,16.578,13.344,17.436,13.673,18.301z M12.5,14.276c-2.856,0-4.93-2.638-4.93-6.273V1.73h9.859v6.273
 				C17.43,11.638,15.357,14.276,12.5,14.276z M21.215,7.138h-1.452c-0.197,0-0.39,0.024-0.572,0.07v-2.06
-				c0-1.097,0.908-1.99,2.024-1.99c1.117,0,2.025,0.893,2.025,1.99C23.241,6.246,22.333,7.138,21.215,7.138z" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-            </symbol>
+				c0-1.097,0.908-1.99,2.024-1.99c1.117,0,2.025,0.893,2.025,1.99C23.241,6.246,22.333,7.138,21.215,7.138z" /></symbol>
             </svg>
         </div>
     </section>
-
     <section class="wow animated fadeIn">
         <article class="panel panel-primary">
             <div class="panel-heading">
@@ -140,7 +138,7 @@
                         foreach (KeyValuePair<string, int>
                             s in tags)
                         {
-                            <a href="/tag/@s.Key" class="tagc-@(new Random().Next(100) % 10)" style="font-size: @(s.Value)px; line-height: @(s.Value * 1.2)px;">@s.Key</a>
+                            <a asp-controller="Home" asp-action="Tag" asp-route-id="@s.Key" class="tagc-@(new Random().Next(100) % 10)" style="font-size: @(s.Value)px; line-height: @(s.Value * 1.2)px;">@s.Key</a>
                         }
                     }
                     else
@@ -160,7 +158,7 @@
             <div class="panel-body text-center line-height24">
                 @foreach (var s in Model.HotSearch)
                 {
-                    <a href="/s/@s.Keywords">
+                    <a asp-controller="Search" asp-action="Search" asp-route-wd="@s.Keywords">
                         @if (s.Keywords.Length > 15)
                         {
                             <span class="label label-@colors[s.Count % colors.Length]">@s.Keywords.Substring(0, 15)... (@s.Count)</span>

+ 2 - 2
src/Masuit.MyBlogs.Core/Views/Shared/_Layout.cshtml

@@ -107,7 +107,7 @@
                                 <a href="#">@m.Name</a>
                                 <ul class="cd-secondary-nav is-hidden">
                                     <li class="go-back"><a href="#">上级菜单</a></li>
-                                    <li class="see-all"><a href="/all">所有 @m.Name</a></li>
+                                    <li class="see-all"><a asp-controller="Post" asp-action="All">所有 @m.Name</a></li>
                                     @foreach (MenuOutputDto m2 in menus.Where(s => s.ParentId == m.Id).OrderBy(c => c.Sort))
                                     {
                                         if (menus.Any(mm => mm.ParentId == m2.Id))
@@ -175,7 +175,7 @@
                                 <a href="#">@m.Name</a>
                                 <ul class="cd-nav-gallery is-hidden">
                                     <li class="go-back"><a href="#">返回主菜单</a></li>
-                                    <li class="see-all"><a href="/all">所有 @m.Name</a></li>
+                                    <li class="see-all"><a asp-controller="Post" asp-action="All">所有 @m.Name</a></li>
                                     @{
                                         var ms = menus.Where(s => s.ParentId == m.Id);
                                         foreach (var mm in ms)

+ 12 - 0
src/Masuit.MyBlogs.Core/bundleconfig.json

@@ -46,5 +46,17 @@
     "inputFiles": [
       "wwwroot/Scripts/global/leavemsg.js"
     ]
+  },
+  {
+    "outputFileName": "wwwroot/Assets/jquery.tocify/jquery.tocify.min.css",
+    "inputFiles": [
+      "wwwroot/Assets/jquery.tocify/jquery.tocify.css"
+    ]
+  },
+  {
+    "outputFileName": "wwwroot/Assets/banner/bootstrap-touch-slider.min.css",
+    "inputFiles": [
+      "wwwroot/Assets/banner/bootstrap-touch-slider.css"
+    ]
   }
 ]

+ 1 - 2
src/Masuit.MyBlogs.Core/wwwroot/Assets/banner/bootstrap-touch-slider.css

@@ -1,7 +1,6 @@
-
 .bs-slider{
     overflow: hidden;
-    max-height: 720px;
+    max-height: 600px;
     position: relative;
     background: #000000;
 }

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/Assets/banner/bootstrap-touch-slider.min.css


+ 4 - 2
src/Masuit.MyBlogs.Core/wwwroot/Assets/jquery.tocify/jquery.tocify.css

@@ -17,7 +17,9 @@
 }
 @media only screen and (min-width: 1420px) {
     .tocify {
-        right: 0px;
+        right: 0;
+        max-width: 200px;
+        overflow-x: hidden;
     }
 }
 
@@ -38,7 +40,7 @@
     float: right;
     z-index:20;
 }
-/* The Table of Contents is composed of multiple nested unordered lists.  These styles remove the default styling of an unordered list because it is ugly. */
+
 .tocify ul, .tocify li {
     list-style: none;
     margin: 0;

+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/Assets/jquery.tocify/jquery.tocify.min.css

@@ -1 +1 @@
-.tocify{position:fixed;top:160px;z-index:3;min-width:160px;max-height:66vh;overflow:auto;border:1px solid #ccc;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}@media only screen and (max-width:1419px){.tocify{display:none !important}}@media only screen and (min-width:1420px){.tocify{right:0}}@media only screen and (min-width:1900px){.tocify{right:50px;max-width:320px}}@media only screen and (min-width:2100px){.tocify{right:100px;max-width:420px}}.tocify>.close{float:right;z-index:20}.tocify ul,.tocify li{list-style:none;margin:0;padding:0;border:none;line-height:30px}.tocify>ul:first-of-type{margin-top:24px}.tocify-header{text-indent:10px}.tocify-subheader{text-indent:20px;display:none}.tocify-subheader .tocify-subheader{text-indent:30px}.tocify-subheader .tocify-subheader .tocify-subheader{text-indent:40px}.nav-list>li>a,.nav-list .nav-header{margin:0}.nav-list>li>a{padding:5px}.tocify-subheader li{font-size:15px}.tocify-subheader li>a{padding-left:15px;line-height:24px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.2);background-color:#08c}
+.tocify{position:fixed;top:160px;z-index:3;min-width:160px;max-height:66vh;overflow:auto;border:1px solid #ccc;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}@media only screen and (max-width:1419px){.tocify{display:none !important}}@media only screen and (min-width:1420px){.tocify{right:0;max-width:200px;overflow-x:hidden}}@media only screen and (min-width:1900px){.tocify{right:50px;max-width:320px}}@media only screen and (min-width:2100px){.tocify{right:100px;max-width:420px}}.tocify>.close{float:right;z-index:20}.tocify ul,.tocify li{list-style:none;margin:0;padding:0;border:none;line-height:30px}.tocify>ul:first-of-type{margin-top:24px}.tocify-header{text-indent:10px}.tocify-subheader{text-indent:20px;display:none}.tocify-subheader .tocify-subheader{text-indent:30px}.tocify-subheader .tocify-subheader .tocify-subheader{text-indent:40px}.nav-list>li>a,.nav-list .nav-header{margin:0}.nav-list>li>a{padding:5px}.tocify-subheader li{font-size:15px}.tocify-subheader li>a{padding-left:15px;line-height:24px}.nav-list>.active>a,.nav-list>.active>a:hover{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.2);background-color:#08c}

+ 5 - 0
src/Masuit.MyBlogs.Core/wwwroot/Content/common/articlestyle.css

@@ -152,6 +152,10 @@
         white-space: pre-line;
     }
 
+    .ibox-content div.col-sm-3 {
+        text-align: center;
+    }
+
 .article>.ibox>.ibox-content>main header.page-header a h2 {
     font-size: 24px;
     line-height: 32px;
@@ -163,6 +167,7 @@
     word-break: break-all;
 }
 .ibox-content .thumb {
+    max-height: 125px;
     transition: ease-in-out .3s;
     overflow: hidden;
 }

+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.js

@@ -185,7 +185,7 @@
 	$("#donate").on("click", function (e) {
 		$.post("/system/getsetting", { name: "Donate" }, function (data) {
 			swal({
-				title: "支付宝扫一扫付款捐赠!",
+				title: "支付宝扫一扫付款打赏!",
 				html:"<a href='/donate'>更多方式</a>",
 				showCancelButton: true,
 				confirmButtonColor: "#DD6B55",
@@ -199,7 +199,7 @@
 			}).then(function() {
 
 			}, function() {
-				swal("您的捐赠将会支持本站做的更好!", null, "error");
+				swal("您的打赏将会支持本站做的更好!", null, "error");
 			});
 		});
 	});

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/article.min.js


+ 384 - 291
src/Masuit.MyBlogs.Core/wwwroot/Scripts/global/scripts.js

@@ -1,319 +1,412 @@
 ;(function($) {
-	$.fn.serializeObject = function() {
-		var o = {};
-		var a = this.serializeArray();
-		$.each(a, function() {
-			if (o[this.name]) {
-				if (!o[this.name].push) {
-					o[this.name] = [o[this.name]];
-				}
-				o[this.name].push(this.value || '');
-			} else {
-				o[this.name] = this.value || '';
-			}
-		});
-		return o;
-	}
+    $.fn.serializeObject = function() {
+        var o = {};
+        var a = this.serializeArray();
+        $.each(a, function() {
+                if (o[this.name]) {
+                    if (!o[this.name].push) {
+                        o[this.name] = [o[this.name]];
+                    }
+                    o[this.name].push(this.value || '');
+                } else {
+                    o[this.name] = this.value || '';
+                }
+            });
+        return o;
+    }
 })(jQuery);
 $(function() {
-	window.ifvisible.blur(function() {
-		$("body").animate({
-			opacity: 0.5
-		}, 100);
-	});
-	window.ifvisible.wakeup(function(e) {
-		$("body").animate({
-			opacity: 1
-		}, 100);
-	});
-	popBrowserTips();
-	$("img").lazyload({
-		effect: "fadeIn", //渐现,show(直接显示),fadeIn(淡入),slideDown(下拉)
-		threshold: 180, //预加载,在图片距离屏幕180px时提前载入
-		//event: 'click',  // 事件触发时才加载,click(点击),mouseover(鼠标划过),sporty(运动的),默认为scroll(滑动)
-		//container: $("#container"), // 指定对某容器中的图片实现效果
-		failure_limit: 2 //加载2张可见区域外的图片,lazyload默认在找到第一张不在可见区域里的图片时则不再继续加载,但当HTML容器混乱的时候可能出现可见区域内图片并没加载出来的情况
-	});
-	$(".demo1").bootstrapNews({
-		newsPerPage: 5,
-		autoplay: true,
-		pauseOnHover: true,
-		direction: 'up',
-		newsTickerInterval: 2000,
-		onToDo: function() {
-			//console.log(this);
-		}
-	});
-	$(".notices").bootstrapNews({
-		newsPerPage: 4,
-		autoplay: true,
-		pauseOnHover: true,
-		navigation: false,
-		direction: 'down',
-		newsTickerInterval: 2500,
-		onToDo: function() {
-			//console.log(this);
-		}
-	});
+    window.ifvisible.blur(function() {
+        $("body").animate({
+                opacity: 0.5
+            }, 100);
+    });
+    window.ifvisible.wakeup(function(e) {
+        $("body").animate({
+                opacity: 1
+            }, 100);
+    });
+    popBrowserTips();
+    $("img").lazyload({
+        effect: "fadeIn", //渐现,show(直接显示),fadeIn(淡入),slideDown(下拉)
+        threshold: 180, //预加载,在图片距离屏幕180px时提前载入
+        //event: 'click',  // 事件触发时才加载,click(点击),mouseover(鼠标划过),sporty(运动的),默认为scroll(滑动)
+        //container: $("#container"), // 指定对某容器中的图片实现效果
+        failure_limit: 2 //加载2张可见区域外的图片,lazyload默认在找到第一张不在可见区域里的图片时则不再继续加载,但当HTML容器混乱的时候可能出现可见区域内图片并没加载出来的情况
+    });
+    $(".demo1").bootstrapNews({
+        newsPerPage: 5,
+        autoplay: true,
+        pauseOnHover: true,
+        direction: 'up',
+        newsTickerInterval: 2000,
+        onToDo: function() {
+            //console.log(this);
+        }
+    });
+    $(".notices").bootstrapNews({
+        newsPerPage: 4,
+        autoplay: true,
+        pauseOnHover: true,
+        navigation: false,
+        direction: 'down',
+        newsTickerInterval: 2500,
+        onToDo: function() {
+            //console.log(this);
+        }
+    });
 
-	//全局加载动画
-	$("a[href]").click(function(e) {
-		if ($(this).attr("target") == "_blank") {
-			return;
-		}
-		if ($(this).attr("href").indexOf("#") >= 0 || $(this).attr("href").indexOf("javascript") >= 0) {
-			return;
-		}
-		loading();
-		setTimeout(function() {
-			loadingDone();
-			window.notie.alert({
-				type: 4,
-				text: "页面加载失败!",
-				time: 4
-			});
-		}, 60000);
-	});
+    //全局加载动画
+    $("a[href]").click(function(e) {
+        if ($(this).attr("target") == "_blank") {
+            return;
+        }
+        if ($(this).attr("href").indexOf("#") >= 0 || $(this).attr("href").indexOf("javascript") >= 0) {
+            return;
+        }
+        loading();
+        setTimeout(function() {
+                loadingDone();
+                window.notie.alert({
+                    type: 4,
+                    text: "页面加载失败!",
+                    time: 4
+                });
+            }, 60000);
+    });
 
-	function subscribe() {
-		loading();
-		$.post("/subscribe/subscribe", $("#subscribe").serialize(), function(data) {
-			loadingDone();
-			if (data && data.Success) {
-				window.notie.alert({
-					type: 1,
-					text: data.Message,
-					time: 4
-				});
-				$(':input', '#article-form').not(':button,:submit,:reset,:hidden').val('').removeAttr('checked').removeAttr('checked');
-			} else {
-				window.notie.alert({
-					type: 3,
-					text: data.Message,
-					time: 4
-				});
-			}
-		});
-	}
+    function subscribe() {
+        loading();
+        $.post("/subscribe/subscribe", $("#subscribe").serialize(), function(data) {
+                loadingDone();
+                if (data && data.Success) {
+                    window.notie.alert({
+                        type: 1,
+                        text: data.Message,
+                        time: 4
+                    });
+                    $(':input', '#article-form').not(':button,:submit,:reset,:hidden').val('').removeAttr('checked').removeAttr('checked');
+                } else {
+                    window.notie.alert({
+                        type: 3,
+                        text: data.Message,
+                        time: 4
+                    });
+                }
+            });
+    }
 
-	//订阅表单验证
-	$("#subscribe").on("submit", function(e) {
-		e.preventDefault();
-		if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test($(".email").val().trim())) {
-			window.notie.alert(3, "请输入正确的邮箱格式!", 4);
-			return;
-		}
-		if($(".email").val().trim().indexOf("163")>1||$(".email").val().trim().indexOf("126")>1) {
-			swal({
-				title: '邮箱确认',
-				text: "检测到您输入的邮箱是网易邮箱,本站的邮件服务器可能会因为您的反垃圾设置而无法将邮件正常发送到您的邮箱,建议使用您的其他邮箱,或者检查反垃圾设置后,再点击确定按钮继续!",
-				type: 'warning',
-				showCancelButton: true,
-				confirmButtonColor: '#3085d6',
-				cancelButtonColor: '#d33',
-				confirmButtonText: '确定',
-				cancelButtonText: '换个邮箱',
-				confirmButtonClass: 'btn btn-success btn-lg',
-				cancelButtonClass: 'btn btn-danger btn-lg',
-				buttonsStyling: false
-			}).then(function(isConfirm) {
-				if (isConfirm === true) {
-					subscribe();
-				}
-			});
-			return;
-		}
-		subscribe();
-	});
-	//new WOW().init();//滚动加载
-	var nav=$(".cd-main-header");
-	if (document.documentElement.scrollTop || document.body.scrollTop > 0) {
-		nav.css("background-color", "white");
-	} else {
-		nav.css("background-color", "transparent");
-	}
-	document.onscroll = function() {
-		if (document.documentElement.scrollTop || document.body.scrollTop > 10) {
-			nav.css({
-				"background-color": "white",
-				transition: "all 1s ease-in-out"
-			});
-		} else {
-			nav.css({
-				"background-color": "transparent",
-				transition: "all 1s ease-in-out"
-			});
-		}
-	}
+    //订阅表单验证
+    $("#subscribe").on("submit", function(e) {
+            e.preventDefault();
+            if (!/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/.test($(".email").val().trim())) {
+                window.notie.alert(3, "请输入正确的邮箱格式!", 4);
+                return;
+            }
+            if ($(".email").val().trim().indexOf("163") > 1 || $(".email").val().trim().indexOf("126") > 1) {
+                swal({
+                    title: '邮箱确认',
+                    text: "检测到您输入的邮箱是网易邮箱,本站的邮件服务器可能会因为您的反垃圾设置而无法将邮件正常发送到您的邮箱,建议使用您的其他邮箱,或者检查反垃圾设置后,再点击确定按钮继续!",
+                    type: 'warning',
+                    showCancelButton: true,
+                    confirmButtonColor: '#3085d6',
+                    cancelButtonColor: '#d33',
+                    confirmButtonText: '确定',
+                    cancelButtonText: '换个邮箱',
+                    confirmButtonClass: 'btn btn-success btn-lg',
+                    cancelButtonClass: 'btn btn-danger btn-lg',
+                    buttonsStyling: false
+                }).then(function(isConfirm) {
+                    if (isConfirm === true) {
+                        subscribe();
+                    }
+                });
+                return;
+            }
+            subscribe();
+        });
+    //new WOW().init();//滚动加载
+    var nav = $(".cd-main-header");
+    if (document.documentElement.scrollTop || document.body.scrollTop > 0) {
+        nav.css("background-color", "white");
+    } else {
+        nav.css("background-color", "transparent");
+    }
+    document.onscroll = function() {
+        if (document.documentElement.scrollTop || document.body.scrollTop > 10) {
+            nav.css({
+                "background-color": "white",
+                transition: "all 1s ease-in-out"
+            });
+        } else {
+            nav.css({
+                "background-color": "transparent",
+                transition: "all 1s ease-in-out"
+            });
+        }
+    }
 
-	//搜索建议
-	$("#search").bsSuggest({
-		allowNoKeyword: false, //是否允许无关键字时请求数据。为 false 则无输入时不执行过滤请求  
-		multiWord: true, //以分隔符号分割的多关键字支持  
-		separator: ",", //多关键字支持时的分隔符,默认为空格  
-		getDataMethod: "url", //获取数据的方式,总是从 URL 获取  
-		url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?p=3&t=' + (new Date()).getTime() +
-			'&wd=', /*优先从url ajax 请求 json 帮助数据,注意最后一个参数为关键字请求参数*/
-		jsonp: 'cb', //如果从 url 获取数据,并且需要跨域,则该参数必须设置  
-		processData: function(json) { // url 获取数据时,对数据的处理,作为 getData 的回调函数  
-			var i, len, data = {
-							value: []
-						};
-			if (!json || !json.s || json.s.length === 0) {
-				return false;
-			}
-			len = json.s.length;
-			var jsonStr = "{'value':[";
-			for (i = 0; i < len; i++) {
-				data.value.push({
-					word: json.s[i]
-				});
-			}
-			data.defaults = 'baidu';
-			//字符串转化为 js 对象  
-			return data;
-		}
-	});
+    //搜索建议
+    $("#search").bsSuggest({
+        allowNoKeyword: false, //是否允许无关键字时请求数据。为 false 则无输入时不执行过滤请求  
+        multiWord: true, //以分隔符号分割的多关键字支持  
+        separator: ",", //多关键字支持时的分隔符,默认为空格  
+        getDataMethod: "url", //获取数据的方式,总是从 URL 获取  
+        url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?p=3&t=' + (new Date()).getTime() +
+            '&wd=', /*优先从url ajax 请求 json 帮助数据,注意最后一个参数为关键字请求参数*/
+        jsonp: 'cb', //如果从 url 获取数据,并且需要跨域,则该参数必须设置  
+        processData: function(json) { // url 获取数据时,对数据的处理,作为 getData 的回调函数  
+            var i, data = {
+                    value: []
+                };
+            if (!json || !json.s || json.s.length === 0) {
+                return false;
+            }
+            var len = json.s.length;
+            for (i = 0; i < len; i++) {
+                data.value.push({
+                    word: json.s[i]
+                });
+            }
+            data.defaults = 'baidu';
+            //字符串转化为 js 对象  
+            return data;
+        }
+    });
 
-	var tippy = new Tippy('.tippy-scale', {
-		position: 'bottom',
-		animation: 'scale',
-		arrow: 'true',
-		'theme': 'light'
-	}); //注册tooltips
-	
-	$(".btn").on("mousedown", function(e) {
-		window.ripplet(e, {
-			color: null,
-			className: 'rainbow',
-			clearingDuration: '3s',
-			spreadingDuration: '1s'
-		});
-	});
-	if (!Object.prototype.hasOwnProperty.call(window, 'event')) {
-		['mousedown', 'mouseenter', 'onmouseleave'].forEach(function(eventType) {
-			window.addEventListener(eventType, function(event) {
-				window.event = event;
-			}, true);
-		});
-	}
+    var tippy = new Tippy('.tippy-scale', {
+            position: 'bottom',
+            animation: 'scale',
+            arrow: 'true',
+            'theme': 'light'
+        }); //注册tooltips
 
-	var nid = window.localStorage.getItem("notice") || '0';
-	window.fetch("/notice/last", {
-		credentials: 'include',
-		method: 'POST',
-		mode: 'cors'
-	}).then(function(response) {
-		return response.json();
-	}).then(function(data) {
-		data = data.Data;
-		if (nid != data.Id) {
-			//公告层
-			layer.open({
-				title: '网站公告:' + data.Title,
-				offset: (window.screen.width > 400 ? "100px" : "40px"),
-				area: (window.screen.width > 400 ? 400 : window.screen.width - 10) + 'px',
-				shade: 0.6,
-				closeBtn: true,
-				content: data.Content,
-				btn: ["查看详情", '知道了', '哦'],
-				btn1: function(layero) {
-					window.localStorage.setItem("notice", data.Id);
-					window.location.href = "/n/" + data.Id;
-					loading();
-				},
-				btn2: function(index) {
-					window.localStorage.setItem("notice", data.Id);
-					layer.closeAll();
-				},
-				btn3: function(index) {
-					//return true;
-				}
-			});
-		}
-	}).catch(function(e) {
-		console.log("Oops, error");
-	});
+    $(".btn").on("mousedown", function(e) { 
+        window.ripplet(e, {
+                color: null,
+                className: 'rainbow',
+                clearingDuration: '3s',
+                spreadingDuration: '1s'
+            });
+        });
+    if (!Object.prototype.hasOwnProperty.call(window, 'event')) {
+        ['mousedown', 'mouseenter', 'onmouseleave'].forEach(function(eventType) {
+            window.addEventListener(eventType, function(event) {
+                    window.event = event;
+                }, true);
+        });
+    }
+
+    var nid = window.localStorage.getItem("notice") || '0';
+    window.fetch("/notice/last", {
+            credentials: 'include',
+            method: 'POST',
+            mode: 'cors'
+        }).then(function(response) {
+        return response.json();
+    }).then(function(data) {
+        data = data.Data;
+        if (nid != data.Id) {
+            //公告层
+            layer.open({
+                title: '网站公告:' + data.Title,
+                offset: (window.screen.width > 400 ? "100px" : "40px"),
+                area: (window.screen.width > 400 ? 400 : window.screen.width - 10) + 'px',
+                shade: 0.6,
+                closeBtn: true,
+                content: data.Content,
+                btn: ["查看详情", '知道了', '哦'],
+                btn1: function(layero) {
+                    window.localStorage.setItem("notice", data.Id);
+                    window.location.href = "/n/" + data.Id;
+                    loading();
+                },
+                btn2: function(index) {
+                    window.localStorage.setItem("notice", data.Id);
+                    layer.closeAll();
+                },
+                btn3: function(index) {
+                    //return true;
+                }
+            });
+        }
+    }).catch(function(e) {
+        console.log("Oops, error");
+    });
 });
 
 //两个全局加载动画
 function loading() {
-	let r = new Date().getMilliseconds();
-	//$(".loading" + (r % 3 + 1)).show();
-	$(".loading1").show();
+    let r = new Date().getMilliseconds();
+    //$(".loading" + (r % 3 + 1)).show();
+    $(".loading1").show();
 }
 
 function loadingDone() {
-	$(".loading1").hide();
-	$(".loading2").hide();
-	$(".loading3").hide();
+    $(".loading1").hide();
+    $(".loading2").hide();
+    $(".loading3").hide();
 }
 
 function GetOperatingSystem(os) {
-	if (os) {
-		if (os.indexOf("Windows") >= 0) {
-			return `<i class="icon-windows8"></i>${os}`;
-		} else if (os.indexOf("Mac") >= 0) {
-			return `<i class="icon-apple"></i>${os}`;
-		} else if (os.indexOf("Chrome") >= 0) {
-			return `<i class="icon-chrome"></i>${os}`;
-		} else if (os.indexOf("Android") >= 0) {
-			return `<i class="icon-android"></i>${os}`;
-		} else {
-			return `<i class="icon-stats"></i>${os}`;
-		}
-	} else {
-		return `<i class="icon-stats"></i>未知操作系统`;
-	}
+    if (os) {
+        if (os.indexOf("Windows") >= 0) {
+            return `<i class="icon-windows8"></i>${os}`;
+        } else if (os.indexOf("Mac") >= 0) {
+            return `<i class="icon-apple"></i>${os}`;
+        } else if (os.indexOf("Chrome") >= 0) {
+            return `<i class="icon-chrome"></i>${os}`;
+        } else if (os.indexOf("Android") >= 0) {
+            return `<i class="icon-android"></i>${os}`;
+        } else {
+            return `<i class="icon-stats"></i>${os}`;
+        }
+    } else {
+        return `<i class="icon-stats"></i>未知操作系统`;
+    }
 }
 
 function GetBrowser(browser) {
-	if (browser) {
-		if (browser.indexOf("Chrome") >= 0) {
-			return `<i class="icon-chrome"></i>${browser}`;
-		} else if (browser.indexOf("Firefox") >= 0) {
-			return `<i class="icon-firefox"></i>${browser}`;
-		} else if (browser.indexOf("IE") >= 0) {
-			return `<i class="icon-IE"></i>${browser}`;
-		} else if (browser.indexOf("Edge") >= 0) {
-			return `<i class="icon-edge"></i>${browser}`;
-		} else if (browser.indexOf("Opera") >= 0) {
-			return `<i class="icon-opera"></i>${browser}`;
-		} else if (browser.indexOf("Safari") >= 0) {
-			return `<i class="icon-safari"></i>${browser}`;
-		} else {
-			return `<i class="icon-browser2"></i>${browser}`;
-		}
-	} else {
-		return `<i class="icon-browser2"></i>未知浏览器`;
-	}
+    if (browser) {
+        if (browser.indexOf("Chrome") >= 0) {
+            return `<i class="icon-chrome"></i>${browser}`;
+        } else if (browser.indexOf("Firefox") >= 0) {
+            return `<i class="icon-firefox"></i>${browser}`;
+        } else if (browser.indexOf("IE") >= 0) {
+            return `<i class="icon-IE"></i>${browser}`;
+        } else if (browser.indexOf("Edge") >= 0) {
+            return `<i class="icon-edge"></i>${browser}`;
+        } else if (browser.indexOf("Opera") >= 0) {
+            return `<i class="icon-opera"></i>${browser}`;
+        } else if (browser.indexOf("Safari") >= 0) {
+            return `<i class="icon-safari"></i>${browser}`;
+        } else {
+            return `<i class="icon-browser2"></i>${browser}`;
+        }
+    } else {
+        return `<i class="icon-browser2"></i>未知浏览器`;
+    }
 }
 
 function getFile(obj, inputName) {
-	var file_name = $(obj).val();
-	console.log(file_name);
-	$("input[name='" + inputName + "']").val(file_name);
+    var file_name = $(obj).val();
+    console.log(file_name);
+    $("input[name='" + inputName + "']").val(file_name);
 }
 
 function popBrowserTips() {
-	if (window.sessionStorage) {
-		var deny = window.sessionStorage.getItem("deny") || false;
-		if (window.screen.width <= 320 && !deny) {
-			swal({
-				title: '访问受限制?',
-				html: "首先欢迎您的访问,由于检测到您的设备<span style='color:red'>屏幕宽度过小</span>,网站的部分功能可能不会兼容你的设备,但是您<span style='color:red'>可以继续浏览</span>,为确保最佳用户体验,建议使用<span style='color:red'>5寸以上移动设备</span>,或分辨率大于<span style='color:red'>1360 x 768</span>的<span style='color:red'>电脑浏览器</span>访问本站,感谢您的来访和支持!",
-				type: 'error',
-				showCloseButton: true,
-				showCancelButton: true,
-				confirmButtonColor: '#3085d6',
-				cancelButtonColor: '#d33',
-				confirmButtonText: '我知道了',
-				cancelButtonText: '哦哦'
-			}).then(function(isConfirm) {
-				if (isConfirm) {
-					window.sessionStorage.setItem("deny", true);
-				}
-			});
-		}
-	}
-}
+    if (window.sessionStorage) {
+        var deny = window.sessionStorage.getItem("deny") || false;
+        if (window.screen.width <= 320 && !deny) {
+            swal({
+                title: '访问受限制?',
+                html: "首先欢迎您的访问,由于检测到您的设备<span style='color:red'>屏幕宽度过小</span>,网站的部分功能可能不会兼容你的设备,但是您<span style='color:red'>可以继续浏览</span>,为确保最佳用户体验,建议使用<span style='color:red'>5寸以上移动设备</span>,或分辨率大于<span style='color:red'>1360 x 768</span>的<span style='color:red'>电脑浏览器</span>访问本站,感谢您的来访和支持!",
+                type: 'error',
+                showCloseButton: true,
+                showCancelButton: true,
+                confirmButtonColor: '#3085d6',
+                cancelButtonColor: '#d33',
+                confirmButtonText: '我知道了',
+                cancelButtonText: '哦哦'
+            }).then(function(isConfirm) {
+                if (isConfirm) {
+                    window.sessionStorage.setItem("deny", true);
+                }
+            });
+        }
+    }
+}
+
+/**
+ * 鼠标桃心
+ */
+(function(window, document) {
+    var hearts = [];
+    window.requestAnimationFrame = (function() {
+        return window.requestAnimationFrame ||
+            window.webkitRequestAnimationFrame ||
+            window.mozRequestAnimationFrame ||
+            window.oRequestAnimationFrame ||
+            window.msRequestAnimationFrame ||
+            function(callback) {
+                setTimeout(callback, 1000 / 60);
+            }
+    })();
+    init();
+
+    function init() {
+        css(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
+        attachEvent();
+        gameloop();
+    }
+
+    function gameloop() {
+        for (var i = 0; i < hearts.length; i++) {
+            if (hearts[i].alpha <= 0) {
+                document.body.removeChild(hearts[i].el);
+                hearts.splice(i, 1);
+                continue;
+            }
+            hearts[i].y--;
+            hearts[i].scale += 0.004;
+            hearts[i].alpha -= 0.013;
+            hearts[i].el.style.cssText =
+                "left:" +
+                hearts[i].x +
+                "px;top:" +
+                hearts[i].y +
+                "px;opacity:" +
+                hearts[i].alpha +
+                ";transform:scale(" +
+                hearts[i].scale +
+                "," +
+                hearts[i].scale +
+                ") rotate(45deg);background:" +
+                hearts[i].color;
+        }
+        requestAnimationFrame(gameloop);
+    }
+
+    function attachEvent() {
+        var old = typeof window.onclick === "function" && window.onclick;
+        window.onclick = function(event) {
+            old && old();
+            createHeart(event);
+        }
+    }
+
+    function createHeart(event) {
+        var d = document.createElement("div");
+        d.className = "heart";
+        hearts.push({
+            el: d,
+            x: event.clientX - 5,
+            y: event.clientY - 5,
+            scale: 1,
+            alpha: 1,
+            color: randomColor()
+        });
+        document.body.appendChild(d);
+    }
+
+    function css(css) {
+        var style = document.createElement("style");
+        style.type = "text/css";
+        try {
+            style.appendChild(document.createTextNode(css));
+        } catch (ex) {
+            style.styleSheet.cssText = css;
+        }
+        document.getElementsByTagName('head')[0].appendChild(style);
+    }
+
+    function randomColor() {
+        return "rgb(" +
+            (~~(Math.random() * 255)) +
+            "," +
+            (~~(Math.random() * 255)) +
+            "," +
+            (~~(Math.random() * 255)) +
+            ")";
+    }
+})(window, document);

+ 8 - 5
src/Masuit.MyBlogs.Core/wwwroot/ng-views/app/route.config.js

@@ -100,13 +100,16 @@ myApp.config(["$stateProvider", "$urlRouterProvider", "$locationProvider",
 						cpath + "/post.js"]);
 				}]
 			}
-		}).state("top-post", {
-			url: "/post/top",
-			templateUrl: vpath + "/post/top.html",
-			controller: "toppost",
+		}).state("partner", {
+			url: "/partner",
+			templateUrl: vpath + "/partner.html",
+			controller: "partner as list",
 			resolve: {
 				deps: ["$ocLazyLoad", function($ocLazyLoad) {
-					return $ocLazyLoad.load([cpath + "/post.js"]);
+					return $ocLazyLoad.load([{
+						files: ["/Assets/semantic/semantic.css","https://cdn.staticfile.org/semantic-ui/2.4.1/semantic.min.js"],
+						cache: true
+					},cpath + "/partner.js"]);
 				}]
 			}
 		}).state("post-cat", {

+ 5 - 5
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/donate.js

@@ -33,7 +33,7 @@
 	};
 	self.del = function(row) {
 		swal({
-			title: "确认删除这条捐赠记录吗?",
+			title: "确认删除这条打赏记录吗?",
 			text: row.NickName,
 			showCancelButton: true,
 			confirmButtonColor: "#DD6B55",
@@ -78,11 +78,11 @@
 			};
 		}
 		swal({
-			title: '添加捐赠记录',
+			title: '添加打赏记录',
 			html: '<div class="input-group"><span class="input-group-addon">昵称: </span><input type="text" id="name" class="form-control input-lg" placeholder="请输入昵称" value="' + row.NickName+'"></div>' +
-			'<div class="input-group"><span class="input-group-addon">捐赠时间: </span><input id="date" type="text" class="form-control input-lg date datainp dateicon" readonly placeholder="请输入捐赠时间" value="' + row.DonateTime +'"></div>	' +
-			'<div class="input-group"><span class="input-group-addon">捐赠金额: </span><input id="amount" type="text" class="form-control input-lg" placeholder="请输入金额" value="' + row.Amount +'"></div>' +
-			'<div class="input-group"><span class="input-group-addon">捐赠方式: </span><input id="via" type="text" class="form-control input-lg" placeholder="请输入捐赠方式" value="' + row.Via +'"></div>' +
+			'<div class="input-group"><span class="input-group-addon">打赏时间: </span><input id="date" type="text" class="form-control input-lg date datainp dateicon" readonly placeholder="请输入打赏时间" value="' + row.DonateTime +'"></div>	' +
+			'<div class="input-group"><span class="input-group-addon">打赏金额: </span><input id="amount" type="text" class="form-control input-lg" placeholder="请输入金额" value="' + row.Amount +'"></div>' +
+			'<div class="input-group"><span class="input-group-addon">打赏方式: </span><input id="via" type="text" class="form-control input-lg" placeholder="请输入打赏方式" value="' + row.Via +'"></div>' +
 			'<div class="input-group"><span class="input-group-addon">Email: </span><input type="email" id="email" class="form-control input-lg" placeholder="请输入Email" value="' + row.Email +'"></div>' +
 			'<div class="input-group"><span class="input-group-addon">QQ或微信: </span><input type="text" id="qq" class="form-control input-lg" placeholder="请输入QQ或微信" value="' + row.QQorWechat +'"></div>' +
 			'<div class="input-group"><span class="input-group-addon">显示Email: </span><input type="text" id="demail" class="form-control input-lg" placeholder="请输入显示Email" value="' + row.EmailDisplay +'"></div>' +

+ 224 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.js

@@ -0,0 +1,224 @@
+myApp.controller("partner", ["$scope", "$http", "$location", "$timeout","NgTableParams", function ($scope, $http, $location, $timeout,NgTableParams) {
+	window.hub.stop();
+	var self = this;
+	$scope.isAdd = true;
+	$scope.allowUpload=false;
+	$scope.loading();
+	$scope.partner = {};
+	$scope.kw = "";
+	$scope.paginationConf = {
+		currentPage:  1,
+		itemsPerPage: 10,
+		pagesLength: 25,
+		perPageOptions: [1, 5, 10, 15, 20, 30, 40, 50, 100, 200],
+		rememberPerPage: 'perPageItems',
+		onChange: function() {
+			self.GetPageData($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
+		}
+	};
+	this.GetPageData = function (page, size) {
+		$scope.loading();
+		$http.post("/ads/getpagedata", {
+			page,
+			size,
+			kw: $scope.kw
+		}).then(function(res) {
+			$scope.paginationConf.totalItems = res.data.TotalCount;
+			$("div[ng-table-pagination]").remove();
+			self.tableParams = new NgTableParams({
+				count: 50000
+			}, {
+				filterDelay: 0,
+				dataset: res.data.Data
+			});
+			self.data = res.data.Data;
+			$scope.loadingDone();
+		});
+	}
+    $('.ui.dropdown.types').dropdown({
+		onChange: function (value) {
+			$scope.partner.Types = value;
+		}
+	});
+    $scope.getCategory = function () {
+		$scope.loading();
+		$http.post("/category/getcategories", null).then(function (res) {
+			$scope.loadingDone();
+			var data = res.data;
+			if (data.Success) {
+				$scope.cat = data.Data;
+				$('.ui.dropdown.category').dropdown({
+					onChange: function (value) {
+						$scope.partner.CategoryId = value;
+					},
+					message: {
+						maxSelections: '最多选择 {maxCount} 项',
+						noResults: '无搜索结果!'
+					}
+				});
+			} else {
+				window.notie.alert({
+					type: 3,
+					text: '获取文章分类失败!',
+					time: 4
+				});
+			}
+		});
+	}
+	$scope.getCategory();
+	$scope.remove = function(partner) {
+		layer.closeAll();
+		swal({
+			title: '确定移除这条广告吗?',
+			text: partner.Title,
+			type: 'warning',
+			showCancelButton: true,
+			confirmButtonColor: '#3085d6',
+			cancelButtonColor: '#d33',
+			confirmButtonText: '确定',
+			cancelButtonText: '取消'
+		}).then(function(isConfirm) {
+			if (isConfirm) {
+				$scope.loading();
+				$scope.request("/ads/delete/"+partner.Id, null, function(data) {
+					swal(data.Message, null, 'success');
+			        self.GetPageData($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
+				});
+			}
+		}).catch(swal.noop);
+	}
+	$scope.add = function() {
+		$scope.partner = {};
+		$scope.isAdd = true;
+		$scope.allowUpload=false;
+		layer.open({
+			type: 1,
+			zIndex: 20,
+			title: '添加广告推广',
+			area: (window.screen.width > 650 ? 650 : window.screen.width) + 'px',// '340px'], //宽高
+			content: $("#edit"),
+			cancel: function(index, layero) {
+				setTimeout(function() {
+					$("#edit").css("display", "none");
+				}, 500);
+				return true;
+			}
+		});
+	}
+	$scope.edit = function (item) {
+		$scope.partner = angular.copy(item);
+		$scope.isAdd = false;
+		$scope.allowUpload=false;
+		layer.closeAll();
+        $timeout(function () {
+		    $('.ui.dropdown.category').dropdown('clear');
+		    $('.ui.dropdown.types').dropdown('clear');
+			$('.ui.dropdown.category').dropdown('set selected', [item.CategoryId]);
+		    $('.ui.dropdown.types').dropdown('set selected', item.Types.split(','));
+		}, 10);
+		layer.open({
+			type: 1,
+			zIndex: 20,
+			title: '保存广告',
+			area: (window.screen.width > 650 ? 650 : window.screen.width) + 'px',// '340px'], //宽高
+			content: $("#edit"),
+			cancel: function(index, layero) {
+				setTimeout(function() {
+					$("#edit").css("display", "none");
+				}, 500);
+				return true;
+			}
+		});
+	}
+	$scope.closeAll= function() {
+		layer.closeAll();
+		setTimeout(function() {
+			$("#edit").css("display", "none");
+		}, 500);
+	}
+	$scope.submit = function(partner) {
+		if ($scope.isAdd) {
+			partner.Id = 0;
+		}
+		$scope.request("/ads/save", partner, function(data) {
+			$scope.closeAll();
+			window.notie.alert({
+				type: 1,
+				text: data.Message,
+				time: 4
+			});
+			self.GetPageData($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
+			$scope.partner.ImageUrl = "";
+			$scope.partner.Description = "";
+		});
+	}
+	$scope.uploadImage = function(field) {
+		$scope.loading();
+        $("#uploadform").ajaxSubmit({
+			url: "/Upload",
+			type: "post",
+			success: function(data) {
+				$scope.loadingDone();
+				document.getElementById("uploadform").reset();
+				$scope.$apply(function () {
+					$scope.partner[field] = data.Data;
+                    layer.close(layer.index);
+			    });
+			}
+		});
+    };
+	
+	$scope.upload = function(field) {
+		$scope.imgField=field;
+        layer.open({
+			type: 1,
+			zIndex: 20,
+			title: '上传图片',
+			area: [(window.screen.width > 300 ? 300 : window.screen.width) + 'px', '80px'], //宽高
+			content: $("#img-upload"),
+			cancel: function(index, layero) {
+                return true;
+			}
+		});
+	}
+
+	var _timeout;
+	$scope.search = function (kw) {
+		if (_timeout) {
+			$timeout.cancel(_timeout);
+		}
+		_timeout = $timeout(function () {
+			$scope.kw = kw;
+			self.GetPageData($scope.paginationConf.currentPage, $scope.paginationConf.itemsPerPage);
+			_timeout = null;
+		}, 500);
+	}
+
+    $scope.changeState= function(row) {
+        $scope.request("/ads/ChangeState/"+row.Id, null, function(data) {
+			window.notie.alert({
+				type: 1,
+				text: data.Message,
+				time: 4
+			});
+		});
+    }
+
+    $scope.detail = function (item) {
+		$scope.partner = angular.copy(item);
+		layer.closeAll();
+		$('.ui.dropdown.types').dropdown('clear');
+		$('.ui.dropdown.types').dropdown('set selected', item.Types.split(','));
+		layer.open({
+			type: 1,
+			zIndex: 20,
+            offset: '50px',
+			title: item.Title,
+			area: (window.screen.width > 850 ? 850 : window.screen.width) + 'px',// '340px'], //宽高
+			content: $("#detail"),
+			cancel: function(index, layero) {
+				return true;
+			}
+		});
+	}
+}]);

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.min.js


+ 0 - 111
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.js

@@ -607,117 +607,6 @@ myApp.controller("postedit", ["$scope", "$http", "$location", "$timeout", functi
 		},5000);
     }
 }]);
-myApp.controller("toppost", ["$scope", "$http", "$location", "$timeout", function ($scope, $http, $location, $timeout) {
-	window.hub.stop();
-	$scope.isAdd = true;
-	$scope.allowUpload=false;
-	$scope.loading();
-	$scope.banner = {};
-	$scope.getdata = function() {
-		$scope.request("/banner/get", null, function(data) {
-			$scope.banners = data.Data;
-		});
-	}
-	$scope.getdata();
-	$scope.remove = function(banner) {
-		swal({
-			title: '确定移除这个banner吗?',
-			text: banner.Title,
-			type: 'warning',
-			showCancelButton: true,
-			confirmButtonColor: '#3085d6',
-			cancelButtonColor: '#d33',
-			confirmButtonText: '确定',
-			cancelButtonText: '取消'
-		}).then(function(isConfirm) {
-			if (isConfirm) {
-				$scope.loading();
-				$scope.request("/banner/delete/"+banner.Id, null, function(data) {
-					swal(data.Message, null, 'success');
-					$scope.getdata();
-				});
-			}
-		}).catch(swal.noop);
-	}
-	$scope.add = function() {
-		$scope.banner = {};
-		$scope.isAdd = true;
-		$scope.allowUpload=false;
-		layer.open({
-			type: 1,
-			zIndex: 20,
-			title: '设置头图页文章',
-			area: (window.screen.width > 650 ? 650 : window.screen.width) + 'px',// '340px'], //宽高
-			content: $("#modal"),
-			cancel: function(index, layero) {
-				setTimeout(function() {
-					$("#modal").css("display", "none");
-				}, 500);
-				return true;
-			}
-		});
-	}
-	$scope.edit = function (item) {
-		$scope.banner = item;
-		$scope.isAdd = false;
-		$scope.allowUpload=false;
-		layer.open({
-			type: 1,
-			zIndex: 20,
-			title: '设置头图页文章',
-			area: (window.screen.width > 650 ? 650 : window.screen.width) + 'px',// '340px'], //宽高
-			content: $("#modal"),
-			cancel: function(index, layero) {
-				setTimeout(function() {
-					$("#modal").css("display", "none");
-				}, 500);
-				return true;
-			}
-		});
-	}
-	$scope.closeAll= function() {
-		layer.closeAll();
-		setTimeout(function() {
-			$("#modal").css("display", "none");
-		}, 500);
-	}
-	$scope.submit = function(banner) {
-		if ($scope.isAdd) {
-			banner.Id = 0;
-		}
-		$scope.request("/banner/save", banner, function(data) {
-			//Custombox.close();
-			$scope.closeAll();
-			window.notie.alert({
-				type: 1,
-				text: data.Message,
-				time: 4
-			});
-			$scope.getdata();
-			$scope.banner.ImageUrl = "";
-			$scope.banner.Description = "";
-		});
-	}
-	$scope.uploadImage = function() {
-		$scope.loading();
-        $("#coverform").ajaxSubmit({
-			url: "/Upload",
-			type: "post",
-			success: function(data) {
-				$scope.loadingDone();
-				document.getElementById("coverform").reset();
-				$scope.$apply(function () {
-			      $scope.allowUpload=false;
-					$scope.banner.ImageUrl = data.Data;
-			    });
-			}
-		});
-    };
-	
-	$scope.upload = function() {
-		$scope.allowUpload=true;
-	}
-}]);
 myApp.controller("category", ["$scope", "$http", "NgTableParams", function ($scope, $http, NgTableParams) {
 	window.hub.stop();
 	var self = this;

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/post.min.js


+ 5 - 3
src/Masuit.MyBlogs.Core/wwwroot/ng-views/template/sidebar-left.html

@@ -44,7 +44,6 @@
                 <li><a data-ui-sref-active="active" data-ui-sref="post-list" data-ng-click="mactrl.sidebarStat($event)">文章列表</a></li>
                 <li><a data-ui-sref-active="active" data-ui-sref="merge-list" data-ng-click="mactrl.sidebarStat($event)">文章合并</a></li>
                 <li><a data-ui-sref-active="active" data-ui-sref="write-blog" data-ng-click="mactrl.sidebarStat($event)">写文章</a></li>
-                <li><a data-ui-sref-active="active" data-ui-sref="top-post" data-ng-click="mactrl.sidebarStat($event)">banner头图管理</a></li>
                 <li><a data-ui-sref-active="active" data-ui-sref="post-cat" data-ng-click="mactrl.sidebarStat($event)">文章分类管理</a></li>
                 <li><a data-ui-sref-active="active" data-ui-sref="seminar" data-ng-click="mactrl.sidebarStat($event)"><i class="zmdi zmdi-format-underlined"></i>文章专题管理</a></li>
                 <li><a data-ui-sref-active="active" data-ui-sref="share" data-ng-click="mactrl.sidebarStat($event)">快速分享</a></li>
@@ -57,7 +56,7 @@
                 <li><a data-ui-sref-active="active" data-ui-sref="msg" data-ng-click="mactrl.sidebarStat($event)">留言审核</a></li>
             </ul>
         </li>
-        
+
         <li class="sub-menu" data-ng-class="{ 'active toggled': mactrl.$state.includes('form') }">
             <a href="" toggle-submenu><i class="zmdi zmdi-collection-text"></i>网站公告</a>
 
@@ -91,7 +90,10 @@
             </ul>
         </li>
         <li data-ui-sref-active="active">
-            <a data-ui-sref-active="active" data-ui-sref="donate" data-ng-click="mactrl.sidebarStat($event)"><i class="zmdi zmdi-money"></i>捐赠列表</a>
+            <a data-ui-sref-active="active" data-ui-sref="partner" data-ng-click="mactrl.sidebarStat($event)"><i class="zmdi zmdi-assignment"></i>广告管理</a>
+        </li>
+        <li data-ui-sref-active="active">
+            <a data-ui-sref-active="active" data-ui-sref="donate" data-ng-click="mactrl.sidebarStat($event)"><i class="zmdi zmdi-money"></i>打赏列表</a>
         </li>
         <li data-ui-sref-active="active">
             <a data-ui-sref-active="active" data-ui-sref="subscribe" data-ng-click="mactrl.sidebarStat($event)"><i class="zmdi zmdi-swap-alt"></i>订阅管理</a>

+ 2 - 2
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/donate.html

@@ -20,7 +20,7 @@
     </div>
     <table ng-table="list.tableParams" class="table table-bordered table-hover table-condensed" ng-form="list.tableForm" disable-filter="list.isAdding" tracked-table="list.tableTracker">
         <tr ng-repeat="row in $data" ng-form="rowForm" tracked-table-row="row">
-            <td title="'捐赠时间'" filter="{DonateTime: 'text'}" sortable="'DonateTime'">
+            <td title="'打赏时间'" filter="{DonateTime: 'text'}" sortable="'DonateTime'">
                 {{row.DonateTime|date:'yyyy-MM-dd'}}
             </td>
             <td title="'昵称'" filter="{NickName: 'text'}" sortable="'NickName'">
@@ -29,7 +29,7 @@
             <td title="'金额'" filter="{Amount: 'number'}" sortable="'Amount'">
                 {{row.Amount}}
             </td>
-            <td title="'捐赠方式'" filter="{Via: 'text'}" sortable="'Via'">
+            <td title="'打赏方式'" filter="{Via: 'text'}" sortable="'Via'">
                 {{row.Via}}
             </td>
             <td title="'Email'" filter="{Email: 'text'}" sortable="'Email'">

+ 269 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/partner.html

@@ -0,0 +1,269 @@
+<style>
+    .height800 {
+        max-height: 800px;
+        overflow-y: auto;
+    }
+</style>
+<div>
+    <div class="form-inline pull-right">
+        <button class="btn btn-info waves-effect" ng-click="list.GetPageData(paginationConf.currentPage, paginationConf.itemsPerPage);">
+            <span class="glyphicon glyphicon-refresh"></span>
+        </button>
+        <button class="btn btn-info waves-effect" ng-click="add()">
+            <span class="glyphicon glyphicon-plus"></span>
+        </button>
+        <div class="input-group">
+            <span class="input-group-addon">全局搜索:</span>
+            <div class="fg-line">
+                <input type="text" class="form-control" ng-change="search(kw)" ng-model="kw" placeholder="全局搜索" />
+            </div>
+        </div>
+    </div>
+    <table ng-table="list.tableParams" class="table table-bordered table-hover table-condensed listTable" ng-form="list.tableForm" tracked-table="list.tableTracker">
+        <tr ng-repeat="row in $data" ng-form="rowForm" tracked-table-row="row">
+            <td title="'标题'">
+                <a ng-click="detail(row)" target="_blank">{{row.Title}}</a>
+            </td>
+            <td title="'推广地址'">
+                <a ng-href="{{row.Url}}" target="_blank">{{row.Url}}</a>
+            </td>
+            <td title="'当前竞价'">
+                {{row.Price}}
+            </td>
+            <td title="'权重'">
+                {{row.Weight}}
+            </td>
+            <td title="'点击量'">
+                {{row.ViewCount}}
+            </td>
+            <td title="'分类'">
+                {{row.CategoryName}}
+            </td>
+            <td title="'创建时间'">
+                {{row.CreateTime|date:'yyyy-MM-dd HH:mm:ss'}}
+            </td>
+            <td title="'修改时间'">
+                {{row.UpdateTime|date:'yyyy-MM-dd HH:mm:ss'}}
+            </td>
+            <td title="'状态'">
+                <label class="el-switch">
+                    <input type="checkbox" name="switch" ng-checked="row.Status==1">
+                    <span class="el-switch-style" ng-click="changeState(row)"></span>
+                </label>
+            </td>
+            <td title="'操作'" style="width: 185px;">
+                <div class="btn-group">
+                    <button class="btn btn-info btn-sm waves-effect" ng-click="edit(row)">
+                        <i class="icon-pencil"></i>
+                    </button>
+                    <button class="btn btn-danger btn-sm waves-effect" ng-click="remove(row)">
+                        <i class="icon-bin"></i>
+                    </button>
+                </div>
+            </td>
+        </tr>
+    </table>
+    <tm-pagination conf="paginationConf"></tm-pagination>
+</div>
+<div id="edit" class="modal">
+    <div class="container-fluid" style="margin: 15px 0;">
+        <div class="card-body bgm-white">
+            <div ng-show="!isAdd">
+                <input type="hidden" ng-model="partner.Id">
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">广告标题:</label>
+                    <div class="fg-line">
+                        <input type="text" class="form-control" id="title" placeholder="请输入广告标题" ng-model="partner.Title">
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">广告描述:</label>
+                    <div class="fg-line">
+                        <textarea type="text" class="form-control" id="desc" placeholder="请输入广告描述" ng-model="partner.Description" rows="5"></textarea>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">推广地址:</label>
+                    <div class="fg-line">
+                        <input type="text" class="form-control" id="url" placeholder="请输入推广目标地址" ng-model="partner.Url">
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">宣传大图片:</label>
+                    <div class="fg-line">
+                        <input type="text" class="form-control" id="imgurl" placeholder="请输入图片URL" ng-model="partner.ImageUrl">
+                    </div>
+                    <span class="input-group-btn">
+                        <button class="btn btn-success" ng-click="upload('ImageUrl')">上传本地图片</button>
+                    </span>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">宣传小图片:</label>
+                    <div class="fg-line">
+                        <input type="text" class="form-control" id="ThumbImgUrl" placeholder="请输入图片URL" ng-model="partner.ThumbImgUrl">
+                    </div>
+                    <span class="input-group-btn">
+                        <button class="btn btn-success" ng-click="upload('ThumbImgUrl')">上传本地图片</button>
+                    </span>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">分类:</label>
+                    <div class="ui fluid top search selection dropdown category">
+                        <input name="category" type="hidden" id="category">
+                        <i class="dropdown icon"></i>
+                        <div class="default text">请选择分类</div>
+                        <div class="menu">
+                            <div class="item" data-value>无</div>
+                            <div class="item" ng-repeat="item in cat track by $index" data-value="{{item.Id}}">{{item.Name}}</div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">推广区域:</label>
+                    <div class="ui fluid search multiple selection dropdown types">
+                        <input name="types" type="hidden" id="types">
+                        <i class="dropdown icon"></i>
+                        <div class="default text">请选择推广区域</div>
+                        <div class="menu">
+                            <div class="item" data-value="1">轮播图</div>
+                            <div class="item" data-value="2">列表项</div>
+                            <div class="item" data-value="3">边栏</div>
+                            <div class="item" data-value="4">内页</div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">权重:</label>
+                    <div class="fg-line">
+                        <input type="number" class="form-control" id="weight" placeholder="权重值" ng-model="partner.Weight">
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="input-group">
+                    <label for="desc" class="input-group-addon control-label">当前竞价:</label>
+                    <div class="fg-line">
+                        <input type="number" class="form-control" id="price" placeholder="竞价" ng-model="partner.Price">
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="col-xs-12">
+                    <div class="fg-line">
+                        <div class="btn-group">
+                            <button type="button" class="btn btn-info" ng-click="submit(partner)">
+                                保存
+                            </button>
+                            <button type="button" class="btn btn-danger" ng-click="closeAll()">
+                                取消
+                            </button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="detail" class="modal">
+    <div class="container-fluid" style="margin: 15px 0;">
+        <div class="card-body bgm-white height800">
+            <table class="table table-bordered table-hover">
+                <tr>
+                    <td style="min-width: 110px">描述</td>
+                    <td colspan="2">{{partner.Description}}</td>
+                </tr>
+                <tr>
+                    <td>推广地址</td>
+                    <td colspan="2">
+                        <a ng-href="{{partner.Url}}" target="_blank">{{partner.Url}}</a>
+                    </td>
+                </tr>
+                <tr>
+                    <td>类型:</td>
+                    <td>
+                        <div class="ui fluid search multiple selection dropdown types">
+                            <input name="types" type="hidden">
+                            <i class="dropdown icon"></i>
+                            <div class="default text">类型</div>
+                            <div class="menu">
+                                <div class="item" data-value="1">轮播图</div>
+                                <div class="item" data-value="2">列表项</div>
+                                <div class="item" data-value="3">边栏</div>
+                                <div class="item" data-value="4">内页</div>
+                            </div>
+                        </div>
+                    </td>
+                    <td>分类:{{partner.CategoryName}}</td>
+                </tr>
+                <tr>
+                    <td>宣传大图片</td>
+                    <td colspan="2">
+                        <a ng-href="{{partner.ImageUrl}}" target="_blank">
+                            <img class="img-rounded img-responsive" style="max-height: fit-content" src="{{partner.ImageUrl}}" />
+                        </a>
+                    </td>
+                </tr>
+                <tr>
+                    <td>宣传小图片</td>
+                    <td colspan="2">
+                        <a ng-href="{{partner.ThumbImgUrl}}" target="_blank">
+                            <img class="img-rounded img-responsive" style="max-height: fit-content" src="{{partner.ThumbImgUrl}}" />
+                        </a>
+                    </td>
+                </tr>
+                <tr>
+                    <td>竞价:{{partner.Price}}</td>
+                    <td>权重:{{partner.Weight}}</td>
+                    <td>点击量:{{partner.ViewCount}}</td>
+                </tr>
+                <tr>
+                    <td>上架时间:</td>
+                    <td>{{partner.CreateTime|date:'yyyy-MM-dd HH:mm:ss'}}</td>
+                    <td>更新时间:{{partner.UpdateTime|date:'yyyy-MM-dd HH:mm:ss'}}</td>
+                </tr>
+            </table>
+            <div class="btn-group">
+                <button class="btn btn-info btn-sm waves-effect" ng-click="edit(partner)">
+                    编辑
+                </button>
+                <button class="btn btn-danger btn-sm waves-effect" ng-click="remove(partner)">
+                    删除
+                </button>
+            </div>
+        </div>
+    </div>
+</div>
+<div id="img-upload" class="modal">
+    <form id="uploadform" onsubmit="return false" enctype="multipart/form-data">
+        <div class="file-box">
+            <div class="input-group">
+                <div class="fg-line">
+                    <input type="text" name="file0" disabled class="form-control" placeholder="请选择一张图片" />
+                </div>
+                <span class="input-group-btn hidden">
+                    <a href="javascript:void(0);" class="btn btn-info ">浏览</a>
+                </span>
+                <input type="file" class="uploadFile" name="file" onchange="getFile(this, 'file0')" style="width: 200px"/>
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-primary" ng-click="uploadImage(imgField)">开始上传</button>
+                </span>
+            </div>
+        </div>
+    </form>
+</div>

+ 1 - 6
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/post/postlist.html

@@ -1,9 +1,4 @@
-<style>
-    .page-list {
-        display: flex;
-    }
-</style>
-<div>
+<div>
     <div class="form-inline pull-right">
         <button class="btn btn-info waves-effect" ng-click="list.GetPageData(paginationConf.currentPage, paginationConf.itemsPerPage);">
             <span class="glyphicon glyphicon-refresh"></span>

+ 1 - 1
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/system/home.html

@@ -151,7 +151,7 @@
         </div>
     </div>
     <div class="row">
-        <h2>捐赠二维码</h2>
+        <h2>打赏二维码</h2>
         <div class="col-md-3 col-xs-6">
             <h3>支付宝:</h3>
             <img class="img img-responsive img-thumbnail img-rounded" ng-src="{{Settings.Donate}}" style="max-height: 300px;" ng-click="setImage('Donate')" />

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff