精华内容
下载资源
问答
  • asp.net图书管理系统,使用layui搭建后台mvc模式,实现了图书的增删改查操作,连接sqlserver数据库
  • 使用 Vue + LayUI后台管理、RESTful 交互

    万次阅读 热门讨论 2018-08-22 14:27:08
    故以后选择 MVC/MVVM 框架的话,建议首选 vue,主要是其代码结构,清晰简单。 2、使用 vue + layui 了,但 layui 里边的 layui.js 模块 vue.js 冲突, 因此放弃使用 layui.js,导致很多高级...

    一、前言

    1、之前使用了 React/Angular,使用起来显然是比 jQuery 好多了,但时隔半年,最近再次深入研究了 vue,很惊喜。

    故以后选择 MVC/MVVM 框架的话,建议首选 vue,主要是其代码结构,清晰简单。
    [增删改查] 使用 React + LayUI 做后台管理 CRUD 界面和 RESTful 交互

    2、使用 vue + layui 了,但 layui 里边的 layui.js 模块 vue.js 冲突,
    因此放弃使用 layui.js,导致很多高级功能用不了,如日期 laydate 模块,
    只使用了 layui 的 css样式,这时显得 layui 鸡肋了好多。

    故以后使用 UI 配合 vue 的话,建议选择 ElementUI/MintUI,毕竟是基于 vue 的,无缝对接。

    二、功能演示

    1、显示 index.html

    这里写图片描述

    2、添加 add.html

    这里写图片描述

    3、搜索 index.html

    这里写图片描述

    4、修改 update.html

    这里写图片描述

    5、删除 index.html

    这里写图片描述

    三、代码与结构

    这里写图片描述
    前端的代码已经放到 github 上了:https://github.com/larger5/CRUD_Vue_LayUI.git
    后台完整的代码也已经放到 github 上了:https://github.com/larger5/springboot_Vue.git(ITAEM团队官网后台 v1.0)

    1.index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>ITAEM</title>
        <link href="lib/layui.css" type="text/css" rel="stylesheet">
        <script src="lib/vue-2.4.0.js" type="text/javascript"></script>
        <script src="lib/vue-resource-1.3.4.js"></script>
    </head>
    <body>
    <div id="app" class="layui-container">
    
        <div className="layui-row layui-col-space2">
            <div class="layui-col-md1">
                <input type="text" v-model="searchId" required lay-verify="required" placeholder="id" class="layui-input" autocomplete="off"/>
            </div>
            <div class="layui-col-md1">
                <button id="btn2"  class="layui-btn" @click.prevent="search()">搜索</button>
            </div>
        </div>
    
        <table class="layui-table">
            <colgroup>
                <col width="150">
                <col width="200">
                <col>
            </colgroup>
            <thead>
            <tr>
                <th>博客id</th>
                <th>标题</th>
                <th>文章链接</th>
                <th>作者</th>
                <th>是否展示</th>
                <th>修改</th>
                <th>删除</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="item in list" :key="item.id">
                <td>{{item.id}}</td>
                <td>{{item.title}}</td>
                <td>{{item.link}}</td>
                <td>{{item.author}}</td>
                <td>{{item.tag}}</td>
                <th>
                    <a class="layui-btn layui-btn-normal" @click.prevent="upd(item.id)">修改</a>
                </th>
                <th>
                    <button class="layui-btn layui-btn-danger" @click.prevent="del(item.id)">删除</button>
                </th>
            </tr>
            </tbody>
        </table>
        <a href="add.html" class="layui-btn layui-btn-warm">添加</a>
    </div>
    <script>
    
        // 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 /  ,否则 不会启用根路径做拼接;
        Vue.http.options.root = 'http://120.79.197.130:8080/';
    
        // 全局启用 emulateJSON 选项:如果Web服务器无法处理编码为application/json的请求,你可以启用emulateJSON选项。
        Vue.http.options.emulateJSON = true;
    
        var vm = new Vue({
            el: '#app',
            data: {
                searchId: '',// 搜索用的
                list: [] // 存放列表数据
            },
            created() { // 当 vm 实例 的 data 和 methods 初始化完毕后,vm实例会自动执行created 这个生命周期函数
                this.getAllList()
            },
            methods: {
                getAllList() {
                    // 由于已经导入了 Vue-resource这个包,所以 ,可以直接通过  this.$http 来发起数据请求
                    this.$http.get('getAllBlogs').then(result => {
                        // 注意: 通过 $http 获取到的数据,都在 result.body 中放着
                        var result = result.body
                        if (result.code === 200) {
                            // 成功了
                            this.list = result.data
                            console.log(result.data)
                        } else {
                            // 失败了
                            alert('获取数据失败!')
                        }
                    })
                },
                del(id) {
                    this.$http.delete('deleteBlogById/' + id).then(result => {
                        if (result.body.code === 200) {
                            // 删除成功
                            this.getAllList()
                        } else {
                            alert('删除失败!')
                        }
                    })
                },
                search() {
                    this.$http.get('getBlogById/'+this.searchId).then(result => {
                        var result = result.body
                        if (result.code === 200) {
                            this.list=[]
                            this.list.push(result.data)
                        } else {
                            alert('查找失败!')
                        }
                    })
                },
                upd(updateId){
                    window.location.href="http://localhost:63343/forVue/VueCRUD/update.html?#"+updateId
                }
            }
        })
    </script>
    </body>
    </html>
    

    2.add.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>ITAEM</title>
        <link href="lib/layui.css" type="text/css" rel="stylesheet">
        <script src="lib/vue-2.4.0.js" type="text/javascript"></script>
        <script src="lib/vue-resource-1.3.4.js"></script>
    </head>
    <body>
    <div id="app" class="layui-container">
        <form class="layui-form" action="">
            <div class="layui-form-item">
                <label class="layui-form-label">标题</label>
                <div class="layui-input-block">
                    <input v-model="title" type="text" placeholder="title" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">链接</label>
                <div class="layui-input-block">
                    <input v-model="link" type="text" placeholder="https://www.baidu.com/" class="layui-input"
                           autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">作者</label>
                <div class="layui-input-block">
                    <input v-model="author" type="text" placeholder="author" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">是否展示</label>
                <div class="layui-input-block">
                    <input v-model="tag" type="text" placeholder="0/1" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" @click="add">添加</button>
                </div>
            </div>
        </form>
        <a href="index.html" class="layui-btn layui-btn-primary">返回</a>
    </div>
    <script>
        // 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 /  ,否则 不会启用根路径做拼接;
        Vue.http.options.root = 'http://120.79.197.130:8080/';
    
        // 全局启用 emulateJSON 选项:如果Web服务器无法处理编码为application/json的请求,你可以启用emulateJSON选项。
        Vue.http.options.emulateJSON = true;
    
        var vm = new Vue({
            el: '#app',
            data: {
                title: '',
                link: '',
                author: '',
                tag: ''
            },
            methods: {
                add() {
                    this.$http.post('insertBlog',
                        {
                            title: this.title,
                            link: this.link,
                            author: this.author,
                            tag: this.tag,
                        })
                        .then(result => {
                            var result = result.body
                            if (result.code === 200) {
                                alert('添加成功!')
                            } else {
                                // 失败了
                                alert('添加失败!')
                            }
                        })
                }
            }
        })
    </script>
    </body>
    </html>
    

    3.update.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>ITAEM</title>
        <link href="lib/layui.css" type="text/css" rel="stylesheet">
        <script src="lib/vue-2.4.0.js" type="text/javascript"></script>
        <script src="lib/vue-resource-1.3.4.js"></script>
    </head>
    <body>
    <div id="app" class="layui-container">
        <form class="layui-form" action="">
            <div class="layui-form-item">
                <label class="layui-form-label">id</label>
                <div class="layui-input-block">
                    <input v-model="id" type="text" placeholder="title" class="layui-input" autocomplete="off" disabled>
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">标题</label>
                <div class="layui-input-block">
                    <input v-model="title" type="text" placeholder="title" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">链接</label>
                <div class="layui-input-block">
                    <input v-model="link" type="text" placeholder="https://www.baidu.com/" class="layui-input"
                           autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">作者</label>
                <div class="layui-input-block">
                    <input v-model="author" type="text" placeholder="author" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">是否展示</label>
                <div class="layui-input-block">
                    <input v-model="tag" type="text" placeholder="0/1" class="layui-input" autocomplete="off">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" @click="upd()">修改</button>
                </div>
            </div>
        </form>
        <a href="index.html" class="layui-btn layui-btn-primary">返回</a>
    
    </div>
    <script>
        // 如果我们通过全局配置了,请求的数据接口 根域名,则 ,在每次单独发起 http 请求的时候,请求的 url 路径,应该以相对路径开头,前面不能带 /  ,否则 不会启用根路径做拼接;
        Vue.http.options.root = 'http://120.79.197.130:8080/';
    
        // 全局启用 emulateJSON 选项:如果Web服务器无法处理编码为application/json的请求,你可以启用emulateJSON选项。
        Vue.http.options.emulateJSON = true;
    
        var vm = new Vue({
            el: '#app',
            data: {
                id: '',
                title: '',
                link: '',
                author: '',
                tag: ''
            },
            created() { // 当 vm 实例 的 data 和 methods 初始化完毕后,vm实例会自动执行created 这个生命周期函数
                var userId = location.hash.substring(1) // 去掉 #
                this.$http.get('getBlogById/' + userId).then(result => {
                    // 注意: 通过 $http 获取到的数据,都在 result.body 中放着
                    var result = result.body
                    if (result.code === 200) {
                        // 成功了
                        this.id = result.data.id
                        this.title = result.data.title
                        this.link = result.data.link
                        this.author = result.data.author
                        this.tag = result.data.tag
                    } else {
                        // 失败了
                        alert('获取数据失败!')
                    }
                })
            },
            methods: {
                upd() { // 注意方法名不能为 update
                    this.$http.put('updateBlog',
                        {
                            'id': this.id,
                            'title': this.title,
                            'link': this.link,
                            'author': this.author,
                            'tag': this.tag
                        })
                        .then(result => {
                            var result = result.body
                            if (result.code === 200) {
                                alert('修改成功!')
                            } else {
                                // 失败了
                                alert('修改失败!')
                            }
                        })
                }
            }
        })
    </script>
    </body>
    </html>
    

    四、小结

    本文使用的框架 VueLayUI,以及推荐的 UI 框架 ElementUI(pc端)/MintUI(移动端),都是国产的,值得支持。

    2018/8/25 更新

    解决本文前言的痛点(没有使用 webpack,代码冗余)

    [增删改查] 使用 Vue + ElementUI + Webpack + VueRouter 做后台管理、REST 交互

    展开全文
  • layui下载 官网下载,解压到项目文件夹下的public目录下 layui使用 资源引入注意,通常入口文件中会配置静态资源根目录为/public 当服务器开启时,通过 地址+/(localhost:8080/) 就是到public目录下 所以引入资源...

    layui下载

    官网下载,解压到项目文件夹下的public目录下

    layui使用

    资源引入注意,通常入口文件中会配置静态资源根目录为/public
    当服务器开启时,通过 地址+/(localhost:8080/) 就是到public目录下
    所以引入资源就像这样

    layui里面有两个js文件,layui.js小一点,使用到一些功能时,可以另外引入(使用layui.use())

    使用mongodb的一个小坑

    var schema = new mongoose.Schema({ name: 'string', size: 'string' });
    var Tank = mongoose.model('Tank', schema);
    
    // 第一个参数是跟 model 对应的集合( collection )名字的 单数 形式。 Mongoose 会自动找到名称是 model 名字 复数 形式的 collection 。 对于上例,Tank 这个 model 就对应数据库中 tanks 这个 collection。.model() 这个函数是对 schema 做了拷贝(生成了 model)。 你要确保在调用 .model() 之前把所有需要的东西都加进 schema 里了!
    

    注意 `

    • collection名称应该为第三个参数var Tank = mongoose.model('Tank', schema,my_db),若为缺省,会自动根据参数name的值以复数形式生成collection(Tank对应的是tanks的集合,它会自动加上s,还有他会转小写,数据库起名最好是下划线命名法my_db)

    局部更新

    iframe标签实现局部更新
    目录选项更改iframe的src属性值

    <div class="layui-body" style="overflow: hidden;">
        <!-- 内容主体区域 -->
        <iframe src="/main" id="frame" frameborder="0" height="100%" width="100%" ></iframe>
      </div>
    

    分类列表页分页功能

    <!DOCTYPE html>
    <html>
    <body>
     
    <div id="test1"></div>
     
    <script>
    layui.use('laypage', function(){
      var laypage = layui.laypage;
      
      //执行一个laypage实例
      laypage.render({
        elem: 'laypage' //注意,这里的 test1 是 ID,不用加 # 号
          ,count: <%= count %> //数据总数,从服务端得到
          ,limit: 2 //每页显示的条数。laypage将会借助 count 和 limit 计算出分页数。
          ,curr: <%= curr %> //当前页
          ,jump: function(obj, first){
            //obj包含了当前分页的所有参数,比如:
            // console.log(obj.curr); //得到当前页,以便向服务端请求对应页的数据。
            // console.log(obj.limit); //得到每页显示的条数 
            //首次不执行
            if(!first){
              location.href = `/cateList?curr=${obj.curr}&pageSize=${obj.limit}`
            }
          }
      });
    });
    </script>
    </body>
    </html>
    

    需要获得分类总数方法和获取指定页数的分类的方法
    pro/model/models

    const mongoose  = require('mongoose')
    const { cateSchema } = require('../schemas')
    
    var cateModel = mongoose.model("cate",cateSchema)
    // 获取分类列表
    var getCatesList = (params={}) => cateModel.find().limit(params.pageSize).skip((params.curr-1)*params.pageSize)
    // 获取分类数量
    var getCatesNum = () => cateModel.find().count() 
    
    module.exports = {
      getCatesList,
      getCatesNum
    }
    

    路由处理业务逻辑
    pro/routes/cate.js

    1. 分页切换再次发送请求,携带参数(curr:切换的目标页,pageSize:每页数量)
    2. 第一次请求时,它没带参数,记得要设置默认值
    const router = require('express').Router()
    const { getCatesList, getCatesNum } = require('../model/models/cateModel')
    
    router.get('/cateList',async (req,res)=>{
      // 没传的话,设置当前页为1,页面尺寸为2
      var { curr=1 ,pageSize=2 } = req.query
      // 注意传过来的是字符串
      curr = parseInt(curr)
      pageSize = parseInt(pageSize)
      var cates = await getCatesList({ curr, pageSize })
      var count = await getCatesNum()
      res.render('cateList',{
        cates,
        count,
        curr
      })
    })
    
    module.exports = router
    

    删除功能实现

    1. 通过自定义属性将要操作的_id传递过去
    2. 发请求让后端操作数据库
    3. 知道删除成功后,刷新页面
    <button class="del layui-btn layui-btn-danger" _id="<%= cates[i]._id %>">删除</button>
    
    // 删除按钮
    $(".del").click(function(){
      layer.confirm('确定删除吗?', {icon: 3, title:'提示'},(index)=>{
        $.ajax({
          url: '/cateDel',
          type: 'POST',
          data: {
            _id: $(this).attr('_id')
          },
          success(res){
            if(res.code===0){
              layer.msg(res.msg,{time: 200},function(){
                history.go(0)
              })
            }else{
              layer.msg(res.msg)
            }
          }
          
        });
        layer.close(index);
      });
    });
    

    pro/routes/cate.js

    const router = require('express').Router()
    const { delCate } = require('../model/models/cateModel')
    // 删除分类
    router.post('/cateDel',(req,res)=>{
      let { _id } = req.body
      delCate(_id).then(re=>{
        res.send({
          code: 0,
          msg: '删除成功'
        })
      }).catch(err=>{
        res.send({
          code: 1,
          msg: '删除失败'
        })
      })
    })
    
    module.exports = router
    

    pro/model/models/cateModel.js

    const mongoose  = require('mongoose')
    const { cateSchema } = require('../schemas')
    
    var cateModel = mongoose.model("cate",cateSchema)
    
    // 删除分类
    var delCate = (_id) => cateModel.remove({_id})
    module.exports = {
      delCate
    }
    

    文件上传

    layui 里面有文件上传

    <div class="layui-form-item">
      <label class="layui-form-label">分类图标</label>
      <div class="layui-input-block">
        <div class="preview_img" style="margin-bottom: 10px;">
          <img src="" alt="预览图片" style="max-width: 390px;">
        </div>
        <button type="button" class="layui-btn" id="picUpload">
          <i class="layui-icon">&#xe67c;</i>选择图片
        </button>
      </div>
    </div>
    
    layui.use(["form","element","upload","jquery","layer"], function(){
    var form = layui.form
    var element = layui.element
    var upload = layui.upload
    var layer = layui.layer
    var $ = layui.jquery
    
    // 保存上传图片的绝对路径,之后合并
    var _data = {}
    
    //执行实例
    var uploadInst = upload.render({
      elem: '#picUpload' //绑定元素
      ,url: '/upload' //上传接口
      ,multiple: false //只允许上传单个文件
      ,auto: true //选择文件后自动上传
      ,field: "img" //**设定文件域的字段名(后台根据字段名获取)
      ,choose: function(obj) {
        // 选择文件后的回调函数
    
        //将每次选择的文件追加到文件队列
        var files = obj.pushFile();
        
        //预读本地文件,如果是多文件,则会遍历。(不支持ie8/9)
        obj.preview(function(index, file, result){
          // console.log(result); //得到文件base64编码,比如图片
    
          $(".preview_img img").attr('src',result);
        });
      }
      ,done: function(res){
        //上传完毕回调
        layer.msg('code:'+res.code+",msg:"+res.msg)
        _data.cateIcon = '/uploads/'+res.data
      }
      ,error: function(){
        //请求异常回调
        layer.msg("图片上传失败")
      }
    });
    

    multer中间件使用
    pro/utils/upload.js

    const multer = require('multer')
    // 最后路由文件都会挂到index.js上,所以路径·
    // var upload = multer({dest: './public/uploads/'})
    // single里的参数是上传文件的表单元素的name属性值
    
    var storage = multer.diskStorage({
      destination: function (req, file, cb) {
        // 最后路由文件都会挂到index.js上,所以路径·
        cb(null, './public/uploads')
      },
      filename: function (req, file, cb) {
        var extName = file.mimetype.split('/').reverse()[0]
        cb(null, file.fieldname + '-' + Date.now()+"."+extName)
      }
    })
     
    var upload = multer({ storage: storage })
    
    module.exports = upload
    

    文件上传接口处理
    pro/routes/upload.js

    var router = require('express').Router()
    
    var upload = require('../utils/upload')
    router.post('/upload',upload.single('img'),(req,res)=>{
      res.send({
        code: 0,
        msg: '图片上传成功',
        data: req.file.filename
      })
    })
    module.exports = router
    

    增加分类功能实现

    1. 对于父级元素option项,应该请求获取所有的类
    2. option当不写value的时候,value默认等于元素的文本内容;第一条作提示时,可以让value值为"",layui有表单验证

    使用layui里提供的表单元素

    <div class="layui-form-item">
      <label class="layui-form-label">父级分类</label>
      <div class="layui-input-block">
        <select name="catePId" lay-verify="required"> 
          <option value="">请选择</option>
          <option value="0">顶层分类</option>
          <% for(var i in cates) { %>
          <option value="<%= cates[i]._id %>"><%= cates[i].cateName %></option>
          <% } %>
        </select>
      </div>
    </div>
    
    //监听提交
    form.on('submit(formDemo)', function(data){
      // data.field获取表单提交内容
      // console.log(data.field)
      $.ajax({
        url:'/cateAdd',
        type: 'POST',
        dataType: 'JSON',
        data: Object.assign(_data,data.field),
        success: function(res){
          // 上传成功清空表单
          // $(".layui-form")[0].reset();
          // $(".preview_img img").attr('src','');
          layer.msg(res.msg,{
            time: 200
          },function(){
            // 添加成功后跳转到列表页
            location.href="/cateList"
          })
          // console.log(res)
        }
      })
      // 取消默认表单提交事件
      return false;
    });
    })
    
    1. 添加页面渲染(要携带父级分类所需数据)
    2. 提交添加请求操作数据库
      pro/routes/cate.js
    var express = require('express');
    var router = express.Router();
    const { getAllCates,addCates } = require('../model/models/cate')
    // 添加分类页面渲染
    router.get('/cateAdd', async function(req, res, next) {
      let cates  = await getAllCates();
      res.render('cateAdd',{
        cates
      });
    });
    // 添加分类
    router.post('/cateAdd',(req,res)=>{
      console.log(req.body);
      // 调用 增加数据 model函数
     
      addCates(req.body).then(re=>{
        res.send({
          code:0,
          msg:"添加成功"
        });
      }).catch(err=>{
        console.log(err)
        res.send({
          code:1,
          msg:"添加失败"
        });
      })
      
    })
    
    1. 查询所有分类操作
    2. 插入数据操作
      pro/model/models/cate.js
    const cateModel = mongoose.model("qf_cate",cateSchema);
    
    // 获取分类
    const getAllCates = ()=>cateModel.find()
    // 添加分类
    const addCates = (params)=> cateModel.insertMany(params)
    
    module.exports = {
    	getAllCates,
    	addCates
    };
    

    修改分类

    布局渲染基本跟添加分类一致,但请求携带除了目前所有分类之外,还要知道渲染的是哪个
    下拉菜单选项的预选通过设置selected属性实现

    <div class="layui-form-item">
      <label class="layui-form-label">父级分类</label>
      <div class="layui-input-block">
        <select name="catePId" lay-verify="required"> 
          <option value="0" <%= cate.catePId==="0"?'selected':'' %>>顶层分类</option>
          <% for(var i in cates) { %>
          <option value="<%= cates[i]._id %>" <%= cate.catePId===cates[i]._id?'selected':'' %>><%= cates[i].cateName %></option>
          <% } %>
        </select>
      </div>
    </div>
    

    注意图片因为是想用img字段,而数据库中的字段是cateIcon,不一样,且它的值是保存在服务器静态资源目录下的,要改一下,所以把它单独拎form外面了,layui的data.field数据是来自from里面的表单元素的name和值
    那么就要单独判断有没有修改图片

    form.on('submit(formDemo)', function(data){
    // JSON.stringify(_data) 有问题
    _data = _data.cateIcon?_data:{cateIcon: $(".preview_img img").attr('src')}
    $.ajax({
      url:'/cateEdit',
      type: 'POST',
      dataType: 'JSON',
      data: Object.assign(_data,data.field),
      success: function(res){
        // 上传成功清空表单
        // $(".layui-form")[0].reset();
        // $(".preview_img img").attr('src','');
        layer.msg(res.msg,{
          time: 200
        },function(){
          location.href="/cateList"
        })
        // console.log(res)
      }
    })
    // 取消默认表单提交事件
    return false;
    });
    

    编辑分类的接口
    pro/routes/cate.js

    var express = require('express');
    var router = express.Router();
    const { getAllCates,getCateById,editCate} = require('../model/models/cate')
    
    
    // 编辑分类页面渲染
    router.get('/cateEdit', async function(req, res, next) {
      let cates  = await getAllCates();
      let cate = await getCateById(req.query.id);
      console.log(req.query.id);
      res.render('cateEdit',{
        cates,
        _id:req.query.id,
        cate
      });
    });
    // 修改数据库请求
    router.post('/cateEdit', async function(req, res, next) {
      let cate = req.body;
      // 创建一个 编辑model
      editCate(cate).then(re=>{
        res.send({
          code:0,
          msg:'修改成功'
        });
      }).catch(err=>{
        res.send({
          code:-1,
          msg:'修改失败'
        });
      })
      
    });
    
    module.exports = router;
    
    

    操作数据库,修改数据

    const mongoose = require('mongoose');
    const {cateSchema} = require("../schemas");
    
    const cateModel = mongoose.model("qf_cate",cateSchema);
    
    // 获取分类
    const getAllCates = ()=>cateModel.find()
    // 根据id 获取这条分类数据
    const getCateById = (_id)=>cateModel.findById(_id);
    // 修改model
    const editCate = (params)=>cateModel.update({
    	_id:params._id
    },params);
    
    module.exports = {
    	getAllCates,
    	getCateById,
    	editCate
    };
    

    登录功能实现

    登录成功时种cookie,退出时移除cookie

    res.cookie("user",JSON.stringify({
      "userName":re.userName,
      "isLogin":true
    }));
    
    res.cookie('user','',{maxAge:0});
    

    pro/routes/user.js

    const router = require("express").Router()
    const { getUser } = require('../model/models/loginModel')
    
    // 登录页渲染
    router.get("/login",(req,res)=>{
      res.render('login')
    })
    
    // 退出登录
    router.post("/loginOut",(req,res)=>{
      // 设置cookie值为空,过期时间为0
      res.cookie('user','',{maxAge:0})
      res.send({
        msg: '退出成功',
        code: 0
      })
    })
    
    // 账号密码提交
    router.post("/login",(req,res)=>{
      var user = req.body
      getUser({userName: user.userName}).then(re=>{
        if (re){
          // 数据库有这个用户,判断密码对不对
          if(re.psw === user.psw){
            // 登录成功时种cookie
            res.cookie("user",JSON.stringify({
              "userName": re.userName,
              "isLogin": true
            }))
            res.send({
              code: 0,
              msg: '登录成功'
            })
          }else{
            res.send({
              code: -1,
              msg: '用户名或密码错误'
            })
          }
    
        }else {
          // 数据库没有这用户名,返回null
          // 用户名错误
          res.send({
            code: -1,
            msg: '用户名或密码错误'
          })
        }
      }).catch(err=>{
        res.send({
          code: 1,
          msg: '登录失败'
        })
      })
    })
    
    module.exports = router
    

    登录鉴权
    通过中间来进行全局路由拦截

    pro/middleware/loginCheck.js

    function loginCheck (req,res,next) {
      if(req.url === '/login'){
        next()
      }else{
        var loginState = req.cookies.user? JSON.parse(req.cookies.user): null
        if(loginState){
          // 如果登陆过了
          next()
        }else{
          res.redirect('/login')
        }
      }
    }
    
    module.exports = loginCheck
    

    全局使用登录验证中间件,记住要放在cookie-parser下面
    pro/app.js

    // 导入自定义中间件
    var loginCheck = require('./middleware/loginCheck')
    
    // 使用中间件,记住要在cookie-parser下面使用
    app.use(loginCheck);
    
    展开全文
  • 项目名称:学生信息管理系统1.0后台框架:.Net Core 3.1 + EF Core yrjw.ORM.Chimp前端框架:ASP.NET Core MVC + LayUI + Bootstrap数据库:MS-SQL 或 MySQL 二选一迁移方式:Code First (推荐CLI命令初始化数据库和...

    f080563abeceea92da5a9a23bd6601c0.gif

    项目名称:学生信息管理系统1.0 

    后台框架:.Net Core 3.1 + EF Core    yrjw.ORM.Chimp

    前端框架:ASP.NET Core MVC  +  LayUI + Bootstrap

    数据库:MS-SQL 或 MySQL 二选一

    迁移方式:Code First  (推荐CLI命令初始化数据库和升级)

    框架结构图:

    a6c31da1475883307f7ab5ce03d71dcf.png

    项目需求:  (_PS:不包含业务,只为展示开发案例。)

    登录模块案例 ,用户名密码验证码登录方式—— 已完成。

    配置信息设置案例,存库方式——已完成。

    操作员管理案例,简单CURD操作——已完成。

    部门管理案例,简单CURD操作——已完成。

    学生信息查询案例,简单CURD操作——已完成。

    框架封装介绍:

    1、简单三层架构,前后端分离模式,目前采用Layui前端组件的MVC框架,后面可直接替换UI层VUE等前端框架。

    2、集成了之前发布的yrjw.ORM.Chimp包,该组件只是将EF Core使用仓储模式的工作单元进行了封装,常用的CURD方法和API统一返回的模型。

    3、依赖注入,原来部分使用了Autofac属性注入,后来发现使用Lazy<>冲突,将全部采用DI构造函数方式注入服务。

    4、封装配置项绑定绑定,支持ChangeToken.OnChange热更新。

    5、添加服务,服务端缓存、CORS、ORM、Lazy懒加载、AutoMapper、认证、授权、异常处理。

    6、添加Swagger,添加Jwt身份认证,模型验证结果格式化。

    7、封装了Serilog日志组件。

    8、封装了MemoryCache缓存

    9、封装了Auth.Jwt身份认证。

    10、封装了yrjw.CommonToolsCore常用类库扩展包。

    11、MVC项目中使用WebApiClient组件,调用WebApi接口数据。

     73dd5cf412ba9865e051cab6350bbe0e.png

     看到这必须上代码了,框架源码下载地址:

     https://gitee.com/h_gxi/StudentManageSystem.git

    使用说明:

    1、Star源代码或下载压缩包后,使用VS2019打开解决方案,如上图。

    2、设置数据库连接字符串,Student.Core.API项目中找到配置文件进行修改,创建数据库前右键重新生成项目。

    3、进入Student.Core.API项目根目录下,执行CMD命令。

    使用dotnet ef命令,必须安装dotnet-ef,依次执行命令:
    展开全文
  • 项目名称:学生信息管理系统1.0后台框架:.Net Core 3.1 + EF Core yrjw.ORM.Chimp前端框架:ASP.NET Core MVC + LayUI + Bootstrap数据库:MS-SQL 或 MySQL 二选一迁移方式:Code First (推荐CLI命令初始化数据库和...

    项目名称:学生信息管理系统1.0

    后台框架:.Net Core 3.1 + EF Core    yrjw.ORM.Chimp

    前端框架:ASP.NET Core MVC  +  LayUI + Bootstrap

    数据库:MS-SQL 或 MySQL 二选一

    迁移方式:Code First  (推荐CLI命令初始化数据库和升级)

    框架结构图:

    项目需求: (_PS:不包含业务,只为展示开发案例。)

    登录模块案例,用户名密码验证码登录方式—— 已完成。

    配置信息设置案例,存库方式——已完成。

    操作员管理案例,简单CURD操作——已完成。

    部门管理案例,简单CURD操作——已完成。

    学生信息查询案例,简单CURD操作——已完成。

    框架封装介绍:

    1、简单三层架构,前后端分离模式,目前采用Layui前端组件的MVC框架,后面可直接替换UI层VUE等前端框架。

    2、集成了之前发布的yrjw.ORM.Chimp包,该组件只是将EF Core使用仓储模式的工作单元进行了封装,常用的CURD方法和API统一返回的模型。

    3、依赖注入,原来部分使用了Autofac属性注入,后来发现使用Lazy<>冲突,将全部采用DI构造函数方式注入服务。

    4、封装配置项绑定绑定,支持ChangeToken.OnChange热更新。

    5、添加服务,服务端缓存、CORS、ORM、Lazy懒加载、AutoMapper、认证、授权、异常处理。

    6、添加Swagger,添加Jwt身份认证,模型验证结果格式化。

    7、封装了Serilog日志组件。

    8、封装了MemoryCache缓存。

    9、封装了Auth.Jwt身份认证。

    10、封装了yrjw.CommonToolsCore常用类库扩展包。

    11、MVC项目中使用WebApiClient组件,调用WebApi接口数据。

    看到这必须上代码了,框架源码下载地址:

    https://gitee.com/h_gxi/StudentManageSystem.git

    使用说明:

    1、Star源代码或下载压缩包后,使用VS2019打开解决方案,如上图。

    2、设置数据库连接字符串,Student.Core.API项目中找到配置文件进行修改,创建数据库前右键重新生成项目。

    3、进入Student.Core.API项目根目录下,执行CMD命令。

    使用dotnet ef命令,必须安装dotnet-ef,依次执行命令:

    dotnet tool install -g dotnet-ef

    dotnet tool update -g dotnet-ef

    通过数据库迁移命令自动生成数据库表:

    dotnet ef database update

    看到这里数据库已生成成功了。

    4、设置启动项目,将Student.Core.API和StudentManageSystem设为启动。

    5、F5启动,就可以访问项目了,初始用户名:admin  密码:123456

    笔记:

    下面总结下本人在学习.net core中遇到的一些问题,以及整个框架搭建的思路和使用开发教程。

    去年刚接触.net core时发布过一篇关于.Net Core2.2 + EF Core + DI,三层框架项目搭建教程,当初想法比较简单框架也不够成熟,通过一年的学习与积累重新搭建了这套框架,一套比较完整的单应用系统,适用于中小型项目的开发应用。

    起初构建框架思想是,前后端分离、微服务架构、负载均衡、分库分表,解决高并发需求为目的,但后来学习微服务架构中遇到了一些问题,再就是前端VUE初学阶段,直接使用Vue.js UI等框架有些吃力,最终选择先采用MVC版代替UI层进行过度一下,关于微服务这块本人一直在学习过程中,搭建微服务架构还需要一点点时间吧,先来个单应用程序部署,对于小项目来说也是最佳选择不是吗。

    先说说本次框架都有哪些改变,由之前的.Net Core2.2直接升级采用最新版.Net Core3.1开发,ORM框架还是采用官方的EF Core(为什么选他就不多纠结了,只为学习目的,后期也可能会换成其他轻量级框架如FreeSQL),使用ORM工作单元的封装也不需要自己单独弄了,我找了一个封装好的组件包进行了升级和改造,并支持了.Net Core3.1版,组件包开源地址yrjw.ORM.Chimp,使用的是Code First模式,后期维护可以使用数据库迁移工具命令,也可以单独sql脚本进行维护。当前项目中虽然没用VUE.js,但还是按前后端分离模式做的,多了MVC项目代替UI层(StudentManageSystem),所有业务实现都是通过WebApi接口获取数据。框架封装除了ORM使用方便以外,Service层也有封装基类方法,减少CURD操作重复代码量。

    这一年公司事情诸多,各种个性化项目对接,只能是在忙里偷闲弄这项目可能存在有些细节未处理到位的地方,其实这套框架早在几个月前已经差不多封装好了,一直没有时间去整理文档,刚抽空整理文档也做了些优化和完善了部分功能。这项目不存在任何业务逻辑,除了登录模块其他的都按开发案例作参考。

    框架源代码已经发布出来了,有喜欢的朋友点赞,不喜欢的也欢迎拍砖,在这里就不贴代码了,我的单应用程序框架基本就是这个样子的,有不明白的地方可以留言我,我会抽空回复的。

    往期

    精彩

    回顾

    展开全文

空空如也

空空如也

1 2 3 4 5 6
收藏数 112
精华内容 44
关键字:

layuimvc后台管理