NeuCharFramework (NCF)
  • NCF - NeuCharFramework
  • 项目

    • 准备
    • 基础库源码分析
    • 动态WebApiEngine
    • 部署
    • MCP (Model Context Protocol) 模块
    • Senparc.AI
  • 帮助

    • 在线资源
    • 问答社区
    • QQ群(147054579)
    • Senparc微信SDK
  • Gitee
  • GitHub
  • English
  • 简体中文
GitHub
  • NCF - NeuCharFramework
  • 项目

    • 准备
    • 基础库源码分析
    • 动态WebApiEngine
    • 部署
    • MCP (Model Context Protocol) 模块
    • Senparc.AI
  • 帮助

    • 在线资源
    • 问答社区
    • QQ群(147054579)
    • Senparc微信SDK
  • Gitee
  • GitHub
  • English
  • 简体中文
GitHub
  • NCF 概要

    • NCF - NeuCharFramework
    • 关于 NCF
    • 环境要求
    • 前后端分离模式
    • Xncf模块简介
  • 准备开发

    • 获取 NCF 模板源码
    • 使用 Visual Studio 运行 NCF
    • 使用 CLI 运行 NCF
    • 安装
    • 管理员登录
    • 管理员后台
    • appsettings.json 配置
    • 模块管理
    • 获取文档
  • 配置

    • 入口文件
    • 数据库设置
    • appsettings.json 配置
    • Docker
    • Dapr
    • 配置多租户
    • Redis 缓存
  • 模块化开发

    • NCF 模块化开发思想
    • Xncf 的构成
    • 创建第一个 Xncf 模块
    • Xncf 模块 Sample 详解
    • 实现自己的业务逻辑
    • 更新 Xncf 模块
    • 模块之间如何调用
    • 发布Xncf 模块到nuget.org
    • 更新基础库
    • Xncf 模块开发
    • 嵌入静态资源文件到NCF中
    • 发布本地Nuget包
    • 进阶
  • 数据库

    • 数据库设置
    • 多数据库支持
    • 指定数据库
    • 更新 Senparc.Service 项目的数据库迁移文件(Migrations)
    • 多数据库原理
    • 数据库停机坪(DatabasePlant)
    • 停机坪操作数据库迁移和更新
  • 单元测试

    • NCF 单元测试介绍
    • 开始开发
    • 进阶
    • 附录
  • Q&A

    • NCF名词解释
    • NCF常见问题
  • 新发布

    • 新功能
    • 升级指导
    • 日志

实现自己的业务逻辑

建立需求

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

数据库

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

数据库设计

表名称表注释
Senparc_Admin_User用户

Senparc_Admin_User(用户表)

字段名称数据类型注释类型长度注释详细说明
Idint主键Id-
Flagbool标识-
AddTimeDateTime添加时间-
LastUpdateTimeDateTime最后更新时间-
AdminRemarkstring管理员备注50
Remarkstring备注50
UnionIdstring微信UnionId50
WxOpenIdstring微信OpenId50
WxNickNamestring微信昵称100
Thumbstring头像200
Genderint性别(1-男;2-女;)-
Countrystring国家100
Provincestring省份100
Citystring城市100

创建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, 0.5)
  ); /*考虑浏览器兼容性:兼容 Chrome, Safari, Opera */
  filter: drop-shadow(10px 10px 10px rgba(0, 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
	}
}

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

在 GitHub 上编辑此页
上次更新:
贡献者: Jeffrey Su, MLTechnology
Prev
Xncf 模块 Sample 详解
Next
更新 Xncf 模块