开发一个主页面包含工序路径的主子表单控件GridView表格图表的页面,工序管理带拖拽式控件,相关分菜单页面,SQLSugar相关函数预备5,6组工序数据,项目框架主要使用ASP.NET Core Razor Pages 应用,实现本地文件化数据库的Web端界面化实现的代码解决方案实现组件
ASP.NET Core Razor Pages 工序管理系统实现方案
一、项目基础架构
1. 项目创建与依赖
创建ASP.NET Core Razor Pages 项目(.NET 8),安装以下 NuGet 包:
powershell
Install-Package SQLSugarCore
Install-Package Microsoft.EntityFrameworkCore.Sqlite
Install-Package Bootstrap
Install-Package jQuery
Install-Package SortableJS
Install-Package Chart.js
2. 数据库配置(SQLite 本地文件数据库)
(1) SQLSugar 配置类
csharp
运行
// Services/SqlSugarSetup.cs
using SqlSugar;
namespace ProcessManagement.Services
{
public static class SqlSugarSetup
{
public static void AddSqlSugar(this IServiceCollection services)
{
var db = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = "Data Source=ProcessManagement.db", // 本地SQLite文件
DbType = DbType.Sqlite,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
});
// 初始化数据库和表
db.CodeFirst.InitTables<Process, ProcessPath, ProcessStep>();
// 预插入测试数据
InitTestData(db);
services.AddSingleton<ISqlSugarClient>(db);
}
/// <summary>
/// 初始化5-6组工序测试数据
/// </summary>
private static void InitTestData(ISqlSugarClient db)
{
if (!db.Queryable<Process>().Any())
{
var processes = new List<Process>
{
new Process { Id = 1, ProcessName = "机械加工工序", CreateTime = DateTime.Now, Status = 1 },
new Process { Id = 2, ProcessName = "表面处理工序", CreateTime = DateTime.Now, Status = 1 },
new Process { Id = 3, ProcessName = "装配工序", CreateTime = DateTime.Now, Status = 1 },
new Process { Id = 4, ProcessName = "检测工序", CreateTime = DateTime.Now, Status = 1 },
new Process { Id = 5, ProcessName = "包装工序", CreateTime = DateTime.Now, Status = 1 },
new Process { Id = 6, ProcessName = "热处理工序", CreateTime = DateTime.Now, Status = 0 }
};
db.Insertable(processes).ExecuteCommand();
// 工序路径数据
var paths = new List<ProcessPath>
{
new ProcessPath { Id = 1, ProcessId = 1, PathName = "主轴加工路径", PathCode = "PATH001" },
new ProcessPath { Id = 2, ProcessId = 1, PathName = "外壳加工路径", PathCode = "PATH002" },
new ProcessPath { Id = 3, ProcessId = 2, PathName = "镀锌处理路径", PathCode = "PATH003" },
new ProcessPath { Id = 4, ProcessId = 3, PathName = "总装路径", PathCode = "PATH004" },
new ProcessPath { Id = 5, ProcessId = 4, PathName = "精度检测路径", PathCode = "PATH005" },
new ProcessPath { Id = 6, ProcessId = 5, PathName = "成品包装路径", PathCode = "PATH006" }
};
db.Insertable(paths).ExecuteCommand();
// 工序步骤(用于拖拽排序)
var steps = new List<ProcessStep>
{
new ProcessStep { Id = 1, PathId = 1, StepName = "粗加工", Sort = 1 },
new ProcessStep { Id = 2, PathId = 1, StepName = "半精加工", Sort = 2 },
new ProcessStep { Id = 3, PathId = 1, StepName = "精加工", Sort = 3 },
new ProcessStep { Id = 4, PathId = 2, StepName = "打磨", Sort = 1 },
new ProcessStep { Id = 5, PathId = 2, StepName = "抛光", Sort = 2 },
new ProcessStep { Id = 6, PathId = 3, StepName = "脱脂", Sort = 1 },
new ProcessStep { Id = 7, PathId = 3, StepName = "酸洗", Sort = 2 },
new ProcessStep { Id = 8, PathId = 3, StepName = "镀锌", Sort = 3 },
new ProcessStep { Id = 9, PathId = 3, StepName = "钝化", Sort = 4 }
};
db.Insertable(steps).ExecuteCommand();
}
}
}
}
(2) 实体类定义
csharp
运行
// Models/Process.cs
using SqlSugar;
namespace ProcessManagement.Models
{
/// <summary>
/// 工序主表
/// </summary>
[SugarTable("Process")]
public class Process
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(ColumnName = "ProcessName", Length = 100, IsNullable = false)]
public string ProcessName { get; set; } = string.Empty;
[SugarColumn(ColumnName = "CreateTime")]
public DateTime CreateTime { get; set; }
[SugarColumn(ColumnName = "Status")] // 1-启用 0-禁用
public int Status { get; set; }
}
/// <summary>
/// 工序路径表(子表)
/// </summary>
[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 = "PathName", Length = 100, IsNullable = false)]
public string PathName { get; set; } = string.Empty;
[SugarColumn(ColumnName = "PathCode", Length = 50, IsNullable = false)]
public string PathCode { get; set; } = string.Empty;
// 导航属性
[SugarColumn(IsIgnore = true)]
public List<ProcessStep> Steps { get; set; } = new List<ProcessStep>();
}
/// <summary>
/// 工序步骤表(用于拖拽排序)
/// </summary>
[SugarTable("ProcessStep")]
public class ProcessStep
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }
[SugarColumn(ColumnName = "PathId")]
public int PathId { get; set; }
[SugarColumn(ColumnName = "StepName", Length = 100, IsNullable = false)]
public string StepName { get; set; } = string.Empty;
[SugarColumn(ColumnName = "Sort")] // 排序字段
public int Sort { get; set; }
}
}
(3) Program.cs 注册服务
csharp
运行
using ProcessManagement.Services;
var builder = WebApplication.CreateBuilder(args);
// 添加Razor Pages支持
builder.Services.AddRazorPages();
// 注册SQLSugar
builder.Services.AddSqlSugar();
var app = builder.Build();
// 中间件配置
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
// 配置路由
app.MapRazorPages();
app.Run();
二、核心页面实现
1. 主页面(Index.cshtml)- 包含 GridView、图表、主子表单
(1) Index.cshtml.cs 后台逻辑
csharp
运行
// Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ProcessManagement.Models;
using SqlSugar;
using System.Data;
namespace ProcessManagement.Pages
{
public class IndexModel : PageModel
{
private readonly ISqlSugarClient _db;
public IndexModel(ISqlSugarClient db)
{
_db = db;
}
/// <summary>
/// 工序列表(主表)
/// </summary>
public List<Process> ProcessList { get; set; } = new List<Process>();
/// <summary>
/// 选中工序的路径列表(子表)
/// </summary>
public List<ProcessPath> SelectedPathList { get; set; } = new List<ProcessPath>();
/// <summary>
/// 图表数据
/// </summary>
public string ChartData { get; set; } = string.Empty;
public void OnGet(int? processId = 1)
{
// 获取所有工序
ProcessList = _db.Queryable<Process>().ToList();
// 获取选中工序的路径(含步骤)
SelectedPathList = _db.Queryable<ProcessPath>()
.Where(p => p.ProcessId == processId)
.LeftJoin<ProcessStep>((p, s) => p.Id == s.PathId)
.Select((p, s) => new ProcessPath
{
Id = p.Id,
ProcessId = p.ProcessId,
PathName = p.PathName,
PathCode = p.PathCode,
Steps = SqlFunc.GroupBySelect<ProcessStep>(s, it => it).OrderBy(it => it.Sort).ToList()
})
.GroupBy(p => p.Id)
.ToList();
// 构建图表数据(工序数量统计)
var chartData = _db.Queryable<Process>()
.GroupBy(p => p.Status)
.Select(p => new {
Name = SqlFunc.Case().When(p.Status == 1).Then("启用").Else("禁用").End(),
Count = SqlFunc.Count(p.Id)
}).ToList();
ChartData = System.Text.Json.JsonSerializer.Serialize(chartData);
}
/// <summary>
/// 切换工序
/// </summary>
public IActionResult OnGetSwitchProcess(int processId)
{
SelectedPathList = _db.Queryable<ProcessPath>()
.Where(p => p.ProcessId == processId)
.LeftJoin<ProcessStep>((p, s) => p.Id == s.PathId)
.Select((p, s) => new ProcessPath
{
Id = p.Id,
ProcessId = p.ProcessId,
PathName = p.PathName,
PathCode = p.PathCode,
Steps = SqlFunc.GroupBySelect<ProcessStep>(s, it => it).OrderBy(it => it.Sort).ToList()
})
.GroupBy(p => p.Id)
.ToList();
return new JsonResult(new { success = true, data = SelectedPathList });
}
}
}
(2) Index.cshtml 前端页面
html
预览
@page
@model ProcessManagement.Pages.IndexModel
@{
ViewData["Title"] = "工序管理主页面";
}
<!-- 引入静态资源 -->
<link href="~/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/chart.js/chart.umd.js"></script>
<script src="~/lib/sortablejs/Sortable.min.js"></script>
<div class="container mt-4">
<h2 class="mb-4">工序管理系统</h2>
<!-- 1. 工序统计图表 -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">工序状态统计</div>
<div class="card-body">
<canvas id="processChart"></canvas>
</div>
</div>
</div>
<!-- 2. 工序主表GridView -->
<div class="col-md-6">
<div class="card">
<div class="card-header">工序列表</div>
<div class="card-body">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>ID</th>
<th>工序名称</th>
<th>创建时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@foreach (var process in Model.ProcessList)
{
<tr @(process.Id == 1 ? "class='table-primary'" : "")>
<td>@process.Id</td>
<td>@process.ProcessName</td>
<td>@process.CreateTime.ToString("yyyy-MM-dd")</td>
<td>@(process.Status == 1 ? "启用" : "禁用")</td>
<td>
<button class="btn btn-sm btn-primary switch-process"
data-process-id="@process.Id">选择</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 3. 工序路径子表 + 拖拽式步骤管理 -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">工序路径(子表)</div>
<div class="card-body">
<div id="pathContainer">
@foreach (var path in Model.SelectedPathList)
{
<div class="mb-4 border p-3">
<h5>路径名称:@path.PathName(编码:@path.PathCode)</h5>
<div class="mb-2">
<strong>工序步骤(拖拽排序):</strong>
</div>
<ul id="stepList_@path.Id" class="list-group sortable-step-list">
@foreach (var step in path.Steps)
{
<li class="list-group-item" data-step-id="@step.Id">
@step.Sort. @step.StepName
</li>
}
</ul>
<button class="btn btn-sm btn-success mt-2 save-sort"
data-path-id="@path.Id">保存排序</button>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// 1. 初始化图表
const chartData = @Html.Raw(Model.ChartData);
const ctx = document.getElementById('processChart').getContext('2d');
new Chart(ctx, {
type: 'pie',
data: {
labels: chartData.map(item => item.Name),
datasets: [{
label: '工序数量',
data: chartData.map(item => item.Count),
backgroundColor: ['#28a745', '#dc3545']
}]
},
options: {
responsive: true
}
});
// 2. 切换工序
$('.switch-process').click(function () {
const processId = $(this).data('process-id');
$.get(`?handler=SwitchProcess&processId=${processId}`, function (res) {
if (res.success) {
let html = '';
res.data.forEach(path => {
let stepHtml = '';
path.steps.forEach(step => {
stepHtml += `<li class="list-group-item" data-step-id="${step.id}">
${step.sort}. ${step.stepName}
</li>`;
});
html += `<div class="mb-4 border p-3">
<h5>路径名称:${path.pathName}(编码:${path.pathCode})</h5>
<div class="mb-2">
<strong>工序步骤(拖拽排序):</strong>
</div>
<ul id="stepList_${path.id}" class="list-group sortable-step-list">
${stepHtml}
</ul>
<button class="btn btn-sm btn-success mt-2 save-sort"
data-path-id="${path.id}">保存排序</button>
</div>`;
});
$('#pathContainer').html(html);
initSortable(); // 重新初始化拖拽
}
});
});
// 3. 初始化拖拽排序
function initSortable() {
$('.sortable-step-list').each(function () {
new Sortable(this, {
animation: 150,
handle: '.list-group-item',
ghostClass: 'bg-light'
});
});
}
// 4. 保存排序
$(document).on('click', '.save-sort', function () {
const pathId = $(this).data('path-id');
const stepList = $(`#stepList_${pathId}`);
const sortData = [];
stepList.find('li').each(function (index) {
sortData.push({
stepId: $(this).data('step-id'),
sort: index + 1
});
});
// 提交排序数据(后续实现保存接口)
$.post(`/ProcessStep/SaveSort?pathId=${pathId}`,
JSON.stringify(sortData),
function (res) {
if (res.success) {
alert('排序保存成功!');
} else {
alert('排序保存失败!');
}
}, 'json')
.fail(function () {
alert('请求失败!');
});
});
// 页面加载时初始化拖拽
$(function () {
initSortable();
});
</script>
2. 工序步骤排序保存页面(ProcessStep/SaveSort.cshtml.cs)
csharp
运行
// Pages/ProcessStep/SaveSort.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ProcessManagement.Models;
using SqlSugar;
namespace ProcessManagement.Pages.ProcessStep
{
public class SaveSortModel : PageModel
{
private readonly ISqlSugarClient _db;
public SaveSortModel(ISqlSugarClient db)
{
_db = db;
}
public async Task<IActionResult> OnPostAsync(int pathId, [FromBody] List<StepSortDto> sortData)
{
try
{
// 批量更新排序
foreach (var item in sortData)
{
await _db.Updateable<ProcessStep>()
.SetColumns(s => s.Sort == item.Sort)
.Where(s => s.Id == item.StepId)
.ExecuteCommandAsync();
}
return new JsonResult(new { success = true });
}
catch (Exception ex)
{
return new JsonResult(new { success = false, message = ex.Message });
}
}
public class StepSortDto
{
public int StepId { get; set; }
public int Sort { get; set; }
}
}
}
3. 分菜单页面示例(ProcessManage.cshtml)
html
预览
@page "/ProcessManage"
@model ProcessManagement.Pages.ProcessManageModel
@{
ViewData["Title"] = "工序管理";
}
<link href="~/lib/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<script src="~/lib/jquery/jquery.min.js"></script>
<div class="container mt-4">
<h2>工序基础管理</h2>
<!-- 工序新增/编辑表单 -->
<div class="card mb-4">
<div class="card-header">工序信息维护</div>
<div class="card-body">
<form id="processForm">
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label">工序名称</label>
<input type="text" class="form-control" id="processName" required />
</div>
<div class="col-md-6">
<label class="form-label">状态</label>
<select class="form-select" id="processStatus">
<option value="1">启用</option>
<option value="0">禁用</option>
</select>
</div>
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
</div>
</div>
<!-- 工序列表 -->
<div class="card">
<div class="card-header">工序列表</div>
<div class="card-body">
<table class="table table-bordered" id="processTable">
<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.ProcessName</td>
<td>@process.CreateTime.ToString("yyyy-MM-dd")</td>
<td>@(process.Status == 1 ? "启用" : "禁用")</td>
<td>
<button class="btn btn-sm btn-warning edit-process" data-id="@process.Id">编辑</button>
<button class="btn btn-sm btn-danger delete-process" data-id="@process.Id">删除</button>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<script>
// 表单提交
$('#processForm').submit(function (e) {
e.preventDefault();
const data = {
ProcessName: $('#processName').val(),
Status: $('#processStatus').val()
};
// 提交新增/编辑请求(后续实现接口)
$.post('/ProcessManage/SaveProcess', data, function (res) {
if (res.success) {
alert('保存成功!');
window.location.reload();
} else {
alert('保存失败:' + res.message);
}
}, 'json');
});
// 删除工序
$('.delete-process').click(function () {
if (confirm('确认删除该工序?')) {
const id = $(this).data('id');
$.post(`/ProcessManage/DeleteProcess?id=${id}`, function (res) {
if (res.success) {
alert('删除成功!');
window.location.reload();
} else {
alert('删除失败:' + res.message);
}
}, 'json');
}
});
</script>
三、关键功能说明
1. 拖拽排序实现
使用 SortableJS 实现工序步骤的拖拽排序,通过data-step-id绑定步骤 ID,拖拽后获取新的排序序号,提交到后台批量更新Sort字段。
2. 主子表单联动
通过点击主表(工序)的 "选择" 按钮,异步加载对应子表(工序路径)数据,更新页面展示。
3. 本地文件数据库
使用 SQLite 作为本地文件数据库,无需额外安装数据库服务,数据存储在ProcessManagement.db文件中。
4. SQLSugar 核心函数示例
csharp
运行
// 1. 关联查询+分组
var pathWithSteps = _db.Queryable<ProcessPath>()
.LeftJoin<ProcessStep>((p, s) => p.Id == s.PathId)
.Select((p, s) => new ProcessPath
{
Id = p.Id,
PathName = p.PathName,
Steps = SqlFunc.GroupBySelect<ProcessStep>(s, it => it).OrderBy(it => it.Sort).ToList()
})
.GroupBy(p => p.Id)
.ToList();
// 2. 批量插入
_db.Insertable(processList).ExecuteCommand();
// 3. 条件更新
_db.Updateable<ProcessStep>()
.SetColumns(s => s.Sort == newSort)
.Where(s => s.Id == stepId)
.ExecuteCommand();
// 4. 分组统计
var stat = _db.Queryable<Process>()
.GroupBy(p => p.Status)
.Select(p => new { Count = SqlFunc.Count(p.Id) })
.ToList();
// 5. 左连接查询
var joinData = _db.Queryable<Process>()
.LeftJoin<ProcessPath>((p, pp) => p.Id == pp.ProcessId)
.Select((p, pp) => new { p.ProcessName, pp.PathName })
.ToList();
四、部署与运行
- 将项目发布为本地文件,或直接在 VS 中运行
- 首次运行会自动创建
ProcessManagement.dbSQLite 数据库文件,并插入测试数据 - 访问
https://localhost:5001即可进入主页面 - 支持工序选择、路径步骤拖拽排序、图表展示、基础工序管理等功能
五、扩展建议
- 添加分页功能:对工序列表实现分页查询,优化大数据量展示
- 增加数据验证:在表单提交时添加前端 + 后端双重验证
- 完善权限控制:添加用户登录、角色权限管理
- 导出功能:支持将工序数据导出为 Excel/CSV 格式
- 日志记录:添加操作日志,记录工序修改、排序变更等操作