diff --git a/TEMUKJD/Extension/BindingExtensions.cs b/TEMUKJD/Extension/BindingExtensions.cs new file mode 100644 index 0000000..21cb119 --- /dev/null +++ b/TEMUKJD/Extension/BindingExtensions.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace TemuKJD.Extension; + +public class DataContext : BindingSource +{ + public DataContext() { } + public DataContext(TSource dataSource) : base(dataSource, null) { } + + // 强类型的 DataSource,避免调用方还要自己转型 + public new TSource DataSource + { + get => (TSource)base.DataSource; + set => base.DataSource = value; + } +} +public static class BindingExtensions +{ + + + /// + /// TSource 必须显式指定(因为无法从参数推断),TControl 自动从调用方控件推断 + /// + public static Binding Bind( + this TControl ctrl, + DataContext dataSource, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + where TControl : Control + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), dataSource, dataPropertyExpr, mode, format, parseHandler); + + public static Binding Bind( + this TControl ctrl, + BindingSource dataSource, + Expression> controlPropertyExpr, + string dataPropertyName, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) where TControl : Control + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), dataSource, dataPropertyName, mode, format, parseHandler); + + // ---------- 共享的核心实现,不对外暴露 ---------- + private static Binding BindCore( + Control ctrl, + string controlPropertyName, + BindingSource bs, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode, + string format, + ConvertEventHandler parseHandler) => BindCore(ctrl, controlPropertyName, bs, dataPropertyExpr.GetPropertyName(), mode, format, parseHandler); + + private static Binding BindCore( + Control ctrl, + string controlPropertyName, + BindingSource bs, + string dataPropertyName, + DataSourceUpdateMode mode, + string format, + ConvertEventHandler parseHandler) + { + var binding = new Binding(controlPropertyName, bs, dataPropertyName, true, mode); + + if (!string.IsNullOrEmpty(format)) + binding.Format += (s, e) => e.Value = string.Format($"{{0:{format}}}", e.Value); + + if (parseHandler != null) + binding.Parse += parseHandler; + + ctrl.DataBindings.Add(binding); + return binding; + } + // ---------- TextBox ---------- + public static Binding Bind( + this TextBox ctrl, + BindingSource bs, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler); + + // ---------- CheckBox ---------- + public static Binding Bind( + this CheckBox ctrl, + BindingSource bs, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler); + + // ---------- DateTimePicker ---------- + public static Binding Bind( + this DateTimePicker ctrl, + BindingSource bs, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler); + + // ---------- ComboBox ---------- + public static Binding Bind( + this ComboBox ctrl, + BindingSource bs, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler); + + // ---------- NumericUpDown ---------- + public static Binding Bind( + this NumericUpDown ctrl, + BindingSource bs, + Expression> controlPropertyExpr, + Expression> dataPropertyExpr, + DataSourceUpdateMode mode = DataSourceUpdateMode.OnPropertyChanged, + string format = null, + ConvertEventHandler parseHandler = null) + => BindCore(ctrl, controlPropertyExpr.GetPropertyName(), bs, dataPropertyExpr, mode, format, parseHandler); + + + public static string GetPropertyName( + this Expression> propertyExpr) + { + if (propertyExpr.Body is MemberExpression member) + return member.Member.Name; + + // 处理值类型属性被隐式转换为 object 的情况(如 decimal -> object) + if (propertyExpr.Body is UnaryExpression unary && + unary.Operand is MemberExpression unaryMember) + return unaryMember.Member.Name; + + throw new ArgumentException("表达式必须是形如 x => x.PropertyName 的属性访问", nameof(propertyExpr)); + } +} \ No newline at end of file diff --git a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs index b41a94a..b29e8f8 100644 --- a/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs +++ b/TEMUKJD/WXForms/ProductForms/WXDBProdUploadForm.cs @@ -1,4 +1,4 @@ -using AllBase; +using AllBase; using AllBase.Extension; using AllBase.Helper; using AllBase.Model.Config; @@ -38,6 +38,7 @@ using Work; using Work.BLL.Tools; using Work.Services; using static System.Net.Mime.MediaTypeNames; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar; namespace TemuKJD.WXForms.ProductForms { @@ -79,7 +80,50 @@ namespace TemuKJD.WXForms.ProductForms { InitializeComponent(); } + DataContext _bs; + WaitingProductConfig waiting_product_config = new WaitingProductConfig(); + + void InitWaitingProductConfigBinding() + { + Debug.WriteLine($"初始化配置"); + Invoke(new Action(() => + { + waiting_product_config = NaiBa.Util.Helper.ConfigHelper.LoadConfig(nameof(waiting_product_config)); + if (waiting_product_config == null) + { + waiting_product_config = new WaitingProductConfig(); + } + _bs = new DataContext(waiting_product_config); + //NaiBa.Util.Helper.ConfigHelper.SaveConfig("wx_default_add_price_rule", defaultAddPriceRule); + ////txtIdFilter.Bind(_bs,a=>a.) + txtIdFilter.Bind(_bs, a => a.Text, a => a.IdFilter); + txtSearchGoodsName.Bind(_bs, a => a.Text, a => a.SearchGoodsName); + cmbSourceType.Bind(_bs, a => a.Text, a => a.SourceType); + cmbGroupFilter.Bind(_bs, a => a.Text, a => a.GroupFilter); + cmbHot.Bind(_bs, a => a.Text, a => a.Hot); + cmbWxSafe.Bind(_bs, a => a.Text, a => a.WxSafe); + cmbCateFilter1.Bind(_bs, a => a.Text, a => a.CateFilter1); + cmbCateFilter2.Bind(_bs, a => a.Text, a => a.CateFilter2); + cmbCateFilter3.Bind(_bs, a => a.Text, a => a.CateFilter3); + cmbNotes.Bind(_bs, a => a.Text, a => a.Notes); + cmbJstInitSet.Bind(_bs, a => a.Text, a => a.JstInitSet); + txtAddCount.Bind(_bs, a => a.Text, a => a.AddCount); + chkSkipQualification.Bind(_bs, a => a.Checked, a => a.SkipQualification); + chkRandomFissionProduct.Bind(_bs, a => a.Checked, a => a.RandomFissionProduct); + txtRandomFissionProductCount.Bind(_bs, a => a.Text, a => a.RandomFissionProductCount); + chkWXComplateProd.Bind(_bs, a => a.Checked, a => a.WXComplateProd); + chkUpdateInDays.Bind(_bs, a => a.Checked, a => a.UpdateInDaysChecked); + txtUpdateInDays.Bind(_bs, a => a.Text, a => a.UpdateInDays); + cmbUpdateInDays.Bind(_bs, a => a.Text, a => a.UpdateInDaysType); + chkFilterViewUv.Bind(_bs, a => a.Checked, a => a.FilterViewUvChecked); + numFilterViewUv.Bind(_bs, a => a.Value, a => a.FilterViewUv); + chkFilterSaleCountNum.Bind(_bs, a => a.Checked, a => a.FilterSaleCountNumChecked); + numFilterSaleCountNum.Bind(_bs, a => a.Value, a => a.FilterSaleCountNum); + })); + Debug.WriteLine($"初始化配置END"); + + } private void UserSelectDbProductForm_Load(object sender, EventArgs e) { Instance = this; @@ -167,6 +211,8 @@ namespace TemuKJD.WXForms.ProductForms } catch { } + + txtAddTimes.TextChanged -= txtAddPrice_TextChanged; txtAddMorePrice.TextChanged -= txtAddPrice_TextChanged; txtSinglePriceAdd.TextChanged -= txtAddPrice_TextChanged; @@ -611,7 +657,7 @@ namespace TemuKJD.WXForms.ProductForms { RefreshShopLogin(); updateAuthor(); - RefreshDbProdGroupAndCate(); + RefreshDbProdGroupAndCate(InitWaitingProductConfigBinding); lblWaitToUp.Text = "待上传 " + lstTemuDBProductSelect.Count + " 件"; if (!isInit) @@ -637,7 +683,7 @@ namespace TemuKJD.WXForms.ProductForms } } - private void RefreshDbProdGroupAndCate() + private void RefreshDbProdGroupAndCate(Action call = null) { Task.Run(() => { @@ -664,7 +710,10 @@ namespace TemuKJD.WXForms.ProductForms this.btnSwitchCatType.Text = "P"; btnSwitchCatType_Click(null, null); pbLoadingShop.Visible = false; + })); + call?.Invoke(); + }); } @@ -989,6 +1038,8 @@ namespace TemuKJD.WXForms.ProductForms private void btnUpload_Click(object sender, EventArgs e) { + NaiBa.Util.Helper.ConfigHelper.SaveConfig(nameof(waiting_product_config), waiting_product_config); + var shopIdLst = dgvShop.Rows.Cast().Where(row => row.Cells["colSelect"]?.Value.ParseToBool() == true).Select(r => r.Cells["colShopId"]?.Value.ToString()).ToList(); if (shopIdLst.Count <= 0) @@ -2095,7 +2146,7 @@ namespace TemuKJD.WXForms.ProductForms // 同颜色规格向下浮动同价格 if (chkSameSpecPriceDown.Checked) { - var priceTextLst = dbProd.price_text.ParseToString().Split('-'); + var priceTextLst = dbProd.price_text.ParseToString().Split('-'); if (priceTextLst.Length != 2 || priceTextLst[0] != priceTextLst[1]) { Dictionary specPriceDic = new Dictionary(); @@ -7030,6 +7081,7 @@ namespace TemuKJD.WXForms.ProductForms private void cmbCateFilter1_TextChanged(object sender, EventArgs e) { + Debug.WriteLine($"Filter1 修改:{cmbCateFilter1.Text}"); //if (cmbSourceType.Text != "厂商直采" && cmbSourceType.Text != "1688厂家" && cmbSourceType.Text != "微信店采" && cmbSourceType.Text != "微信小店") if (btnSwitchCatType.Text != "W") return; @@ -7051,6 +7103,7 @@ namespace TemuKJD.WXForms.ProductForms private void cmbCateFilter2_TextChanged(object sender, EventArgs e) { + Debug.WriteLine($"Filter1 修改:{cmbCateFilter2.Text}"); //if (cmbSourceType.Text != "厂商直采" && cmbSourceType.Text != "1688厂家" && cmbSourceType.Text != "微信店采" && cmbSourceType.Text != "微信小店") if (btnSwitchCatType.Text != "W") return; @@ -7998,4 +8051,241 @@ Content-Type: application/octet-stream public string PlatformUrl { get; set; } public bool IsCache { get; set; } } + + class WaitingProductConfig : BindableBase + { + public WaitingProductConfig() + { + IdFilter = ""; + SearchGoodsName = ""; + SourceType = "厂商直采"; + GroupFilter = ""; + Hot = "普通商品"; + WxSafe = "安全+未校验"; + CateFilter1 = "全部"; + CateFilter2 = "全部"; + CateFilter3 = "全部"; + Notes = ""; + JstInitSet = "是"; + AddCount = "500"; + SkipQualification = false; + RandomFissionProduct = false; + RandomFissionProductCount = "100"; + WXComplateProd = false; + UpdateInDaysChecked = false; + UpdateInDays = "30"; + UpdateInDaysType = "新增"; + FilterViewUvChecked = false; + FilterViewUv = 200m; + FilterSaleCountNumChecked = false; + FilterSaleCountNum = 1m; + } + + /// + /// txtIdFilter + /// + public string IdFilter + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// txtSearchGoodsName + /// + public string SearchGoodsName + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbSourceType + /// + public string SourceType + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbGroupFilter + /// + public string GroupFilter + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbHot + /// + public string Hot + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbWxSafe + /// + public string WxSafe + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbCateFilter1 + /// + public string CateFilter1 + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbCateFilter2 + /// + public string CateFilter2 + { + get => GetValue(); + set => base.SetValue(value); + } + + /// + /// cmbCateFilter3 + /// + public string CateFilter3 + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbNotes + /// + public string Notes + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbJstInitSet + /// + public string JstInitSet + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// txtAddCount + /// + public string AddCount + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkSkipQualification + /// + public bool SkipQualification + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkRandomFissionProduct + /// + public bool RandomFissionProduct + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// txtRandomFissionProductCount + /// + public string RandomFissionProductCount + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkWXComplateProd + /// + public bool WXComplateProd + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkUpdateInDays + /// + public bool UpdateInDaysChecked + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// txtUpdateInDays + /// + public string UpdateInDays + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// cmbUpdateInDays + /// + public string UpdateInDaysType + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkFilterViewUv + /// + public bool FilterViewUvChecked + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// numFilterViewUv + /// + public decimal FilterViewUv + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// chkFilterSaleCountNum + /// + public bool FilterSaleCountNumChecked + { + get => GetValue(); + set => SetValue(value); + } + + /// + /// numFilterSaleCountNum + /// + public decimal FilterSaleCountNum + { + get => GetValue(); + set => SetValue(value); + } + } }