写点什么

NCF 实战功能(三)

作者:MartyZane
  • 2021 年 12 月 12 日
  • 本文字数:14270 字

    阅读完需:约 47 分钟

概述

本节主要说的内容是,如何使用 Xncf 实现自己的业务逻辑,帮助你完成增删改查等基本的功能,会写了增删改查,其他的功能也就都可以根据实际的需要衍生出来了。

建立需求

下面我们举例来实现一下 User 的功能

数据库

首先我们先确定一下表所需要的字段

Senparc_Admin_User(用户)

Senparc_Admin_User(用户表)

创建 Model

在路径自定义的 Xncf Module 下

创建\Models\DatabaseModel\User.cs

源码如下:

using Senparc.Ncf.Core.Models;using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;using System.Text;
using Senparc.Xncf.Admin.Models.DatabaseModel.Dto;
namespace Senparc.Xncf.Admin.Models.DatabaseModel{ /// <summary> /// User 实体类 /// </summary> [Table(Register.DATABASE_PREFIX + nameof(User))]//必须添加前缀,防止全系统中发生冲突 [Serializable] public class User : EntityBase<string> { public User() { Id = Guid.NewGuid().ToString(); AddTime = DateTime.Now; this.LastUpdateTime = AddTime; }
public User(UserDto userDto) : this() { LastUpdateTime = userDto.LastUpdateTime; UnionId = userDto.UnionId; WxOpenId = userDto.WxOpenId; WxNickName = userDto.WxNickName; Thumb = userDto.Thumb; Gender = userDto.Gender; Country = userDto.Country; Province = userDto.Province; City = userDto.City; }
public void Update(UserDto userDto) { LastUpdateTime = userDto.LastUpdateTime; UnionId = userDto.UnionId; WxOpenId = userDto.WxOpenId; WxNickName = userDto.WxNickName; Thumb = userDto.Thumb; Gender = userDto.Gender; Country = userDto.Country; Province = userDto.Province; City = userDto.City; }
/// <summary> /// 微信UnionId /// </summary> [MaxLength(50)] public string UnionId { get; set; }
/// <summary> /// 微信OpenId /// </summary> [MaxLength(50)] public string WxOpenId { get; set; }
/// <summary> /// 微信昵称 /// </summary> [MaxLength(100)] public string WxNickName { get; set; }
/// <summary> /// 头像 /// </summary> [MaxLength(200)] public string Thumb { get; set; }
/// <summary> /// 性别(1-男;2-女;) /// </summary> public int Gender { get; set; }
/// <summary> /// 国家 /// </summary> [MaxLength(100)] public string Country { get; set; }
/// <summary> /// 省份 /// </summary> [MaxLength(100)] public string Province { get; set; }
/// <summary> /// 城市 /// </summary> [MaxLength(100)] public string City { get; set; }
}}
复制代码

创建 \Models\DatabaseModel\Dto\UserDto.cs

源码如下:

using Senparc.Ncf.Core.Models;using System;using System.Collections.Generic;using System.ComponentModel.DataAnnotations;using System.ComponentModel.DataAnnotations.Schema;using System.Text;
namespace Senparc.Xncf.Admin.Models.DatabaseModel.Dto{ public class UserDto : DtoBase { public UserDto() { }
public UserDto(string id, string unionId, string wxOpenId, string wxNickName, string thumb, int gender, string country, string province, string city) { Id = id; UnionId = unionId; WxOpenId = wxOpenId; WxNickName = wxNickName; Thumb = thumb; Gender = gender; Country = country; Province = province; City = city; }
public string Id { get; set; }
/// <summary> /// 微信UnionId /// </summary> [MaxLength(50)] public string UnionId { get; set; }
/// <summary> /// 微信OpenId /// </summary> [MaxLength(50)] public string WxOpenId { get; set; }
/// <summary> /// 微信昵称 /// </summary> [MaxLength(100)] public string WxNickName { get; set; }
/// <summary> /// 头像 /// </summary> [MaxLength(200)] public string Thumb { get; set; }
/// <summary> /// 性别(1-男;2-女;) /// </summary> public int Gender { get; set; }
/// <summary> /// 国家 /// </summary> [MaxLength(100)] public string Country { get; set; }
/// <summary> /// 省份 /// </summary> [MaxLength(100)] public string Province { get; set; }
/// <summary> /// 城市 /// </summary> [MaxLength(100)] public string City { get; set; }
}}
复制代码

创建 \Models\DatabaseModel\Mapping\Admin_UserConfigurationMapping.cs

源码如下:

using Microsoft.EntityFrameworkCore.Metadata.Builders;using Senparc.Ncf.Core.Models.DataBaseModel;using Senparc.Ncf.XncfBase.Attributes;using Senparc.Xncf.Admin.Models.DatabaseModel;
namespace Senparc.Xncf.Admin.Models{ [XncfAutoConfigurationMapping] public class Admin_UserConfigurationMapping : ConfigurationMappingWithIdBase<User, string> { public override void Configure(EntityTypeBuilder<User> builder) { // do something } }}
复制代码

修改 \Models\DatabaseModel\AdminSenparcEntities.cs

using Microsoft.EntityFrameworkCore;using Senparc.Ncf.Database;using Senparc.Ncf.Core.Models;using Senparc.Ncf.XncfBase.Database;
namespace Senparc.Xncf.Admin.Models.DatabaseModel{ public class AdminSenparcEntities : XncfDatabaseDbContext { public AdminSenparcEntities(DbContextOptions dbContextOptions) : base(dbContextOptions) { }
//DOT REMOVE OR MODIFY THIS LINE 请勿移除或修改本行 - Entities Point //ex. public DbSet<Color> Colors { get; set; }
public DbSet<User> Users { get; set; }
//如无特殊需需要,OnModelCreating 方法可以不用写,已经在 Register 中要求注册 //protected override void OnModelCreating(ModelBuilder modelBuilder) //{ //} }}
复制代码

网页部分

模块中的网页部分在

页面可以根据自己的实际需要去排版

html

index.cshtml 源码:

@page@model Senparc.Xncf.Admin.Areas.Admin.Pages.User.IndexModel@{    ViewData["Title"] = "用户页面";    Layout = "_Layout_Vue";}
@section Style{ <link href="~/css/Admin/User/User.css" rel="stylesheet" />}
@section breadcrumbs { <el-breadcrumb-item>扩展模块</el-breadcrumb-item> <el-breadcrumb-item>用户管理</el-breadcrumb-item> <el-breadcrumb-item>用户列表</el-breadcrumb-item>}
<div> <div class="admin-role"> <el-row class="filter-condition" :gutter="18"> <el-col :span="4"><el-input v-model="keyword" placeholder="请输入关键字"></el-input></el-col> <el-col :span="6"> <el-button type="primary" @@click="handleSearch()">查询</el-button> <el-button type="primary" @@click="resetCondition()">重置</el-button> </el-col> </el-row> <div class="filter-container"> <el-button class="filter-item" size="mini" type="primary" icon="el-icon-plus" @@click="handleEdit('','','add')">新增</el-button> </div> <el-table :data="tableData" style="width: 100%;margin-bottom: 20px;" row-key="id" border ref="multipleTable" @@selection-change="handleSelectionChange"> <el-table-column label="序号" width="65"> <template scope="scope"> <el-radio :label="scope.$index" v-model="radio" @@change.native="getCurrentRow(scope.row)"></el-radio> </template> </el-table-column>
<el-table-column prop="unionId" align="left" label="微信UnionId"></el-table-column> <el-table-column prop="wxOpenId" align="left" label="微信OpenId"></el-table-column> <el-table-column prop="wxNickName" align="left" label="微信昵称"></el-table-column> <el-table-column prop="thumb" align="center" label="头像"> <template slot-scope="scope"> <a :href="scope.row.thumb ? scope.row.thumb : 'demo.png'" target="_blank"> <img :src="scope.row.thumb ? scope.row.thumb : 'demo.png'"> </a> </template> </el-table-column> <el-table-column prop="gender" align="left" label="性别"></el-table-column> <el-table-column prop="country" align="left" label="国家"></el-table-column> <el-table-column prop="province" align="left" label="省份"></el-table-column> <el-table-column prop="city" align="left" label="城市"></el-table-column> <el-table-column align="center" label="添加时间"> <template slot-scope="scope"> {{formaTableTime(scope.row.addTime)}} </template> </el-table-column> <el-table-column label="操作" align="center" fixed="right" width="150"> <template slot-scope="scope"> <el-button size="mini" type="primary" @@click="handleEdit(scope.$index, scope.row,'edit')">编辑</el-button> <el-popconfirm placement="top" title="确认删除此用户吗?" @@on-confirm="handleDelete(scope.$index, scope.row)"> <el-button size="mini" type="danger" slot="reference">删除</el-button> </el-popconfirm> </template> </el-table-column> </el-table>
<pagination :total="paginationQuery.total" :page.sync="listQuery.pageIndex" :limit.sync="listQuery.pageSize" @@pagination="getList"></pagination> <!--编辑、新增--> <el-dialog :title="dialog.title" :visible.sync="dialog.visible" :close-on-click-modal="false" width="700px"> <el-form ref="dataForm" :rules="dialog.rules" :model="dialog.data" :disabled="dialog.disabled" label-position="left" label-width="100px" style="max-width: 200px; margin-left:50px;"> <el-form-item label="微信UnionId" prop="unionId"> <el-input v-model="dialog.data.unionId" clearable placeholder="请输入微信UnionId" /> </el-form-item>
<el-form-item label="微信OpenId" prop="wxOpenId"> <el-input v-model="dialog.data.wxOpenId" clearable placeholder="请输入微信OpenId" /> </el-form-item>
<el-form-item label="微信昵称" prop="wxNickName"> <el-input v-model="dialog.data.wxNickName" clearable placeholder="请输入微信昵称" /> </el-form-item>
<el-form-item label="头像"> <el-upload action="@Model.UpFileUrl" list-type="picture-card" show-file-list="true" accept="image/png, image/jpeg" :on-success="uploadSuccess" :on-error="uploadError" :on-preview="handlePictureCardPreview" :on-remove="handleRemove"> <i class="el-icon-plus"></i> <div class="el-upload__tip" slot="tip">不能超过100MB</div> </el-upload> <img width="100%" :src="dialogImageUrl" alt=""> <el-input class="hidden" v-model="dialog.data.thumb" clearable placeholder="头像" /> </el-form-item> <el-form-item label="性别"> <el-input v-model="dialog.data.gender" clearable placeholder="请输入性别" /> </el-form-item>
<el-form-item label="国家" prop="country"> <el-input v-model="dialog.data.country" clearable placeholder="请输入国家" /> </el-form-item>
<el-form-item label="省份" prop="province"> <el-input v-model="dialog.data.province" clearable placeholder="请输入省份" /> </el-form-item>
<el-form-item label="城市" prop="city"> <el-input v-model="dialog.data.city" clearable placeholder="请输入城市" /> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button @@click="dialog.visible=false">取消</el-button> <el-button :loading="dialog.updateLoading" :disabled="dialog.disabled" type="primary" @@click="updateData">确认</el-button> </div> </el-dialog> </div></div>@section scripts{ <script src="~/js/Admin/Pages/User/user.js"></script>}
复制代码

index.cshtml.cs 源码

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.RazorPages;using Senparc.Ncf.Service;using Microsoft.Extensions.DependencyInjection;using Senparc.Ncf.Core.Models;using Senparc.CO2NET.Trace;using Senparc.Ncf.Utility;using Senparc.Xncf.Admin.Models.DatabaseModel.Dto;using Senparc.Xncf.Admin.Services;
namespace Senparc.Xncf.Admin.Areas.Admin.Pages.User{ public class IndexModel : Senparc.Ncf.AreaBase.Admin.AdminXncfModulePageModelBase { private readonly UserService _userService; private readonly IServiceProvider _serviceProvider; public UserDto userDto { get; set; } public string Token { get; set; } public string UpFileUrl { get; set; } public string BaseUrl { get; set; }
public IndexModel(Lazy<XncfModuleService> xncfModuleService, UserService userService, IServiceProvider serviceProvider) : base(xncfModuleService) { CurrentMenu = "User"; this._userService = userService; this._serviceProvider = serviceProvider; }
[BindProperty(SupportsGet = true)] public int PageIndex { get; set; } = 1; public PagedList<Models.DatabaseModel.User> User { get; set; }
public Task OnGetAsync() { BaseUrl = $"{Request.Scheme}://{Request.Host.Value}"; UpFileUrl = $"{BaseUrl}/api/v1/common/upload"; return Task.CompletedTask; }
public async Task<IActionResult> OnGetUserAsync(string keyword, string orderField, int pageIndex, int pageSize) { var seh = new SenparcExpressionHelper<Models.DatabaseModel.User>(); seh.ValueCompare.AndAlso(!string.IsNullOrEmpty(keyword), _ => _.WxNickName.Contains(keyword)); var where = seh.BuildWhereExpression(); var response = await _userService.GetObjectListAsync(pageIndex, pageSize, where, orderField); return Ok(new { response.TotalCount, response.PageIndex, List = response.Select(_ => new { _.Id, _.LastUpdateTime, _.Remark, _.UnionId, _.WxOpenId, _.WxNickName, _.Thumb, _.Gender, _.Country, _.Province, _.City, _.AddTime }) }); } }}
复制代码

Edit.cshtml 源码

@page@model Senparc.Xncf.Admin.Areas.Admin.Pages.User.EditModel@{    ViewData["Title"] = $"{ (!string.IsNullOrEmpty(Model.Id.ToString()) ? "编辑" : "新增")}用户";}
复制代码

Edit.cshtml.cs 源码

using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using Microsoft.AspNetCore.Mvc;using Microsoft.AspNetCore.Mvc.RazorPages;using Senparc.Ncf.Service;using Senparc.CO2NET.Trace;using Senparc.CO2NET.Extensions;using Senparc.Xncf.Admin.Models.DatabaseModel.Dto;using Senparc.Xncf.Admin.Services;
namespace Senparc.Xncf.Admin.Areas.Admin.Pages.User{ public class EditModel : Senparc.Ncf.AreaBase.Admin.AdminXncfModulePageModelBase { private readonly UserService _userService; public EditModel(UserService userService, Lazy<XncfModuleService> xncfModuleService) : base(xncfModuleService) { CurrentMenu = "User"; _userService = userService; }
[BindProperty(SupportsGet = true)] public string Id { get; set; } public UserDto UserDto { get; set; }
/// <summary> /// Handler=Save /// </summary> /// <returns></returns> public async Task<IActionResult> OnPostSaveAsync([FromBody] UserDto userDto) { if (userDto == null) { return Ok(false); } await _userService.CreateOrUpdateAsync(userDto); return Ok(true); }
public async Task<IActionResult> OnPostDeleteAsync([FromBody] string[] ids) { var entity = await _userService.GetFullListAsync(_ => ids.Contains(_.Id)); await _userService.DeleteAllAsync(entity); IEnumerable<string> unDeleteIds = ids.Except(entity.Select(_ => _.Id)); return Ok(unDeleteIds); } }}
复制代码

style

创建 \wwwroot\css\Admin\User\User.css

源码如下:

.el-dialog .el-form-item .el-input,.el-dialog .el-form-item .el-textarea {    width: 30rem;}
.el-form-item__content { width: 30rem;}
.filter-condition { margin-bottom: 1rem;}
.hidden { display: none;}
.col-thumb img { width: 6rem; height: 4rem;}
.col-file video { width: 8rem; height: 5rem;}
.col-file audio { width: 10rem; height: 5rem;}
.item .item-left { text-align: right; padding-right: 1rem; background: #909399; color: #fff; height: 2rem; line-height: 2rem;}
.item .item-right { text-align: left; padding-left: 1rem; /*background: #909399;*/ border:1px solid #909399; color: #000; height: 2rem; line-height: 2rem;}
.item-right img, .item-right video { width: 15rem; -webkit-filter: drop-shadow(10px 10px 10px rgba(0,0,0,.5)); /*考虑浏览器兼容性:兼容 Chrome, Safari, Opera */ filter: drop-shadow(10px 10px 10px rgba(0,0,0,.5));}
.el-row{padding-bottom:1rem;}
.el-form-item{margin-bottom:5px;}
复制代码

javascript

创建 \wwwroot\js\Admin\Pages\User\user.js

源码如下:

new Vue({    el: "#app",    data() {        var validateCode = (rule, value, callback) => {            callback();        };        return {            defaultMSG: null,            editorData: '',            form:            {                content: ''            },            config:            {                initialFrameHeight: 500            },            //分页参数            paginationQuery:            {                total: 5            },            //分页接口传参            listQuery: {                pageIndex: 1,                pageSize: 20,                keyword: '',                orderField: ''            },            keyword:'',            multipleSelection: '',            radio: '',            props: { multiple: true },            // 表格数据            tableData:[],            uid: '',            fileList:[],            dialogImageUrl: '',            dialogVisible: false,            dialog:            {                title: '新增用户',                visible: false,                data:                {                    id:'',unionId: '', wxOpenId: '', wxNickName: '', thumb: '', gender: 0, country: '', province: '', city: ''                },                rules:                {                    name:                    [                        { required: true, message: "用户名称为必填项", trigger: "blur" }                    ]                },                updateLoading: false,                disabled: false,                checkStrictly: true // 是否严格的遵守父子节点不互相关联            }        }    },    created: function() {        let that = this        that.getList();    },    watch:    {        'dialog.visible': function(val, old) {            // 关闭dialog,清空            if (!val)            {                this.dialog.data = {                    id:'',unionId: '', wxOpenId: '', wxNickName: '', thumb: '', gender: 0, country: '', province: '', city: ''                };                this.dialog.updateLoading = false;                this.dialog.disabled = false;            }        }    },    methods:    {        handleChange(value) {            console.log(value);        },        handleRemove(file, fileList) {            log(file, fileList,2);        },        handlePictureCardPreview(file) {            let that = this            that.dialogImageUrl = file.url;            that.dialogVisible = true;        },        uploadSuccess(res, file, fileList) {            let that = this            that.fileList = fileList;            if (res.code == 200)            {                that.$notify({                    title: '成功',                    message: '恭喜你,上传成功',                    type: 'success'                });                that.dialog.data.cover = res.data;            }            else            {                that.$notify.error({                    title: '失败',                    message: '上传失败,请重新上传'                });            }        },        uploadError() {            let that = this            that.$notify.error({                title: '失败',                message: '上传失败,请重新上传'            });        },        // 获取列表        async getList()        {            let that = this            let { pageIndex, pageSize, keyword, orderField } = that.listQuery;            if (orderField == '' || orderField == undefined)            {                orderField = 'AddTime Desc';            }            if (that.keyword != '' && that.keyword != undefined) {                keyword = that.keyword;            }
await service.get(`/Admin/User/Index?handler=User&pageIndex=${pageIndex}&pageSize=${pageSize}&keyword=${keyword}&orderField=${orderField}`).then(res => { that.tableData = res.data.data.list; that.paginationQuery.total = res.data.data.totalCount; }); }, // 编辑 // 新增用户 // 增加下一级 handleEdit(index, row, flag) { let that = this that.dialog.visible = true; if (flag === 'add') { // 新增 that.dialog.title = '新增用户'; that.dialogImageUrl = ''; return; } // 编辑 let { id,unionId, wxOpenId, wxNickName, thumb, gender, country, province, city } = row; that.dialog.data = { id,unionId, wxOpenId, wxNickName, thumb, gender, country, province, city }; if (flag === 'edit') { that.dialog.title = '编辑用户'; } }, // 设置父级菜单默认显示 递归 recursionFunc(row, source, dest) { if (row.categoryId === null) { return; } for (let i in source) { let ele = source[i]; if (row.categoryId === ele.id) { this.recursionFunc(ele, this.categoryData, dest); dest.push(ele.id); } else { this.recursionFunc(row, ele.children, dest); } } }, // 更新新增、编辑 updateData() { let that = this that.dialog.updateLoading = true; that.$refs['dataForm'].validate(valid => { // 表单校验 if (valid) { that.dialog.updateLoading = true; let data = { Id: that.dialog.data.id, UnionId: that.dialog.data.unionId, WxOpenId: that.dialog.data.wxOpenId, WxNickName: that.dialog.data.wxNickName, Thumb: that.dialog.data.thumb, Gender: that.dialog.data.gender, Country: that.dialog.data.country, Province: that.dialog.data.province, City: that.dialog.data.city }; service.post("/Admin/User/Edit?handler=Save", data).then(res => { if (res.data.success) { that.getList(); that.$notify({ title: "Success", message: "成功", type: "success", duration: 2000 }); that.dialog.visible = false; } }); } }); }, // 删除 handleDelete(index, row) { let that = this let ids = [row.id]; service.post("/Admin/User/edit?handler=Delete", ids).then(res => { if (res.data.success) { that.getList(); that.$notify({ title: "Success", message: "删除成功", type: "success", duration: 2000 }); } }); }, getCurrentRow(row) { let that = this that.multipleSelection = row; }, handleSearch() { let that = this that.getList(); }, resetCondition() { let that = this that.keyword = ''; } }});
复制代码

权限配置

修改 \Register.Area.cs

源码如下:

using Microsoft.AspNetCore.Hosting;using Microsoft.Extensions.DependencyInjection;using Senparc.CO2NET.Trace;using Senparc.Ncf.Core.Areas;using Senparc.Ncf.Core.Config;using System;using Senparc.Ncf.XncfBase;using System.Collections.Generic;using System.IO;using Microsoft.Extensions.Hosting;using Microsoft.AspNetCore.Builder;using Senparc.CO2NET.RegisterServices;using Microsoft.Extensions.FileProviders;using System.Reflection;
namespace Senparc.Xncf.Admin{ public partial class Register : IAreaRegister, //注册 XNCF 页面接口(按需选用) IXncfRazorRuntimeCompilation //赋能 RazorPage 运行时编译 { #region IAreaRegister 接口
public string HomeUrl => "/Admin/Admin/Index";
public List<AreaPageMenuItem> AareaPageMenuItems => new List<AreaPageMenuItem>() { new AreaPageMenuItem(GetAreaHomeUrl(),"首页","fa fa-laptop"), //新增的菜单 new AreaPageMenuItem(GetAreaUrl($"/Admin/User/Index"),"用户","fa fa-bookmark-o"), };
public IMvcBuilder AuthorizeConfig(IMvcBuilder builder, IHostEnvironment env) { builder.AddRazorPagesOptions(options => { //此处可配置页面权限 });
SenparcTrace.SendCustomLog("Admin 启动", "完成 Area:AllTheCode.Xncf.Admin 注册");
return builder; }
public override IApplicationBuilder UseXncfModule(IApplicationBuilder app, IRegisterService registerService) { app.UseStaticFiles(new StaticFileOptions { FileProvider = new ManifestEmbeddedFileProvider(Assembly.GetExecutingAssembly(), "wwwroot") });
return base.UseXncfModule(app, registerService); }
#endregion
#region IXncfRazorRuntimeCompilation 接口 public string LibraryPath => Path.GetFullPath(Path.Combine(SiteConfig.WebRootPath, "..", "..", "Senparc.Xncf.Admin")); #endregion }}
复制代码

根据以上的创建方法可以完成系统中任何需要的功能

发布于: 3 小时前阅读数: 6
用户头像

MartyZane

关注

坚持不懈,直到成功 2021.03.15 加入

技术痴一枚,资深开发,喜欢交流,热爱开源,希望能与更多优秀的开发者一共为社会的进步贡献一份自己的力量

评论

发布
暂无评论
NCF 实战功能(三)