实现自己的业务逻辑
建立需求
下面我们举例来实现一下User的功能
数据库
首先我们先确定一下表所需要的字段
数据库设计
表名称 | 表注释 |
---|---|
Senparc_Admin_User | 用户 |
Senparc_Admin_User(用户表)
字段名称 | 数据类型 | 注释 | 类型长度 | 注释详细说明 |
---|---|---|---|---|
Id | int | 主键Id | - | |
Flag | bool | 标识 | - | |
AddTime | DateTime | 添加时间 | - | |
LastUpdateTime | DateTime | 最后更新时间 | - | |
AdminRemark | string | 管理员备注 | 50 | |
Remark | string | 备注 | 50 | |
UnionId | string | 微信UnionId | 50 | |
WxOpenId | string | 微信OpenId | 50 | |
WxNickName | string | 微信昵称 | 100 | |
Thumb | string | 头像 | 200 | |
Gender | int | 性别(1-男;2-女;) | - | |
Country | string | 国家 | 100 | |
Province | string | 省份 | 100 | |
City | string | 城市 | 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
}
}
根据以上的创建方法可以完成系统中任何需要的功能。