# 后台基础代码扩展实现

# 查询、新建、新建前、修改、修改前、修改后、导入、导出、删除、打印、审核、一对一、一对多

说明

  1. 此文档是生成页面的后台业务处理
  2. 每张表生成的后台代码都有一个【表Service.cs】类,下面的实现方法较多,按需在Service类中实现下面的方法
  3. 自定义业务实现包括但不限于:查询、修改、新增、导入、导出、删除、打印、审核、一对一、一对多
  4. 注意:代码只能写在Partial文件夹下的【表Service.cs】类中

如果没有看懂每个方法的结构,请点击【完整示例代码】

# 文件下载、下载pdf、下载excel

见:前端开发(常用示例)->文件下载、下载pdf、下载excel

# 查询

  //查询
    public override PageGridData<SellOrder> GetPageData(PageDataOptions options)
    {
        //options.Value可以从前台查询的方法提交一些其他参数放到value里面
        //前端提交方式,见文档:组件api->viewgrid组件里面的searchBefore方法
        object extraValue = options.Value;

        //此处是从前台提交的原生的查询条件,这里可以自己过滤
        string 字段 = "";
        QueryRelativeList = (List<SearchParameters> parameters) =>
        {
            foreach (var item in parameters)
            {
                //如果需要取出某个查询值,并单独使用
                if (item.Name=="字段")
                {
                    字段 = item.Value;
                    //清空字段值后框架不会使用此字段
                    item.Value = null;
                }  
            }
        };

        //设置原生查询的sql语句,这里必须返回select * 表所有字段
        //(先内部过滤数据,内部调用EF方法FromSqlRaw,自己写的sql注意sql注入的问题),不会影响界面上提交的查询
        /*  
         *  string date = DateTime.Now.AddYears(-10).ToString("yyyy-MM-dd");
            QuerySql = $@"select * from SellOrder  
                                   where createdate>'{date}'
                                       and  Order_Id in (select Order_Id from SellOrderList)
                                       and CreateID={UserContext.Current.UserId}";
        */

        //此处与上面QuerySql只需要实现其中一个就可以了
        //查询前可以自已设定查询表达式的条件
        
        //sqlsugar后台将IQueryable改为ISugarQueryable
        QueryRelativeExpression = (IQueryable<SellOrder> queryable) =>
        {
            //当前用户只能操作自己(与下级角色)创建的数据,如:查询、删除、修改等操作
            //IQueryable<int> userQuery = RoleContext.GetCurrentAllChildUser();
            //queryable = queryable.Where(x => x.CreateID == UserContext.Current.UserId || userQuery.Contains(x.CreateID ?? 0));
           //或者直接写查询条件
           //queryable=queryable.Where(x=>条件)

           //查看生成的sql语句:Console.Write(queryable.ToQueryString())
            return queryable;
        };

        //指定多个字段进行排序
        OrderByExpression = x => new Dictionary<object, QueryOrderBy>() {
            { x.CreateDate,QueryOrderBy.Desc },
            { x.SellNo,QueryOrderBy.Asc}
        };

        //int a = 1;
        //按条件指定多个字段按条件进行排序
        //OrderByExpression = x => new Dictionary<object, QueryOrderBy>() {
        //    { x.CreateDate,QueryOrderBy.Desc },
        //    { x.SellNo,a==1?QueryOrderBy.Desc:QueryOrderBy.Asc}
        //};

        //查询完成后,在返回页面前可对查询的数据进行操作
        GetPageDataOnExecuted = (PageGridData<SellOrder> grid) =>
        {
            //可对查询的结果的数据操作
            List<SellOrder> sellOrders = grid.rows;
        };

        //查询table界面显示合计
        SummaryExpress = (IQueryable<SellOrder> queryable) =>
        {
            return queryable.GroupBy(x => 1).Select(x => new
            {
                //AvgPrice注意大小写和数据库字段大小写一样
                Qty = x.Sum(o => o.Qty)
            })
            .FirstOrDefault();
        };

        return base.GetPageData(options);
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

# 查看查询实际执行的sql

查看实际执行的sql

  Console.Write(queryable.ToQueryString())
 //见上面的查询里QueryRelativeExpression使用说明

1
2
3

# or查询

与上面的查询是一个方法,这里单独说明了怎么配置or查询

  public override PageGridData<SellOrder> GetPageData(PageDataOptions options)
    {
        System.Linq.Expressions.Expression<Func<SellOrder, bool>> orFilter = null;
        QueryRelativeList = (List<SearchParameters> parameters) =>
        {
            //方式1:动态生成or查询条件
            foreach (var item in parameters)
            {
                if (!string.IsNullOrEmpty(item.Value))
                {
                    //注意:这里只需要判断or查询的字段,其他的字段不需要处理
                    //这里必须拷贝value值
                    if (orFilter == null && (item.Name == "TranNo" || item.Name == "SellNo")) { orFilter = x => false; }
                    string value = item.Value;
                    if (item.Name == "TranNo")
                    {
                        //进行or模糊查询
                        orFilter = orFilter.Or(x => x.TranNo.Contains(value));
                        //清空原来的数据
                        item.Value = null;
                    }
                    else if (item.Name == "SellNo")
                    {
                        //进行or等于查询
                        orFilter = orFilter.Or(x => x.SellNo == value);
                        //清空原来的数据
                        item.Value = null;
                    }
                }
            }
            ///方式2:原生sql查询,需要自己处理sql注入问题(不建议使用此方法)
            //string sql = null;
            //foreach (var item in parameters)
            //{
            //    if (!string.IsNullOrEmpty(item.Value))
            //    {
            //        if (sql == null)
            //        {
            //            sql = "where 1=2";
            //        }
            //        string value = item.Value;
            //        //清空原来的数据
            //        item.Value = null;
            //        if (item.Name == "TranNo")
            //        {
            //            sql += $" or TranNo='{value}'";
            //            //清空原来的数据
            //            item.Value = null;
            //        }
            //        else if (item.Name == "SellNo")
            //        {
            //            sql += $" or SellNo='{value}'";
            //        }
            //    }
            //}
            //QuerySql = "select * from sellorder " + sql;
        };

        QueryRelativeExpression = (IQueryable<SellOrder> queryable) =>
        {
            if (orFilter != null)
            {
                queryable = queryable.Where(orFilter);
            }
            return queryable;
        };
        return base.GetPageData(options);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# 明细表合计

明细表合计,需要前端初始化配置,在前端开发文档上找合计配置示例

      /// <summary>
    /// 设置弹出框明细表的合计信息
    /// </summary>
    /// <typeparam name="detail"></typeparam>
    /// <param name="queryeable"></param>
    /// <returns></returns>
    protected override object GetDetailSummary<detail>(IQueryable<detail> queryeable)
    { 
        //判断是哪张明细表
        if (typeof(detail)==typeof(SellOrderList))
        {
            return (queryeable as IQueryable<SellOrderList>).GroupBy(x => 1).Select(x => new
            {
                //Weight/Qty注意大小写和数据库字段大小写一样
                Weight = x.Sum(o => o.Weight),
                Qty = x.Sum(o => o.Qty)
            }).FirstOrDefault();
        }
        return null
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 明细表查询

     /// <summary>
    /// 明细表查询
    /// </summary>
    /// <param name="pageData"></param>
    /// <returns></returns>
    public override object GetDetailPage(PageDataOptions pageData)
    {
        //自定义查询胆细表

        ////明细表自定义查询方式一:EF
        //var query = SellOrderListRepository.Instance.IQueryablePage<SellOrderList>(
        //     pageData.Page,
        //     pageData.Rows,
        //     out int count,
        //     x => x.Order_Id == pageData.Value.GetGuid(),
        //      orderBy: x => new Dictionary<object, QueryOrderBy>() { { x.CreateDate, QueryOrderBy.Desc } }
        //    );
        //PageGridData<SellOrderList> detailGrid = new PageGridData<SellOrderList>();
        //detailGrid.rows = query.ToList();
        //detailGrid.total = count;

        ////明细表自定义查询方式二:dapper
        //string sql = "select count(1) from SellOrderList where Order_Id=@orderId";
        //detailGrid.total = repository.DapperContext.ExecuteScalar(sql, new { orderId = pageData.Value }).GetInt();

        //sql = @$"select * from (
        //              select *,ROW_NUMBER()over(order by createdate desc) as rowId
        //           from SellOrderList where Order_Id=@orderId
        //        ) as s where s.rowId between {((pageData.Page - 1) * pageData.Rows + 1)} and {pageData.Page * pageData.Rows} ";
        //detailGrid.rows = repository.DapperContext.QueryList<SellOrderList>(sql, new { orderId = pageData.Value });

        //return detailGrid;

        return base.GetDetailPage(pageData);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 新建

 /// <summary>
    /// 新建
    /// </summary>
    /// <param name="saveDataModel"></param>
    /// <returns></returns>
    public override WebResponseContent Add(SaveModel saveDataModel)
    {
        //此处saveModel是从前台提交的原生数据,可对数据进修改过滤
        AddOnExecute = (SaveModel saveModel) =>
        {
            //如果返回false,后面代码不会再执行
            return webResponse.OK();
        };
        // 在保存数据库前的操作,所有数据都验证通过了,这一步执行完就执行数据库保存
        AddOnExecuting = (SellOrder order, object list) =>
        {
            //明细表对象
            List<SellOrderList> orderLists = list as List<SellOrderList>;

            //自定义逻辑
            if (orderLists == null || orderLists.Count == 0)
            {//如果没有界面上没有填写明细,则中断执行
                return webResponse.Error("必须填写明细数据");
            }
            if (orderLists.Exists(x => x.Qty <= 20))
                return webResponse.Error("明细数量必须大于20");

            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            // webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //此方法中已开启了事务,如果在此方法中做其他数据库操作,请不要再开启事务
        // 在保存数据库后的操作,此时已进行数据提交,但未提交事务,如果返回false,则会回滚提交
        AddOnExecuted = (SellOrder order, object list) =>
        {
            //明细表对象
            // List<SellOrderList> orderLists = list as List<SellOrderList>;

            if (order.Qty < 10)
            {  //如果输入的销售数量<10,会回滚数据库
                return webResponse.Error("销售数量必须大于1000");
            }

            return webResponse.OK("已新建成功,台AddOnExecuted方法返回的消息");
        };

        //新建的数据进入审批流程前处理,
        AddWorkFlowExecuting = (SellOrder order) =>
        {
            //返回false,当前数据不会进入审批流程
            return true;
        };

        //新建的数据写入审批流程后,第二个参数为审批人的用户id
        AddWorkFlowExecuted = (SellOrder order, List<int> userIds) =>
        {
            //这里可以做发邮件通知
            //var userInfo = repository.DbContext.Set<Sys_User>()
            //                .Where(x => userIds.Contains(x.User_Id))
            //                .Select(s => new { s.User_Id, s.UserTrueName, s.Email, s.PhoneNo }).ToList();

            //发送邮件方法
            //MailHelper.Send()
        };

        return base.Add(saveDataModel);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 编辑

 /// <summary>
    /// 编辑操作
    /// </summary>
    /// <param name="saveModel"></param>
    /// <returns></returns>
    public override WebResponseContent Update(SaveModel saveModel)
    {
        //注意:如果要给其他字段设置值,请在此处设置,如:(代码生成器上将字段编辑行设置为0,然后点生成model)
        //saveModel.MainData["字段"] = "值";

        //此处saveModel是从前台提交的原生数据,可对数据进修改过滤
        UpdateOnExecute = (SaveModel model) =>
        {
            ////这里的设置配合下面order.Remark = "888"代码位置使用
            // saveModel.MainData.TryAdd("Remark", "1231");

            //如果不想前端提交某些可以编辑的字段的值,直接移除字段
            // saveModel.MainData.Remove("字段");

            //如果返回false,后面代码不会再执行
            return webResponse.OK();

        };

        //编辑方法保存数据库前处理
        UpdateOnExecuting = (SellOrder order, object addList, object updateList, List<object> delKeys) =>
        {
            if (order.TranNo == "2019000001810001")
            {
                //如果设置code=-1会强制返回,不再继续后面的操作,2021.07.04更新LambdaExtensions文件后才可以使用此属性
                //webResponse.Code = "-1";
                // webResponse.Message = "测试强制返回";
                //return webResponse.OK();
                return webResponse.Error("不能更新此[" + order.TranNo + "]单号");
            }

            ////如果要手动设置某些字段的值,值不是前端提交的(代码生成器里面编辑行必须设置为0并生成model),如Remark字段:
            ////注意必须设置上面saveModel.MainData.TryAdd("Remark", "1231")
            //order.Remark = "888";

            //新增的明细表
            List<SellOrderList> add = addList as List<SellOrderList>;
            //修改的明细表
            List<SellOrderList> update = updateList as List<SellOrderList>;
            //删除明细表Id
            var guids = delKeys?.Select(x => (Guid)x);

            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            // webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //编辑方法保存数据库后处理
        //此方法中已开启了事务,如果在此方法中做其他数据库操作,请不要再开启事务
        // 在保存数据库后的操作,此时已进行数据提交,但未提交事务,如果返回false,则会回滚提交
        UpdateOnExecuted = (SellOrder order, object addList, object updateList, List<object> delKeys) =>
        {
            //新增的明细
            List<SellOrderList> add = addList as List<SellOrderList>;
            //修改的明细
            List<SellOrderList> update = updateList as List<SellOrderList>;
            //删除的行的主键
            var guids = delKeys?.Select(x => (Guid)x);
            return webResponse.OK();
        };

        return base.Update(saveModel);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

# 删除

  /// <summary>
    /// 删除
    /// </summary>
    /// <param name="keys">删除的行的主键</param>
    /// <param name="delList">删除时是否将明细也删除</param>
    /// <returns></returns>
    public override WebResponseContent Del(object[] keys, bool delList = true)
    {
        //删除前处理
        //删除的行的主键
        DelOnExecuting = (object[] _keys) =>
        {
            return webResponse.OK();
        };
        //删除后处理
        //删除的行的主键
        DelOnExecuted = (object[] _keys) =>
        {
            return webResponse.OK();
        };
        return base.Del(keys, delList);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 审批、审核

public override WebResponseContent Audit(object[] keys, int? auditStatus, string auditReason)
    {
        //status当前审批状态,lastAudit是否最后一个审批节点
        AuditWorkFlowExecuting = (SellOrder order, AuditStatus status, bool lastAudit) =>
        {
            return webResponse.OK();
        };
        //status当前审批状态,nextUserIds下一个节点审批人的帐号id(可以从sys_user表中查询用户具体信息),lastAudit是否最后一个审批节点
        AuditWorkFlowExecuted = (SellOrder order, AuditStatus status, List<int> nextUserIds, bool lastAudit) =>
        {
            //lastAudit=true时,流程已经结束
            if (!lastAudit)
            {
                //这里可以给下一批审批发送邮件通知
                //var userInfo = repository.DbContext.Set<Sys_User>()
                //             .Where(x => nextUserIds.Contains(x.User_Id))
                //             .Select(s => new { s.User_Id, s.UserTrueName, s.Email, s.PhoneNo }).ToList();
            }

            //审批流程回退功能,回到第一个审批人重新审批(重新生成审批流程)
            //if (status==AuditStatus.审核未通过||status==AuditStatus.驳回)
            //{
            //    base.RewriteFlow(order);
            //}

            return webResponse.OK();
        };

        //审核保存前处理(不是审批流程)
        AuditOnExecuting = (List<SellOrder> order) =>
        {
            return webResponse.OK();
        };
        //审核后处理(不是审批流程)
        AuditOnExecuted = (List<SellOrder> order) =>
        {
            return webResponse.OK();
        };


        return base.Audit(keys, auditStatus, auditReason);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 导出

     /// <summary>
    /// 导出
    /// </summary>
    /// <param name="pageData"></param>
    /// <returns></returns>
    public override WebResponseContent Export(PageDataOptions pageData)
    {
        //设置最大导出的数量
        Limit = 1000;
        //指定导出的字段(2020.05.07)
        ExportColumns = x => new { x.SellNo, x.TranNo, x.CreateDate };

        //查询要导出的数据后,在生成excel文件前处理
        //list导出的实体,ignore过滤不导出的字段
        ExportOnExecuting = (List<SellOrder> list, List<string> ignore) =>
        {
            return webResponse.OK();
        };

        return base.Export(pageData);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 下载模板

导入时弹出框中的下载模板

    /// <summary>
    /// 下载模板(导入时弹出框中的下载模板)(2020.05.07)
    /// </summary>
    /// <returns></returns>
    public override WebResponseContent DownLoadTemplate()
    {
        //指定导出模板的字段,如果不设置DownLoadTemplateColumns,默认导出查所有页面上能看到的列(2020.05.07)
        DownLoadTemplateColumns = x => new { x.SellNo, x.TranNo, x.Remark, x.CreateDate };
        return base.DownLoadTemplate();
    }
1
2
3
4
5
6
7
8
9
10

# 导入

 /// <summary>
    /// 导入
    /// </summary>
    /// <param name="files"></param>
    /// <returns></returns>
    public override WebResponseContent Import(List<IFormFile> files)
    {
        //(2020.05.07)
        //设置导入的字段(如果指定了上面导出模板的字段,这里配置应该与上面DownLoadTemplate方法里配置一样)
        //如果不设置导入的字段DownLoadTemplateColumns,默认显示所有界面上所有可以看到的字段
        DownLoadTemplateColumns = x => new { x.SellNo, x.TranNo, x.Remark, x.CreateDate };

        /// <summary>
        /// 2022.06.20增加原生excel读取方法(导入时可以自定义读取excel内容)
        /// string=当前读取的excel单元格的值
        /// ExcelWorksheet=excel对象
        /// ExcelRange当前excel单元格对象
        /// int=当前读取的第几行
        /// int=当前读取的第几列
        /// string=返回的值
        /// </summary>
        ImportOnReadCellValue = (string value, ExcelWorksheet worksheet, ExcelRange excelRange, int rowIndex, int columnIndex) =>
        {
            string 表头列名 = worksheet.Cells[1, columnIndex].Value?.ToString();
            //这里可以返回处理后的值,值最终写入到model字段上
            return value;
        };

        //导入保存前处理(可以对list设置新的值)
        ImportOnExecuting = (List<SellOrder> list) =>
        {
            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            //webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //导入后处理(已经写入到数据库了)
        ImportOnExecuted = (List<SellOrder> list) =>
        {
            return webResponse.OK();
        };
        return base.Import(files);
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 上传

上传对应的是表格、表单里面的图片、文件上传

  public override WebResponseContent Upload(List<IFormFile> files)
    {
        //自定义上传文件路径(目前只支持配置相对路径,默认上传到wwwwroot下)
        //2022.10.07更新ServiceBase.cs、ServiceFunFilter.cs后才能使用
        UploadFolder = $"test/{DateTime.Now.ToString("yyyyMMdd")}";
        return base.Upload(files);
    }
1
2
3
4
5
6
7

# 完整示例代码


//按需将下面的实现代码复制到【表Service.cs】类中即可

public partial class SellOrderService
{
    public string GetServiceDate()
    {
        return DateTime.Now.ToString("yyyy-MM-dd HH:mm:sss");
    }
    //此SellOrderService.cs类由代码生成器生成,默认是没有任何代码,如果需要写业务代码,请在此类中实现
    //如果默认的增、删、改、查、导入、导出、审核满足不了业务,请参考下面的方法进行业务代码扩展(扩展代码是对ServiceFunFilter.cs的实现)
    WebResponseContent webResponse = new WebResponseContent();
    private IHttpContextAccessor _httpContextAccessor;
    private ISellOrderRepository _repository;
    [ActivatorUtilitiesConstructor]
    public SellOrderService(IHttpContextAccessor httpContextAccessor, ISellOrderRepository repository)
        : base(repository)
    {
        _httpContextAccessor = httpContextAccessor;
        _repository = repository;
        //2020.08.15
        //开启数据隔离功能,开启后会对查询、导出、删除、编辑功能同时生效
        //如果只需要对某个功能生效,如编辑,则在重写编辑方法中设置 IsMultiTenancy = true;
        // IsMultiTenancy = true;
    }
    //查询
    public override PageGridData<SellOrder> GetPageData(PageDataOptions options)
    {
        //options.Value可以从前台查询的方法提交一些其他参数放到value里面
        //前端提交方式,见文档:组件api->viewgrid组件里面的searchBefore方法
        object extraValue = options.Value;

        //此处是从前台提交的原生的查询条件,这里可以自己过滤
        QueryRelativeList = (List<SearchParameters> parameters) =>
        {

        };

        //2020.08.15
        //设置原生查询的sql语句,这里必须返回select * 表所有字段
        //(先内部过滤数据,内部调用EF方法FromSqlRaw,自己写的sql注意sql注入的问题),不会影响界面上提交的查询
        /*  
         *  string date = DateTime.Now.AddYears(-10).ToString("yyyy-MM-dd");
            QuerySql = $@"select * from SellOrder  
                                   where createdate>'{date}'
                                       and  Order_Id in (select Order_Id from SellOrderList)
                                       and CreateID={UserContext.Current.UserId}";
        */

        //2020.08.15
        //此处与上面QuerySql只需要实现其中一个就可以了
        //查询前可以自已设定查询表达式的条件
        QueryRelativeExpression = (IQueryable<SellOrder> queryable) =>
        {
            //当前用户只能操作自己(与下级角色)创建的数据,如:查询、删除、修改等操作
            //IQueryable<int> userQuery = RoleContext.GetCurrentAllChildUser();
            //queryable = queryable.Where(x => x.CreateID == UserContext.Current.UserId || userQuery.Contains(x.CreateID ?? 0));
            return queryable;
        };

        //指定多个字段进行排序
        OrderByExpression = x => new Dictionary<object, QueryOrderBy>() {
            { x.CreateDate,QueryOrderBy.Desc },
            { x.SellNo,QueryOrderBy.Asc}
        };

        //int a = 1;
        ////指定多个字段按条件进行排序(需要2021.07.04更新LambdaExtensions类后才能使用)
        //OrderByExpression = x => new Dictionary<object, QueryOrderBy>() {
        //    { x.CreateDate,QueryOrderBy.Desc },
        //    { x.SellNo,a==1?QueryOrderBy.Desc:QueryOrderBy.Asc}
        //};

        //查询完成后,在返回页面前可对查询的数据进行操作
        GetPageDataOnExecuted = (PageGridData<SellOrder> grid) =>
        {
            //可对查询的结果的数据操作
            List<SellOrder> sellOrders = grid.rows;
        };
        //查询table界面显示求和
        SummaryExpress = (IQueryable<SellOrder> queryable) =>
        {
            return queryable.GroupBy(x => 1).Select(x => new
            {
                //AvgPrice注意大小写和数据库字段大小写一样
                Qty = x.Sum(o => o.Qty)
            })
            .FirstOrDefault();
        };

        return base.GetPageData(options);
    }
    /// <summary>
    /// 2023.02.03增加or查询条件示例
    /// 注意:如果有导出功能,GetPageData方法内的代码在下面的export方法里需要同样的复制一份
    /// </summary>
    /// <param name="options"></param>
    /// <returns></returns>
    public override PageGridData<SellOrder> GetPageData(PageDataOptions options)
    {
        System.Linq.Expressions.Expression<Func<SellOrder, bool>> orFilter = null;
        QueryRelativeList = (List<SearchParameters> parameters) =>
        {
            //方式1:动态生成or查询条件
            foreach (var item in parameters)
            {
                if (!string.IsNullOrEmpty(item.Value))
                {
                    //注意:这里只需要判断or查询的字段,其他的字段不需要处理
                    //这里必须拷贝value值
                    if (orFilter == null && (item.Name == "TranNo" || item.Name == "SellNo")) { orFilter = x => false; }
                    string value = item.Value;
                    if (item.Name == "TranNo")
                    {
                        //进行or模糊查询
                        orFilter = orFilter.Or(x => x.TranNo.Contains(value));
                        //清空原来的数据
                        item.Value = null;
                    }
                    else if (item.Name == "SellNo")
                    {
                        //进行or等于查询
                        orFilter = orFilter.Or(x => x.SellNo == value);
                        //清空原来的数据
                        item.Value = null;
                    }
                }
            }
            ///方式2:原生sql查询,需要自己处理sql注入问题(不建议使用此方法)
            //string sql = null;
            //foreach (var item in parameters)
            //{
            //    if (!string.IsNullOrEmpty(item.Value))
            //    {
            //        if (sql == null)
            //        {
            //            sql = "where 1=2";
            //        }
            //        string value = item.Value;
            //        //清空原来的数据
            //        item.Value = null;
            //        if (item.Name == "TranNo")
            //        {
            //            sql += $" or TranNo='{value}'";
            //            //清空原来的数据
            //            item.Value = null;
            //        }
            //        else if (item.Name == "SellNo")
            //        {
            //            sql += $" or SellNo='{value}'";
            //        }
            //    }
            //}
            //QuerySql = "select * from sellorder " + sql;
        };

        QueryRelativeExpression = (IQueryable<SellOrder> queryable) =>
        {
            if (orFilter != null)
            {
                queryable = queryable.Where(orFilter);
            }
            return queryable;
        };
        return base.GetPageData(options);
    }
    /// <summary>
    /// 设置弹出框明细表的合计信息
    /// </summary>
    /// <typeparam name="detail"></typeparam>
    /// <param name="queryeable"></param>
    /// <returns></returns>
    protected override object GetDetailSummary<detail>(IQueryable<detail> queryeable)
    {
        if (typeof(detail)==typeof(SellOrderList))
        {
            return (queryeable as IQueryable<SellOrderList>).GroupBy(x => 1).Select(x => new
            {
                //Weight/Qty注意大小写和数据库字段大小写一样
                Weight = x.Sum(o => o.Weight),
                Qty = x.Sum(o => o.Qty)
            }).FirstOrDefault();
        }
        return null
    }

    /// <summary>
    /// 查询业务代码编写(主从明细表(明细表查询))
    /// </summary>
    /// <param name="pageData"></param>
    /// <returns></returns>
    public override object GetDetailPage(PageDataOptions pageData)
    {
        //自定义查询胆细表

        ////明细表自定义查询方式一:EF
        //var query = SellOrderListRepository.Instance.IQueryablePage<SellOrderList>(
        //     pageData.Page,
        //     pageData.Rows,
        //     out int count,
        //     x => x.Order_Id == pageData.Value.GetGuid(),
        //      orderBy: x => new Dictionary<object, QueryOrderBy>() { { x.CreateDate, QueryOrderBy.Desc } }
        //    );
        //PageGridData<SellOrderList> detailGrid = new PageGridData<SellOrderList>();
        //detailGrid.rows = query.ToList();
        //detailGrid.total = count;

        ////明细表自定义查询方式二:dapper
        //string sql = "select count(1) from SellOrderList where Order_Id=@orderId";
        //detailGrid.total = repository.DapperContext.ExecuteScalar(sql, new { orderId = pageData.Value }).GetInt();

        //sql = @$"select * from (
        //              select *,ROW_NUMBER()over(order by createdate desc) as rowId
        //           from SellOrderList where Order_Id=@orderId
        //        ) as s where s.rowId between {((pageData.Page - 1) * pageData.Rows + 1)} and {pageData.Page * pageData.Rows} ";
        //detailGrid.rows = repository.DapperContext.QueryList<SellOrderList>(sql, new { orderId = pageData.Value });

        //return detailGrid;

        return base.GetDetailPage(pageData);
    }

    /// <summary>
    /// 新建
    /// </summary>
    /// <param name="saveDataModel"></param>
    /// <returns></returns>
    public override WebResponseContent Add(SaveModel saveDataModel)
    {
        //此处saveModel是从前台提交的原生数据,可对数据进修改过滤
        AddOnExecute = (SaveModel saveModel) =>
        {
            //如果返回false,后面代码不会再执行
            return webResponse.OK();
        };
        // 在保存数据库前的操作,所有数据都验证通过了,这一步执行完就执行数据库保存
        AddOnExecuting = (SellOrder order, object list) =>
        {
            //明细表对象
            List<SellOrderList> orderLists = list as List<SellOrderList>;

            //自定义逻辑
            if (orderLists == null || orderLists.Count == 0)
            {//如果没有界面上没有填写明细,则中断执行
                return webResponse.Error("必须填写明细数据");
            }
            if (orderLists.Exists(x => x.Qty <= 20))
                return webResponse.Error("明细数量必须大于20");

            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            // webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //此方法中已开启了事务,如果在此方法中做其他数据库操作,请不要再开启事务
        // 在保存数据库后的操作,此时已进行数据提交,但未提交事务,如果返回false,则会回滚提交
        AddOnExecuted = (SellOrder order, object list) =>
        {
            //明细表对象
            // List<SellOrderList> orderLists = list as List<SellOrderList>;

            if (order.Qty < 10)
            {  //如果输入的销售数量<10,会回滚数据库
                return webResponse.Error("销售数量必须大于1000");
            }

            return webResponse.OK("已新建成功,台AddOnExecuted方法返回的消息");
        };

        //新建的数据进入审批流程前处理,
        AddWorkFlowExecuting = (SellOrder order) =>
        {
            //返回false,当前数据不会进入审批流程
            return true;
        };

        //新建的数据写入审批流程后,第二个参数为审批人的用户id
        AddWorkFlowExecuted = (SellOrder order, List<int> userIds) =>
        {
            //这里可以做发邮件通知
            //var userInfo = repository.DbContext.Set<Sys_User>()
            //                .Where(x => userIds.Contains(x.User_Id))
            //                .Select(s => new { s.User_Id, s.UserTrueName, s.Email, s.PhoneNo }).ToList();

            //发送邮件方法
            //MailHelper.Send()
        };

        return base.Add(saveDataModel);
    }
    /// <summary>
    /// 编辑操作
    /// </summary>
    /// <param name="saveModel"></param>
    /// <returns></returns>
    public override WebResponseContent Update(SaveModel saveModel)
    {
        //注意:如果要给其他字段设置值,请在此处设置,如:(代码生成器上将字段编辑行设置为0,然后点生成model)
        //saveModel.MainData["字段"] = "值";

        //此处saveModel是从前台提交的原生数据,可对数据进修改过滤
        UpdateOnExecute = (SaveModel model) =>
        {
            ////这里的设置配合下面order.Remark = "888"代码位置使用
            // saveModel.MainData.TryAdd("Remark", "1231");

            //如果不想前端提交某些可以编辑的字段的值,直接移除字段
            // saveModel.MainData.Remove("字段");

            //如果返回false,后面代码不会再执行
            return webResponse.OK();

        };

        //编辑方法保存数据库前处理
        UpdateOnExecuting = (SellOrder order, object addList, object updateList, List<object> delKeys) =>
        {
            if (order.TranNo == "2019000001810001")
            {
                //如果设置code=-1会强制返回,不再继续后面的操作,2021.07.04更新LambdaExtensions文件后才可以使用此属性
                //webResponse.Code = "-1";
                // webResponse.Message = "测试强制返回";
                //return webResponse.OK();
                return webResponse.Error("不能更新此[" + order.TranNo + "]单号");
            }

            ////如果要手动设置某些字段的值,值不是前端提交的(代码生成器里面编辑行必须设置为0并生成model),如Remark字段:
            ////注意必须设置上面saveModel.MainData.TryAdd("Remark", "1231")
            //order.Remark = "888";

            //新增的明细表
            List<SellOrderList> add = addList as List<SellOrderList>;
            //修改的明细表
            List<SellOrderList> update = updateList as List<SellOrderList>;
            //删除明细表Id
            var guids = delKeys?.Select(x => (Guid)x);

            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            // webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //编辑方法保存数据库后处理
        //此方法中已开启了事务,如果在此方法中做其他数据库操作,请不要再开启事务
        // 在保存数据库后的操作,此时已进行数据提交,但未提交事务,如果返回false,则会回滚提交
        UpdateOnExecuted = (SellOrder order, object addList, object updateList, List<object> delKeys) =>
        {
            //新增的明细
            List<SellOrderList> add = addList as List<SellOrderList>;
            //修改的明细
            List<SellOrderList> update = updateList as List<SellOrderList>;
            //删除的行的主键
            var guids = delKeys?.Select(x => (Guid)x);
            return webResponse.OK();
        };

        return base.Update(saveModel);
    }

    /// <summary>
    /// 删除
    /// </summary>
    /// <param name="keys">删除的行的主键</param>
    /// <param name="delList">删除时是否将明细也删除</param>
    /// <returns></returns>
    public override WebResponseContent Del(object[] keys, bool delList = true)
    {
        //删除前可以做一些查询判断

        //将keys转换为表的主键类型一致,用于下面的查询
        var ids = keys.Select(s => s.GetGuid()).ToList();
        //如果主键是int类型,用GetInt转换
        //var ids = keys.Select(s => s.GetInt()).ToList();
        var data= repository.FindAsIQueryable(x => ids.Contains(x.FormId)).Select(s => new { }).ToList();
        if (判断条件)
        {
            //  return WebResponseContent.Instance.Error("提示信息");
        }

        //也可以对其他表查询 
        //操作同上
        // repository.DbContext.Set<表>().Where(x=>ids.Contains(x.FormId)); ;

        //或者删除时判断其他表没有有数据
        bool b =  repository.DbContext.Set<>().Where(x => ids.Any(x.FormId)); ;
        if (b)
        {
            //  return WebResponseContent.Instance.Error("提示信息");
        }

        //删除前处理
        //删除的行的主键
        DelOnExecuting = (object[] _keys) =>
        {
            return webResponse.OK();
        };
        //删除后处理
        //删除的行的主键
        DelOnExecuted = (object[] _keys) =>
        {
            return webResponse.OK();
        };
        return base.Del(keys, delList);
    }
    public override WebResponseContent Audit(object[] keys, int? auditStatus, string auditReason)
    {
        //status当前审批状态,lastAudit是否最后一个审批节点
        AuditWorkFlowExecuting = (SellOrder order, AuditStatus status, bool lastAudit) =>
        {
            return webResponse.OK();
        };
        //status当前审批状态,nextUserIds下一个节点审批人的帐号id(可以从sys_user表中查询用户具体信息),lastAudit是否最后一个审批节点
        AuditWorkFlowExecuted = (SellOrder order, AuditStatus status, List<int> nextUserIds, bool lastAudit) =>
        {
            //lastAudit=true时,流程已经结束
            if (!lastAudit)
            {
                //这里可以给下一批审批发送邮件通知
                //var userInfo = repository.DbContext.Set<Sys_User>()
                //             .Where(x => nextUserIds.Contains(x.User_Id))
                //             .Select(s => new { s.User_Id, s.UserTrueName, s.Email, s.PhoneNo }).ToList();
            }

            //审批流程回退功能,回到第一个审批人重新审批(重新生成审批流程)
            //if (status==AuditStatus.审核未通过||status==AuditStatus.驳回)
            //{
            //    base.RewriteFlow(order);
            //}

            return webResponse.OK();
        };

        //审核保存前处理(不是审批流程)
        AuditOnExecuting = (List<SellOrder> order) =>
        {
            return webResponse.OK();
        };
        //审核后处理(不是审批流程)
        AuditOnExecuted = (List<SellOrder> order) =>
        {
            return webResponse.OK();
        };


        return base.Audit(keys, auditStatus, auditReason);
    }

    /// <summary>
    /// 导出
    /// </summary>
    /// <param name="pageData"></param>
    /// <returns></returns>
    public override WebResponseContent Export(PageDataOptions pageData)
    {
        //设置最大导出的数量
        Limit = 1000;
        //指定导出的字段(2020.05.07)
        ExportColumns = x => new { x.SellNo, x.TranNo, x.CreateDate };

        //查询要导出的数据后,在生成excel文件前处理
        //list导出的实体,ignore过滤不导出的字段
        ExportOnExecuting = (List<SellOrder> list, List<string> ignore) =>
        {
            return webResponse.OK();
        };

        return base.Export(pageData);
    }

    /// <summary>
    /// 下载模板(导入时弹出框中的下载模板)(2020.05.07)
    /// </summary>
    /// <returns></returns>
    public override WebResponseContent DownLoadTemplate()
    {
        //指定导出模板的字段,如果不设置DownLoadTemplateColumns,默认导出查所有页面上能看到的列(2020.05.07)
        DownLoadTemplateColumns = x => new { x.SellNo, x.TranNo, x.Remark, x.CreateDate };
        return base.DownLoadTemplate();
    }

    /// <summary>
    /// 导入
    /// </summary>
    /// <param name="files"></param>
    /// <returns></returns>
    public override WebResponseContent Import(List<IFormFile> files)
    {
        //(2020.05.07)
        //设置导入的字段(如果指定了上面导出模板的字段,这里配置应该与上面DownLoadTemplate方法里配置一样)
        //如果不设置导入的字段DownLoadTemplateColumns,默认显示所有界面上所有可以看到的字段
        DownLoadTemplateColumns = x => new { x.SellNo, x.TranNo, x.Remark, x.CreateDate };

        /// <summary>
        /// 2022.06.20增加原生excel读取方法(导入时可以自定义读取excel内容)
        /// string=当前读取的excel单元格的值
        /// ExcelWorksheet=excel对象
        /// ExcelRange当前excel单元格对象
        /// int=当前读取的第几行
        /// int=当前读取的第几列
        /// string=返回的值
        /// </summary>
        ImportOnReadCellValue = (string value, ExcelWorksheet worksheet, ExcelRange excelRange, int rowIndex, int columnIndex) =>
        {
            string 表头列名 = worksheet.Cells[1, columnIndex].Value?.ToString();
            //这里可以返回处理后的值,值最终写入到model字段上
            return value;
        };

        //导入保存前处理(可以对list设置新的值)
        ImportOnExecuting = (List<SellOrder> list) =>
        {
            //设置webResponse.Code = "-1"会中止后面代码执行,与返回 webResponse.Error()一样,区别在于前端提示的是成功或失败
            //webResponse.Code = "-1";
            //webResponse.Message = "测试强制返回";
            //return webResponse.OK("ok");

            return webResponse.OK();
        };

        //导入后处理(已经写入到数据库了)
        ImportOnExecuted = (List<SellOrder> list) =>
        {
            return webResponse.OK();
        };
        return base.Import(files);
    }

    public override WebResponseContent Upload(List<IFormFile> files)
    {
        //自定义上传文件路径(目前只支持配置相对路径,默认上传到wwwwroot下)
        //2022.10.07更新ServiceBase.cs、ServiceFunFilter.cs后才能使用
        UploadFolder = $"test/{DateTime.Now.ToString("yyyyMMdd")}";
        return base.Upload(files);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542