开发一个主页面包含工序路径的主子表单控件GridView表格图表的页面,工序管理页面,工序资源组装带拖拽式控件,相关Tab页签分菜单页面,SQLSugar相关函数预备5,6组工序数据,项目框架主要使用ASP.NET Core Razor Pages 应用,实现本地文件化数据库的Web端界面化实现的代码解决方案实现组件



一、项目整体架构
基于ASP.NET Core Razor Pages + SQLSugar + SQLite(本地文件数据库) + Bootstrap + jQuery + Sortable.js(拖拽) + ECharts(图表)实现,以下是完整的代码解决方案:
二、环境准备
-
新建ASP.NET Core Razor Pages 项目(.NET 6/7)
-
安装 NuGet 包: plaintext
Install-Package SQLSugarCore Install-Package Microsoft.EntityFrameworkCore.Sqlite Install-Package ECharts
三、数据库配置(SQLSugar + SQLite)
1. 数据库上下文配置(SQLSugarSetup.cs)
csharp
运行
using SqlSugar;
namespace ProcessManagement
{
public static class SqlSugarSetup
{
public static void AddSqlSugar(this IServiceCollection services, IConfiguration configuration)
{
// SQLite本地文件数据库
var dbPath = Path.Combine(AppContext.BaseDirectory, "ProcessDB.db");
var connectionString = $"Data Source={dbPath}";
// 初始化SQLSugar
var db = new SqlSugarClient(new ConnectionConfig
{
ConnectionString = connectionString,
DbType = DbType.Sqlite,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
// 自动创建表
db.CodeFirst.InitTables(typeof(Process), typeof(ProcessPath), typeof(ProcessResource));
// 初始化测试数据
InitTestData(db);
services.AddSingleton<ISqlSugarClient>(db);
}
// 初始化5-6组工序测试数据
private static void InitTestData(ISqlSugarClient db)
{
if (db.Queryable<Process>().Any()) return;
var processes = new List<Process>
{
new Process { Id = 1, ProcessName = "原材料切割", ProcessCode = "PROC001", CreateTime = DateTime.Now },
new Process { Id = 2, ProcessName = "零部件打磨", ProcessCode = "PROC002", CreateTime = DateTime.Now },
new Process { Id = 3, ProcessName = "组装焊接", ProcessCode = "PROC003", CreateTime = DateTime.Now },
new Process { Id = 4, ProcessName = "表面喷涂", ProcessCode = "PROC004", CreateTime = DateTime.Now },
new Process { Id = 5, ProcessName = "质量检测", ProcessCode = "PROC005", CreateTime = DateTime.Now },
new Process { Id = 6, ProcessName = "成品包装", ProcessCode = "PROC006", CreateTime = DateTime.Now }
};
db.Insertable(processes).ExecuteCommand();
// 工序路径数据
var paths = new List<ProcessPath>
{
new ProcessPath { Id = 1, ProcessId = 1, NextProcessId = 2, PathName = "切割-打磨", Sort = 1 },
new ProcessPath { Id = 2, ProcessId = 2, NextProcessId = 3, PathName = "打磨-焊接", Sort = 2 },
new ProcessPath { Id = 3, ProcessId = 3, NextProcessId = 4, PathName = "焊接-喷涂", Sort = 3 },
new ProcessPath { Id = 4, ProcessId = 4, NextProcessId = 5, PathName = "喷涂-检测", Sort = 4 },
new ProcessPath { Id = 5, ProcessId = 5, NextProcessId = 6, PathName = "检测-包装", Sort = 5 }
};
db.Insertable(paths).ExecuteCommand();
// 工序资源数据
var resources = new List<ProcessResource>
{
new ProcessResource { Id = 1, ProcessId = 1, ResourceName = "切割机", ResourceType = "设备", Sort = 1 },
new ProcessResource { Id = 2, ProcessId = 1, ResourceName = "切割刀片", ResourceType = "耗材", Sort = 2 },
new ProcessResource { Id = 3, ProcessId = 2, ResourceName = "打磨机", ResourceType = "设备", Sort = 1 },
new ProcessResource { Id = 4, ProcessId = 3, ResourceName = "焊接机", ResourceType = "设备", Sort = 1 },
new ProcessResource { Id = 5, ProcessId = 4, ResourceName = "喷涂枪", ResourceType = "设备", Sort = 1 },
new ProcessResource { Id = 6, ProcessId = 5, ResourceName = "检测仪器", ResourceType = "设备", Sort = 1 }
};
db.Insertable(resources).ExecuteCommand();
}
}
}
2. 实体类定义
Process.cs(工序主表)
csharp
运行
using SqlSugar;
namespace ProcessManagement
{
[SugarTable("Process")]
public class Process
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(ColumnName = "ProcessName", Length = 50, IsNullable = false)]
public string ProcessName { get; set; }
[SugarColumn(ColumnName = "ProcessCode", Length = 20, IsNullable = false)]
public string ProcessCode { get; set; }
[SugarColumn(ColumnName = "CreateTime")]
public DateTime CreateTime { get; set; }
}
}
ProcessPath.cs(工序路径表)
csharp
运行
using SqlSugar;
namespace ProcessManagement
{
[SugarTable("ProcessPath")]
public class ProcessPath
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(ColumnName = "ProcessId")]
public int ProcessId { get; set; }
[SugarColumn(ColumnName = "NextProcessId")]
public int NextProcessId { get; set; }
[SugarColumn(ColumnName = "PathName", Length = 50)]
public string PathName { get; set; }
[SugarColumn(ColumnName = "Sort")]
public int Sort { get; set; }
// 导航属性
[SugarColumn(IsIgnore = true)]
public Process CurrentProcess { get; set; }
[SugarColumn(IsIgnore = true)]
public Process NextProcess { get; set; }
}
}
ProcessResource.cs(工序资源表)
csharp
运行
using SqlSugar;
namespace ProcessManagement
{
[SugarTable("ProcessResource")]
public class ProcessResource
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(ColumnName = "ProcessId")]
public int ProcessId { get; set; }
[SugarColumn(ColumnName = "ResourceName", Length = 50)]
public string ResourceName { get; set; }
[SugarColumn(ColumnName = "ResourceType", Length = 20)]
public string ResourceType { get; set; }
[SugarColumn(ColumnName = "Sort")]
public int Sort { get; set; }
// 导航属性
[SugarColumn(IsIgnore = true)]
public Process Process { get; set; }
}
}
四、页面实现
1. Program.cs 配置
csharp
运行
using ProcessManagement;
var builder = WebApplication.CreateBuilder(args);
// 添加Razor Pages支持
builder.Services.AddRazorPages();
// 添加SQLSugar配置
builder.Services.AddSqlSugar(builder.Configuration);
var app = builder.Build();
// 中间件配置
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// 配置Razor Pages路由
app.MapRazorPages();
app.Run();
2. 主页面(Index.cshtml + Index.cshtml.cs)
Index.cshtml.cs(后台逻辑)
csharp
运行
using Microsoft.AspNetCore.Mvc.RazorPages;
using SqlSugar;
namespace ProcessManagement.Pages
{
public class IndexModel : PageModel
{
private readonly ISqlSugarClient _db;
public IndexModel(ISqlSugarClient db)
{
_db = db;
}
// 工序列表
public List<Process> ProcessList { get; set; }
// 工序路径列表
public List<ProcessPath> ProcessPathList { get; set; }
// 图表数据(工序-资源数量)
public List<object> ChartData { get; set; }
public void OnGet()
{
// 获取工序列表
ProcessList = _db.Queryable<Process>().OrderBy(p => p.Id).ToList();
// 获取工序路径(关联工序名称)
ProcessPathList = _db.Queryable<ProcessPath>()
.LeftJoin<Process>((pp, p) => pp.ProcessId == p.Id)
.LeftJoin<Process>((pp, p1, p2) => pp.NextProcessId == p2.Id)
.Select((pp, p1, p2) => new ProcessPath
{
Id = pp.Id,
ProcessId = pp.ProcessId,
NextProcessId = pp.NextProcessId,
PathName = pp.PathName,
Sort = pp.Sort,
CurrentProcess = p1,
NextProcess = p2
})
.OrderBy(pp => pp.Sort)
.ToList();
// 构建图表数据:工序名称 + 对应资源数量
var resourceCount = _db.Queryable<ProcessResource>()
.GroupBy(p => p.ProcessId)
.Select(p => new { ProcessId = p.ProcessId, Count = p.Count() })
.ToList();
ChartData = ProcessList.Join(resourceCount,
p => p.Id,
rc => rc.ProcessId,
(p, rc) => new { Name = p.ProcessName, Value = rc.Count })
.Select(item => new object[] { item.Name, item.Value })
.ToList();
}
}
}
Index.cshtml(前端页面)
html
预览
@page
@model ProcessManagement.Pages.IndexModel
@{
ViewData["Title"] = "工序管理主页面";
}
<!-- 引入依赖 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<!-- 页签导航 -->
<ul class="nav nav-tabs mt-3" id="mainTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="process-tab" data-bs-toggle="tab" data-bs-target="#process" type="button" role="tab">工序列表</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="path-tab" data-bs-toggle="tab" data-bs-target="#path" type="button" role="tab">工序路径</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="resource-tab" data-bs-toggle="tab" data-bs-target="#resource" type="button" role="tab">工序资源(拖拽排序)</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="chart-tab" data-bs-toggle="tab" data-bs-target="#chart" type="button" role="tab">数据图表</button>
</li>
</ul>
<!-- 页签内容 -->
<div class="tab-content mt-3" id="mainTabContent">
<!-- 1. 工序列表 -->
<div class="tab-pane fade show active" id="process" role="tabpanel">
<div class="card">
<div class="card-header">
<h5>工序列表</h5>
</div>
<div class="card-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>ID</th>
<th>工序编码</th>
<th>工序名称</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var process in Model.ProcessList)
{
<tr>
<td>@process.Id</td>
<td>@process.ProcessCode</td>
<td>@process.ProcessName</td>
<td>@process.CreateTime.ToString("yyyy-MM-dd HH:mm")</td>
<td>
<a asp-page="/ProcessDetail" asp-route-id="@process.Id" class="btn btn-sm btn-primary">详情</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<!-- 2. 工序路径 -->
<div class="tab-pane fade" id="path" role="tabpanel">
<div class="card">
<div class="card-header">
<h5>工序路径</h5>
</div>
<div class="card-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>排序</th>
<th>路径名称</th>
<th>当前工序</th>
<th>下一道工序</th>
</tr>
</thead>
<tbody>
@foreach (var path in Model.ProcessPathList)
{
<tr>
<td>@path.Sort</td>
<td>@path.PathName</td>
<td>@path.CurrentProcess.ProcessName</td>
<td>@path.NextProcess.ProcessName</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<!-- 3. 工序资源(拖拽排序) -->
<div class="tab-pane fade" id="resource" role="tabpanel">
<div class="card">
<div class="card-header">
<h5>工序资源(拖拽调整排序)</h5>
</div>
<div class="card-body">
<div class="row">
@foreach (var process in Model.ProcessList)
{
var resources = Model._db.Queryable<ProcessResource>().Where(r => r.ProcessId == process.Id).OrderBy(r => r.Sort).ToList();
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-header">@process.ProcessName</div>
<div id="resourceList_@process.Id" class="list-group list-group-flush">
@foreach (var res in resources)
{
<div class="list-group-item" data-id="@res.Id">
@res.ResourceName (@res.ResourceType)
<span class="badge bg-secondary float-end">排序:@res.Sort</span>
</div>
}
</div>
</div>
</div>
}
</div>
</div>
</div>
</div>
<!-- 4. 数据图表 -->
<div class="tab-pane fade" id="chart" role="tabpanel">
<div class="card">
<div class="card-header">
<h5>工序-资源数量统计</h5>
</div>
<div class="card-body">
<div id="processChart" style="width: 100%; height: 400px;"></div>
</div>
</div>
</div>
</div>
<script>
// 1. 拖拽排序初始化
$(function () {
@foreach (var process in Model.ProcessList)
{
<text>
var el_@(process.Id) = document.getElementById('resourceList_@(process.Id)');
var sortable_@(process.Id) = new Sortable(el_@(process.Id), {
animation: 150,
onEnd: function (evt) {
// 拖拽完成后更新排序(可对接后台接口)
var resourceId = evt.item.getAttribute('data-id');
var newSort = evt.newIndex + 1;
console.log(`资源ID: ${resourceId}, 新排序: ${newSort}, 工序ID: @process.Id`);
// 此处可添加AJAX请求更新数据库排序
/*
$.post('/Process/UpdateResourceSort', {
resourceId: resourceId,
newSort: newSort
}, function (res) {
if (res.success) {
alert('排序更新成功');
}
});
*/
}
});
</text>
}
// 2. ECharts初始化
var chartDom = document.getElementById('processChart');
var myChart = echarts.init(chartDom);
var option = {
title: { text: '工序资源数量统计' },
xAxis: { type: 'category' },
yAxis: { type: 'value' },
series: [{
name: '资源数量',
type: 'bar',
data: @Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.ChartData))
}]
};
myChart.setOption(option);
});
</script>
3. 工序详情页(ProcessDetail.cshtml + ProcessDetail.cshtml.cs)
ProcessDetail.cshtml.cs
csharp
运行
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using SqlSugar;
namespace ProcessManagement.Pages
{
public class ProcessDetailModel : PageModel
{
private readonly ISqlSugarClient _db;
public ProcessDetailModel(ISqlSugarClient db)
{
_db = db;
}
public Process Process { get; set; }
public List<ProcessResource> ProcessResources { get; set; }
public IActionResult OnGet(int id)
{
// 获取工序详情
Process = _db.Queryable<Process>().FirstOrDefault(p => p.Id == id);
if (Process == null)
{
return NotFound();
}
// 获取工序关联资源
ProcessResources = _db.Queryable<ProcessResource>().Where(r => r.ProcessId == id).OrderBy(r => r.Sort).ToList();
return Page();
}
}
}
ProcessDetail.cshtml
html
预览
@page
@model ProcessManagement.Pages.ProcessDetailModel
@{
ViewData["Title"] = "工序详情";
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<div class="container mt-5">
<a asp-page="/Index" class="btn btn-secondary mb-3">返回首页</a>
<div class="card">
<div class="card-header">
<h4>工序详情:@Model.Process.ProcessName</h4>
</div>
<div class="card-body">
<div class="row mb-3">
<div class="col-md-2 fw-bold">工序编码:</div>
<div class="col-md-10">@Model.Process.ProcessCode</div>
</div>
<div class="row mb-3">
<div class="col-md-2 fw-bold">创建时间:</div>
<div class="col-md-10">@Model.Process.CreateTime.ToString("yyyy-MM-dd HH:mm")</div>
</div>
<h5 class="mt-4">关联资源</h5>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>排序</th>
<th>资源名称</th>
<th>资源类型</th>
</tr>
</thead>
<tbody>
@foreach (var res in Model.ProcessResources)
{
<tr>
<td>@res.Sort</td>
<td>@res.ResourceName</td>
<td>@res.ResourceType</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
五、核心功能说明
- 本地文件数据库 :使用 SQLite 作为本地文件数据库,数据库文件存储在项目运行目录下的
ProcessDB.db,无需额外数据库服务。 - SQLSugar 操作:通过 SQLSugar 实现数据库 CRUD、关联查询、分组统计等操作,初始化时自动创建表并填充 5-6 组测试数据。
- 主子表单 & GridView:主页面通过表格展示工序(主表)、工序路径(子表)、工序资源(子表),实现主子表联动展示。
- 拖拽控件:基于 Sortable.js 实现工序资源的拖拽排序,拖拽后可通过 AJAX 更新数据库排序(示例中仅打印日志,可扩展接口)。
- Tab 页签:基于 Bootstrap Tab 实现工序列表、路径、资源、图表的分页面展示。
- 图表展示:基于 ECharts 实现工序 - 资源数量的柱状图统计。
- Razor Pages 架构 :遵循ASP.NET Core Razor Pages 的页面模型分离设计,逻辑与视图解耦。
六、扩展建议
- 增加资源排序更新接口:实现拖拽后同步更新数据库
ProcessResource表的Sort字段。 - 增加工序 CRUD 操作:添加新增、编辑、删除工序的功能页面。
- 增加表单验证:对工序名称、编码等字段添加前端 / 后端验证。
- 优化图表展示:支持多维度图表(如饼图、折线图),增加数据筛选功能。
- 增加权限控制:基于ASP.NET Core Identity 实现用户权限管理。
- 美化 UI:结合 AdminLTE 等模板优化页面样式。
七、运行说明
- 将代码放入ASP.NET Core Razor Pages 项目中,确保 NuGet 包安装完成。
- 运行项目,自动创建 SQLite 数据库并初始化测试数据。
- 访问首页(/Index)即可查看所有功能:工序列表、路径、拖拽资源、图表统计。
- 点击工序列表的 "详情" 按钮可进入工序详情页查看关联资源。