diff --git a/BBWYB.Client/APIServices/BaseApiService.cs b/BBWYB.Client/APIServices/BaseApiService.cs new file mode 100644 index 0000000..2234c1f --- /dev/null +++ b/BBWYB.Client/APIServices/BaseApiService.cs @@ -0,0 +1,58 @@ +using BBWY.Common.Http; +using BBWY.Common.Models; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net.Http; +using Microsoft.Extensions.Configuration; +namespace BBWY.Client.APIServices +{ + public class BaseApiService + { + private RestApiService restApiService; + + protected GlobalContext globalContext; + + public BaseApiService(RestApiService restApiService, GlobalContext globalContext) + { + this.restApiService = restApiService; + this.globalContext = globalContext; + } + + protected ApiResponse SendRequest(string apiHost, + string apiPath, + object param, + IDictionary headers, + HttpMethod httpMethod, + string contentType = RestApiService.ContentType_Json, + ParamPosition paramPosition = ParamPosition.Body, + bool enableRandomTimeStamp = false) + { + try + { + if (headers == null) + headers = new Dictionary(); + if (!headers.ContainsKey("ClientCode")) + headers.Add("ClientCode", "BBWY"); + if (!headers.ContainsKey("ClientVersion")) + headers.Add("ClientVersion", "1.0.0.0"); + if (!headers.ContainsKey("Authorization") && !string.IsNullOrEmpty(globalContext.UserToken)) + headers.Add("Authorization", $"Bearer {globalContext.UserToken}"); + if (!headers.ContainsKey("qy")) + headers.Add("qy", "qy"); + + var result = restApiService.SendRequest(apiHost, apiPath, param, headers, httpMethod, contentType, paramPosition, enableRandomTimeStamp); + if (result.StatusCode != System.Net.HttpStatusCode.OK && + result.Content.Contains("\"Success\"") && + result.Content.Contains("\"Msg\"") && + result.Content.Contains("\"Data\"")) + throw new BusinessException($"{result.StatusCode} {result.Content}") { Code = (int)result.StatusCode }; + return JsonConvert.DeserializeObject>(result.Content); + } + catch (Exception ex) + { + return ApiResponse.Error((ex is BusinessException) ? (ex as BusinessException).Code : 0, ex.Message); + } + } + } +} diff --git a/BBWYB.Client/APIServices/MdsApiService.cs b/BBWYB.Client/APIServices/MdsApiService.cs new file mode 100644 index 0000000..cbbe17b --- /dev/null +++ b/BBWYB.Client/APIServices/MdsApiService.cs @@ -0,0 +1,113 @@ +using BBWYB.Client.Models; +using BBWY.Common.Http; +using BBWY.Common.Models; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace BBWY.Client.APIServices +{ + public class MdsApiService : BaseApiService, IDenpendency + { + public MdsApiService(RestApiService restApiService, GlobalContext globalContext) : base(restApiService, globalContext) + { + + } + + public ApiResponse GetUserInfo(string userToken) + { + return SendRequest(globalContext.MDSApiHost, + "/TaskList/User/GetUserInfo", + null, + new Dictionary() + { + { "Authorization", $"Bearer {userToken}" } + }, HttpMethod.Get); + } + + + + //public ApiResponse> GetShopsByUserTeam(long userId) + //{ + // return SendRequest>(globalContext.MDSApiHost, "TaskList/Shop/GetShopsByUserTeam", $"userId={userId}", null, System.Net.Http.HttpMethod.Get); + //} + + public ApiResponse> GetShopDetailList() + { + var response = new ApiResponse>(); + var response2 = SendRequest(globalContext.MDSApiHost, "TaskList/UserDepartment/GetShopDetailList", null, null, HttpMethod.Get); + if (!response.Success) + { + response.Code = response2.Code; + response.Msg = response2.Msg; + return response; + } + + response.Data = new List(); + foreach (var jDepartment in response2.Data) + { + var jayShops = jDepartment.Value("ShopList"); + if (jayShops == null || !jayShops.HasValues) + continue; //排除空店部门 + var d = new Department() + { + Id = jDepartment.Value("Id"), + Name = jDepartment.Value("DepartmentName") + }; + response.Data.Add(d); + foreach (var jShop in jayShops) + { + var shopId = jShop.Value("ShopId"); + if (shopId == null || string.IsNullOrEmpty(jShop.Value("AppToken"))) + continue; //排除未授权 + try + { + var jayAccounts = jShop.Value("AccountList"); + if ((jayAccounts == null || !jayAccounts.HasValues) && d.ShopList.Count(s => s.ShopId == shopId) > 0) + { + continue; + } + var shop = new Shop() + { + ShopId = shopId.Value, + AppKey = jShop.Value("AppKey"), + AppSecret = jShop.Value("AppSecret"), + AppToken = jShop.Value("AppToken"), + ManagePwd = jShop.Value("ManagePwd"), + Platform = (Platform)jShop.Value("PlatformId"), + PlatformCommissionRatio = jShop.Value("PlatformCommissionRatio") ?? 0.05M, + ShopName = jShop.Value("ShopName"), + VenderType = jShop.Value("ShopType"), + TeamId = jShop.Value("TeamId") + }; + d.ShopList.Add(shop); + + shop.PurchaseAccountList = new List(); + foreach (var jPurchaseAccount in jayAccounts) + { + shop.PurchaseAccountList.Add(new PurchaseAccount() + { + Id = jPurchaseAccount.Value("Id"), + AccountName = jPurchaseAccount.Value("AccountName"), + AppKey = jPurchaseAccount.Value("AppKey"), + AppSecret = jPurchaseAccount.Value("AppSecret"), + AppToken = jPurchaseAccount.Value("AppToken"), + ShopId = shop.ShopId, + PurchasePlatformId = (Platform)jPurchaseAccount.Value("AppPlatformId") + }); + } + } + catch (Exception ex) + { + Console.WriteLine(jShop.ToString()); + throw; + } + } + } + + return response; + } + } +} diff --git a/BBWYB.Client/APIServices/OneBoundAPIService.cs b/BBWYB.Client/APIServices/OneBoundAPIService.cs new file mode 100644 index 0000000..d88d80f --- /dev/null +++ b/BBWYB.Client/APIServices/OneBoundAPIService.cs @@ -0,0 +1,51 @@ +using BBWY.Common.Http; +using BBWY.Common.Models; +using Newtonsoft.Json.Linq; +using System; +using System.Net.Http; + +namespace BBWY.Client.APIServices +{ + public class OneBoundAPIService : IDenpendency + { + private RestApiService restApiService; + private string key = "t5060712539"; + private string secret = "20211103"; + + public OneBoundAPIService(RestApiService restApiService) + { + this.restApiService = restApiService; + } + + /// + /// 产品详细信息接口 + /// + /// 1699/jd/taobao 更多值参阅https://open.onebound.cn/help/api/ + /// + /// + /// + /// + public ApiResponse GetProductInfo(string platform, string productId) + { + try + { + //https://api-gw.onebound.cn/1688/item_get/key=t5060712539&secret=20211103&num_iid=649560646832&lang=zh-CN&cache=no + var result = restApiService.SendRequest("https://api-gw.onebound.cn/", $"{platform}/item_get", $"key={key}&secret={secret}&num_iid={productId}&lang=zh-CN&cache=no", null, HttpMethod.Get, paramPosition: ParamPosition.Query, enableRandomTimeStamp: true); + if (result.StatusCode != System.Net.HttpStatusCode.OK) + throw new Exception($"{result.StatusCode} {result.Content}"); + + var j = JObject.Parse(result.Content); + return new ApiResponse() + { + Data = j, + Code = j.Value("error_code") == "0000" ? 200 : 0, + Msg = j.Value("error") + }; + } + catch (Exception ex) + { + return ApiResponse.Error(0, ex.Message); + } + } + } +} diff --git a/BBWYB.Client/APIServices/ProductService.cs b/BBWYB.Client/APIServices/ProductService.cs new file mode 100644 index 0000000..9bcb591 --- /dev/null +++ b/BBWYB.Client/APIServices/ProductService.cs @@ -0,0 +1,49 @@ +using BBWYB.Client.Models; +using BBWY.Common.Http; +using BBWY.Common.Models; +using System.Collections.Generic; +using System.Net.Http; + +namespace BBWY.Client.APIServices +{ + public class ProductService : BaseApiService, IDenpendency + { + public ProductService(RestApiService restApiService, GlobalContext globalContext) : base(restApiService, globalContext) { } + + public ApiResponse GetProductList(string spu, string productName, string productItem, int pageIndex) + { + return SendRequest(globalContext.BBYWApiHost, + "api/product/GetProductList", + new + { + Spu = spu, + ProductName = productName, + ProductItem = productItem, + PageIndex = pageIndex, + Platform = globalContext.User.Shop.Platform, + AppKey = globalContext.User.Shop.AppKey, + AppSecret = globalContext.User.Shop.AppSecret, + AppToken = globalContext.User.Shop.AppToken + }, + null, + HttpMethod.Post); + } + + public ApiResponse> GetProductSkuList(string spu, string sku) + { + return SendRequest>(globalContext.BBYWApiHost, + "api/product/GetProductSkuList", + new + { + Spu = spu, + Sku = sku, + Platform = globalContext.User.Shop.Platform, + AppKey = globalContext.User.Shop.AppKey, + AppSecret = globalContext.User.Shop.AppSecret, + AppToken = globalContext.User.Shop.AppToken + }, + null, + HttpMethod.Post); + } + } +} diff --git a/BBWYB.Client/APIServices/PurchaseOrderService.cs b/BBWYB.Client/APIServices/PurchaseOrderService.cs new file mode 100644 index 0000000..7ba0ffa --- /dev/null +++ b/BBWYB.Client/APIServices/PurchaseOrderService.cs @@ -0,0 +1,166 @@ +using BBWYB.Client.Models; +using BBWY.Common.Http; +using BBWY.Common.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; + +namespace BBWY.Client.APIServices +{ + public class PurchaseOrderService : BaseApiService, IDenpendency + { + public PurchaseOrderService(RestApiService restApiService, GlobalContext globalContext) : base(restApiService, globalContext) + { + + } + + public ApiResponse AddPurchaseOrder(PurchaseOrder purchaseOrder) + { + return SendRequest(globalContext.BBYWApiHost, + "api/PurchaseOrder/AddPurchaseOrder", + purchaseOrder, + null, + HttpMethod.Post); + } + + public ApiResponse EditPurchaseOrder(PurchaseOrder purchaseOrder) + { + return SendRequest(globalContext.BBYWApiHost, + "api/PurchaseOrder/EditPurchaseOrder", + purchaseOrder, + null, + HttpMethod.Put); + } + + public ApiResponse> GetList(IList skuIdList, StorageType storageType, long shopId) + { + return SendRequest>(globalContext.BBYWApiHost, + "api/PurchaseOrder/GetList", + new { SkuIdList = skuIdList, StorageType = storageType, ShopId = shopId }, + null, + HttpMethod.Post); + } + + public ApiResponse DeletePurchaseOrder(long id) + { + return SendRequest(globalContext.BBYWApiHost, + $"api/purchaseOrder/deletePurchaseOrder/{id}", + null, + null, + HttpMethod.Delete); + } + + /// + /// 预览订单 + /// + /// + /// + /// + public ApiResponse PreviewPurchaseOrder(Consignee consignee, IList purchaseSchemeProductSkuList, Platform purchasePlatform, PurchaseAccount purchaseAccount, PurchaseOrderMode purchaseOrderMode) + { + return SendRequest(globalContext.BBYWApiHost, "api/purchaseOrder/PreviewPurchaseOrder", new + { + purchaseOrderMode, + consignee, + CargoParamList = purchaseSchemeProductSkuList.Select(sku => new + { + ProductId = sku.PurchaseProductId, + SkuId = sku.PurchaseSkuId, + SpecId = sku.PurchaseSkuSpecId, + Quantity = sku.ItemTotal, + BelongSkuId = sku.SkuId + }), + Platform = purchasePlatform, + AppKey = purchaseAccount.AppKey, + AppSecret = purchaseAccount.AppSecret, + AppToken = purchaseAccount.AppToken, + SaveResponseLog = true + }, null, HttpMethod.Post); + } + + /// + /// 创建采购单 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public ApiResponse FastCreateOrder(Consignee consignee, + IList purchaseSchemeProductSkuList, + Platform purchasePlatform, + PurchaseAccount purchaseAccount, + PurchaseOrderMode purchaseOrderMode, + string tradeMode, + string remark, + string orderId, + long shopId, + long purchaseAccountId, + string buyerAccount, + string sellerAccount, + string purchaserId, + decimal platformCommissionRatio, + string extensions) + { + return SendRequest(globalContext.BBYWApiHost, "api/purchaseOrder/NewFastCreateOrder", new + { + purchaseOrderMode, + consignee, + CargoParamList = purchaseSchemeProductSkuList.Select(sku => new + { + ProductId = sku.PurchaseProductId, + SkuId = sku.PurchaseSkuId, + SpecId = sku.PurchaseSkuSpecId, + Quantity = sku.ItemTotal, + BelongSkuId = sku.SkuId + }), + Platform = purchasePlatform, + AppKey = purchaseAccount.AppKey, + AppSecret = purchaseAccount.AppSecret, + AppToken = purchaseAccount.AppToken, + SaveResponseLog = true, + tradeMode, + remark, + orderId, + shopId, + purchaseAccountId, + buyerAccount, + sellerAccount, + purchaserId, + platformCommissionRatio, + extensions + }, null, HttpMethod.Post); + } + + + + /// + /// 查询审核采购单 + /// + /// + /// + /// + /// + public ApiResponse> GetAuditPurchaseOrderList(IList shopIdList, DateTime startDate, DateTime endDate) + { + return SendRequest>(globalContext.BBYWApiHost, "Api/PurchaseOrder/GetAuditPurchaseOrderList", new + { + startDate, + endDate, + shopIdList + }, null, HttpMethod.Post); + } + } +} diff --git a/BBWYB.Client/APIServices/PurchaseProductAPIService.cs b/BBWYB.Client/APIServices/PurchaseProductAPIService.cs new file mode 100644 index 0000000..bbc55ea --- /dev/null +++ b/BBWYB.Client/APIServices/PurchaseProductAPIService.cs @@ -0,0 +1,278 @@ +using BBWY.Common.Http; +using BBWY.Common.Models; +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; + +namespace BBWY.Client.APIServices +{ + public class PurchaseProductAPIService : IDenpendency + { + private RestApiService restApiService; + private IMemoryCache memoryCache; + private string oneBoundKey = "t5060712539"; + private string oneBoundSecret = "20211103"; + + private string qtAppId = "BBWY2023022001"; + private string qtAppSecret = "908e131365d5448ca651ba20ed7ddefe"; + + private TimeSpan purchaseProductCacheTimeSpan; + //private TimeSpan _1688SessionIdTimeSpan; + + //private ConcurrentDictionary purchaseSchemeProductSkus)> productChaches; + + private IDictionary _1688ProductDetailRequestHeader; + + public PurchaseProductAPIService(RestApiService restApiService, IMemoryCache memoryCache) + { + this.restApiService = restApiService; + this.memoryCache = memoryCache; + _1688ProductDetailRequestHeader = new Dictionary() + { + { "Host","detail.1688.com"}, + { "User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Safari/537.36 Edg/104.0.1293.70"}, + { "Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"}, + { "Accept-Encoding","gzip, deflate, br"}, + { "Accept-Language","zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6"} + }; + purchaseProductCacheTimeSpan = TimeSpan.FromDays(1); + } + + public (Purchaser purchaser, IList purchaseSchemeProductSkus)? GetProductInfo(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode, PurchaseProductAPIMode apiMode) + { + if (memoryCache.TryGetValue<(Purchaser, IList)>($"{purchaseProductId}_{priceMode}", out var tuple)) + return tuple.Copy(); + + (Purchaser purchaser, IList purchaseSchemeProductSkus)? data = null; + + if (apiMode == PurchaseProductAPIMode.Spider) + { + data = LoadFromSpider(platform, productId, skuId, purchaseProductId, priceMode); + if (data == null) + data = LoadFromOneBound(platform, productId, skuId, purchaseProductId, priceMode); + } + else if (apiMode == PurchaseProductAPIMode.OneBound) + { + data = LoadFromOneBound(platform, productId, skuId, purchaseProductId, priceMode); + if (data == null) + data = LoadFromSpider(platform, productId, skuId, purchaseProductId, priceMode); + } + + if (data != null) + { + try + { + memoryCache.Set<(Purchaser, IList)>($"{purchaseProductId}_{priceMode}", data.Value, purchaseProductCacheTimeSpan); + } + catch { } + } + + return data?.Copy(); + } + + private (Purchaser purchaser, IList purchaseSchemeProductSkus)? LoadFromOneBound(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode) + { + try + { + string platformStr = string.Empty; + if (platform == Platform.阿里巴巴) + platformStr = "1688"; + + if (string.IsNullOrEmpty(platformStr)) + return null; + + var result = restApiService.SendRequest("https://api-gw.onebound.cn/", $"{platformStr}/item_get", $"key={oneBoundKey}&secret={oneBoundSecret}&num_iid={purchaseProductId}&lang=zh-CN&cache=no&agent={(priceMode == PurchaseOrderMode.批发 ? 0 : 1)}", null, HttpMethod.Get, paramPosition: ParamPosition.Query, enableRandomTimeStamp: true); + if (result.StatusCode != System.Net.HttpStatusCode.OK) + throw new Exception($"{result.StatusCode} {result.Content}"); + + var jobject = JObject.Parse(result.Content); + var isOK = jobject.Value("error_code") == "0000"; + if (isOK) + { + var skuJArray = (JArray)jobject["item"]["skus"]["sku"]; + if (skuJArray.Count == 0) + { + //errorMsg = $"商品{purchaseSchemeProduct.PurchaseProductId}缺少sku信息"; + return null; + } + + var list = skuJArray.Select(j => new PurchaseSchemeProductSku() + { + ProductId = productId, + SkuId = skuId, + PurchaseProductId = purchaseProductId, + Price = j.Value("price"), + PurchaseSkuId = j.Value("sku_id"), + PurchaseSkuSpecId = j.Value("spec_id"), + Title = j.Value("properties_name"), + Logo = GetOneBoundSkuLogo(j, (JArray)jobject["item"]["prop_imgs"]["prop_img"]) + }).ToList(); + + var purchaserId = jobject["item"]["seller_info"].Value("user_num_id"); + var purchaserName = jobject["item"]["seller_info"].Value("title"); + if (string.IsNullOrEmpty(purchaserName)) + purchaserName = jobject["item"]["seller_info"].Value("shop_name"); + var purchaserLocation = jobject["item"].Value("location"); + + return (new Purchaser() + { + Id = purchaserId, + Name = purchaserName, + Location = purchaserLocation + }, list); + } + } + catch { } + { + return null; + } + } + + private string GetOneBoundSkuLogo(JToken skuJToken, JArray prop_img) + { + if (!prop_img.HasValues) + return "pack://application:,,,/Resources/Images/defaultItem.png"; + var properties = skuJToken.Value("properties").Split(';', StringSplitOptions.RemoveEmptyEntries); + foreach (var p in properties) + { + var imgJToken = prop_img.FirstOrDefault(g => g.Value("properties") == p); + if (imgJToken != null) + return imgJToken.Value("url"); + } + return "pack://application:,,,/Resources/Images/defaultItem.png"; + } + + private (Purchaser purchaser, IList purchaseSchemeProductSkus)? LoadFromSpider(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode) + { + switch (platform) + { + case Platform.阿里巴巴: + return LoadFrom1688Spider(platform, productId, skuId, purchaseProductId, priceMode); + case Platform.拳探: + return LoadFromQTSpider(platform, productId, skuId, purchaseProductId, priceMode); + } + return null; + } + + private (Purchaser purchaser, IList purchaseSchemeProductSkus)? LoadFrom1688Spider(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode) + { + //https://detail.1688.com/offer/672221374773.html?clickid=65f3772cd5d16f190ce4991414607&sessionid=3de47a0c26dcbfde4692064bd55861&sk=order + + //globalData/tempModel/sellerUserId + //globalData/tempModel/companyName + //data/1081181309101/data/location + + + //data/1081181309582/data/pirceModel/[currentPrices]/[0]price + + try + { + var _1688pageResult = restApiService.SendRequest("https://detail.1688.com", + $"offer/{purchaseProductId}.html", + $"clickid={Guid.NewGuid().ToString().Md5Encrypt()}&sessionid={Guid.NewGuid().ToString().Md5Encrypt()}&sk={(priceMode == PurchaseOrderMode.批发 ? "order" : "consign")}", + _1688ProductDetailRequestHeader, + HttpMethod.Get, + httpClientName: "gzip"); + + if (_1688pageResult.StatusCode != System.Net.HttpStatusCode.OK) + return null; + + var match = Regex.Match(_1688pageResult.Content, @"(window\.__INIT_DATA=)(.*)(\r*\n*\s*)"); + if (!match.Success) + return null; + + var jsonStr = match.Groups[2].Value; + var jobject = JObject.Parse(jsonStr); + + //16347413030323 + var purchaser = new Purchaser() + { + Id = jobject["globalData"]["tempModel"]["sellerUserId"].ToString(), + Name = jobject["globalData"]["tempModel"]["companyName"].ToString(), + Location = jobject["data"]["1081181309101"] != null ? + jobject["data"]["1081181309101"]["data"]["location"].ToString() : + jobject["data"]["16347413030323"]["data"]["location"].ToString() + }; + + var colorsProperty = jobject["globalData"]["skuModel"]["skuProps"].FirstOrDefault(j => j.Value("fid") == 3216 || + j.Value("fid") == 1627207 || + j.Value("fid") == 1234 || + j.Value("fid") == 3151)["value"] + .Children() + .Select(j => new + { + name = j.Value("name"), + imageUrl = j.Value("imageUrl") + }).ToList(); + + var firstPrice = jobject["data"]["1081181309582"] != null ? + jobject["data"]["1081181309582"]["data"]["priceModel"]["currentPrices"][0].Value("price") : + jobject["data"]["16347413030316"]["data"]["priceModel"]["currentPrices"][0].Value("price"); + + var purchaseSchemeProductSkus = new List(); + + foreach (var jsku in jobject["globalData"]["skuModel"]["skuInfoMap"].Children()) + { + var jskuProperty = jsku as JProperty; + var name = jskuProperty.Name; + var matchName = name.Contains(">") ? name.Substring(0, name.IndexOf(">")) : name; + var value = jskuProperty.Value; + + var skuPrice = value.Value("price"); + + purchaseSchemeProductSkus.Add(new PurchaseSchemeProductSku() + { + ProductId = productId, + SkuId = skuId, + PurchaseProductId = purchaseProductId, + Price = skuPrice == 0M ? firstPrice : skuPrice, + Title = name, + PurchaseSkuId = value.Value("skuId"), + PurchaseSkuSpecId = value.Value("specId"), + Logo = colorsProperty.FirstOrDefault(c => c.name == matchName)?.imageUrl ?? "pack://application:,,,/Resources/Images/defaultItem.png" + }); + } + + return (purchaser, purchaseSchemeProductSkus); + } + catch + { + + return null; + } + } + + private (Purchaser purchaser, IList purchaseSchemeProductSkus)? LoadFromQTSpider(Platform platform, string productId, string skuId, string purchaseProductId, PurchaseOrderMode priceMode) + { + try + { + var response = quanTanProductClient.GetProductInfo(purchaseProductId, qtAppId, qtAppSecret); + if (response.Status != 200) + return null; + return (new Purchaser() + { + Id = response.Data.Supplier.VenderId, + Name = response.Data.Supplier.VerdenName, + Location = response.Data.Supplier.Location + }, response.Data.ProductSku.Select(qtsku => new PurchaseSchemeProductSku() + { + ProductId = productId, + SkuId = skuId, + PurchaseProductId = purchaseProductId, + Price = qtsku.Price, + Title = qtsku.Title, + PurchaseSkuId = qtsku.SkuId, + PurchaseSkuSpecId = string.Empty, + Logo = qtsku.Logo + }).ToList()); + } + catch + { + return null; + } + } + } +} diff --git a/BBWYB.Client/APIServices/PurchaseService.cs b/BBWYB.Client/APIServices/PurchaseService.cs new file mode 100644 index 0000000..80d980d --- /dev/null +++ b/BBWYB.Client/APIServices/PurchaseService.cs @@ -0,0 +1,84 @@ +using BBWYB.Client.Models; +using BBWY.Common.Http; +using BBWY.Common.Models; +using System.Collections.Generic; +using System.Net.Http; + +namespace BBWY.Client.APIServices +{ + public class PurchaseService : BaseApiService, IDenpendency + { + public PurchaseService(RestApiService restApiService, GlobalContext globalContext) : base(restApiService, globalContext) { } + + /// + /// 获取采购方案 + /// + /// + /// + /// + /// + /// + /// + public ApiResponse> GetPurchaseSchemeList(IList skuIdList = null, + string purchaserId = "", + long? shopId = null, + long? schemeId = null, + Platform? purchasePlatform = null) + { + return SendRequest>(globalContext.BBYWApiHost, + "api/PurchaseScheme/GetPurchaseSchemeList", + new + { + skuIdList, + purchaserId, + shopId, + schemeId, + purchasePlatform + }, + null, + HttpMethod.Post); + } + + /// + /// 获取共有采购商 + /// + /// + /// + /// + public ApiResponse> GetSharePurchaser(IList skuIdList, long shopId) + { + return SendRequest>(globalContext.BBYWApiHost, + "api/PurchaseScheme/GetSharePurchaser", + new { skuIdList, shopId }, + null, + HttpMethod.Post); + } + + public ApiResponse EditPurchaseScheme(IList addPurchaseSchemeList, IList editPurchaseSchemeList) + { + return SendRequest(globalContext.BBYWApiHost, + "api/purchasescheme/EditPurchaseScheme", + new + { + AddPurchaseSchemeList = addPurchaseSchemeList, + EditPurchaseSchemeList = editPurchaseSchemeList + }, + null, + HttpMethod.Post); + } + + public ApiResponse DeletePurchaser(string productId, string purchaserId) + { + return SendRequest(globalContext.BBYWApiHost, + "api/purchasescheme/DeletePurchaser", + new { productId, purchaserId }, + null, + HttpMethod.Delete); + } + + public ApiResponse DeletePurchaseScheme(long schemeId) + { + return SendRequest(globalContext.BBYWApiHost, $"api/purchasescheme/DeletePurchaseScheme/{schemeId}", null, null, HttpMethod.Delete); + } + } +} diff --git a/BBWYB.Client/APIServices/ShopService.cs b/BBWYB.Client/APIServices/ShopService.cs new file mode 100644 index 0000000..e50a002 --- /dev/null +++ b/BBWYB.Client/APIServices/ShopService.cs @@ -0,0 +1,91 @@ +using BBWYB.Client.Models; +using BBWY.Common.Http; +using BBWY.Common.Models; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; + +namespace BBWY.Client.APIServices +{ + public class ShopService : BaseApiService, IDenpendency + { + public ShopService(RestApiService restApiService, GlobalContext globalContext) : base(restApiService, globalContext) { } + + public ApiResponse SaveShopSetting(long shopId, + string managerPwd, + decimal platformCommissionRatio, + PurchaseAccount purchaseAccount, + string dingDingWebHook, + string dingDingKey, + int skuSafeTurnoverDays, + string siNanDingDingWebHook, + string siNanDingDingKey, + int siNanPolicyLevel) + { + return SendRequest(globalContext.BBYWApiHost, "api/vender/SaveShopSetting", new + { + shopId, + managerPwd, + platformCommissionRatio, + PurchaseAccountId = purchaseAccount.Id, + purchaseAccount.AccountName, + purchaseAccount.AppKey, + purchaseAccount.AppSecret, + purchaseAccount.AppToken, + purchaseAccount.PurchasePlatformId, + dingDingWebHook, + dingDingKey, + skuSafeTurnoverDays, + siNanDingDingWebHook, + siNanDingDingKey, + siNanPolicyLevel + }, null, HttpMethod.Post); + } + + /// + /// 根据订单Id查询归属店铺 + /// + /// + /// + public ApiResponse> GetOrderBelongShop(IList orderIdList) + { + return SendRequest>(globalContext.BBYWApiHost, "api/order/GetOrderBelongShop", orderIdList, null, HttpMethod.Post); + } + + /// + /// 获取部门及下属店铺 + /// + /// + public ApiResponse> GetDepartmentList() + { + return SendRequest>(globalContext.BBYWApiHost, "api/vender/GetDeparmentList", null, + new Dictionary() + { + { "bbwyTempKey", "21jfhayu27q" } + }, HttpMethod.Get); + } + + public ApiResponse> GetShopListByIds(IList shopIds) + { + return SendRequest>(globalContext.BBYWApiHost, "api/vender/GetShopListByShopIds", new + { + shopIds + }, new Dictionary() + { + { "bbwyTempKey", "21jfhayu27q" } + }, HttpMethod.Post); + } + + public ApiResponse> GetServiceGroupList() + { + return SendRequest>(globalContext.BBYWApiHost, "api/vender/GetServiceGroupList", new + { + globalContext.User.Shop.Platform, + globalContext.User.Shop.AppKey, + globalContext.User.Shop.AppSecret, + globalContext.User.Shop.AppToken, + }, null, HttpMethod.Post); + } + } +} diff --git a/BBWYB.Client/BBWYB.Client.csproj b/BBWYB.Client/BBWYB.Client.csproj index 4106cb0..d4fc390 100644 --- a/BBWYB.Client/BBWYB.Client.csproj +++ b/BBWYB.Client/BBWYB.Client.csproj @@ -7,4 +7,26 @@ true + + + + + + + + + + + + + + + + + + + + + + diff --git a/BBWYB.Client/GlobalContext.cs b/BBWYB.Client/GlobalContext.cs new file mode 100644 index 0000000..cdd8079 --- /dev/null +++ b/BBWYB.Client/GlobalContext.cs @@ -0,0 +1,31 @@ +using BBWYB.Client.Models; +using System.Collections.Generic; + +namespace BBWYB.Client +{ + public class GlobalContext : NotifyObject + { + public GlobalContext() + { + } + + private User user; + + public User User { get => user; set { Set(ref user, value); } } + + public string UserToken { get; set; } + + public IList LogisticsResponseList { get; set; } + + /// + /// JD客户端 + /// + //public IJdClient JdClient { get; set; } + + #region APIHost + public string BBYWApiHost { get; set; } + + public string MDSApiHost { get; set; } + #endregion + } +} \ No newline at end of file diff --git a/BBWYB.Client/MainWindow.xaml b/BBWYB.Client/MainWindow.xaml deleted file mode 100644 index 06898df..0000000 --- a/BBWYB.Client/MainWindow.xaml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - diff --git a/BBWYB.Client/MainWindow.xaml.cs b/BBWYB.Client/MainWindow.xaml.cs deleted file mode 100644 index f80b15d..0000000 --- a/BBWYB.Client/MainWindow.xaml.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace BBWYB.Client -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - } - } -} diff --git a/BBWYB.Client/Models/APIModel/Request/BillCorrectionRequest.cs b/BBWYB.Client/Models/APIModel/Request/BillCorrectionRequest.cs new file mode 100644 index 0000000..e132236 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Request/BillCorrectionRequest.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class BillCorrectionRequest + { + public string OrderId { get; set; } + + public decimal NewDeliveryExpressFreight { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderListResponse.cs b/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderListResponse.cs new file mode 100644 index 0000000..2e67b64 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderListResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class AfterSaleOrderListResponse + { + public int Count { get; set; } + + public IList Items { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderResponse.cs new file mode 100644 index 0000000..70fde0d --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/AfterSaleOrder/AfterSaleOrderResponse.cs @@ -0,0 +1,160 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class AfterSaleOrderResponse + { + + public long Id { get; set; } + + + public DateTime? CreateTime { get; set; } + + /// + /// 申请时间 + /// + + public DateTime? ApplyTime { get; set; } + + + public string OrderId { get; set; } + + + public string ProductId { get; set; } + + /// + /// 商品处理结果 + /// + public ProductResult? ProductResult { get; set; } + + /// + /// 退款金额 + /// + public decimal? RefundAmount { get; set; } = 0.00M; + + /// + /// 退款时间 + /// + public DateTime? RefundTime { get; set; } + + /// + /// 售后补发成本 + /// + public decimal? ReissueAfterSaleAmount { get; set; } = 0.00M; + + /// + /// 补发快递费 + /// + public decimal? ReissueFreight { get; set; } = 0.00M; + + /// + /// 补发货款成本 + /// + public decimal? ReissueProductAmount { get; set; } = 0.00M; + + /// + /// 服务单处理结果 + /// + public ServiceResult? ServiceResult { get; set; } + + public long? ShopId { get; set; } + + public string SkuId { get; set; } + + /// + /// 服务单号 + /// + public string ServiceId { get; set; } + + /// + /// 退货入仓操作费 + /// + public decimal? RefundInStorageAmount { get; set; } = 0.00M; + + /// + /// 退款采购成本 + /// + public decimal? RefundPurchaseAmount { get; set; } = 0.00M; + + /// + /// 耗材费 + /// + public decimal? ConsumableAmount { get; set; } = 0.00M; + + /// + /// 发货快递费 + /// + public decimal? DeliveryExpressFreight { get; set; } = 0.00M; + + /// + /// 头程费 + /// + public decimal? FirstFreight { get; set; } = 0.00M; + + /// + /// 入仓操作费 + /// + public decimal? InStorageAmount { get; set; } = 0.00M; + + /// + /// 出仓操作费 + /// + public decimal? OutStorageAmount { get; set; } = 0.00M; + + /// + /// 商品情况 + /// + public ProductHealth? ProductHealth { get; set; } + + public string Logo { get; set; } + + public string Title { get; set; } + + public int ItemTotal { get; set; } + + public decimal Price { get; set; } + + public string ContactName { get; set; } + + public string Mobile { get; set; } + + public decimal AfterTotalCost { get; set; } = 0.0M; + + public decimal StorageAmount { get; set; } = 0.0M; + + /// + /// 退款商户订单号 + /// + public string RefundMerchantOrderNo { get; set; } + + /// + /// 退款支付宝订单号 + /// + public string RefundAlipayOrderNo { get; set; } + + /// + /// 采购方式 + /// + public PurchaseMethod? PurchaseMethod { get; set; } + + /// + /// 采购平台 + /// + public Platform? PurchasePlatform { get; set; } + + /// + /// 采购单号 + /// + public string PurchaseOrderId { get; set; } + + /// + /// 采购单主键 + /// + public long? PurchaseOrderPKId { get; set; } + + /// + /// 刷单退货运费 + /// + public decimal SDRefundFreight { get; set; } = 0.00M; + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/BillCorrection/BillCorrectionOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/BillCorrection/BillCorrectionOrderResponse.cs new file mode 100644 index 0000000..b4bfe37 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/BillCorrection/BillCorrectionOrderResponse.cs @@ -0,0 +1,76 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class BillCorrectionOrderResponse + { + /// + /// 订单号 + /// + public string OrderId { get; set; } + + public long ShopId { get; set; } + + /// + /// 订单状态 + /// + public OrderState? OrderState { get; set; } + + /// + /// 订单开始日期 + /// + public DateTime? StartTime { get; set; } + + /// + /// 发货类型 + /// + public StorageType? StorageType { get; set; } + + /// + /// 销售运费 + /// + public decimal DeliveryExpressFreight { get; set; } = 0.00M; + + /// + /// Sku成本(商品成本) + /// + public decimal SkuAmount { get; set; } = 0.00M; + + /// + /// 采购运费 + /// + public decimal PurchaseFreight { get; set; } = 0.00M; + + /// + /// 头程运费 + /// + public decimal FirstFreight { get; set; } = 0.00M; + + /// + /// 入仓操作费 + /// + public decimal InStorageAmount { get; set; } = 0.00M; + + /// + /// 出仓操作费 + /// + public decimal OutStorageAmount { get; set; } = 0.00M; + + /// + /// 耗材费 + /// + public decimal ConsumableAmount { get; set; } = 0.00M; + + /// + /// 仓储费 + /// + public decimal StorageAmount { get; set; } = 0.00M; + + /// + /// 售后费用 + /// + public decimal AfterTotalCost { get; set; } + + public string WaybillNo { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/FinancialTerminal/AuditPayBillResponse.cs b/BBWYB.Client/Models/APIModel/Response/FinancialTerminal/AuditPayBillResponse.cs new file mode 100644 index 0000000..ade32a9 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/FinancialTerminal/AuditPayBillResponse.cs @@ -0,0 +1,107 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class AuditPayBillResponse + { + /// + /// 账单流水号 + /// + public long PayBillNo { get; set; } + + /// + /// 归属店铺 + /// + public string BelongShop { get; set; } + + /// + /// 归属店铺Id + /// + + public int? BelongShopId { get; set; } + + public DateTime? CreateTime { get; set; } + + public DateTime? OrderStartTime { get; set; } + + /// + /// 支出金额 + /// + public decimal? ExpenditureAmount { get; set; } + + /// + /// 收入金额 + /// + public decimal? IncomeAmount { get; set; } + + /// + /// 是否支持商户订单号 + /// + + public bool? IsSupportMerchantOrderNo { get; set; } + + /// + /// 格式化之后的商家订单号 + /// + + public string MerchantOrderNo { get; set; } + + /// + /// 对方账户 + /// + + public string OppositeAccount { get; set; } + + /// + /// 账单类型 + /// + public PayBillType? PayBillType { get; set; } + + /// + /// 支付时间 + /// + public DateTime? PayTime { get; set; } + + /// + /// 商品名称 + /// + + public string ProductName { get; set; } + + /// + /// 关联采购订单号 + /// + public string RelationPurchaseOrderId { get; set; } + + /// + /// 关联平台订单Id + /// + public string RelationShopOrderId { get; set; } + + /// + /// 备注 + /// + + public string Remark { get; set; } + + /// + /// 原始商家订单号 + /// + + public string SourceMerchantOrderNo { get; set; } + + /// + /// 导入时选择的店铺 + /// + public string ImportShopIds { get; set; } + + public string ErrorMessage { get; set; } + + public AuditCapitalType? AuditCapitalType { get; set; } + + /// + /// 自定义资金类型 + /// + public string CustomAuditCapitalType { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Logistics/LogisticsResponse.cs b/BBWYB.Client/Models/APIModel/Response/Logistics/LogisticsResponse.cs new file mode 100644 index 0000000..8b5bc0b --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Logistics/LogisticsResponse.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class LogisticsResponse + { + public string Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/ConsigneeResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/ConsigneeResponse.cs new file mode 100644 index 0000000..ca91cfd --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/ConsigneeResponse.cs @@ -0,0 +1,72 @@ +namespace BBWYB.Client.Models +{ + public class ConsigneeResponse + { + /// + /// 联系人名称 + /// + public string ContactName { get; set; } + + /// + /// 座机 + /// + public string TelePhone { get; set; } + + /// + /// 手机 + /// + public string Mobile { get; set; } + + public string Address { get; set; } + + /// + /// 省 + /// + public string Province { get; set; } + + /// + /// 市 + /// + public string City { get; set; } + + /// + /// 县 + /// + public string County { get; set; } + + /// + /// 镇 + /// + public string Town { get; set; } + + public bool IsDecode { get; set; } + } + + public class ConsigneeSimpleResponse + { + /// + /// 联系人名称 + /// + public string ContactName { get; set; } + + /// + /// 座机 + /// + public string TelePhone { get; set; } + + /// + /// 手机 + /// + public string Mobile { get; set; } + + /// + /// 地址 + /// + public string Address { get; set; } + + /// + /// 买家账号 + /// + //public string BuyerAccount { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/ExportOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/ExportOrderResponse.cs new file mode 100644 index 0000000..218bed9 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/ExportOrderResponse.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BBWYB.Client.Models +{ + public class ExportOrderResponse + { + public string OrderId { get; set; } + + public DateTime OrderStartTime { get; set; } + + public string SkuIds { get; set; } + + /// + /// 代发订单号 + /// + public string PurchaseOrderIds { get; set; } + + /// + /// 订单总额 + /// + public decimal OrderTotalPrice { get; set; } + + /// + /// 商品成本 + /// + public decimal PurchaseSkuAmount { get; set; } + + /// + /// 采购运费 + /// + public decimal PurchaseFreight { get; set; } + + /// + /// 头程费用 + /// + public decimal FirstFreight { get; set; } + + /// + /// 仓储费 + /// + public decimal StorageAmount { get; set; } + + /// + /// 发货快递费 + /// + public decimal DeliveryExpressFreight { get; set; } + + /// + /// 平台扣点金额 + /// + public decimal PlatformCommissionAmount { get; set; } + + /// + /// 补差金额(用户支付) + /// + public decimal FreightPrice { get; set; } + + /// + /// 总成本 + /// + public decimal TotalCost { get; set; } + + public decimal Profit { get; set; } + + public decimal ProfitRatio { get; set; } + + public string ConsigneeStr { get; set; } + + public StorageType? StorageType { get; set; } + + public OrderState OrderState { get; set; } + + public string VenderRemark { get; set; } + + /// + /// 订单货款金额 + /// + public decimal OrderSellerPrice { get; set; } + + /// + /// 实收金额 + /// + public decimal ActualAmount { get; set; } + + /// + /// 用户应付金额 + /// + public decimal OrderPayment { get; set; } + + /// + /// 耗材费 + /// + public decimal ConsumableAmount { get; set; } + + /// + /// 入仓操作费 + /// + public decimal InStorageAmount { get; set; } + + /// + /// 出仓操作费 + /// + public decimal OutStorageAmount { get; set; } + + /// + /// 刷单/空单号费 + /// + public decimal SDOrderAmount { get; set; } = 0.00M; + + public override string ToString() + { + //日期,店铺订单号,SKU编码,订单状态,仓储类型,代发下单单号,售价,商品成本,采购运费,头程费用,仓储服务费,快递费,耗材费,入仓操作费,出仓操作费,刷单/空单号费,平台扣点,补差金额,应付金额,实收金额,利润,利润率,收件人联系方式,商家备注,售后类型,售后与特殊情况备注 + return $"{OrderStartTime:yyyy-MM-dd HH:mm:ss},{OrderId},{SkuIds},{OrderState},{StorageType},{PurchaseOrderIds},{OrderTotalPrice},{PurchaseSkuAmount},{PurchaseFreight},{FirstFreight},{StorageAmount},{DeliveryExpressFreight},{ConsumableAmount},{InStorageAmount},{OutStorageAmount},{SDOrderAmount},{PlatformCommissionAmount},{FreightPrice},{OrderPayment},{ActualAmount},{Profit},{ProfitRatio},{ConsigneeStr},{VenderRemark}"; + } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderCostDetailResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderCostDetailResponse.cs new file mode 100644 index 0000000..15f0c95 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderCostDetailResponse.cs @@ -0,0 +1,88 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class OrderCostDetailResponse + { + + public long Id { get; set; } + + + public DateTime? CreateTime { get; set; } + + /// + /// 扣减数量 + /// + public int DeductionQuantity { get; set; } = 0; + + /// + /// 发货运费 + /// + public decimal DeliveryExpressFreight { get; set; } = 0.00M; + + public string OrderId { get; set; } + + + public string ProductId { get; set; } + + /// + /// 单件成本 + /// + public decimal UnitCost { get; set; } = 0.00M; + + /// + /// 采购单流水Id + /// + public long PurchaseOrderPKId { get; set; } + + public string SkuId { get; set; } + + /// + /// Sku成本(商品成本) + /// + public decimal SkuAmount { get; set; } = 0.00M; + + /// + /// 采购运费 + /// + public decimal PurchaseFreight { get; set; } = 0.00M; + + /// + /// 头程运费 + /// + public decimal FirstFreight { get; set; } = 0.00M; + + + //public decimal OperationAmount { get; set; } = 0.00M; + + /// + /// 入仓操作费 + /// + public decimal InStorageAmount { get; set; } + + /// + /// 出仓操作费 + /// + public decimal OutStorageAmount { get; set; } + + /// + /// 耗材费 + /// + public decimal ConsumableAmount { get; set; } = 0.00M; + + /// + /// 仓储费 + /// + public decimal StorageAmount { get; set; } = 0.00M; + + /// + /// 总计(不含销售运费 历史遗留) + /// + public decimal TotalCost { get; set; } = 0.00M; + + /// + /// 总计 包含销售运费 + /// + public decimal TotalCost2 { get; set; } = 0.00M; + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderCostResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderCostResponse.cs new file mode 100644 index 0000000..643a763 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderCostResponse.cs @@ -0,0 +1,106 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class OrderCostResponse + { + + public string OrderId { get; set; } + + /// + /// 平台扣点金额 + /// + public decimal PlatformCommissionAmount { get; set; } + + /// + /// 平台扣点百分比 + /// + public decimal PlatformCommissionRatio { get; set; } + + /// + /// 优惠金额 + /// + public decimal PreferentialAmount { get; set; } + + /// + /// 毛利 + /// + public decimal Profit { get; set; } + + /// + /// 成本毛利率 + /// + public decimal ProfitRatio + { + get + { + return TotalCost == 0 ? 0 : Math.Round(Profit / TotalCost * 100, 2); + } + } + + /// + /// 采购金额 + /// + public decimal PurchaseAmount { get; set; } = 0.00M; + + /// + /// 发货快递费 + /// + public decimal DeliveryExpressFreight { get; set; } = 0.00M; + + /// + /// 是否手动编辑过成本 + /// + public bool IsManualEdited { get; set; } + + /// + /// 刷单佣金 + /// + public decimal SDCommissionAmount { get; set; } = 0.00M; + + /// + /// 刷单号费 + /// + public decimal SDOrderAmount { get; set; } = 0.00M; + + /// + /// 退款金额 + /// + public decimal RefundAmount { get; set; } = 0.00M; + + /// + /// 退款采购金额 + /// + public decimal RefundPurchaseAmount { get; set; } = 0.0M; + + /// + /// 售后总成本 + /// + public decimal AfterTotalCost { get; set; } = 0.0M; + + /// + /// 售前成本 + /// + public decimal BeforeTotalCost + { + get + { + return PurchaseAmount + DeliveryExpressFreight; + } + } + + /// + /// 成本总计 + /// + public decimal TotalCost + { + get + { + return SDCommissionAmount + SDOrderAmount + + PlatformCommissionAmount + (PurchaseAmount - RefundPurchaseAmount) + + DeliveryExpressFreight + + AfterTotalCost; + } + } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderCouponResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderCouponResponse.cs new file mode 100644 index 0000000..92a4328 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderCouponResponse.cs @@ -0,0 +1,14 @@ +namespace BBWYB.Client.Models +{ + public class OrderCouponResponse + { + public long Id { get; set; } + + public decimal CouponPrice { get; set; } + + public string CouponType { get; set; } + + public string OrderId { get; set; } + public string SkuId { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderDropShippingResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderDropShippingResponse.cs new file mode 100644 index 0000000..6796da2 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderDropShippingResponse.cs @@ -0,0 +1,32 @@ +namespace BBWYB.Client.Models +{ + public class OrderDropShippingResponse + { + public long Id { get; set; } + + public string OrderId { get; set; } + + public string BuyerAccount { get; set; } + + public decimal DeliveryFreight { get; set; } + + public decimal PurchaseAmount { get; set; } + + public string PurchaseOrderId { get; set; } + + public string MerchantOrderId { get; set; } + + public Platform? PurchasePlatform { get; set; } + + public string SellerAccount { get; set; } + + public decimal SkuAmount + { + get; set; + } + public decimal PurchaseFreight + { + get; set; + } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderResponse.cs new file mode 100644 index 0000000..aaeb53e --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderResponse.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class OrderResponse + { + public string Id { get; set; } + + /// + /// 商家Id + /// + public string VenderId { get; set; } + + /// + /// 下单时间 + /// + public DateTime OrderStartTime { get; set; } + + /// + /// 结单时间 + /// + public DateTime? OrderEndTime { get; set; } + + public DateTime OrderModifyTime { get; set; } + + /// + /// 买家账号 + /// + public string BuyerAccount { get; set; } + + /// + /// 订单平台 + /// + public Platform Platform { get; set; } + + /// + /// 订单类型 + /// + public OrderType OrderType { get; set; } + + /// + /// 支付方式 + /// + public PayType PayType { get; set; } + + /// + /// 订单状态 + /// + public OrderState OrderState { get; set; } + + /// + /// 订单状态中文说明 + /// + public string OrderStateText { get; set; } + + /// + /// 订单总价 + /// + public decimal OrderTotalPrice { get; set; } + + /// + /// 订单货款金额 + /// + public decimal OrderSellerPrice { get; set; } + + /// + /// 用户应付金额 + /// + public decimal OrderPayment { get; set; } + + /// + /// 商品运费(用户付) + /// + public decimal FreightPrice { get; set; } + + public decimal PreferentialAmount { get; set; } + + /// + /// 买家备注 + /// + public string BuyerRemark { get; set; } + + /// + /// 商家备注 + /// + public string VenderRemark { get; set; } + + /// + /// 采购备注 + /// + public string PurchaseRemark { get; set; } + + public StorageType? StorageType { get; set; } + + /// + /// 运单号 + /// + public string WaybillNo { get; set; } + + /// + /// 仓库Id + /// + public string StoreId { get; set; } + + /// + /// 仓库名称 + /// + public string StoreName { get; set; } + + /// + /// 订单旗帜 + /// + public string Flag { get; set; } + + /// + /// 刷单类型 + /// + public SDType? SDType { get; set; } + + /// + /// 刷单关键词 + /// + public string SDKey { get; set; } + + /// + /// 刷单支付渠道 + /// + public PayBillType? SDPayChannel { get; set; } + + /// + /// 刷单交易账单号 + /// + public string SDPayBillNo { get; set; } + + /// + /// 刷单人 + /// + public string SDOperator { get; set; } + + /// + /// 是否包含售后单 + /// + public bool IsAfterSaleOrder { get; set; } + + /// + /// 收货人信息 + /// + public ConsigneeResponse Consignee { get; set; } + + /// + /// 订单成本 + /// + public OrderCostResponse OrderCost { get; set; } + + + public IList ItemList { get; set; } + + /// + /// 优惠券列表 + /// + public IList OrderCouponList { get; set; } + + /// + /// 订单成本明细列表 + /// + public IList OrderCostDetailList { get; set; } + + /// + /// 代发信息 + /// + public IList OrderDropShippingList { get; set; } + + /// + /// 历史代发信息 + /// + public IList HistoryOrderDropShippingList { get; set; } + + /// + /// 售后信息 + /// + public IList AfterSaleOrderList { get; set; } + } + + public class OrderListResponse + { + public int Count { get; set; } + + public IList Items { get; set; } + + /// + /// 当前条件汇总利润 + /// + public decimal CurrentConditionsTotalProfit { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Order/OrderSkuResponse.cs b/BBWYB.Client/Models/APIModel/Response/Order/OrderSkuResponse.cs new file mode 100644 index 0000000..fcaa7ad --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Order/OrderSkuResponse.cs @@ -0,0 +1,32 @@ +namespace BBWYB.Client.Models +{ + public class OrderSkuResponse + { + /// + /// 购买数量 + /// + public int ItemTotal { get; set; } + + public string Id { get; set; } + + public string ProductId { get; set; } + + public string ProductNo { get; set; } + + public double Price { get; set; } + + /// + /// Sku标题 + /// + public string Title { get; set; } + + public string Logo { get; set; } + + /// + /// 代发信息Id + /// + public long? OrderDropShippingId { get; set; } + + public bool IsRefund { get; set; } = false; + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/OrderBelongShopResponse.cs b/BBWYB.Client/Models/APIModel/Response/OrderBelongShopResponse.cs new file mode 100644 index 0000000..60d0056 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/OrderBelongShopResponse.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class OrderBelongShopResponse + { + public long ShopId { get; set; } + + public string ShopName { get; set; } + + public IList OrderIdList { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/ProductListResponse.cs b/BBWYB.Client/Models/APIModel/Response/ProductListResponse.cs new file mode 100644 index 0000000..b227495 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/ProductListResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class ProductListResponse + { + public int Count { get; set; } + + public IList Items { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/AuditPurchaseOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/AuditPurchaseOrderResponse.cs new file mode 100644 index 0000000..2644a0d --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/AuditPurchaseOrderResponse.cs @@ -0,0 +1,25 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class AuditPurchaseOrderResponse + { + public long OrderDropShippingId { get; set; } + + public string PurchaseOrderId { get; set; } + + public string MerchantOrderId { get; set; } + + public long ShopId { get; set; } + + public decimal PurchaseAmount { get; set; } + + public string OrderId { get; set; } + + public DateTime? PurchaseTime { get; set; } + + public Platform? PurchasePlatform { get; set; } + + public DateTime? OrderStartTime { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/OrderTradeTypeResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/OrderTradeTypeResponse.cs new file mode 100644 index 0000000..e72437b --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/OrderTradeTypeResponse.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class OrderTradeTypeResponse + { + public string Code { get; set; } + + public string Name { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/PreviewOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/PreviewOrderResponse.cs new file mode 100644 index 0000000..09518a3 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/OnlinePurchase/PreviewOrderResponse.cs @@ -0,0 +1,30 @@ +namespace BBWYB.Client.Models +{ + public class PreviewOrderResponse + { + /// + /// 总额 + /// + public decimal TotalAmount { get; set; } + + /// + /// 货款总额 + /// + public decimal ProductAmount { get; set; } + + /// + /// 运费 + /// + public decimal FreightAmount { get; set; } + + /// + /// 交易方式 + /// + public OrderTradeTypeResponse OrderTradeType { get; set; } + + /// + /// 扩展数据 + /// + public string Extensions { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/PurchaseOrderResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/PurchaseOrderResponse.cs new file mode 100644 index 0000000..7e1e341 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseOrder/PurchaseOrderResponse.cs @@ -0,0 +1,83 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class PurchaseOrderResponse + { + public long Id { get; set; } + + public DateTime? CreateTime { get; set; } + + public string ProductId { get; set; } + + + public PurchaseMethod? PurchaseMethod { get; set; } + + public string PurchaseOrderId { get; set; } + + + public Platform? PurchasePlatform { get; set; } + + + public int PurchaseQuantity { get; set; } + + + public int RemainingQuantity { get; set; } + + public string SkuId { get; set; } + + public StorageType? StorageType { get; set; } + + public long ShopId { get; set; } + + /// + /// 单件发货运费 + /// + public decimal SingleDeliveryFreight { get; set; } = 0.00M; + + ///// + ///// 单件操作费 + ///// + //public decimal SingleOperationAmount { get; set; } = 0.00M; + + /// + /// 单件入仓操作费 + /// + public decimal SingleInStorageAmount { get; set; } = 0.00M; + + /// + /// 单件出仓操作费 + /// + public decimal SingleOutStorageAmount { get; set; } = 0.00M; + + /// + /// 单件耗材费 + /// + public decimal SingleConsumableAmount { get; set; } = 0.00M; + + /// + /// 单件仓储费 + /// + public decimal SingleStorageAmount { get; set; } = 0.00M; + + /// + /// 单件Sku成本 + /// + public decimal SingleSkuAmount { get; set; } = 0.00M; + + /// + /// 单件采购运费 + /// + public decimal SingleFreight { get; set; } = 0.00M; + + /// + /// 单件头程运费 + /// + public decimal SingleFirstFreight { get; set; } = 0.00M; + + /// + /// 退货入仓单价 + /// + public decimal SingleRefundInStorageAmount { get; set; } = 0.00M; + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductResponse.cs new file mode 100644 index 0000000..712aaaf --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductResponse.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public partial class PurchaseSchemeProductResponse + { + public long Id { get; set; } + public DateTime? CreateTime { get; set; } + public string ProductId { get; set; } + public string PurchaseProductId { get; set; } + public string PurchaseUrl { get; set; } + public string SkuId { get; set; } + public long SkuPurchaseSchemeId { get; set; } + public long UserId { get; set; } + public List PurchaseSchemeProductSkuList { get; set; } + + } + +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductSkuResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductSkuResponse.cs new file mode 100644 index 0000000..342cd38 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeProductSkuResponse.cs @@ -0,0 +1,18 @@ +using System; + +namespace BBWYB.Client.Models +{ + public partial class PurchaseSchemeProductSkuResponse + { + public long Id { get; set; } + public DateTime? CreateTime { get; set; } + public string ProductId { get; set; } + public string PurchaseProductId { get; set; } + public string PurchaseSkuId { get; set; } + public string PurchaseSkuSpecId { get; set; } + public string SkuId { get; set; } + public long SkuPurchaseSchemeId { get; set; } + public long UserId { get; set; } + } + +} diff --git a/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeResponse.cs b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeResponse.cs new file mode 100644 index 0000000..258f506 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/PurchaseSchemeResponse.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +namespace BBWYB.Client.Models +{ + public partial class PurchaseSchemeResponse + { + public long Id { get; set; } + public DateTime? CreateTime { get; set; } + /// + /// 采购默认成本 + /// + public decimal? DefaultCost { get; set; } + public string ProductId { get; set; } + /// + /// 采购商Id + /// + public string PurchaserId { get; set; } + public string PurchaserName { get; set; } + /// + /// 发货地 + /// + public string PurchaserLocation { get; set; } + /// + /// 采购实际成本 + /// + public decimal? RealCost { get; set; } + public string SkuId { get; set; } + public long ShopId { get; set; } + + /// + /// 采购平台 + /// + public Platform PurchasePlatform { get; set; } + + public List PurchaseSchemeProductList { get; set; } + } + +} diff --git a/BBWYB.Client/Models/APIModel/Response/ServiceGroup/WaiterResponse.cs b/BBWYB.Client/Models/APIModel/Response/ServiceGroup/WaiterResponse.cs new file mode 100644 index 0000000..9d4d7a0 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/ServiceGroup/WaiterResponse.cs @@ -0,0 +1,11 @@ +namespace BBWYB.Client.Models +{ + public class WaiterResponse + { + public string Id { get; set; } + + public string Name { get; set; } + + public string Level { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Shop/PurchaseAccountResponse.cs b/BBWYB.Client/Models/APIModel/Response/Shop/PurchaseAccountResponse.cs new file mode 100644 index 0000000..53d6255 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Shop/PurchaseAccountResponse.cs @@ -0,0 +1,19 @@ +namespace BBWYB.Client.Models +{ + public class PurchaseAccountResponse + { + public long Id { get; set; } + + public string AccountName { get; set; } + + public long ShopId { get; set; } + + public Platform PurchasePlatformId { get; set; } + + public string AppKey { get; set; } + + public string AppSecret { get; set; } + + public string AppToken { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Shop/ShopPopularizeResponse.cs b/BBWYB.Client/Models/APIModel/Response/Shop/ShopPopularizeResponse.cs new file mode 100644 index 0000000..02de4d1 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Shop/ShopPopularizeResponse.cs @@ -0,0 +1,18 @@ +using System; + +namespace BBWYB.Client.Models +{ + + public class ShopPopularizeResponse + { + public decimal Cost { get; set; } = 0.0M; + + //public DateTime? Date { get; set; } + + public string ItemName { get; set; } + + public long? ShopId { get; set; } + + } + +} diff --git a/BBWYB.Client/Models/APIModel/Response/Shop/ShopResponse.cs b/BBWYB.Client/Models/APIModel/Response/Shop/ShopResponse.cs new file mode 100644 index 0000000..73c0b8a --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Shop/ShopResponse.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class ShopResponse + { + public string Id { get; set; } + + public Platform PlatformId { get; set; } + + public long? ShopId { get; set; } + + public string ShopName { get; set; } + + public string ShopType { get; set; } + + public string AppKey { get; set; } + + public string AppSecret { get; set; } + + public string AppToken { get; set; } + + public string AppKey2 { get; set; } + + public string AppSecret2 { get; set; } + + public string AppToken2 { get; set; } + + public IList PurchaseList { get; set; } + + public string ManagePwd { get; set; } + + public decimal? PlatformCommissionRatio { get; set; } + + public string TeamId { get; set; } + + public string TeamName { get; set; } + + public string DingDingWebHook { get; set; } + + public string DingDingKey { get; set; } + + public int SkuSafeTurnoverDays { get; set; } + + /// + /// 司南策略等级 + /// + public int SiNanPolicyLevel { get; set; } + + /// + /// 司南钉钉WebHook地址 + /// + public string SiNanDingDingWebHook { get; set; } + + /// + /// 司南钉钉密钥 + /// + public string SiNanDingDingKey { get; set; } + } + + public class DepartmentResponse + { + public string Id { get; set; } + + public string Name { get; set; } + + public IList ShopList { get; set; } + } + + public class DepartmentResponse2 + { + public string DepartmentId { get; set; } + + public string DepartmentName { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Statistics/OrderCountStatisticsResponse.cs b/BBWYB.Client/Models/APIModel/Response/Statistics/OrderCountStatisticsResponse.cs new file mode 100644 index 0000000..76ed866 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Statistics/OrderCountStatisticsResponse.cs @@ -0,0 +1,13 @@ +namespace BBWYB.Client.Models +{ + public class OrderCountStatisticsResponse + { + public long WaitPurchaseCount { get; set; } + + public long ExceptionCount { get; set; } + + public long WaitOutStoreCount { get; set; } + + public long AfterSaleOrderUnhandleCount { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Statistics/SDGroupPersonStatisticsResponse.cs b/BBWYB.Client/Models/APIModel/Response/Statistics/SDGroupPersonStatisticsResponse.cs new file mode 100644 index 0000000..43bd790 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Statistics/SDGroupPersonStatisticsResponse.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class SDGroupPersonStatisticsResponse + { + public int MySDCount { get; set; } + + public decimal OrderPayment { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Statistics/SkuRecentSaleResponse.cs b/BBWYB.Client/Models/APIModel/Response/Statistics/SkuRecentSaleResponse.cs new file mode 100644 index 0000000..a5b8f29 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Statistics/SkuRecentSaleResponse.cs @@ -0,0 +1,12 @@ +namespace BBWYB.Client.Models +{ + public class SkuRecentSaleResponse + { + public string SkuId { get; set; } + + /// + /// 销量 + /// + public decimal SaleCount { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/Statistics/ToDayOrderAchievementResponse.cs b/BBWYB.Client/Models/APIModel/Response/Statistics/ToDayOrderAchievementResponse.cs new file mode 100644 index 0000000..a2c8351 --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/Statistics/ToDayOrderAchievementResponse.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class ToDayOrderAchievementResponse + { + + /// + /// 销售额(用户实付) + /// + public decimal SaleAmount { get; set; } + + /// + /// 利润 + /// + public decimal Profit { get; set; } + + /// + /// 利润率 + /// + public decimal ProfitRaito { get; set; } + + /// + /// 订单数量 + /// + public int OrderCount { get; set; } + + /// + /// 采购金额 + /// + public decimal PurchaseAmount { get; set; } + + /// + /// 销售运费 + /// + public decimal DeliveryExpressFreight { get; set; } + + /// + /// 平台扣点 + /// + public decimal PlatformCommissionAmount { get; set; } + + /// + /// 总成本 + /// + public decimal TotalCost { get; set; } + + /// + /// 广告统计(海投,快车,包含SD) + /// + public decimal AdvCost { get; set; } + + + /// + /// 人工成本 + /// + public decimal EmployeeCost { get; set; } + + /// + /// 税务成本 + /// + public decimal TaxCost { get; set; } + + /// + /// SD成本 + /// + public decimal SDCost { get; set; } + + /// + /// 推广花费截至时间 + /// + public DateTime? PularizeEndDate { get; set; } + + /// + /// 推广成本明细 + /// + public IList ShoppopularizeList { get; set; } + } +} diff --git a/BBWYB.Client/Models/APIModel/Response/User/MDSUserResponse.cs b/BBWYB.Client/Models/APIModel/Response/User/MDSUserResponse.cs new file mode 100644 index 0000000..754fd4a --- /dev/null +++ b/BBWYB.Client/Models/APIModel/Response/User/MDSUserResponse.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class MDSUserResponse + { + public long Id { get; set; } + public string DepartmentName { get; set; } + public string DepartmentId { get; set; } + + public string UserName { get; set; } + + public string UserNick { get; set; } + + public IList SonDepartmentList { get; set; } + } +} diff --git a/BBWYB.Client/Models/Enums.cs b/BBWYB.Client/Models/Enums.cs new file mode 100644 index 0000000..51453eb --- /dev/null +++ b/BBWYB.Client/Models/Enums.cs @@ -0,0 +1,214 @@ +namespace BBWYB.Client.Models +{ + /// + /// 电商平台 + /// + public enum Platform + { + 淘宝 = 0, + 京东 = 1, + 阿里巴巴 = 2, + 拼多多 = 3, + 微信 = 4, + 拳探 = 10 + } + + /// + /// 采购方式 + /// + public enum PurchaseMethod + { + 线上采购 = 0, + 线下采购 = 1 + } + + /// + /// 采购单模式 + /// + public enum PurchaseOrderMode + { + 批发 = 0, + 代发 = 1 + } + + /// + /// 采购商品API模式 + /// + public enum PurchaseProductAPIMode + { + Spider = 0, + OneBound = 1 + } + + /// + /// 仓储类型 + /// + public enum StorageType + { + 京仓 = 0, + 云仓 = 1, + 本地自发 = 2, + 代发 = 3, + SD = 4 + } + + /// + /// 订单类型 + /// + public enum OrderType + { + #region JD订单类型 + SOP = 22, + LOC = 75, + FBP = 21 + #endregion + + #region 拳探订单类型 + + #endregion + } + + /// + /// 支付方式 + /// + public enum PayType + { + 货到付款 = 1, + 邮局汇款 = 2, + 自提 = 3, + 在线支付 = 4, + 公司转账 = 5, + 银行卡转账 = 6 + } + + /// + /// 订单状态 + /// + public enum OrderState + { + 待付款 = 0, + 等待采购 = 1, + 待出库 = 2, + 待收货 = 3, + 已完成 = 4, + 锁定 = 5, + 已取消 = 6, + 暂停 = 7, + 已退款 = 8 + } + + /// + /// 刷单类型 + /// + public enum SDType + { + 自刷 = 0, + 其他 = 1, + 京礼金 = 2, + 刷单组 = 3 + } + + /// + /// 代发方式 + /// + public enum DFType + { + 在线采购 = 0, + 关联订单 = 1 + } + + public enum PayBillType + { + 支付宝 = 0, + 微信 = 1, + 银行卡 = 2 + } + + public enum AuditFileType + { + 账单 = 0, + 采购单 = 1, + 销售订单 = 2 + } + + /// + /// 账单矫正类型 + /// + public enum BillCorrectionType + { + 销售运费账单 = 0, + 入仓操作账单 = 1, + 出仓操作账单 = 2 + } + + /// + /// 资金类型 + /// + public enum AuditCapitalType + { + 当月商品采购 = 0, + 当月商品退款 = 1, + 上月商品采购 = 2, + 上月商品退款 = 3, + 批量采购商品 = 4, + 采购运费 = 5, + 入仓运费 = 6, + 售后成本 = 7, + 发票点数 = 8, + 快递单号 = 9, + 诚e赊还款 = 10, + 空单号 = 11, + 购买刷单号 = 12, + 手机费 = 13, + 质检报告 = 14, + 备用金转入 = 15, + 平台补贴 = 16, + 快递赔付 = 17, + 自定义 = 18, + 备用金充值 = 19 + } + + /// + /// 服务单处理结果 + /// + public enum ServiceResult + { + 退货 = 0, + 换新 = 1, + 原返 = 2, + 线下换新 = 3, + 维修 = 4, + 商品补发 = 5, + 仅退款 = 6, + SD退货 = 7 + } + + /// + /// 商品处理方式 + /// + public enum ProductResult + { + 一件代发_退回厂家 = 0, + 退回齐越仓 = 1, + 退回京仓 = 2, + 退回云仓 = 3, + 客户无退货 = 4 + } + + /// + /// 商品情况 + /// + public enum ProductHealth + { + 可二次销售 = 0, + 残次品_无法二次销售 = 1, + 厂家退货退款 = 2, + 客户无退货 = 3, + 破损 = 4 + } + + public enum SiNanPolicyLevel + { + 初级策略, 中级策略, 高级策略 + } +} diff --git a/BBWYB.Client/Models/KVModel.cs b/BBWYB.Client/Models/KVModel.cs new file mode 100644 index 0000000..6fec62d --- /dev/null +++ b/BBWYB.Client/Models/KVModel.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class KVModel : NotifyObject + { + public string Key { get; set; } + + public string Value { get; set; } + } +} diff --git a/BBWYB.Client/Models/MappingProfile.cs b/BBWYB.Client/Models/MappingProfile.cs new file mode 100644 index 0000000..4cffcec --- /dev/null +++ b/BBWYB.Client/Models/MappingProfile.cs @@ -0,0 +1,35 @@ +using AutoMapper; + +namespace BBWYB.Client.Models +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap().ForMember(t => t.ProductItemNum, opt => opt.MapFrom(f => f.ProductNo)); + CreateMap(); + CreateMap().ForMember(t => t.TeamId, opt => opt.MapFrom(f => f.DepartmentId)) + .ForMember(t => t.TeamName, opt => opt.MapFrom(f => f.DepartmentName)) + .ForMember(t => t.Name, opt => opt.MapFrom(f => f.UserName)); + + CreateMap().ForMember(t => t.VenderType, opt => opt.MapFrom(f => f.ShopType)) + .ForMember(t => t.Platform, opt => opt.MapFrom(f => f.PlatformId)) + .ForMember(t => t.PurchaseAccountList, opt => opt.MapFrom(f => f.PurchaseList)); + + CreateMap(); + CreateMap(); + + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + } + } +} diff --git a/BBWYB.Client/Models/MenuModel.cs b/BBWYB.Client/Models/MenuModel.cs new file mode 100644 index 0000000..183bd35 --- /dev/null +++ b/BBWYB.Client/Models/MenuModel.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +namespace BBWYB.Client.Models +{ + public class MenuModel : NotifyObject + { + private bool isSelected; + + + public bool IsSelected { get => isSelected; set { Set(ref isSelected, value); } } + + public string Name { get; set; } + + public string Url { get; set; } + + public IList ChildList { get; set; } + } +} diff --git a/BBWYB.Client/Models/NotifyObject.cs b/BBWYB.Client/Models/NotifyObject.cs new file mode 100644 index 0000000..d5f6861 --- /dev/null +++ b/BBWYB.Client/Models/NotifyObject.cs @@ -0,0 +1,23 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace BBWYB.Client.Models +{ + public class NotifyObject : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged([CallerMemberName]string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool Set(ref T oldValue, T newValue, [CallerMemberName]string propertyName = "") + { + if (Equals(oldValue, newValue)) + return false; + oldValue = newValue; + OnPropertyChanged(propertyName); + return true; + } + } +} diff --git a/BBWYB.Client/Models/Product/Product.cs b/BBWYB.Client/Models/Product/Product.cs new file mode 100644 index 0000000..96f32ad --- /dev/null +++ b/BBWYB.Client/Models/Product/Product.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace BBWYB.Client.Models +{ + public class Product : NotifyObject + { + public Product() + { + PurchaserList = new ObservableCollection(); + PurchasePlatformList = new List(); + } + + private Platform selectedPurchasePlatformModel; + + /// + /// 商品Id + /// + public string Id { get; set; } + + /// + /// 商品货号 + /// + public string ProductItemNum { get; set; } + + /// + /// 商品标题 + /// + public string Title { get; set; } + + /// + /// Sku列表 + /// + public IList SkuList { get; set; } + + /// + /// 采购商集合 + /// + public IList PurchaserList { get; set; } + + /// + /// 采购平台集合 + /// + public IList PurchasePlatformList { get; set; } + + /// + /// 选中的采购平台 + /// + public Platform SelectedPurchasePlatformModel + { + get => selectedPurchasePlatformModel; + set { Set(ref selectedPurchasePlatformModel, value); } + } + + public void CreatePlatformList() + { + PurchasePlatformList.Add(Platform.阿里巴巴); + PurchasePlatformList.Add(Platform.拳探); + SelectedPurchasePlatformModel = PurchasePlatformList[0]; + } + } +} diff --git a/BBWYB.Client/Models/Product/ProductSku.cs b/BBWYB.Client/Models/Product/ProductSku.cs new file mode 100644 index 0000000..b3e3871 --- /dev/null +++ b/BBWYB.Client/Models/Product/ProductSku.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; + +namespace BBWYB.Client.Models +{ + public class ProductSku : NotifyObject + { + private PurchaseScheme selectedPurchaseScheme; + private StorageModel selectedStorageModel; + public string Id { get; set; } + + public string ProductId { get; set; } + + public double Price { get; set; } + + /// + /// Sku标题 + /// + public string Title { get; set; } + + public string Logo { get; set; } + + /// + /// 采购方案 + /// + public IList PurchaseSchemeList { get; set; } + + /// + /// 采购订单 + /// + public IList PurchaseOrderList { get; set; } + + /// + /// 仓储平台 + /// + public IList StorageList { get; set; } + + /// + /// 选中的采购方案 + /// + public PurchaseScheme SelectedPurchaseScheme + { + get => selectedPurchaseScheme; + set { Set(ref selectedPurchaseScheme, value); } + } + + /// + /// 选中的仓储平台 + /// + public StorageModel SelectedStorageModel { get => selectedStorageModel; set { Set(ref selectedStorageModel, value); } } + + public ProductSku() + { + PurchaseSchemeList = new ObservableCollection(); + PurchaseOrderList = new ObservableCollection(); + StorageList = new ObservableCollection(); + } + } +} diff --git a/BBWYB.Client/Models/Product/PurchasePlatformModel.cs b/BBWYB.Client/Models/Product/PurchasePlatformModel.cs new file mode 100644 index 0000000..7004163 --- /dev/null +++ b/BBWYB.Client/Models/Product/PurchasePlatformModel.cs @@ -0,0 +1,9 @@ +namespace BBWYB.Client.Models +{ + public class PurchasePlatformModel + { + public string ProductId { get; set; } + + public Platform PurchasePlatform { get; set; } + } +} diff --git a/BBWYB.Client/Models/Product/PurchaseScheme.cs b/BBWYB.Client/Models/Product/PurchaseScheme.cs new file mode 100644 index 0000000..ad77309 --- /dev/null +++ b/BBWYB.Client/Models/Product/PurchaseScheme.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace BBWYB.Client.Models +{ + /// + /// 采购方案 + /// + public class PurchaseScheme : NotifyObject + { + private decimal defaultCost; + private decimal realCost; + public long Id { get; set; } + + public long ShopId { get; set; } + public string ProductId { get; set; } + + public string SkuId { get; set; } + public decimal DefaultCost { get => defaultCost; set { Set(ref defaultCost, value); } } + public decimal RealCost { get => realCost; set { Set(ref realCost, value); } } + + public string PurchaserId { get; set; } + public string PurchaserName { get; set; } + public string PurchaserLocation { get; set; } + public string PurchaseProductId1 { get; set; } + public int PurchaseProductSkuCount1 { get; set; } + public string PurchaseProductId2 { get; set; } + public int PurchaseProductSkuCount2 { get; set; } + public string PurchaseProductId3 { get; set; } + public int PurchaseProductSkuCount3 { get; set; } + public string PurchaseProductId4 { get; set; } + public int PurchaseProductSkuCount4 { get; set; } + + /// + /// 采购平台 + /// + public Platform PurchasePlatform { get; set; } + + /// + /// 采购方案的商品集合 + /// + public IList PurchaseSchemeProductList { get; set; } + + public PurchaseScheme() + { + PurchaseSchemeProductList = new ObservableCollection(); + } + + /// + /// 是否转换方案中已选中的sku + /// + /// + /// + /// + public static PurchaseScheme Convert(PurchaseSchemeResponse apiModel) + { + var model = new PurchaseScheme() + { + Id = apiModel.Id, + ProductId = apiModel.ProductId, + SkuId = apiModel.SkuId, + DefaultCost = apiModel.DefaultCost ?? 0, + RealCost = apiModel.RealCost ?? 0, + PurchaserId = apiModel.PurchaserId, + PurchaserName = apiModel.PurchaserName, + PurchaserLocation = apiModel.PurchaserLocation, + PurchasePlatform = apiModel.PurchasePlatform + }; + + foreach (var apiProduct in apiModel.PurchaseSchemeProductList) + { + model.PurchaseSchemeProductList.Add(PurchaseSchemeProduct.Convert(apiProduct)); + } + for (var i = 0; i < model.PurchaseSchemeProductList.Count; i++) + { + var purchaseProductId = model.PurchaseSchemeProductList[i].PurchaseProductId; + var purchaseProductSkuCount = model.PurchaseSchemeProductList[i].PurchaseSkuCount; + if (i == 0) + { + model.PurchaseProductId1 = purchaseProductId; + model.PurchaseProductSkuCount1 = purchaseProductSkuCount; + } + else if (i == 1) + { + model.PurchaseProductId2 = purchaseProductId; + model.PurchaseProductSkuCount2 = purchaseProductSkuCount; + } + else if (i == 2) + { + model.PurchaseProductId3 = purchaseProductId; + model.PurchaseProductSkuCount3 = purchaseProductSkuCount; + } + else if (i == 3) + { + model.PurchaseProductId4 = purchaseProductId; + model.PurchaseProductSkuCount4 = purchaseProductSkuCount; + } + } + return model; + } + } +} diff --git a/BBWYB.Client/Models/Product/PurchaseSchemeProduct.cs b/BBWYB.Client/Models/Product/PurchaseSchemeProduct.cs new file mode 100644 index 0000000..c0838b9 --- /dev/null +++ b/BBWYB.Client/Models/Product/PurchaseSchemeProduct.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Text; +using System.Linq; + +namespace BBWYB.Client.Models +{ + /// + /// 采购商品 + /// + public class PurchaseSchemeProduct : NotifyObject + { + + private string purchaseUrl; + private string purchaseProductId; + private bool isEditing; + private string searchPurchaseSkuName; + + /// + /// 采购商品和采购方案的关系Id + /// + public long Id { get; set; } + + public string ProductId { get; set; } + + public string SkuId { get; set; } + + public string PurchaseUrl { get => purchaseUrl; set { Set(ref purchaseUrl, value); } } + public string PurchaseProductId { get => purchaseProductId; set => purchaseProductId = value; } + + public bool IsEditing + { + get => isEditing; + set + { + SearchPurchaseSkuName = string.Empty; + Set(ref isEditing, value); + } + } + + public IList SkuList { get; set; } + + public IList SearchSkuList { get; set; } + + public IList PurchaseSchemeProductSkuList { get; set; } + + public List SelectedSkuIdList { get; set; } + + public int PurchaseSkuCount + { + get { return SelectedSkuIdList.Count(); } + } + + public string SearchPurchaseSkuName + { + get => searchPurchaseSkuName; + set { Set(ref searchPurchaseSkuName, value); } + } + + public PurchaseSchemeProduct() + { + SkuList = new ObservableCollection(); + SearchSkuList = new ObservableCollection(); + PurchaseSchemeProductSkuList = new ObservableCollection(); + SelectedSkuIdList = new List(); + } + + /// + /// + /// + /// + /// 是否转换方案中已选的sku + /// + public static PurchaseSchemeProduct Convert(PurchaseSchemeProductResponse apiModel) + { + var model = new PurchaseSchemeProduct() + { + Id = apiModel.Id, + ProductId = apiModel.ProductId, + SkuId = apiModel.SkuId, + PurchaseProductId = apiModel.PurchaseProductId, + PurchaseUrl = apiModel.PurchaseUrl + }; + model.SelectedSkuIdList.AddRange(apiModel.PurchaseSchemeProductSkuList.Select(s => s.PurchaseSkuId)); + return model; + } + } +} diff --git a/BBWYB.Client/Models/Product/PurchaseSchemeProductSku.cs b/BBWYB.Client/Models/Product/PurchaseSchemeProductSku.cs new file mode 100644 index 0000000..14c94cc --- /dev/null +++ b/BBWYB.Client/Models/Product/PurchaseSchemeProductSku.cs @@ -0,0 +1,57 @@ +using System; + +namespace BBWYB.Client.Models +{ + /// + /// 采购商品的Sku + /// + public class PurchaseSchemeProductSku : NotifyObject + { + /// + /// 采购商品的SKU和采购方案的关系Id + /// + public long Id { get; set; } + + private bool isSelected; + public bool IsSelected { get => isSelected; set { Set(ref isSelected, value); } } + + public decimal Price { get; set; } + + /// + /// Sku标题 + /// + public string Title { get; set; } + + /// + /// Sku图片 + /// + public string Logo { get; set; } + + public string SkuId { get; set; } + public string ProductId { get; set; } + public string PurchaseProductId { get; set; } + public string PurchaseSkuId { get; set; } + public string PurchaseSkuSpecId { get; set; } + public long SkuPurchaseSchemeId { get; set; } + public long UserId { get; set; } + + public int ItemTotal + { + get => itemTotal; set + { + if (Set(ref itemTotal, value)) + { + SkuAmount = value * Price; + OnItemTotalChanged?.Invoke(value); + } + } + } + + public decimal SkuAmount { get => skuAmount; set { Set(ref skuAmount, value); } } + + private int itemTotal; + private decimal skuAmount; + + public Action OnItemTotalChanged { get; set; } + } +} diff --git a/BBWYB.Client/Models/Product/Purchaser.cs b/BBWYB.Client/Models/Product/Purchaser.cs new file mode 100644 index 0000000..48be4b3 --- /dev/null +++ b/BBWYB.Client/Models/Product/Purchaser.cs @@ -0,0 +1,25 @@ +namespace BBWYB.Client.Models +{ + /// + /// 采购商 + /// + public class Purchaser : NotifyObject + { + private int skuUseCount; + + public string Id { get; set; } + + public string Name { get; set; } + + /// + /// 使用该采购商的SKU数量 + /// + public int SkuUseCount { get => skuUseCount; set { Set(ref skuUseCount, value); } } + + public string ProductId { get; set; } + + public string Location { get; set; } + + public Platform Platform { get; set; } + } +} diff --git a/BBWYB.Client/Models/Shop/Department.cs b/BBWYB.Client/Models/Shop/Department.cs new file mode 100644 index 0000000..26745c5 --- /dev/null +++ b/BBWYB.Client/Models/Shop/Department.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class Department : NotifyObject + { + private bool isSelected; + + public string Id { get; set; } + + public string Name { get; set; } + + public IList ShopList { get; set; } + public bool IsSelected + { + get => isSelected; + set + { + if (Set(ref isSelected, value)) + OnIsSelectedChanged?.Invoke(); + } + } + + public Department() + { + ShopList = new List(); + } + + public Action OnIsSelectedChanged { get; set; } + + public override string ToString() + { + return this.Name; + } + } +} diff --git a/BBWYB.Client/Models/Shop/PurchaseAccount.cs b/BBWYB.Client/Models/Shop/PurchaseAccount.cs new file mode 100644 index 0000000..0c01ce9 --- /dev/null +++ b/BBWYB.Client/Models/Shop/PurchaseAccount.cs @@ -0,0 +1,27 @@ +using System; + +namespace BBWYB.Client.Models +{ + public class PurchaseAccount : NotifyObject,ICloneable + { + private string accountName; + private Platform purchasePlatformId; + private string appKey; + private string appSecret; + private string appToken; + + public long Id { get; set; } + + public long ShopId { get; set; } + public string AccountName { get => accountName; set { Set(ref accountName, value); } } + public Platform PurchasePlatformId { get => purchasePlatformId; set { Set(ref purchasePlatformId, value); } } + public string AppKey { get => appKey; set { Set(ref appKey, value); } } + public string AppSecret { get => appSecret; set { Set(ref appSecret, value); } } + public string AppToken { get => appToken; set { Set(ref appToken, value); } } + + public object Clone() + { + return this.MemberwiseClone(); + } + } +} diff --git a/BBWYB.Client/Models/Shop/Shop.cs b/BBWYB.Client/Models/Shop/Shop.cs new file mode 100644 index 0000000..03f43e9 --- /dev/null +++ b/BBWYB.Client/Models/Shop/Shop.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; + +namespace BBWYB.Client.Models +{ + public class Shop : NotifyObject + { + private bool isSelected; + private string shopName; + + public bool IsSelected { get => isSelected; set { Set(ref isSelected, value); } } + /// + /// 店铺Id + /// + public long ShopId { get; set; } + + /// + /// 商家类型 + /// + public string VenderType { get; set; } + + /// + /// 店铺平台 + /// + public Platform Platform { get; set; } + + public string AppKey { get; set; } + + public string AppSecret { get; set; } + + public string AppToken { get; set; } + + public string AppKey2 { get; set; } + + public string AppSecret2 { get; set; } + + public string AppToken2 { get; set; } + + public string ShopName { get => shopName; set { Set(ref shopName, value); } } + + public IList PurchaseAccountList { get; set; } + + public string ManagePwd { get; set; } + /// + /// 店铺扣点 + /// + public decimal? PlatformCommissionRatio { get; set; } + + public string TeamId { get; set; } + + public string TeamName { get; set; } + + public string DingDingWebHook { get; set; } + + public string DingDingKey { get; set; } + + public int SkuSafeTurnoverDays { get; set; } + + /// + /// 司南策略等级 + /// + public int SiNanPolicyLevel { get; set; } + + /// + /// 司南钉钉WebHook地址 + /// + public string SiNanDingDingWebHook { get; set; } + + /// + /// 司南钉钉密钥 + /// + public string SiNanDingDingKey { get; set; } + + public override string ToString() + { + return ShopName; + } + } +} diff --git a/BBWYB.Client/Models/User/User.cs b/BBWYB.Client/Models/User/User.cs new file mode 100644 index 0000000..149ad53 --- /dev/null +++ b/BBWYB.Client/Models/User/User.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace BBWYB.Client.Models +{ + public class User : NotifyObject + { + //private string name; + + private Shop shop; + + public long Id { get; set; } + + public string Name { get; set; } + + public string TeamId { get; set; } + + public string TeamName { get; set; } + + public string SonDepartmentNames { get; set; } + + public Shop Shop { get => shop; set { Set(ref shop, value); } } + + public IList DepartmentList { get; set; } + + /// + /// 店铺列表 (暂时只有刷单组才需要) + /// + public IList ShopList { get; set; } + + } +} diff --git a/BBWYB.Client/Resources/Images/bbwylogo.ico b/BBWYB.Client/Resources/Images/bbwylogo.ico new file mode 100644 index 0000000..5fb3f35 Binary files /dev/null and b/BBWYB.Client/Resources/Images/bbwylogo.ico differ diff --git a/BBWYB.Client/Resources/Images/defaultItem.png b/BBWYB.Client/Resources/Images/defaultItem.png new file mode 100644 index 0000000..5d294ec Binary files /dev/null and b/BBWYB.Client/Resources/Images/defaultItem.png differ diff --git a/BBWYB.Client/Resources/Themes/Color.xaml b/BBWYB.Client/Resources/Themes/Color.xaml new file mode 100644 index 0000000..e537f37 --- /dev/null +++ b/BBWYB.Client/Resources/Themes/Color.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BBWYB.Client/Resources/Themes/DataTemplate.xaml b/BBWYB.Client/Resources/Themes/DataTemplate.xaml new file mode 100644 index 0000000..825650e --- /dev/null +++ b/BBWYB.Client/Resources/Themes/DataTemplate.xaml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/BBWYB.Client/Resources/Themes/Generic.xaml b/BBWYB.Client/Resources/Themes/Generic.xaml new file mode 100644 index 0000000..5700c1b --- /dev/null +++ b/BBWYB.Client/Resources/Themes/Generic.xaml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BBWYB.Client/Resources/Themes/Path.xaml b/BBWYB.Client/Resources/Themes/Path.xaml new file mode 100644 index 0000000..1c3a46c --- /dev/null +++ b/BBWYB.Client/Resources/Themes/Path.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BBWYB.Server.API/.config/dotnet-tools.json b/BBWYB.Server.API/.config/dotnet-tools.json new file mode 100644 index 0000000..6b93cca --- /dev/null +++ b/BBWYB.Server.API/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "7.0.3", + "commands": [ + "dotnet-ef" + ] + } + } +} \ No newline at end of file diff --git a/BBWYB.Server.API/appsettings.json b/BBWYB.Server.API/appsettings.json index 10f68b8..addc00e 100644 --- a/BBWYB.Server.API/appsettings.json +++ b/BBWYB.Server.API/appsettings.json @@ -2,8 +2,19 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Warning" + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "ConnectionStrings": { + //"DB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=bbwy;charset=utf8;sslmode=none;" + "DB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=bbwy_test;charset=utf8;sslmode=none;", + "MDSDB": "data source=rm-bp1508okrh23710yfao.mysql.rds.aliyuncs.com;port=3306;user id=qyroot;password=kaicn1132+-;initial catalog=mds;charset=utf8;sslmode=none;" + }, + "GlobalSetting": { + "MdsApi": "http://mdsapi.qiyue666.com" + }, + "AllowedSwagger": true, + "Secret": "D96BFA5B-F2AF-45BC-9342-5A55C3F9BBB0" } diff --git a/SJ.Controls/AssemblyInfo.cs b/SJ.Controls/AssemblyInfo.cs new file mode 100644 index 0000000..8b5504e --- /dev/null +++ b/SJ.Controls/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/SJ.Controls/BAsyncImage.cs b/SJ.Controls/BAsyncImage.cs new file mode 100644 index 0000000..c98346b --- /dev/null +++ b/SJ.Controls/BAsyncImage.cs @@ -0,0 +1,705 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Net.Http; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Schedulers; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Interop; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Resources; +using dw = System.Drawing; + +namespace SJ.Controls +{ + public class BAsyncImage : Control + { + #region DependencyProperty + public static readonly DependencyProperty DecodePixelWidthProperty = DependencyProperty.Register("DecodePixelWidth", + typeof(double), typeof(BAsyncImage), new PropertyMetadata(0.0)); + + public static readonly DependencyProperty LoadingTextProperty = + DependencyProperty.Register("LoadingText", typeof(string), typeof(BAsyncImage), new PropertyMetadata("Loading...")); + + public static readonly DependencyProperty IsLoadingProperty = + DependencyProperty.Register("IsLoading", typeof(bool), typeof(BAsyncImage), new PropertyMetadata(false)); + + public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(BAsyncImage)); + + public static readonly DependencyProperty DefaultUrlSourceProperty = DependencyProperty.Register("DefaultUrlSource", typeof(string), typeof(BAsyncImage), new PropertyMetadata("")); + + public static readonly DependencyProperty UrlSourceProperty = + DependencyProperty.Register("UrlSource", typeof(string), typeof(BAsyncImage), new PropertyMetadata(string.Empty, new PropertyChangedCallback((s, e) => + { + var asyncImg = s as BAsyncImage; + if (asyncImg.LoadEventFlag) + { + asyncImg.Load(); + } + }))); + + public static readonly DependencyProperty FailUrlSourceProperty = + DependencyProperty.Register("FailUrlSource", typeof(string), typeof(BAsyncImage), new PropertyMetadata(string.Empty)); + + public static readonly DependencyProperty IsCacheProperty = DependencyProperty.Register("IsCache", typeof(bool), typeof(BAsyncImage), new PropertyMetadata(true)); + + public static readonly DependencyProperty StretchProperty = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(BAsyncImage), new PropertyMetadata(Stretch.Uniform)); + + public static readonly DependencyProperty CacheGroupProperty = DependencyProperty.Register("CacheGroup", typeof(string), typeof(BAsyncImage), new PropertyMetadata("QLAsyncImage_Default")); + #endregion + + #region Property + private static readonly HttpClient httpClient = new HttpClient(); + + public double StaticImageActualPixelWidth { get; set; } = 0; + + public double StaticImageActualPixelHeight { get; set; } = 0; + + public const string LocalRegex = @"^([C-J]):\\([^:&]+\\)*([^:&]+).(jpg|jpeg|png|gif)$"; + public const string HttpRegex = @"^((https|http):\/\/)?([^\\*+@]+)$"; + + private Image _image; + private dw.Bitmap gifBitmap; + private bool LoadEventFlag = false; + private static ConcurrentDictionary ImageCacheList; + private static ConcurrentDictionary GifImageCacheList; + + private static LimitedConcurrencyLevelTaskScheduler httpTaskScheduler; + + public double DecodePixelWidth + { + get { return (double)GetValue(DecodePixelWidthProperty); } + set { SetValue(DecodePixelWidthProperty, value); } + } + public string LoadingText + { + get { return GetValue(LoadingTextProperty) as string; } + set { SetValue(LoadingTextProperty, value); } + } + + public bool IsLoading + { + get { return (bool)GetValue(IsLoadingProperty); } + set { SetValue(IsLoadingProperty, value); } + } + + public string UrlSource + { + get { return GetValue(UrlSourceProperty) as string; } + set { SetValue(UrlSourceProperty, value); } + } + + /// + /// 仅限Resourcesl路径 + /// + public string FailUrlSource + { + get { return GetValue(FailUrlSourceProperty) as string; } + set { SetValue(FailUrlSourceProperty, value); } + } + + public ImageSource ImageSource + { + get { return GetValue(ImageSourceProperty) as ImageSource; } + set { SetValue(ImageSourceProperty, value); } + } + + public bool IsCache + { + get { return (bool)GetValue(IsCacheProperty); } + set { SetValue(IsCacheProperty, value); } + } + + public Stretch Stretch + { + get { return (Stretch)GetValue(StretchProperty); } + set { SetValue(StretchProperty, value); } + } + + public string CacheGroup + { + get { return GetValue(CacheGroupProperty) as string; } + set { SetValue(CacheGroupProperty, value); } + } + + #endregion + + #region RouteEvent + public delegate void QLAsyncImageLoadCompleteHandler(object sender, QLAsyncImageLoadCompleteEventArgs e); + + public static readonly RoutedEvent OnLoadCompleteEvent = EventManager.RegisterRoutedEvent("OnLoadComplete", RoutingStrategy.Bubble, typeof(QLAsyncImageLoadCompleteHandler), typeof(BAsyncImage)); + + public event QLAsyncImageLoadCompleteHandler OnLoadComplete + { + add { AddHandler(OnLoadCompleteEvent, value); } + remove { RemoveHandler(OnLoadCompleteEvent, value); } + } + #endregion + + #region Method + static BAsyncImage() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BAsyncImage), new FrameworkPropertyMetadata(typeof(BAsyncImage))); + ImageCacheList = new ConcurrentDictionary(); + GifImageCacheList = new ConcurrentDictionary(); + httpTaskScheduler = new LimitedConcurrencyLevelTaskScheduler(10); + } + + public BAsyncImage() + { + Loaded += QLAsyncImage_Loaded; + Unloaded += QLAsyncImage_Unloaded; + } + + private void QLAsyncImage_Unloaded(object sender, RoutedEventArgs e) + { + Reset(); + LoadEventFlag = false; + } + + private void QLAsyncImage_Loaded(object sender, RoutedEventArgs e) + { + Init(); + } + + public override void OnApplyTemplate() + { + Init(); + } + + private void Init([CallerMemberName] string eventName = "") + { + if (LoadEventFlag) + return; + + _image = GetTemplateChild("image") as Image; + + if (_image == null) + return; + + LoadEventFlag = true; + Load(); + } + + /// + /// Delete local bitmap resource + /// Reference: http://msdn.microsoft.com/en-us/library/dd183539(VS.85).aspx + /// + [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool DeleteObject(IntPtr hObject); + + /// + /// 重置图像 + /// + private void Reset() + { + //停止播放Gif动画 + if (gifBitmap != null) + StopGif(); + SetSource(null); + } + + private void Load() + { + if (_image == null) + return; + + Reset(); + + if (!string.IsNullOrEmpty(UrlSource)) + { + var url = UrlSource; + var failUrl = FailUrlSource; + var pixelWidth = (int)DecodePixelWidth; + var isCache = IsCache; + var cacheKey = string.Format("{0}_{1}", CacheGroup, url); + IsLoading = !ImageCacheList.ContainsKey(cacheKey) && !GifImageCacheList.ContainsKey(cacheKey); + + #region 读取缓存 + if (ImageCacheList.ContainsKey(cacheKey)) + { + var source = ImageCacheList[cacheKey]; + SetSource(source); + SetStaticImageActualPixelSize(source.Width, source.Height); + LoadComplete(string.Empty, url); + return; + } + else if (GifImageCacheList.ContainsKey(cacheKey)) + { + PlayGif(GifImageCacheList[cacheKey]); + LoadComplete(string.Empty, url); + return; + } + #endregion + + this.Load(url, failUrl, cacheKey, isCache, pixelWidth); + } + } + + private void Load(string url, string failUrl, string cacheKey, bool isCache, int pixelWidth) + { + var errorMessage = string.Empty; + + //解析路径类型 + var pathType = ValidatePathType(url); + if (pathType == PathType.Invalid) + { + LoadFail(failUrl); + LoadComplete(errorMessage, url); + return; + } + + if (pathType == PathType.Http) + { + ////先加载默认图 + //if (!string.IsNullOrEmpty(defaultUrl)) + // LoadLocal(defaultUrl, failUrl, PathType.Resources, defaultUrl, true, 0, excuteComplete: false); //默认图不触发加载完毕事件 + LoadHttp(url, failUrl, cacheKey, isCache, pixelWidth); + } + else + LoadLocal(url, failUrl, pathType, cacheKey, isCache, pixelWidth); + } + + /// + /// 加载失败图像 + /// + /// + private void LoadFail(string failUrl) + { + byte[] imgBytes = null; + string errorMessage = string.Empty; + var pathType = ValidatePathType(failUrl); + if (pathType == PathType.Invalid) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"LoadFail 无效的路径 {failUrl}"); + Console.ResetColor(); + return; + } + if (pathType == PathType.Local) + imgBytes = LoadBytesFromLocal(failUrl, out errorMessage); + else if (pathType == PathType.Resources) + imgBytes = LoadBytesFromApplicationResource(failUrl, out errorMessage); + + + if (string.IsNullOrEmpty(errorMessage) && imgBytes != null) + AnalysisBytes(imgBytes, failUrl, true, 0, out errorMessage); + + if (string.IsNullOrEmpty(errorMessage)) + return; + + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"LoadFail {errorMessage} {failUrl}"); + Console.ResetColor(); + } + + private void LoadLocal(string url, string failUrl, PathType pathType, string cacheKey, bool isCache, int pixelWidth, bool excuteComplete = true) + { + byte[] imgBytes = null; + var errorMessage = string.Empty; + if (pathType == PathType.Local) + imgBytes = LoadBytesFromLocal(url, out errorMessage); + else if (pathType == PathType.Resources) + imgBytes = LoadBytesFromApplicationResource(url, out errorMessage); + + if (string.IsNullOrEmpty(errorMessage) && imgBytes != null) + AnalysisBytes(imgBytes, cacheKey, isCache, pixelWidth, out errorMessage); + + if (!string.IsNullOrEmpty(errorMessage)) + { + LoadFail(failUrl); + Console.ForegroundColor = ConsoleColor.Red; + } + Console.WriteLine($"LoadLocal {errorMessage} {url}"); + Console.ResetColor(); + if (excuteComplete) + LoadComplete(errorMessage, url); + return; + } + + private void LoadHttp(string url, string failUrl, string cacheKey, bool isCache, int pixelWidth) + { + Task.Factory.StartNew(() => + { + //Thread.Sleep(2000); + Console.WriteLine($"LoadHttp Start {url}"); + var errorMessage = string.Empty; + var imgBytes = LoadBytesFromHttp(url, out errorMessage); + + if (string.IsNullOrEmpty(errorMessage) && imgBytes != null) + AnalysisBytes(imgBytes, cacheKey, isCache, pixelWidth, out errorMessage); + + if (!string.IsNullOrEmpty(errorMessage)) + { + LoadFail(failUrl); + Console.ForegroundColor = ConsoleColor.Red; + } + Console.WriteLine($"LoadHttp Completed {errorMessage} {url}"); + Console.ResetColor(); + LoadComplete(errorMessage, url); + return; + }, CancellationToken.None, TaskCreationOptions.None, httpTaskScheduler); + } + + private void AnalysisBytes(byte[] imgBytes, string cacheKey, bool isCache, int pixelWidth, out string errorMessage) + { + errorMessage = string.Empty; + + #region 读取文件类型 + var imgType = GetImageType(imgBytes); + if (imgType == ImageType.Invalid) + { + imgBytes = null; + errorMessage = "Invalid ImageFile"; + return; + } + #endregion + + #region 加载图像 + if (imgType != ImageType.Gif) + { + //加载静态图像 + var imgSource = LoadStaticImage(cacheKey, imgBytes, pixelWidth, isCache, out errorMessage); + if (imgSource == null) + return; + SetStaticImageActualPixelSize(imgSource.Width, imgSource.Height); + SetSource(imgSource); + } + else + { + var frameCount = 0; + using (var memoryStream = new System.IO.MemoryStream(imgBytes)) + { + //读取gif帧数 + var decoder = BitmapDecoder.Create(memoryStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); + frameCount = decoder.Frames.Count; + decoder = null; + } + if (frameCount > 1) + { + CacheGifBytes(cacheKey, imgBytes, isCache); + PlayGif(imgBytes); + } + else + { + //gif只有1帧,视为静态图处理 + var imgSource = LoadStaticImage(cacheKey, imgBytes, pixelWidth, isCache, out errorMessage); + if (imgSource == null) + return; + SetStaticImageActualPixelSize(imgSource.Width, imgSource.Height); + SetSource(imgSource); + } + } + #endregion + } + + + /// + /// 加载静态图像 + /// + /// + /// + /// + /// + /// + private ImageSource LoadStaticImage(string cacheKey, byte[] imgBytes, int pixelWidth, bool isCache, out string errorMessage) + { + errorMessage = string.Empty; + if (ImageCacheList.ContainsKey(cacheKey)) + return ImageCacheList[cacheKey]; + var bit = new BitmapImage() { CacheOption = BitmapCacheOption.OnLoad }; + try + { + bit.BeginInit(); + if (pixelWidth != 0) + { + bit.DecodePixelWidth = pixelWidth; + } + bit.StreamSource = new System.IO.MemoryStream(imgBytes); + bit.EndInit(); + bit.Freeze(); + if (isCache && !ImageCacheList.ContainsKey(cacheKey)) + ImageCacheList.TryAdd(cacheKey, bit); + } + catch (Exception ex) + { + errorMessage = $"LoadStaticImage Error {ex.Message}"; + bit = null; + } + return bit; + } + + /// + /// 加载Gif图像动画 + /// + /// + /// + /// + /// + /// + private void CacheGifBytes(string cacheKey, byte[] imgBytes, bool isCache) + { + if (isCache && !GifImageCacheList.ContainsKey(cacheKey)) + GifImageCacheList.TryAdd(cacheKey, imgBytes); + } + + private byte[] LoadBytesFromHttp(string url, out string errorMessage) + { + errorMessage = string.Empty; + try + { + return httpClient.GetByteArrayAsync(url).Result; + } + catch (Exception ex) + { + errorMessage = $"Dowdload Error {ex.Message}"; + } + return null; + } + + private byte[] LoadBytesFromLocal(string path, out string errorMessage) + { + errorMessage = string.Empty; + if (!System.IO.File.Exists(path)) + { + errorMessage = "File No Exists"; + return null; + } + try + { + return System.IO.File.ReadAllBytes(path); + } + catch (Exception ex) + { + errorMessage = $"Load Local Error {ex.Message}"; + return null; + } + } + + private byte[] LoadBytesFromApplicationResource(string path, out string errorMessage) + { + errorMessage = string.Empty; + try + { + StreamResourceInfo streamInfo = Application.GetResourceStream(new Uri(path, UriKind.RelativeOrAbsolute)); + if (streamInfo.Stream.CanRead) + { + using (streamInfo.Stream) + { + var bytes = new byte[streamInfo.Stream.Length]; + streamInfo.Stream.Read(bytes, 0, bytes.Length); + return bytes; + } + } + } + catch (Exception ex) + { + errorMessage = $"Load Resource Error {ex.Message}"; + return null; + } + return null; + } + + private void SetSource(ImageSource source) + { + Dispatcher.BeginInvoke((Action)delegate + { + if (_image != null) + _image.Source = source; + }); + } + + /// + /// 更新图像实际像素 + /// + /// + private void SetStaticImageActualPixelSize(double pixelWidth, double pixelHeight) + { + Dispatcher.Invoke(() => + { + StaticImageActualPixelWidth = pixelWidth; + StaticImageActualPixelHeight = pixelHeight; + }); + } + + private void PlayGif(byte[] imgBytes) + { + gifBitmap = new dw.Bitmap(new System.IO.MemoryStream(imgBytes)); + if (dw.ImageAnimator.CanAnimate(gifBitmap)) + { + SetStaticImageActualPixelSize(gifBitmap.Width, gifBitmap.Height); + dw.ImageAnimator.Animate(gifBitmap, OnGifFrameChanged); + } + else + { + gifBitmap.Dispose(); + } + } + + private void StopGif() + { + dw.ImageAnimator.StopAnimate(gifBitmap, OnGifFrameChanged); + gifBitmap.Dispose(); + } + + private void OnGifFrameChanged(object sender, EventArgs e) + { + dw.ImageAnimator.UpdateFrames(); + var currentFrameImageSource = GetBitmapSource(); + if (currentFrameImageSource != null) + currentFrameImageSource.Freeze(); + SetSource(currentFrameImageSource); + } + + /// + /// 加载完成 + /// + /// + /// + private void LoadComplete(string errorMessage, string url) + { + this.Dispatcher.BeginInvoke((Action)delegate + { + IsLoading = false; + var args = new QLAsyncImageLoadCompleteEventArgs(OnLoadCompleteEvent, this) + { + IsSuccess = string.IsNullOrEmpty(errorMessage), + UrlSource = url, + ErrorMessage = errorMessage, + StaticImageActualPixelWidth = StaticImageActualPixelWidth, + StaticImageActualPixelHeight = StaticImageActualPixelHeight + }; + this.RaiseEvent(args); + }); + } + + /// + /// 从System.Drawing.Bitmap中获得当前帧图像的BitmapSource + /// + /// + private ImageSource GetBitmapSource() + { + IntPtr handle = IntPtr.Zero; + try + { + handle = gifBitmap.GetHbitmap(); + return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); + } + catch + { + return null; + } + finally + { + if (handle != IntPtr.Zero) + { + DeleteObject(handle); + } + } + } + + public void Refresh() + { + Load(); + } + + private PathType ValidatePathType(string path) + { + if (path.StartsWith("pack://")) + return PathType.Resources; + else if (Regex.IsMatch(path, BAsyncImage.LocalRegex, RegexOptions.IgnoreCase)) + return PathType.Local; + else if (Regex.IsMatch(path, BAsyncImage.HttpRegex, RegexOptions.IgnoreCase)) + return PathType.Http; + else + return PathType.Invalid; + } + + private ImageType GetImageType(byte[] bytes) + { + var type = ImageType.Invalid; + try + { + var fileHead = Convert.ToInt32($"{bytes[0]}{bytes[1]}"); + if (!Enum.IsDefined(typeof(ImageType), fileHead)) + { + type = ImageType.Invalid; + } + else + { + type = (ImageType)fileHead; + } + } + catch (Exception ex) + { + type = ImageType.Invalid; + Console.WriteLine($"获取图片类型失败 {ex.Message}"); + } + return type; + } + + /// + /// 清楚缓存 + /// + /// 缓存Key,格式: CacheGroup_UrlSource + public static void ClearCache(string cacheKey = "") + { + if (string.IsNullOrEmpty(cacheKey)) + { + ImageCacheList.Clear(); + GifImageCacheList.Clear(); + return; + } + + ImageCacheList.Remove(cacheKey, out _); + GifImageCacheList.Remove(cacheKey, out _); + } + + public static ImageSource GetImageCache(string cacheKey) + { + if (ImageCacheList.ContainsKey(cacheKey)) + return ImageCacheList[cacheKey]; + return null; + } + #endregion + } + + + public enum PathType + { + Invalid = 0, Local = 1, Http = 2, Resources = 3 + } + + public enum ImageType + { + Invalid = 0, Gif = 7173, Jpg = 255216, Png = 13780, Bmp = 6677 + } + + public class QLAsyncImageLoadCompleteEventArgs : RoutedEventArgs + { + public QLAsyncImageLoadCompleteEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { } + + public bool IsSuccess { get; set; } + + public string UrlSource { get; set; } + + public string ErrorMessage { get; set; } + + /// + /// 当加载静态图时的实际像素宽度 + /// + public double StaticImageActualPixelWidth { get; set; } + + /// + /// 当加载静态图时的实际像素高度 + /// + public double StaticImageActualPixelHeight { get; set; } + } +} diff --git a/SJ.Controls/BButton.cs b/SJ.Controls/BButton.cs new file mode 100644 index 0000000..a0c18ac --- /dev/null +++ b/SJ.Controls/BButton.cs @@ -0,0 +1,72 @@ +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace SJ.Controls +{ + [StyleTypedProperty(Property = "Style", StyleTargetType = typeof(BButton))] + public class BButton : Button + { + static BButton() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BButton), new FrameworkPropertyMetadata(typeof(BButton))); + } + + public static readonly DependencyProperty BorderCornerRadiusProperty = DependencyProperty.Register("BorderCornerRadius", typeof(CornerRadius), typeof(BButton)); + public static readonly DependencyProperty MouseOverBgColorProperty = DependencyProperty.Register("MouseOverBgColor", typeof(Brush), typeof(BButton)); + public static readonly DependencyProperty MouseOverFontColorProperty = DependencyProperty.Register("MouseOverFontColor", typeof(Brush), typeof(BButton)); + public static readonly DependencyProperty PressedBgColorProperty = DependencyProperty.Register("PressedBgColor", typeof(Brush), typeof(BButton)); + public static readonly DependencyProperty PressedFontColorProperty = DependencyProperty.Register("PressedFontColor", typeof(Brush), typeof(BButton)); + public static readonly DependencyProperty DisableBgColorProperty = DependencyProperty.Register("DisableBgColor", typeof(Brush), typeof(BButton)); + public static readonly DependencyProperty DisableTextProperty = DependencyProperty.Register("DisableText", typeof(string), typeof(BButton)); + public static readonly DependencyProperty PressedScaleProperty = DependencyProperty.Register("PressedScale", typeof(bool), typeof(BButton), new PropertyMetadata(true)); + + public CornerRadius BorderCornerRadius + { + get { return (CornerRadius)GetValue(BorderCornerRadiusProperty); } + set { SetValue(BorderCornerRadiusProperty, value); } + } + + public Brush MouseOverBgColor + { + get { return GetValue(MouseOverBgColorProperty) as Brush; } + set { SetValue(MouseOverBgColorProperty, value); } + } + + public Brush MouseOverFontColor + { + get { return GetValue(MouseOverFontColorProperty) as Brush; } + set { SetValue(MouseOverFontColorProperty, value); } + } + + public Brush PressedBgColor + { + get { return GetValue(PressedBgColorProperty) as Brush; } + set { SetValue(PressedBgColorProperty, value); } + } + + public Brush PressedFontColor + { + get { return GetValue(PressedFontColorProperty) as Brush; } + set { SetValue(PressedFontColorProperty, value); } + } + + public Brush DisableBgColor + { + get { return GetValue(DisableBgColorProperty) as System.Windows.Media.Brush; } + set { SetValue(DisableBgColorProperty, value); } + } + + public string DisableText + { + get { return GetValue(DisableTextProperty).ToString(); } + set { SetValue(DisableTextProperty, value); } + } + + public bool PressedScale + { + get { return (bool)GetValue(PressedScaleProperty); } + set { SetValue(PressedScaleProperty, value); } + } + } +} diff --git a/SJ.Controls/BTextBox.cs b/SJ.Controls/BTextBox.cs new file mode 100644 index 0000000..67d0e18 --- /dev/null +++ b/SJ.Controls/BTextBox.cs @@ -0,0 +1,210 @@ +using System.Text; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace SJ.Controls +{ + [StyleTypedProperty(Property = "Style", StyleTargetType = typeof(BTextBox))] + public class BTextBox : TextBox + { + #region Property + + private bool IsResponseChange; + private StringBuilder PasswordBuilder; + private int lastOffset; + + #endregion + + #region DependencyProperty + + public static readonly DependencyProperty WaterRemarkProperty = DependencyProperty.Register("WaterRemark", typeof(string), typeof(BTextBox)); + public static readonly DependencyProperty WaterRemarkFontColorProperty = DependencyProperty.Register("WaterRemarkFontColor", typeof(Brush), typeof(BTextBox)); + public static readonly DependencyProperty BorderCornerRadiusProperty = DependencyProperty.Register("BorderCornerRadius", typeof(CornerRadius), typeof(BTextBox)); + public static readonly DependencyProperty IsPasswordBoxProperty = DependencyProperty.Register("IsPasswordBox", typeof(bool), typeof(BTextBox), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsPasswordBoxChnage))); + public static readonly DependencyProperty IsNumberBoxProperty = DependencyProperty.Register("IsNumberBox", typeof(bool), typeof(BTextBox), new PropertyMetadata(false, new PropertyChangedCallback(OnIsNumberBoxChnage))); + public static readonly DependencyProperty DisableBgColorProperty = DependencyProperty.Register("DisableBgColor", typeof(Brush), typeof(BTextBox)); + public static readonly DependencyProperty PasswordCharProperty = DependencyProperty.Register("PasswordChar", typeof(char), typeof(BTextBox), new FrameworkPropertyMetadata('●')); + public static readonly DependencyProperty PasswordStrProperty = DependencyProperty.Register("PasswordStr", typeof(string), typeof(BTextBox), new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPasswordStrChanged))); + + /// + /// 水印文字 + /// + public string WaterRemark + { + get { return GetValue(WaterRemarkProperty).ToString(); } + set { SetValue(WaterRemarkProperty, value); } + } + + public Brush WaterRemarkFontColor + { + get { return GetValue(WaterRemarkFontColorProperty) as Brush; } + set { SetValue(WaterRemarkFontColorProperty, value); } + } + + /// + /// 边框角度 + /// + public CornerRadius BorderCornerRadius + { + get { return (CornerRadius)GetValue(BorderCornerRadiusProperty); } + set { SetValue(BorderCornerRadiusProperty, value); } + } + + /// + /// 是否为密码框 + /// + public bool IsPasswordBox + { + get { return (bool)GetValue(IsPasswordBoxProperty); } + set { SetValue(IsPasswordBoxProperty, value); } + } + + /// + /// 是否为数字框 + /// + public bool IsNumberBox + { + get { return (bool)GetValue(IsNumberBoxProperty); } + set { SetValue(IsNumberBoxProperty, value); } + } + + /// + /// 替换明文的密码字符 + /// + public char PasswordChar + { + get { return (char)GetValue(PasswordCharProperty); } + set { SetValue(PasswordCharProperty, value); } + } + + /// + /// 密码字符串 + /// + public string PasswordStr + { + get + { + var value = GetValue(PasswordStrProperty); + return value == null ? string.Empty : value.ToString(); + } + set { SetValue(PasswordStrProperty, value); } + } + + /// + /// 按钮被禁用时的背景颜色 + /// + public Brush DisableBgColor + { + get { return GetValue(DisableBgColorProperty) as Brush; } + set { SetValue(DisableBgColorProperty, value); } + } + #endregion + + + static BTextBox() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBox), new FrameworkPropertyMetadata(typeof(BTextBox))); + } + + public BTextBox() + { + IsResponseChange = true; + PasswordBuilder = new StringBuilder(); + this.Loaded += QLTextBox_Loaded; + } + + private void QLTextBox_Loaded(object sender, RoutedEventArgs e) + { + if (IsPasswordBox && !string.IsNullOrEmpty(PasswordStr) && PasswordStr.Length > 0) + { + OnPasswordStrChanged(); + } + } + + private static void OnPasswordStrChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) + { + (d as BTextBox).OnPasswordStrChanged(); + } + + private void OnPasswordStrChanged() + { + if (!IsResponseChange) + return; + IsResponseChange = false; + this.Text = ConvertToPasswordChar(PasswordStr.Length); + IsResponseChange = true; + } + + private static void OnIsPasswordBoxChnage(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + (sender as BTextBox).SetPwdEvent(); + } + + private static void OnIsNumberBoxChnage(DependencyObject sender, DependencyPropertyChangedEventArgs e) + { + (sender as BTextBox).SetValidationNumberEvent(); + } + + /// + /// 定义TextChange事件 + /// + private void SetPwdEvent() + { + if (IsPasswordBox) + this.TextChanged += QLTextBox_TextChanged; + else + this.TextChanged -= QLTextBox_TextChanged; + } + + private void SetValidationNumberEvent() + { + if (IsNumberBox) + this.PreviewTextInput += QLTextBox_PreviewTextInput; + else + this.PreviewTextInput -= QLTextBox_PreviewTextInput; + } + + private void QLTextBox_TextChanged(object sender, TextChangedEventArgs e) + { + if (!IsResponseChange) + return; + IsResponseChange = false; + foreach (TextChange c in e.Changes) + { + PasswordStr = PasswordStr.Remove(c.Offset, c.RemovedLength); + PasswordStr = PasswordStr.Insert(c.Offset, Text.Substring(c.Offset, c.AddedLength)); + lastOffset = c.Offset; + } + /*将文本转换为密码字符*/ + this.Text = ConvertToPasswordChar(Text.Length); + IsResponseChange = true; + this.SelectionStart = lastOffset + 1; + } + + private void QLTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) + { + Regex re = new Regex("[^0-9.]+"); + e.Handled = re.IsMatch(e.Text); + } + + + /// + /// 按照指定的长度生成密码字符 + /// + /// + /// + private string ConvertToPasswordChar(int length) + { + if (PasswordBuilder != null) + PasswordBuilder.Clear(); + else + PasswordBuilder = new StringBuilder(); + for (var i = 0; i < length; i++) + PasswordBuilder.Append(PasswordChar); + return PasswordBuilder.ToString(); + } + } +} diff --git a/SJ.Controls/BTextBoxAnimation.cs b/SJ.Controls/BTextBoxAnimation.cs new file mode 100644 index 0000000..7457ffc --- /dev/null +++ b/SJ.Controls/BTextBoxAnimation.cs @@ -0,0 +1,109 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace SJ.Controls +{ + [StyleTypedProperty(Property = "Style", StyleTargetType = typeof(BTextBoxAnimation))] + public class BTextBoxAnimation : BTextBox + { + public static readonly DependencyProperty WaterRemarkTopStateColorProperty = DependencyProperty.Register("WaterRemarkTopStateColor", typeof(Brush), typeof(BTextBoxAnimation)); + public static readonly DependencyProperty WaterRemarkStateProperty = DependencyProperty.Register("WaterRemarkState", typeof(WaterRemarkState), typeof(BTextBoxAnimation), new PropertyMetadata(WaterRemarkState.Normal, new PropertyChangedCallback((d, e) => + { + if (e.OldValue != e.NewValue) + { + (d as BTextBoxAnimation).PlayWaterRemarkAnimation(); + } + }))); + + private TextBlock txtRemark; + private TimeSpan animationTimeSpan = new TimeSpan(0, 0, 0, 0, 200); + private IEasingFunction animationEasingFunction = new PowerEase() { EasingMode = EasingMode.EaseInOut }; + + + public Brush WaterRemarkTopStateColor + { + get { return GetValue(WaterRemarkTopStateColorProperty) as Brush; } + set { SetValue(WaterRemarkTopStateColorProperty, value); } + } + + public WaterRemarkState WaterRemarkState + { + get { return (WaterRemarkState)Convert.ToInt32(GetValue(WaterRemarkStateProperty)); } + set { SetValue(WaterRemarkStateProperty, value); } + } + + + static BTextBoxAnimation() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BTextBoxAnimation), new FrameworkPropertyMetadata(typeof(BTextBoxAnimation))); + } + public BTextBoxAnimation() + { + this.Loaded += QLTextBoxAnimation_Loaded; + } + + public override void OnApplyTemplate() + { + txtRemark = GetTemplateChild("txtRemark") as TextBlock; + base.OnApplyTemplate(); + } + + private void QLTextBoxAnimation_Loaded(object sender, RoutedEventArgs e) + { + if (!string.IsNullOrEmpty(Text)) + WaterRemarkState = WaterRemarkState.Top; + } + + protected override void OnTextChanged(TextChangedEventArgs e) + { + base.OnTextChanged(e); + if (!this.IsLoaded) + return; + if (string.IsNullOrEmpty(Text)) + WaterRemarkState = WaterRemarkState.Normal; + else + WaterRemarkState = WaterRemarkState.Top; + } + + protected override void OnGotFocus(RoutedEventArgs e) + { + base.OnGotFocus(e); + WaterRemarkState = WaterRemarkState.Top; + } + + protected override void OnLostFocus(RoutedEventArgs e) + { + base.OnLostFocus(e); + if (string.IsNullOrEmpty(Text)) + WaterRemarkState = WaterRemarkState.Normal; + } + + private void PlayWaterRemarkAnimation() + { + var fontsize = WaterRemarkState == WaterRemarkState.Normal ? FontSize : 10.5; + var row = WaterRemarkState == WaterRemarkState.Normal ? 1 : 0; + + var storyboard = new Storyboard(); + var daukf_Remark_FontSize = new DoubleAnimationUsingKeyFrames(); + daukf_Remark_FontSize.KeyFrames.Add(new EasingDoubleKeyFrame(fontsize, animationTimeSpan, animationEasingFunction)); + Storyboard.SetTargetProperty(daukf_Remark_FontSize, new PropertyPath("(TextBlock.FontSize)")); + Storyboard.SetTarget(daukf_Remark_FontSize, txtRemark); + storyboard.Children.Add(daukf_Remark_FontSize); + + var i32aukf_Remark_Row = new Int32AnimationUsingKeyFrames(); + i32aukf_Remark_Row.KeyFrames.Add(new EasingInt32KeyFrame(row, animationTimeSpan, animationEasingFunction)); + Storyboard.SetTargetProperty(i32aukf_Remark_Row, new PropertyPath("(Grid.Row)")); + Storyboard.SetTarget(i32aukf_Remark_Row, txtRemark); + storyboard.Children.Add(i32aukf_Remark_Row); + storyboard.Begin(); + } + } + + public enum WaterRemarkState + { + Normal, Top + } +} diff --git a/SJ.Controls/BWindow.cs b/SJ.Controls/BWindow.cs new file mode 100644 index 0000000..9cec0b1 --- /dev/null +++ b/SJ.Controls/BWindow.cs @@ -0,0 +1,178 @@ +using System.ComponentModel; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Shell; + +namespace SJ.Controls +{ + [StyleTypedProperty(Property = "Style", StyleTargetType = typeof(BWindow))] + public class BWindow : Window, INotifyPropertyChanged + { + public static readonly DependencyProperty CornerRadiusProperty = + DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(BWindow), new PropertyMetadata(new CornerRadius(0))); + + public static readonly DependencyProperty RightButtonGroupMarginProperty = + DependencyProperty.Register("RightButtonGroupMargin", typeof(Thickness), typeof(BWindow), new PropertyMetadata(new Thickness(0, 16, 16, 0))); + + public static readonly DependencyProperty CloseButtonVisibilityProperty = + DependencyProperty.Register("CloseButtonVisibility", typeof(Visibility), typeof(BWindow), new PropertyMetadata(Visibility.Visible)); + + public static readonly DependencyProperty MinButtonVisibilityProperty = + DependencyProperty.Register("MinButtonVisibility", typeof(Visibility), typeof(BWindow), new PropertyMetadata(Visibility.Visible)); + + public static readonly DependencyProperty MaxButtonVisibilityProperty = + DependencyProperty.Register("MaxButtonVisibility", typeof(Visibility), typeof(BWindow), new PropertyMetadata(Visibility.Visible)); + + public static readonly DependencyProperty CloseButtonColorProperty = + DependencyProperty.Register("CloseButtonColor", typeof(Brush), typeof(BWindow), new PropertyMetadata(new SolidColorBrush(Colors.White))); + + public static readonly DependencyProperty MinButtonColorProperty = + DependencyProperty.Register("MinButtonColor", typeof(Brush), typeof(BWindow), new PropertyMetadata(new SolidColorBrush(Colors.White))); + + public static readonly DependencyProperty MaxButtonColorProperty = + DependencyProperty.Register("MaxButtonColor", typeof(Brush), typeof(BWindow), new PropertyMetadata(new SolidColorBrush(Colors.White))); + + public CornerRadius CornerRadius + { + get { return (CornerRadius)GetValue(CornerRadiusProperty); } + set { SetValue(CornerRadiusProperty, value); } + } + + public Thickness RightButtonGroupMargin + { + get { return (Thickness)GetValue(RightButtonGroupMarginProperty); } + set { SetValue(RightButtonGroupMarginProperty, value); } + } + + public Visibility CloseButtonVisibility + { + get { return (Visibility)GetValue(CloseButtonVisibilityProperty); } + set { SetValue(CloseButtonVisibilityProperty, value); } + } + + public Visibility MinButtonVisibility + { + get { return (Visibility)GetValue(MinButtonVisibilityProperty); } + set { SetValue(MinButtonVisibilityProperty, value); } + } + + public Visibility MaxButtonVisibility + { + get { return (Visibility)GetValue(MaxButtonVisibilityProperty); } + set { SetValue(MaxButtonVisibilityProperty, value); } + } + + public Brush CloseButtonColor + { + get { return (Brush)GetValue(CloseButtonColorProperty); } + set { SetValue(CloseButtonColorProperty, value); } + } + + public Brush MinButtonColor + { + get { return (Brush)GetValue(MinButtonColorProperty); } + set { SetValue(MinButtonColorProperty, value); } + } + + public Brush MaxButtonColor + { + get { return (Brush)GetValue(MaxButtonColorProperty); } + set { SetValue(MaxButtonColorProperty, value); } + } + + static BWindow() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(BWindow), new FrameworkPropertyMetadata(typeof(BWindow))); + } + + public BWindow() + { + WindowStartupLocation = WindowStartupLocation.CenterScreen; + var chrome = new WindowChrome + { + CornerRadius = new CornerRadius(), + GlassFrameThickness = new Thickness(1), + UseAeroCaptionButtons = false, + NonClientFrameEdges = NonClientFrameEdges.None, + ResizeBorderThickness = new Thickness(2), + CaptionHeight = 30 + }; + WindowChrome.SetWindowChrome(this, chrome); + } + + public override void OnApplyTemplate() + { + Button PART_MIN = null; + Button PART_MAX = null; + Button PART_RESTORE = null; + Button PART_CLOSE = null; + + PART_MIN = GetTemplateChild("PART_MIN") as Button; + PART_MAX = GetTemplateChild("PART_MAX") as Button; + PART_RESTORE = GetTemplateChild("PART_RESTORE") as Button; + PART_CLOSE = GetTemplateChild("PART_CLOSE") as Button; + + if (PART_RESTORE != null) + PART_RESTORE.Click += PART_RESTORE_Click; + if (PART_MAX != null) + PART_MAX.Click += PART_MAX_Click; + if (PART_MIN != null) + PART_MIN.Click += PART_MIN_Click; + if (PART_CLOSE != null) + PART_CLOSE.Click += PART_CLOSE_Click; + + base.OnApplyTemplate(); + } + + private void PART_CLOSE_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + + private void PART_MIN_Click(object sender, RoutedEventArgs e) + { + WindowState = WindowState.Minimized; + } + + private void PART_MAX_Click(object sender, RoutedEventArgs e) + { + WindowState = WindowState.Maximized; + } + + private void PART_RESTORE_Click(object sender, RoutedEventArgs e) + { + WindowState = WindowState.Normal; + } + + /// + /// 判断是否为模态窗口 + /// + /// + public bool IsModal() + { + var filedInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic); + return filedInfo != null && (bool)filedInfo.GetValue(this); + } + + + #region PropertyNotify + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + protected bool Set(ref T oldValue, T newValue, [CallerMemberName] string propertyName = "") + { + if (Equals(oldValue, newValue)) + return false; + oldValue = newValue; + OnPropertyChanged(propertyName); + return true; + } + #endregion + } +} diff --git a/SJ.Controls/Extensions/VisualTreeExtension.cs b/SJ.Controls/Extensions/VisualTreeExtension.cs new file mode 100644 index 0000000..1664ae3 --- /dev/null +++ b/SJ.Controls/Extensions/VisualTreeExtension.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Input; +using System.Windows.Media; + +namespace BBGWY.Controls.Extensions +{ + public static class VisualTreeExtension + { + public static T HitTest(this FrameworkElement fe, Point? point) where T : FrameworkElement + { + if (point == null) + point = Mouse.GetPosition(fe); + var result = VisualTreeHelper.HitTest(fe, point.Value); + if (result == null) + return null; + if (result.VisualHit != null) + { + var r = FindParentOfType(result.VisualHit); + return r; + } + return null; + } + + /// + /// 查找父控件 + /// + /// 父控件类型 + /// 子控件实例 + /// + public static T FindParentOfType(this DependencyObject obj) where T : FrameworkElement + { + DependencyObject parent = VisualTreeHelper.GetParent(obj); + while (parent != null) + { + if (parent is T) + { + return (T)parent; + } + parent = VisualTreeHelper.GetParent(parent); + } + return null; + } + + /// + /// 查找子控件 + /// + /// 需要查找的控件类型 + /// 父控件实例 + /// + public static T FindFirstVisualChild(this DependencyObject obj) where T : FrameworkElement + { + var queue = new Queue(); + queue.Enqueue(obj); + while (queue.Count > 0) + { + DependencyObject current = queue.Dequeue(); + for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--) + { + DependencyObject child = VisualTreeHelper.GetChild(current, i); + if (child != null && child is T) + { + return (T)child; + } + queue.Enqueue(child); + } + } + return null; + } + } +} diff --git a/SJ.Controls/Helpers/StoryboardHelper.cs b/SJ.Controls/Helpers/StoryboardHelper.cs new file mode 100644 index 0000000..ba92b60 --- /dev/null +++ b/SJ.Controls/Helpers/StoryboardHelper.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media.Animation; + +namespace SJ.Controls.Helpers +{ + public class StoryboardHelper + { + /// + /// 播放动画 + /// + /// 控件源 + /// 开始值 + /// 结束值 + /// 时间间隔 + /// 是否反向播放 + /// 是否重复播放 + /// 动画类型 + /// 回调函数 + /// 动画属性 + public static void _PlayDoubleAnimation(FrameworkElement fe, double from, double to, TimeSpan duration, bool IsAutoReverse, bool RepeatPlay, IEasingFunction ef, Action Callback, string PropertyPath) + { + Storyboard _sb = new Storyboard(); + _sb.Completed += new EventHandler((s, e) => + { + if (Callback != null) + Callback(); + _sb.Stop(); + _sb.Children.Clear(); + _sb = null; + }); + DoubleAnimation daRotation = new DoubleAnimation(); + daRotation.From = from; + daRotation.To = to; + daRotation.EasingFunction = ef; + daRotation.Duration = duration; + Storyboard.SetTargetProperty(daRotation, new PropertyPath(PropertyPath)); + Storyboard.SetTarget(daRotation, fe); + _sb.Children.Add(daRotation); + _sb.AutoReverse = IsAutoReverse; + if (RepeatPlay) + _sb.RepeatBehavior = RepeatBehavior.Forever; + _sb.Begin(); + } + + public static void _PlayAnimationUsingKeyFrames(IList AnimationUsingKeyFrameList, bool IsAutoReverse, bool IsRepeayPlay, Action Callback) + { + if (AnimationUsingKeyFrameList == null || AnimationUsingKeyFrameList.Count == 0) + return; + Storyboard _sb = new Storyboard(); + _sb.Completed += new EventHandler((s, e) => + { + if (Callback != null) + Callback(); + _sb.Stop(); + _sb.Children.Clear(); + _sb = null; + }); + _sb.AutoReverse = IsAutoReverse; + if (IsRepeayPlay) + _sb.RepeatBehavior = RepeatBehavior.Forever; + foreach (AnimationModel am in AnimationUsingKeyFrameList) + { + AnimationTimeline animationTimeLine = null; + switch (am._KeyFrameType) + { + case KeyFrameType.DoubleKeyFrame: + animationTimeLine = CreateDoubleAnimationUsingKeyFrames(am); + break; + case KeyFrameType.ColorKeyFrame: + animationTimeLine = CreateColorAnimationUsingKeyFrames(am); + break; + case KeyFrameType.ObjectKeyFrame: + animationTimeLine = CreateObjectAnimationUsingKeyFrames(am); + break; + } + _sb.Children.Add(animationTimeLine); + } + _sb.Begin(); + } + + private static AnimationTimeline CreateDoubleAnimationUsingKeyFrames(AnimationModel am) + { + DoubleAnimationUsingKeyFrames animationTimeline = new DoubleAnimationUsingKeyFrames(); + Storyboard.SetTargetProperty(animationTimeline, new PropertyPath(am.PropertyPath)); + Storyboard.SetTarget(animationTimeline, am.Element); + foreach (BaseKeyFrame baseKeyFrame in am.KeyFrames) + { + animationTimeline.KeyFrames.Add( + new EasingDoubleKeyFrame( + Convert.ToInt32(baseKeyFrame.Value), + baseKeyFrame._KeyTime, + baseKeyFrame.EasingFunction) + ); + } + return animationTimeline; + } + + private static AnimationTimeline CreateColorAnimationUsingKeyFrames(AnimationModel am) + { + ColorAnimationUsingKeyFrames animationTimeline = new ColorAnimationUsingKeyFrames(); + Storyboard.SetTargetProperty(animationTimeline, new PropertyPath(am.PropertyPath)); + Storyboard.SetTarget(animationTimeline, am.Element); + foreach (BaseKeyFrame baseKeyFrame in am.KeyFrames) + { + animationTimeline.KeyFrames.Add( + new EasingColorKeyFrame( + (System.Windows.Media.Color)baseKeyFrame.Value, + baseKeyFrame._KeyTime, + baseKeyFrame.EasingFunction) + ); + } + return animationTimeline; + } + + private static AnimationTimeline CreateObjectAnimationUsingKeyFrames(AnimationModel am) + { + ObjectAnimationUsingKeyFrames animationTimeline = new ObjectAnimationUsingKeyFrames(); + Storyboard.SetTargetProperty(animationTimeline, new PropertyPath(am.PropertyPath)); + Storyboard.SetTarget(animationTimeline, am.Element); + foreach (BaseKeyFrame baseKeyFrame in am.KeyFrames) + { + animationTimeline.KeyFrames.Add( + new DiscreteObjectKeyFrame( + baseKeyFrame.Value, + baseKeyFrame._KeyTime) + ); + } + return animationTimeline; + } + } + + /// + /// 关键帧动画类型 + /// + public enum KeyFrameType + { + DoubleKeyFrame = 1, + ColorKeyFrame = 2, + ObjectKeyFrame = 3 + } + + public class AnimationModel + { + public AnimationModel() + { + this.KeyFrames = new List(); + } + /// + /// 执行动画的对象 + /// + public FrameworkElement Element; + + /// + /// 作用于动画的属性 + /// + public string PropertyPath; + + /// + /// 动画类型枚举 + /// + public KeyFrameType _KeyFrameType; + + /// + /// 关键帧动画帧集合 + /// + public IList KeyFrames; + } + + public class BaseKeyFrame + { + /// + /// 动画触发时间 + /// + public TimeSpan _KeyTime; + + /// + /// 值 + /// + public object Value; + + /// + /// 缓动函数类型 + /// + public IEasingFunction EasingFunction; + } +} diff --git a/SJ.Controls/PageControl.xaml b/SJ.Controls/PageControl.xaml new file mode 100644 index 0000000..dc79179 --- /dev/null +++ b/SJ.Controls/PageControl.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + diff --git a/SJ.Controls/PageControl.xaml.cs b/SJ.Controls/PageControl.xaml.cs new file mode 100644 index 0000000..6bc2c2a --- /dev/null +++ b/SJ.Controls/PageControl.xaml.cs @@ -0,0 +1,137 @@ +using System; +using System.Windows; +using System.Windows.Controls; + +namespace SJ.Controls +{ + /// + /// PageControl.xaml 的交互逻辑 + /// + public partial class PageControl : UserControl + { + public PageControl() + { + InitializeComponent(); + this.Loaded += PageControl_Loaded; + } + + private void PageControl_Loaded(object sender, RoutedEventArgs e) + { + this.PageIndex = 1; + pageArgs.PageIndex = this.PageIndex; + } + + /// + /// 分页事件 + /// + public event RoutedEventHandler OnPageIndexChanged; + + /// + /// 分页参数 + /// + private PageArgs pageArgs = new PageArgs(); + + public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register("PageIndex", typeof(int), typeof(PageControl), new PropertyMetadata(1, new PropertyChangedCallback((s, e) => + { + var pageControl = s as PageControl; + pageControl?.OnIndexChanged(); + }))); + + /// + /// 当前页数 + /// + public int PageIndex + { + get { return (int)GetValue(PageIndexProperty); } + set { SetValue(PageIndexProperty, value); } + } + + public static readonly DependencyProperty PageSizeProperty = DependencyProperty.Register("PageSize", typeof(int), typeof(PageControl), new PropertyMetadata(1)); + + /// + /// 每页记录数 + /// + public int PageSize + { + get { return (int)GetValue(PageSizeProperty); } + set { SetValue(PageSizeProperty, value); } + } + + public static readonly DependencyProperty PageCountProperty = DependencyProperty.Register("PageCount", typeof(int), typeof(PageControl), new PropertyMetadata(1)); + + /// + /// 总页数 + /// + public int PageCount + { + get { return (int)GetValue(PageCountProperty); } + set { SetValue(PageCountProperty, value); } + } + + public static readonly DependencyProperty RecordCountProperty = DependencyProperty.Register("RecordCount", typeof(int), typeof(PageControl), new PropertyMetadata(0, new PropertyChangedCallback((s, e) => + { + var pageControl = s as PageControl; + pageControl?.OnRecordChanged(); + }))); + + /// + /// 总记录数 + /// + public int RecordCount + { + get { return (int)GetValue(RecordCountProperty); } + set { SetValue(RecordCountProperty, value); } + } + + private void OnRecordChanged() + { + PageCount = (RecordCount - 1) / PageSize + 1; + } + + private void OnIndexChanged() + { + pageArgs.PageIndex = this.PageIndex; + OnPageIndexChanged?.Invoke(this, pageArgs); + } + + private void Btn_Click(object sender, RoutedEventArgs e) + { + var btn = sender as BButton; + var newPageIndex = Convert.ToInt32(btn.Content); + if (newPageIndex != PageIndex) + PageIndex = newPageIndex; + else + OnIndexChanged(); + } + + private void btn_first_Click(object sender, RoutedEventArgs e) + { + if (PageIndex > 1) + PageIndex = 1; + } + + private void btn_up_Click(object sender, RoutedEventArgs e) + { + if (PageIndex > 1) + PageIndex--; + } + + private void btn_next_Click(object sender, RoutedEventArgs e) + { + if (PageIndex < PageCount) + PageIndex++; + } + + private void btn_last_Click(object sender, RoutedEventArgs e) + { + if (PageIndex < PageCount) + PageIndex = PageCount; + } + } + + public class PageArgs : RoutedEventArgs + { + public int PageIndex; + //其余自行扩展 + } +} diff --git a/SJ.Controls/RoundWaitProgress.xaml b/SJ.Controls/RoundWaitProgress.xaml new file mode 100644 index 0000000..4789023 --- /dev/null +++ b/SJ.Controls/RoundWaitProgress.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SJ.Controls/RoundWaitProgress.xaml.cs b/SJ.Controls/RoundWaitProgress.xaml.cs new file mode 100644 index 0000000..e26ca31 --- /dev/null +++ b/SJ.Controls/RoundWaitProgress.xaml.cs @@ -0,0 +1,180 @@ +using SJ.Controls.Helpers; +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Animation; + +namespace SJ.Controls +{ + /// + /// RoundWaitProgress.xaml 的交互逻辑 + /// + public partial class RoundWaitProgress : UserControl + { + public RoundWaitProgress() + { + InitializeComponent(); + this.Loaded += RoundWaitProgress_Loaded; + InitAnimationData(); + } + + private void RoundWaitProgress_Loaded(object sender, RoutedEventArgs e) + { + if (!IsPlaying) + this.Visibility = Visibility.Collapsed; + } + + /// + /// 初始化动画数据 + /// + private void InitAnimationData() + { + if (ControlList == null) + ControlList = new List() { g1, g2, g3, g4, g5 }; + + if (AngleAnimationParamList == null) + AngleAnimationParamList = new List(); + if (BeginOpacityAnimationParamList == null) + BeginOpacityAnimationParamList = new List(); + if (EndOpacityAnimationParamList == null) + EndOpacityAnimationParamList = new List(); + + AngleAnimationParamList.Clear(); + BeginOpacityAnimationParamList.Clear(); + EndOpacityAnimationParamList.Clear(); + + AngleAnimationParamList.Add(new BaseKeyFrame() { Value = 0, _KeyTime = new TimeSpan(0, 0, 0, 0, 0) }); + AngleAnimationParamList.Add(new BaseKeyFrame() { Value = 112, _KeyTime = new TimeSpan(0, 0, 0, 0, 400) }); + AngleAnimationParamList.Add(new BaseKeyFrame() { Value = 202, _KeyTime = new TimeSpan(0, 0, 0, 0, 1500) }); + AngleAnimationParamList.Add(new BaseKeyFrame() { Value = 472, _KeyTime = new TimeSpan(0, 0, 0, 0, 2200) }); + AngleAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 3100), Value = 562 }); + AngleAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 3500), Value = 720 }); + + BeginOpacityAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 0), Value = 0 }); + BeginOpacityAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 1), Value = 1 }); + + EndOpacityAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 3499), Value = 1 }); + EndOpacityAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 3500), Value = 0 }); + EndOpacityAnimationParamList.Add(new BaseKeyFrame() { _KeyTime = new TimeSpan(0, 0, 0, 0, 4500), Value = 0 }); + } + + /// + /// 动画属性 + /// + public static readonly string AnglePropertyPath = "(UIElement.RenderTransform).(RotateTransform.Angle)"; + public static readonly string OpacityPropertyPath = "(UIElement.Opacity)"; + private IList AngleAnimationParamList; + private IList BeginOpacityAnimationParamList; + private IList EndOpacityAnimationParamList; + private IList ControlList; + private Storyboard _Storyboard; + private bool IsPlaying; + + /// + /// 间隔时间 + /// + public static readonly TimeSpan IntervalTimeSpan = new TimeSpan(0, 0, 0, 0, 200); + + public static readonly DependencyProperty WaitTextProperty = DependencyProperty.Register("WaitText", typeof(string), typeof(RoundWaitProgress), new PropertyMetadata("正在加载数据")); + + public string WaitText + { + get { return GetValue(WaitTextProperty).ToString(); } + set { SetValue(WaitTextProperty, value); } + } + + public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Brush), typeof(RoundWaitProgress), new PropertyMetadata(new SolidColorBrush(Colors.Black))); + + public Brush Color + { + get { return GetValue(ColorProperty) as Brush; } + set { SetValue(ColorProperty, value); } + } + + public static readonly DependencyProperty AnimationSizeProperty = DependencyProperty.Register("AnimationSize", typeof(double), typeof(RoundWaitProgress), new PropertyMetadata(80.0)); + + public double AnimationSize + { + get { return Convert.ToDouble(GetValue(AnimationSizeProperty)); } + set { SetValue(AnimationSizeProperty, value); } + } + + public static readonly DependencyProperty PlayProperty = DependencyProperty.Register("Play", typeof(bool), typeof(RoundWaitProgress), new PropertyMetadata(false, (s, e) => + { + var waitControl = s as RoundWaitProgress; + if (waitControl.Play) + waitControl.Start(); + else + waitControl.Stop(); + })); + + public bool Play + { + get { return (bool)GetValue(PlayProperty); } + set { SetValue(PlayProperty, value); } + } + + private void Start() + { + this.IsPlaying = true; + this.Visibility = Visibility.Visible; + this._Storyboard = new Storyboard(); + foreach (var frameElement in ControlList) + { + (frameElement.RenderTransform as RotateTransform).Angle = 0; + DoubleAnimationUsingKeyFrames daukf = new DoubleAnimationUsingKeyFrames(); + foreach (var item in AngleAnimationParamList) + { + daukf.KeyFrames.Add(new EasingDoubleKeyFrame(Convert.ToDouble(item.Value), item._KeyTime)); + } + Storyboard.SetTargetProperty(daukf, new PropertyPath(AnglePropertyPath)); + Storyboard.SetTarget(daukf, frameElement); + this._Storyboard.Children.Add(daukf); + + DoubleAnimationUsingKeyFrames daukf1 = new DoubleAnimationUsingKeyFrames(); + foreach (var item in BeginOpacityAnimationParamList) + { + daukf1.KeyFrames.Add(new EasingDoubleKeyFrame(Convert.ToDouble(item.Value), item._KeyTime)); + } + foreach (var item in EndOpacityAnimationParamList) + { + daukf1.KeyFrames.Add(new EasingDoubleKeyFrame(Convert.ToDouble(item.Value), item._KeyTime)); + } + Storyboard.SetTargetProperty(daukf1, new PropertyPath(OpacityPropertyPath)); + Storyboard.SetTarget(daukf1, frameElement); + this._Storyboard.Children.Add(daukf1); + + for (var i = 0; i < AngleAnimationParamList.Count; i++) + { + var item = AngleAnimationParamList[i]; + item._KeyTime = item._KeyTime.Add(IntervalTimeSpan); + } + + foreach (var item in BeginOpacityAnimationParamList) + { + item._KeyTime = item._KeyTime.Add(IntervalTimeSpan); + } + + foreach (var item in EndOpacityAnimationParamList) + { + item._KeyTime = item._KeyTime.Add(IntervalTimeSpan); + } + + this._Storyboard.RepeatBehavior = RepeatBehavior.Forever; + } + this._Storyboard.Begin(); + } + + private void Stop() + { + this._Storyboard.Stop(); + this._Storyboard.Children.Clear(); + this._Storyboard = null; + this.IsPlaying = false; + InitAnimationData(); + this.Visibility = Visibility.Collapsed; + } + } +} diff --git a/SJ.Controls/SJ.Controls.csproj b/SJ.Controls/SJ.Controls.csproj new file mode 100644 index 0000000..3d0d4a9 --- /dev/null +++ b/SJ.Controls/SJ.Controls.csproj @@ -0,0 +1,17 @@ + + + + net6.0-windows + enable + true + + + + + + + + + + + diff --git a/SJ.Controls/Themes/Generic.xaml b/SJ.Controls/Themes/Generic.xaml new file mode 100644 index 0000000..94b2b51 --- /dev/null +++ b/SJ.Controls/Themes/Generic.xaml @@ -0,0 +1,377 @@ + + + + + + + + + + + + + + + + + + + diff --git a/bbwyb.sln b/bbwyb.sln index 3be7a6d..8170464 100644 --- a/bbwyb.sln +++ b/bbwyb.sln @@ -9,15 +9,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Server", "Server", "{99D234 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Client", "Client", "{1E7D36DB-A817-4208-8FC6-36A66FAB17E5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Client", "BBWYB.Client\BBWYB.Client.csproj", "{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BBWYB.Client", "BBWYB.Client\BBWYB.Client.csproj", "{D52D0167-EF94-4FC8-91BF-FCE5B3ED9C6A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Server.Business", "BBWYB.Server.Business\BBWYB.Server.Business.csproj", "{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BBWYB.Server.Business", "BBWYB.Server.Business\BBWYB.Server.Business.csproj", "{52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Server.Model", "BBWYB.Server.Model\BBWYB.Server.Model.csproj", "{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BBWYB.Server.Model", "BBWYB.Server.Model\BBWYB.Server.Model.csproj", "{5707BF58-3A98-4283-A6D0-3B78EF7ED2F1}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{1191C1AE-7275-4643-AF24-BEC852717299}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BBWYB.Common", "BBWYB.Common\BBWYB.Common.csproj", "{DD328472-01CE-4CA8-AF29-C098FC499483}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BBWYB.Common", "BBWYB.Common\BBWYB.Common.csproj", "{DD328472-01CE-4CA8-AF29-C098FC499483}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SJ.Controls", "SJ.Controls\SJ.Controls.csproj", "{0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,6 +47,10 @@ Global {DD328472-01CE-4CA8-AF29-C098FC499483}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD328472-01CE-4CA8-AF29-C098FC499483}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD328472-01CE-4CA8-AF29-C098FC499483}.Release|Any CPU.Build.0 = Release|Any CPU + {0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -55,6 +61,7 @@ Global {52DF7178-3C36-4CA6-A2E9-D9E2BB41C0B8} = {99D234A0-6830-4C0C-91E8-C626DA939D59} {5707BF58-3A98-4283-A6D0-3B78EF7ED2F1} = {99D234A0-6830-4C0C-91E8-C626DA939D59} {DD328472-01CE-4CA8-AF29-C098FC499483} = {1191C1AE-7275-4643-AF24-BEC852717299} + {0415B31B-5A4E-4F7C-9F3B-69CB6284E4F1} = {1E7D36DB-A817-4208-8FC6-36A66FAB17E5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0D069898-04B7-4D24-A6A4-D7C703B8BFFC}