1. 程式人生 > 實用技巧 >asp.net core後端+element ui tree樹控制元件基本用法

asp.net core後端+element ui tree樹控制元件基本用法

最近前後端分離大行其道,苦了後端人員,需要學習的東西還不少。於是到網上看了看前端的教程。沒想到前端發展到今天變得如此複雜。前端也包括許可權和路由的東西。不過整體看上去似曾相識,只是需要熟悉些新的語法。昨天晚上試用了一下element ui。感覺這個框架還是不錯的。學了vue,再也不想用jQuery了。不再直接操作dom,而是跟資料打交道。今後打算好好學習下vue,網上做出來的後端框架還是不少的。下面就記錄以下element做前端,asp.net core做後端實現的一個樹控制元件。

後端

  1. 首先涉及到樹型結構,先上Model,這裡以材料型別為例,型別可以巢狀,有的有父類。型別為Material:
using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;

namespace TreeDemo.Models
{
    /// <summary>
    /// 材料型別
    /// </summary>
    public class MaterialType
    {
        /// <summary>
        /// id
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// 材料型別名稱
        /// </summary>
        [StringLength(50)]
        public string Title { get; set; }

        /// <summary>
        /// 父類Id
        /// </summary>
        
        public int? ParentId { get; set; }

        [JsonIgnore]
        [ForeignKey(nameof(ParentId))]
        public virtual MaterialType ParentType { get; set; }


        public virtual ICollection<MaterialType> MaterialTypes { get; set; }


        public static List<MaterialType> GetData(List<MaterialType> data)
        {
            var nodes = data.Where(x => x.ParentId == null).Select(x => new MaterialType
            {
                Id = x.Id,
                ParentId = x.ParentId,
                Title = x.Title
            }).ToList();
            foreach (var item in nodes)
            {
                item.MaterialTypes = GetChildrens(item, data);
            }
            return nodes;
        }

        private static List<MaterialType> GetChildrens(MaterialType item, List<MaterialType> data)
        {
            var children = data.Where(x=>x.ParentId==item.Id).Select(x=> new MaterialType
            {
                Id = x.Id,
                ParentId = x.ParentId,
                Title = x.Title
            }).ToList();
            foreach (var child in children)
            {
                child.MaterialTypes = GetChildrens(child, data);
            }
            return children;
        }
    }
}

  1. asp.net core比較省事,有了模型就可以直接生成增刪改查頁面和api。在visual studio中的Controllers資料夾上右鍵,選擇新增->“新搭建的基架專案",選擇如下圖,再選擇MaterialType作為模型,新增TreeDemoContext,做資料庫上下文類。就可以直接生成增刪改查頁面。
  2. 資料庫ORM用的是Entity Framework Core。這裡需要再TreeDemoContext類中,配置以下表關係:
    (樹形結構的關係配置可以參考我之前寫的一篇文章:
    Entity Framework Core樹狀結構增刪改查)
using Microsoft.EntityFrameworkCore;
using TreeDemo.Models;

public class TreeDemoContext : DbContext
{
    public TreeDemoContext(DbContextOptions<TreeDemoContext> options)
        : base(options)
    {
    }

    public DbSet<MaterialType> MaterialType { get; set; }
   //配置樹狀結構表關係
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MaterialType>()
            .HasMany(x => x.MaterialTypes)
            .WithOne(x => x.ParentType)
            .HasForeignKey(x => x.ParentId)
            .OnDelete(DeleteBehavior.ClientSetNull);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
    }

}
  1. 生成的建立頁面(create.cshtml)需要稍微修改以下,因為預設select選項為第一個,修改的目的是讓select一開始為空:
@model TreeDemo.Models.MaterialType

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>MaterialType</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ParentId" class="control-label"></label>

                <select asp-for="ParentId" class="form-control" asp-items="ViewBag.ParentId">
                    <option value="">選擇父型別</option>
                </select>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

  1. 增刪改查頁面好了,通過頁面新增幾個示例資料:

  2. 在按照步驟二,生成MaterialType的增刪改查api。

  3. 替換System.Text.Json包,這裡通過nuget安Newtonsoft.Json和Microsoft.AspNetCore.Mvc.NewtonsoftJson包,在Startup中配置json格式。為啥配置:因為System.Text.Json不支援迴圈引用,樹中的children在序列化時,會引發迴圈引用錯誤。

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
//在ConfigureServices中新增
services.AddControllersWithViews().AddNewtonsoftJson(options=>
            {
                //忽略迴圈引用
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                //不使用駝峰樣式的key
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                //設定時間格式
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd";
            });
  1. 配置跨域配置:
    (1). 在appsettings.json中新增
  "AppCores": "http://localhost:8080,http://localhost:8081",

(2) 在startup類的ConfigureService方法中配置跨域:

services.AddCors(options => options.AddPolicy("AllowAll", policy => policy.WithOrigins(cores).AllowAnyMethod().AllowAnyHeader().AllowCredentials()));

(3) 還需要再api的方法中啟用跨域:

 [EnableCors("AllowAll")]
 [HttpGet]
   public async Task<ActionResult<IEnumerable<MaterialType>>> GetMaterialType()
   {

       return await _context.MaterialType.ToListAsync();
   }

後端到此結束

前端

1.建立vue專案

vue create treedemo
  1. 安裝element 和axios
npm install element-ui -S
npm install axios --save
  1. 再main.js中引用element ui和axios
import Vue from "vue"
import App from "./App.vue"
import ElementUI from "element-ui"
import "element-ui/lib/theme-chalk/index.css"
import axios from "axios"

axios.defaults.baseURL = "https://localhost:44356"
Vue.use(ElementUI)
Vue.prototype.$http = axios
Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount("#app")
  1. 在App.vue中編寫頁面,替換原來的內容:
<template>
  <div id="app">
    <el-tree
      :data="materialTypesData"
      :props="{ children: 'MaterialTypes', label: 'Title' }"
      @node-click="handleMaterialTypeNodeClick"
    ></el-tree>
  </div>
</template>

<script>
  export default {
    name: "App",
    data() {
      return {
        materialTypesData: []
      }
    },
    mounted() {
      this.getMaterialTypes()
    },
    methods: {
      //遠端獲取資料
      getMaterialTypes() {
        this.$http
          .get("https://localhost:44356/api/materialtypes/tree")
          .then(res => {
            console.log(res)
            this.materialTypesData = res.data
          })
      },
      //樹節點點選事件
      handleMaterialTypeNodeClick(data) {
        this.$message("test" + data)
      }
    }
  }
</script>

<style></style>

  1. 執行:後端在visual studio中按ctrl+F5執行,前端在vscode終端中輸入npm run serve執行,執行效果: