惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

博客园 - known

基于Blazor实现的光伏设备调试软件 基于Blazor实现的跟踪光伏智能运维平台 基于Blazor实现的电梯运行监测系统 基于Blazor实现的样品扫码比对管理系统 基于Blazor实现的简易进销存管理系统 InfluxDB2时序数据库查询教程 InfluxDB2时序数据库安装教程 基于Blazor实现的运输信息管理系统 React+Next.js+MaterialUI+Toolpad技术栈学习——安装 Blazor开发框架KnownPro-创建新项目 Blazor开发框架Known-V2.0.13 Blazor开发框架Known-V2.0.11 Blazor静态服务端呈现(静态SSR)身份认证 Blazor开发框架Known-V2.0.10 Blazor开发框架Known-V2.0.9 Blazor开发框架Known-V2.0.8 Blazor开发框架Known-V2.0.7 Blazor Web 应用如何实现Auto模式 Blazor程序混合Razor页面 Win11系统Docker部署Blazor程序 Known框架实战演练——进销存财务管理 Known框架实战演练——进销存基础数据
Known框架实战演练——进销存业务单据
known · 2024-07-24 · via 博客园 - known

本文介绍如何实现进销存管理系统的业务单据模块,业务单据模块包括采购进货单、采购退货单、销售出货单、销售退货单4个菜单页面。由于进销单据字段大同小异,因此设计共用一个页面组件类。

1. 配置模块

运行项目,在【系统管理-模块管理】中配置如下模块菜单,配置教程参考之前的教程。

一级模块 二级模块 代码 图标 Url 描述
进货管理 Import import
采购进货单 ImportList unordered-list /bms/ImportList 查询和维护采购进货单信息。
采购退货单 ImportReturn unordered-list /bms/ImportReturn 查询和维护采购退货单信息。
销货管理 Export export
销售出货单 ExportList unordered-list /bms/ExportList 查询和维护销售出货单信息。
销售退货单 ExportReturn unordered-list /bms/ExportReturn 查询和维护销售退货单信息。

2. 实体类

JxcLite项目Entities文件夹下面添加JxBillHead.csJxBillList.cs两个实体类文件,实体类代码可以直接复制模块管理中由模型设置生成的代码。文章中只简单描述一下实体类的定义,具体代码参见开源,代码定义如下:

namespace JxcLite.Entities;

/// <summary>
/// 业务单据表头信息类。
/// </summary>
public class JxBillHead : EntityBase { }

/// <summary>
/// 业务单据表体信息类。
/// </summary>
public class JxBillList : EntityBase { }

3. 建表脚本

打开JxcLite.Web项目Resources文件夹下的Tables.sql资源文件,复制粘贴由【模块管理-模型设置】中生成的建表脚本。文章中只简单描述一下建表脚本,具体脚本参见开源,内容如下:

CREATE TABLE [JxBillHead] (
    [Id]         varchar(50)      NOT NULL PRIMARY KEY,
    ...
    [Files]      nvarchar(500)    NULL
);

CREATE TABLE [JxBillList] (
    [Id]         varchar(50)      NOT NULL PRIMARY KEY,
    ...
    [Note]       ntext            NULL
);

4. 服务接口

JxcLite项目Services文件夹下面添加业务单据模块服务接口,文件名定义为IBillService.cs,该接口定义前后端交互的Api访问方法,包括分页查询、批量删除实体、保存实体。具体方法定义如下:

namespace JxcLite.Services;

public interface IBillService : IService
{
    //分页查询业务单据信息
    Task<PagingResult<JxBillHead>> QueryBillsAsync(PagingCriteria criteria);
    //根据单据类型获取默认单据信息
    Task<JxBillHead> GetDefaultBillAsync(string type);
    //根据表头ID获取单据表体信息列表
    Task<List<JxBillList>> GetBillListsAsync(string headId);
    //批量删除业务单据信息
    Task<Result> DeleteBillsAsync(List<JxBillHead> models);
    //保存业务单据信息
    Task<Result> SaveBillAsync(UploadInfo<JxBillHead> info);
}

5. 服务实现

JxcLite.Web项目Services文件夹下面添加业务单据模块服务接口的实现类,文件名定义为BillService.cs,文章中只简单描述一下实现类的定义和继承,具体实现参见开源,定义如下:

namespace JxcLite.Web.Services;

class BillService(Context context) : ServiceBase(context), IBaseDataService
{
    public Task<PagingResult<JxBillHead>> QueryBillsAsync(PagingCriteria criteria) { }
    public Task<JxBillHead> GetDefaultBillAsync(string type) { }
    public Task<List<JxBillList>> GetBillListsAsync(string headId) { }
    public Task<Result> DeleteBillsAsync(List<JxBillHead> models) { }
    public Task<Result> SaveBillAsync(UploadInfo<JxBillHead> info) { }
}

双击打开JxcLite.Web项目中的AppWeb.cs文件,在AddJxcLiteCore方法中注册服务类,前端组件可以通过依赖注入工厂创建服务的实例。代码如下:

public static class AppWeb
{
    public static void AddJxcLiteCore(this IServiceCollection services)
    {
        services.AddScoped<IBillService, BillService>();
    }
}

6. 数据依赖

JxcLite.Web项目Repositories文件夹下面添加业务单据模块数据依赖类,文件名定义为BillRepository.cs,文章中只简单描述一下依赖类的定义,具体实现参见开源,定义如下:

namespace JxcLite.Web.Repositories;

class BillRepository
{
    internal static Task<PagingResult<JxBillHead>> QueryBillsAsync(Database db, PagingCriteria criteria) { }

    internal static Task<List<JxBillList>> GetBillListsAsync(Database db, string headId) { }
    //根据前缀获取最大业务单号
    internal static Task<string> GetMaxBillNoAsync(Database db, string prefix) { }
}

7. 列表页面

JxcLite.Client项目Pages\BillData文件夹下面添加BillList.cs单据列表组件,该组件是进销单及退货单的列表组件共用类,具体实现参见开源,部分代码如下:

namespace JxcLite.Client.Pages.BillData;

public class BillList : BaseTablePage<JxBillHead>
{
    private IBillService Service;
    //取得业务单据类型(进货、进退货、销货、销退货),由具体单据页面重写该类型
    protected virtual string Type { get; }
    
    protected override async Task OnPageInitAsync()
    {
        await base.OnPageInitAsync();
        Service = await CreateServiceAsync<IBillService>();//创建服务
        Table.FormType = typeof(BillForm);//自定义表单类型
        Table.OnQuery = QueryBillsAsync;  //查询方法
        //下面是设置列表栏位显示的模板
        Table.Column(c => c.Status).Template((b, r) => b.Tag(r.Status));
        Table.Column(c => c.BillDate).Type(FieldType.Date);
    }
    //新增
    public async void New()
    {
        var row = await Service.GetDefaultBillAsync(Type);
        Table.NewForm(Service.SaveBillAsync, row);
    }
    //编辑
    public async void Edit(JxBillHead row)
    {
        row.Lists = await Service.GetBillListsAsync(row.Id);
        Table.EditForm(Service.SaveBillAsync, row);
    }
    //批量删除和删除
    public void DeleteM() => Table.DeleteM(Service.DeleteBillsAsync);
    public void Delete(JxBillHead row) => Table.Delete(Service.DeleteBillsAsync, row);
    //复制和退货
    public void Copy() => Table.SelectRow(async row => {});
    public void Return() => Table.SelectRow(async row => {});
    //打印
    public void Print() => Table.SelectRow(async row =>
    {
        row.Lists = await Service.GetBillListsAsync(row.Id);
        //BillPrint为业务单据打印组件
        await JS.PrintAsync<BillPrint>(f => f.Set(c => c.Model, row));
    });
    //导出
    public async void Export() => await ExportDataAsync();
    
    private Task<PagingResult<JxBillHead>> QueryBillsAsync(PagingCriteria criteria)
    {
        //设置单据类型查询条件
        criteria.SetQuery(nameof(JxBillHead.Type), QueryType.Equal, Type);
        return Service.QueryBillsAsync(criteria);
    }
}

8. 供应商和客户选择框

JxcLite.Client项目Shared文件夹下面添加PartnerPicker.cs,该组件继承BasePicker,用于弹窗选择客户和供应商信息,具体实现参见开源,部分代码如下:

namespace JxcLite.Client.Shared;

public class PartnerPicker : BasePicker<JxPartner>
{
    private IBaseDataService Service;
    private TableModel<JxPartner> Table;
    //取得弹框选择的数据列表
    public override List<JxPartner> SelectedItems => Table.SelectedRows?.ToList();
    //取得或设置商业伙伴类型(客户、供应商)
    [Parameter] public string Type { get; set; }
    
    protected override async Task OnInitAsync() {}
    protected override void BuildContent(RenderTreeBuilder builder) => builder.Table(Table);
}

9. 商品信息选择框

JxcLite.Client项目Shared文件夹下面添加GoodsPicker.cs,该组件继承BasePicker,用于弹窗选择商品信息,具体实现参见开源,部分代码如下:

namespace JxcLite.Client.Shared;

public class GoodsPicker : BasePicker<JxGoods>
{
    private IBaseDataService Service;
    private TableModel<JxGoods> Table;
    //取得弹框选择的数据列表
    public override List<JxGoods> SelectedItems => Table.SelectedRows?.ToList();
    
    protected override async Task OnInitAsync() {}
    protected override void BuildContent(RenderTreeBuilder builder) => builder.Table(Table);
}

10. 表单组件

首先在JxcLite.Client项目Shared文件夹下面添加TypeForms.csTypeTables.cs文件,添加业务单据表头类型表单组件和业务单据表体类型表格组件,代码如下:

namespace JxcLite.Client.Shared;

public class BillHeadTypeForm : AntForm<JxBillHead> { }

public class BillListTypeTable : AntTable<JxBillList> { }

再在JxcLite.Client项目Pages\BillData文件夹下面添加BillForm.razorBillForm.razor.cs文件,由于单据表单组件有点复杂,代码较长,所以采用razor语法来实现,该组件是进销单及退货单的列表组件共用类,具体实现参见开源,部分代码如下:

@inherits BaseForm<JxBillHead>

<BillHeadTypeForm Form="Model">
    <AntRow>
        <DataItem Span="6" Label="业务单号" Required>
            <AntInput Disabled @bind-Value="@context.BillNo" />
        </DataItem>
        <DataItem Span="6" Label="单证状态">
            <KTag Text="@context.Status" />
        </DataItem>
        <DataItem Span="6" Label="单证日期" Required>
            <AntDatePicker @bind-Value="@context.BillDate" />
        </DataItem>
        <DataItem Span="6" Label="商业伙伴" Required>
            <PartnerPicker Value="@context.Partner" AllowClear
                           Type="@GetPartnerPickerType(context)" />
        </DataItem>
    </AntRow>
</BillHeadTypeForm>
<KToolbar>
    <KTitle Text="商品明细" />
    <div>
        @if (!Model.IsView)
        {
            <Button Type="@ButtonType.Primary" Icon="plus" OnClick="OnAdd">添加</Button>
        }
    </div>
</KToolbar>
<BillListTypeTable DataSource="Model.Data.Lists" HidePagination ScrollX="1300px" ScrollY="200px">
    <IntegerColumn Title="序号" Field="@context.SeqNo" Width="60" Fixed="left" />
    <StringColumn Title="商品编码" Width="120" Fixed="left">
        <AntInput @bind-Value="@context.Code" Style="width:100px" />
    </StringColumn>
    <StringColumn Title="金额" Width="100">
        <AntDecimal @bind-Value="@context.Amount" OnChange="e=>OnGoodsChange(3, context)" />
    </StringColumn>
    @if (!Model.IsView)
    {
        <ActionColumn Title="操作" Align="ColumnAlign.Center" Width="100" Fixed="right">
            <Tag Color="red-inverse" OnClick="e=>OnDelete(context)">删除</Tag>
        </ActionColumn>
    }
    <SummaryRow>
        <SummaryCell Fixed="left">合计</SummaryCell>
        <SummaryCell>@Model.Data.Lists.Sum(l => l.Amount)</SummaryCell>
        <SummaryCell />
        @if (!Model.IsView)
        {
            <SummaryCell />
        }
    </SummaryRow>
</BillListTypeTable>
namespace JxcLite.Client.Pages.BillData;

partial class BillForm
{
    private KUpload upload;
    private static string GetPartnerPickerType(JxBillHead model) {}
    private async void OnFilesChanged(List<FileDataInfo> files) {}
    private void OnAdd() {}
    private void OnDelete(JxBillList row) => Model.Data.Lists.Remove(row);
    private void OnGoodsChange(int field, JxBillList row) {}
}

11. 打印组件

JxcLite.Client项目Pages\BillData文件夹下面添加BillPrint.cs,该组件是打印业务单据内容组件,具体实现参见开源,部分代码如下:

namespace JxcLite.Client.Pages.BillData;

class BillPrint : ComponentBase
{
    //业务单据实体对象
    [Parameter] public JxBillHead Model { get; set; }

    protected override void BuildRenderTree(RenderTreeBuilder builder)
    {
        BuildStyle(builder);//构建样式表,打印时调用浏览器的预览,选打印机打印
        BuildForm(builder); //构建打印表单
    }

    private static void BuildStyle(RenderTreeBuilder builder)
    {
        builder.Markup(@"<style>
.bill-print {position:relative;}
</style>");
    }

    private void BuildForm(RenderTreeBuilder builder) {}
}