# 1、初步写一个渲染公共模版的方法

# ① 简单过渡

app/extend/context.js代码

//渲染公共模版
async renderTemplate(params = {}) {
   //await this.render('admin/manager/index.html', params);
  await this.render('admin/common/template.html', params);
},

/app/controller/admin/manager.js

  //创建管理员列表页面
  async index(){
    const { ctx,app } = this;
    //分页:可以提炼成一个公共方法page(模型名称,where条件,其他参数options)
    let data = await ctx.page('Manager');
    // await ctx.render('admin/manager/index.html',{
    //   data
    // });
    await ctx.renderTemplate({
      title: '管理员列表',
      data
    });
  }

/app/view/admin/layout/main_app.html主布局栏目名称

<div class="page-header">
    <div class="row align-items-center">
        <div class="col">
            <h3 class="page-title">{{title}}</h3>
            <ul class="breadcrumb">
                <li class="breadcrumb-item"><a href="/admin">后台首页</a></li>
                <li class="breadcrumb-item active">{{title}}</li>
            </ul>
        </div>
    </div>
</div>

# ② 新建公共模版

新建 app/view/admin/common/template.html 模版引擎语法 if:https://nunjucks.bootcss.com/templating.html#if

# 1. 自定义表格上面的按钮和表格头部

/app/controller/admin/manager.js

  //创建管理员列表页面
  async index(){
    const { ctx,app } = this;
    //分页:可以提炼成一个公共方法page(模型名称,where条件,其他参数options)
    let data = await ctx.page('Manager');
    await ctx.renderTemplate({
      title: '管理员列表', //现在网页title,面包屑导航title,页面标题
      data,
      tempType:'table', //模板类型:table表格模板 ,form表单模板
      table:{
          //表格上方按钮,没有不要填buttons
          buttons: [
              {
                  url: '/admin/manager/create',//新增路径
                  desc: '新增管理员',//新增 //按钮名称
                  // icon: 'fa fa-plus fa-lg',//按钮图标
              }
          ],
          //表头
          columns: [
             {
              title: 'ID',
              key:'id',
             },
             {
                 title: '管理员账号',
                 key:'username',
             },
             {
                title: '创建时间',
                key:'create_time',
             },
          ],
      }
    });
  }

app/view/admin/common/template.html 公共模版

{# 申明继承一下主布局 #}
{% extends "admin/layout/main_app.html" %}

{% block title %}
{{title}}
{% endblock %}

{# 对某个区块进行重新编写代码 #}
{% block main %}

{# {{ data }} #}

{# {% for item in data %}
    {{ item.username }} <br/>
{% endfor %} #}


<div class="card card-table">

    {# 表格上面按钮 #}
    {% if table.buttons %}
    <div class="card-header">
        {% for item in table.buttons %}
           <a href="{{item.url}}" class="btn btn-outline-primary">{{item.desc}}</a>
        {% endfor %}
    </div>
    {% endif %}
    
    <div class="card-body">
        <div class="table-responsive">
            {% if table %}
            <table class="table table-hover table-center mb-0">
                {# 表头 #}
                <thead>
                    <tr>
                        {# <th>管理员账号</th>
                        <th width="120">创建时间</th>
                        <th class="text-center" width="100">操作</th> #}
                        {% for item in table.columns %}
                           <th>{{item.title}}</th>
                        {% endfor %}
                    </tr>
                </thead>
                <tbody>
                    {% for item in data %}
                    <tr>
                        {% for item2 in table.columns %}
                           <td>{{ item[item2.key] }}</td>
                        {% endfor %}

                        {# <td>
                            <h2 class="table-avatar">
                                <a href="#" class="avatar avatar-sm mr-2">
                                    <img
                                        class="avatar-img rounded-circle"
                                        src="{{item.avatar}}"
                                        alt="User Image"></a>
                                <a href="#">{{ item.username }}
                                    <span>管理员</span></a>
                            </h2>
                        </td>
                        <td>{{ item.create_time }}</td>
                        <td class="text-right">
                            <div class="actions">
                                <a href="#"
                                    class="btn btn-sm bg-success-light mr-2">
                                    <i class="fe fe-pencil"></i> 修改
                                </a>
                                <a href="#" class="btn btn-sm bg-danger-light">
                                    <i class="fe fe-trash"></i> 删除
                                </a>
                            </div>
                        </td> #}
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
            {% endif %}
        </div>
    </div>

    <div class="card-footer d-flex justify-content-center">
         {# 安全起见,egg.js官方默认将代码过滤成字符串,这里我们采用过滤器进行解码 #}
        {{ ctx.locals.pageHtml | safe }}
    </div>
</div>
{% endblock %}

# 2. 自定义表格头部的操作【修改、删除】部分

/app/controller/admin/manager.js

 //创建管理员列表页面
  async index() {
    const { ctx, app } = this;
    //分页:可以提炼成一个公共方法page(模型名称,where条件,其他参数options)
    let data = await ctx.page('Manager');
    //渲染公共模版
    await ctx.renderTemplate({
      title: '管理员列表',//现在网页title,面包屑导航title,页面标题
      data,
      tempType: 'table', //模板类型:table表格模板 ,form表单模板
      table: {
        //表格上方按钮,没有不要填buttons
        buttons: [
          {
            url: '/admin/manager/create',//新增路径
            desc: '新增管理员',//新增 //按钮名称
            // icon: 'fa fa-plus fa-lg',//按钮图标
          }
        ],
        //表头
        columns:[
           {
             title:'管理员账号',
             key:'username',
             class: 'text-left',
           },
           {
            title:'创建时间',
            key:'create_time',
            class: 'text-center', //可选
            width: 220, //可选
           },
           {
            title:'操作',
            class: 'text-right',
            action: {
                // 修改
                edit: function (id) {
                    return `/admin/manager/edit/${id}`
                },
                // 删除
                delete: function (id) {
                    return `/admin/manager/delete/${id}`
                },
            }
           },
        ],
      },
    });
  }

app/view/admin/common/template.html 公共模版

...
<thead>
    {# 表头 #}
    <tr>
        {# <th>管理员账号</th>
        <th width="120">创建时间</th>
        <th class="text-center" width="100">操作</th> #}
        {% for item in table.columns %}
            <th class="{{item.class}}" width="{{item.width}}">{{item.title}}</th>
        {% endfor %}

    </tr>
</thead>
...
<tbody>
    {% for item in data %}
    <tr>
        {% for item2 in table.columns %}
            {% if item2.key %}
            {# 如果存在key,说明是渲染数据 #}
            <td class="{{item2.class}}" width="{{item2.width}}">{{ item[item2.key] }}</td>
            {% elif item2.action %}
            {# 如果存在action 说明是操作 #}
            <td class="{{item2.class}}" width="{{item2.width}}">
                <div class="actions">
                    {# 存在修改按钮 #}
                    {% if item2.action.edit %}
                    <a href="{{item2.action.edit(item.id)}}"
                        class="btn btn-sm bg-success-light mr-2">
                        <i class="fe fe-pencil"></i> 修改
                    </a>
                    {% endif %}
                    {# 存在删除按钮 #}
                    {% if item2.action.delete %}
                    <a href="{{item2.action.delete(item.id)}}" 
                        class="btn btn-sm bg-danger-light">
                        <i class="fe fe-trash"></i> 删除
                    </a>
                    {% endif %}
                </div>
            </td>  
            {% else %}
            {# 其他情况 #}
            {% endif %}
        {% endfor %}
    </tr>
    {% endfor %}
</tbody>
...

# 3. 处理表格模版、表单模版(创建管理员表单公共模版为例)

可将表单模版,表格模版分别放在新的模版页面方便管理

  1. /app/controller/admin/manager.js 控制器
  //创建管理员---创建页面表单
  async create() {
    const { ctx } = this;
    //await ctx.render('admin/manager/create.html');
    //渲染公共模版
    await ctx.renderTemplate({
      title: '创建管理员',//现在网页title,面包屑导航title,页面标题
      tempType: 'form', //模板类型:table表格模板 ,form表单模板
      form:{
          //提交地址
          action:"/admin/manager/save",
          //字段配置
          fields:[
              {
                label:'管理员账号',
                type: 'text',
                name: 'username',
                placeholder: '请输入管理员账号',
              },
              {
                label:'管理员密码',
                type: 'password',
                name: 'password',
                placeholder: '请输入管理员密码',
              },
              {
                label:'管理员头像',
                type: 'file',
                name: 'avatar',
              }
          ],
      }
    });
  }
  1. app/view/admin/layout/_table.html 表格模版
<div class="card card-table">
    {# 表格上面的按钮 #}
    {% if table.buttons %}
    <div class="card-header">
        {% for item in table.buttons %}
        <a href="{{ item.url}}" class="btn btn-outline-primary">{{item.desc}}</a>
        {% endfor %}
    </div>
    {% endif %}
    
    <div class="card-body">
        <div class="table-responsive">
            <table class="table table-hover table-center mb-0">
                <thead>
                    {# 表头 #}
                    <tr>
                        {# <th>管理员账号</th>
                        <th width="120">创建时间</th>
                        <th class="text-center" width="100">操作</th> #}
                        {% for item in table.columns %}
                            <th class="{{item.class}}" width="{{item.width}}">{{item.title}}</th>
                        {% endfor %}

                    </tr>
                </thead>
                <tbody>
                    {% for item in data %}
                    <tr>
                        {% for item2 in table.columns %}
                        {% if item2.key %}
                        {# 如果存在key,说明是渲染数据 #}    
                        <td class="{{item2.class}}" width="{{item2.width}}">{{ item[item2.key] }}</td>
                        {% elif item2.action %}
                        {# 如果存在action 说明是操作 #}   
                        <td class="{{item2.class}}" width="{{item2.width}}">
                            <div class="actions">
                                {# 存在修改按钮 #}
                                {% if item2.action.edit %}
                                <a href="{{item2.action.edit(item.id)}}" 
                                    class="btn btn-sm bg-success-light mr-2">
                                    <i class="fe fe-pencil"></i> 修改
                                </a>    
                                {% endif %}
                                {# 存在修改删除 #}
                                {% if item2.action.delete %}
                                <a href="{{item2.action.delete(item.id)}}" 
                                    class="btn btn-sm bg-danger-light">
                                    <i class="fe fe-trash"></i> 删除
                                </a>
                                {% endif %}
                            </div>
                        </td>
                        {% else %}
                        {# 其他情况 #}    
                        {% endif %}
                           
                        {% endfor %}
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>

    <div class="card-footer d-flex justify-content-center">
         {# 安全起见,egg.js官方默认将代码过滤成字符串,这里我们采用过滤器进行解码 #}
        {{ ctx.locals.pageHtml | safe }}
    </div>
</div>
  1. app/view/admin/layout/_form.html 表单模版
<div class="card">
    <div class="card-body">
        {% if form %}
        <form action="{{form.action}}" method="post">
            {% for item in form.fields %}
            <div class="form-group row">
                <label class="col-form-label col-md-2">{{item.label}}</label>
                <div class="col-md-10">
                    <input type="{{item.type}}" class="form-control" name="{{item.name}}"  placeholder="{{item.placeholder}}...">
                </div>
            </div>
            {% endfor %}
            {# <div class="form-group row">
                <label class="col-form-label col-md-2">管理员账号</label>
                <div class="col-md-10">
                    <input type="text" class="form-control" name="username" placeholder="管理员账号...">
                </div>
            </div>
            <div class="form-group row">
                <label class="col-form-label col-md-2">管理员密码</label>
                <div class="col-md-10">
                    <input type="password" class="form-control" name="password"  placeholder="管理员密码...">
                </div>
            </div>
            <div class="form-group row">
                <label class="col-form-label col-md-2">管理员头像</label>
                <div class="col-md-10">
                    <input class="form-control" type="file" name="avatar">
                </div>
            </div> #}
            
            <div class="text-right mt-3">											
                <button type="submit" class="btn btn-primary">提 交</button>
            </div>
        </form>   
        {% endif %}
    </div>
</div>   
  1. app/view/admin/common/template.html 公共模版判断(这部分可以写进主模版 main_app.html)
{# 申明继承一下主布局 #}
{% extends "admin/layout/main_app.html" %}

{% block title %}
{{title}}
{% endblock %}

{# 对某个区块进行重新编写代码 #}
{% block main %}

{% if tempType === 'table' %}
    {% include "admin/layout/_table.html" %}
{% endif %}
{% if tempType === 'form' %}
    {% include "admin/layout/_form.html" %}
{% endif %}

{% endblock %}

# ③ 扩展公共模版功能,如公共模版表格中的删除管理员功能,并封装一个提示消息组件

  1. 删除管理员方法: /app/controller/admin/manager.js 控制器
  //删除管理员功能
  async delete(){
    const { ctx, app } = this;
    const id = ctx.params.id;
    await app.model.Manager.destroy({
       where:{
          id:id
       }
    });
    //提示
    ctx.toast('删除成功','success');
    //跳转
    ctx.redirect('/admin/manager/index');
  }
  1. 定义路由 app/router/admin.js
   //删除管理员
   router.get('/admin/manager/delete/:id', controller.admin.manager.delete);
  1. 消息提示方法 app/extend/context.js 用到的知识点: egg.js项目: Cookie 与 Session
  //消息提示
  toast(msg,type='danger'){
     this.cookies.set('toast',JSON.stringify({
       msg:msg,
       type:type
     }),{
        maxAge:1500, //1.5秒后失效
        encrypt:true // 中文提示要加密,因此这里加密
     });
  }
  1. 消息提示的信息合并进公共模版中 app/extend/context.js
  //渲染公共模版
  async renderTemplate(params) {
    //获取cookie中的消息提示内容 toast
    let toast = this.cookies.get('toast',{
       //中文需要解密
       encrypt:true
    });
    //合并到参数中
    params.toast = toast ? JSON.parse(toast) : null;
    
    return await this.render('admin/common/template.html',params);
  },
  1. 考虑到消息提示整个后台都可以用,将消息提示代码放进主布局模版 app/view/admin/layout/main_app.html
...
<script src="/public/assets/js/vue.2.7.0.min.js"></script>
<script src="/public/assets/js/vue.component.js"></script>
<body>
		<!-- Main Wrapper -->
		<div class="main-wrapper" id="vueapp">

			{# 提示组件 #}
			<toast ref="toast"></toast>
....
        <!-- Custom JS -->
		<script src="/public/assets/js/script.js"></script>


		{% block js %}
        <script>
			var Vueapp = new Vue({
                el: '#vueapp',
			});
		</script>    
        {% endblock %}
        
		<script>
			// 调用组件
			var toast = "{{ toast.msg}}";
			if(toast && Vueapp && Vueapp.$refs.toast){
                Vueapp.$refs.toast.show({
					msg:'{{ toast.msg }}',
					type:'{{ toast.type }}'
				});
			}
		</script>
  1. 新建 app/public/assets/js/vue.component.js 文件,专门放vue的组件代码
    知识点:
    1.Vue组件
    2.bootstrap框架提示框组件: 信息提示框
// 自定义组件

// 消息提示组件
Vue.component('toast', {
    template:`
        <div class="alert" style="position: fixed;top: 70px;right: 0px;z-index: 10000;"
         v-if="toast" :class="classes">
            {{ msg }}
        </div>
    `,
    data() {
        return {
            msg:"",
            toast:false,
            timer:null,
            type:"danger"
        }
    },
    computed:{
        classes:function(){
            return 'alert-'+this.type;
        }
    },
    methods: {
        show:function(options){
           this.msg = options.msg || '提示';
           this.type = options.type || 'danger';
           this.toast = true;
           if(this.timer){
               clearTimeout(this.timer);
           }
           this.timer = setTimeout(()=>{
               this.hide();
               this.timer = null;
           }, options.delay ||  1500);
        },
        hide:function(){
            this.toast = false;
        },
    }
});

# ④ 扩展公共模版功能,如公共模版表格中的删除管理员功能,在删除的时候,提示确认删除弹出框组件

  1. app/public/assets/js/vue.component.js 自定义弹出框组件

# bootstrap4菜鸟教程 bootstrap4菜鸟教程模态框

//弹出框组件
Vue.component('confirm', {
    template:`
    <div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalCenterTitle">{{title}}</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    {{content}}
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-dismiss="modal" @click="hide">取消</button>
                    <button type="button" class="btn btn-primary" @click="confirm">确定</button>
                </div>
            </div>
        </div>
    </div>
    `,
    data(){
        return {
            title:'提示',
            content:'提示内容',
            success:null
        }
    },
    methods:{
        show(options){
            this.title = options.title || '提示';
            this.content = options.content || '提示内容';
            this.success = options.success || null;
            $('#exampleModalCenter').modal('show');
        },
        hide(){
            $('#exampleModalCenter').modal('hide');
        },
        confirm(){
            if(this.success && typeof this.success === 'function'){
                this.success();
            }
            this.hide();
        }
    }
});
  1. 主布局模版 app/view/admin/layout/main_app.html
...
<script src="/public/assets/js/vue.2.7.0.min.js"></script>
<script src="/public/assets/js/vue.component.js"></script>
<script>
	var Vueapp = new Vue({
     el: '#vueapp',
	});
</script>  
<body>
		<!-- Main Wrapper -->
		<div class="main-wrapper" id="vueapp">

			{# 提示组件 #}
			<toast ref="toast"></toast>
           {# 弹出框模态框组件 #}
			<confirm ref="confirm"></confirm>
....
        <!-- Custom JS -->
		<script src="/public/assets/js/script.js"></script>


		{% block js %}  
        {% endblock %}
        
		<script>
			// 调用组件
			var toast = "{{ toast.msg}}";
			if(toast && Vueapp && Vueapp.$refs.toast){
                Vueapp.$refs.toast.show({
					msg:'{{ toast.msg }}',
					type:'{{ toast.type }}'
				});
			}

          //调用弹出框
			// Vueapp.$refs.confirm.show({
			//	title:'确认删除',
			//	content:'您确定要删除该管理员吗?',
			//	success:function(){
			//		console.log('删除成功');
			//	}
			// });
		</script>
  1. 表格模版 app/view/admin/layout/_table.html 处理删除按钮
{# 如果存在action 说明是操作 #}   
<td class="{{item2.class}}" width="{{item2.width}}">
    <div class="actions">
        {# 存在修改按钮 #}
        {% if item2.action.edit %}
        <a href="{{item2.action.edit(item.id)}}" 
            class="btn btn-sm bg-success-light mr-2">
            <i class="fe fe-pencil"></i> 修改
        </a>    
        {% endif %}
        {# 存在修改删除 #}
        {% if item2.action.delete %}
        <a  class="btn btn-sm bg-danger-light"
            @click="del('{{item2.action.delete(item.id)}}')">
            <i class="fe fe-trash"></i> 删除
        </a>
        {% endif %}
    </div>
</td>
...

 <script>
  Vueapp = new Vue({
    el:'#vueapp',
     methods:{
        del(url){
           //调用弹出框
           this.$refs.confirm.show({
              title:'确认删除',
              content:'您确定要删除该管理员吗?',
              success:function(){
                 window.location.href=url;
                 console.log('删除成功');
              }
            });
         }
     }
 });
</script>

# ⑤ 扩展公共模版功能,如修改管理员

# 1.先完成修改管理员界面

  1. /app/controller/admin/manager.js控制器定义修改管理员界面方法,以及修改管理员数据功能方法
  //修改管理员界面
  async edit(){
    const { ctx, app } = this;
    const id = ctx.params.id;
    let data = await app.model.Manager.findOne({
      where: {
        id
      }
    });
    if(!data){
        return await ctx.pageFail('该管理员不存在');
    }
    // 将查询结果转成json字符串后转成json对象,删除密码
    data = JSON.parse(JSON.stringify(data));
    delete data.password;
    //console.log(data);

    //渲染公共模版
    await ctx.renderTemplate({
      id,
      title: '修改管理员',//现在网页title,面包屑导航title,页面标题
      tempType: 'form', //模板类型:table表格模板 ,form表单模板
      form:{
         //提交地址
         action:'/admin/manager/update/'+ id,
         //  字段
         fields:[
            {
              label:'管理员账号',
              type: 'text',
              name: 'username',
              placeholder:'请输入管理员账号',
            },
            {
              label:'管理员密码',
              type: 'password',
              name: 'password',
              placeholder:'请输入管理员密码',
            },
            {
              label:'管理员头像',
              type: 'file',
              name: 'avatar',
            }
         ],
         //由于是修改,则表单上面默认有值
         data:data,
      }
    });

  }

  //修改管理员功能
  async update(){
  }
  1. 定义路由 app/router/admin.js
  //修改管理员界面
  router.get('/admin/manager/edit/:id', controller.admin.manager.edit);
  //修改管理员功能
  router.post('/admin/manager/update/:id', controller.admin.manager.update);
  1. 没有数据的错误提示方法 app/extend/context.js
  //页面错误提示
  async pageFail(data='',code=404){
      return await this.render('admin/common/404.html',{
          data,code
      })
  },
  1. 没有数据的错误提示404页面, 新建:app/view/admin/common/404.html
<body class="error-page">
    <!-- Main Wrapper -->
    <div class="main-wrapper">
    
        <div class="error-box">
            <h1>{{code}}</h1>
            <p class="h4 font-weight-normal">{{data}}</p>
            <a href="/admin" class="btn btn-primary">返回后台首页</a>
        </div>
        
    </div>
    <!-- /Main Wrapper -->
 ....
</body>
  1. 修改管理员表单,公共模版绑定默认值 app/view/admin/layout/_form.html
...
<div class="col-md-10">
    <input type="{{item.type}}" class="form-control" name="{{item.name}}"  
    placeholder="{{item.placeholder}}..."
    value="{{form.data[item.name]}}">
</div>
...

# 2.完成修改管理员数据功能,并封装成功或者失败的api接口提示方法

涉及知识点:章节2:egg.js基础-where操作符 /app/controller/admin/manager.js控制器

  //修改管理员数据功能
  async update() {
    const { ctx, app } = this;
    // 参数验证
    ctx.validate({
      id: {
        type: 'int',
        required: true,
      },
      username: {
        type: 'string',  //参数类型
        required: true, //是否必须
        // defValue: '', 
        desc: '管理员账号' //字段含义
      },
      password: {
        type: 'string',
        // required: true,
        // defValue: '', 
        desc: '管理员密码'
      },
    });
    // 拿参数
    const id = ctx.params.id;
    const { username, password } = ctx.request.body;
    // 先看一下管理员是否存在
    const manager = await app.model.Manager.findOne({ where: { id } });
    if (!manager) {
      //return ctx.pageFail('该管理员不存在', 404);
      return ctx.apiFail('该管理员不存在');
    }
    //存在,由于管理员的账号具有唯一性,你不能修改账号的时候,修改成存在的账号
    // if (await app.model.Manager.findOne({
    //   where: {
    //     username
    //   }
    // })) {
    //   return ctx.pageFail('该管理员账号已经存在,不能修改成该管理员账号', 404);
    // }
    //也就是查询的数据id不是当前id,就是修改的当前账号除外
    const Op = app.Sequelize.Op;//拿Op,固定写法
    if (await app.model.Manager.findOne({
      where: {
        username,
        id: { 
          [Op.ne]: id 
        },
      }
    })) {
      //return ctx.pageFail('该管理员账号已经存在,不能修改成该管理员账号', 404);
      return ctx.apiFail('该管理员账号已经存在,不能修改成该管理员账号');
    }

    // 执行修改数据
    manager.username = username;
    if(password){
       manager.password = password;
    }
    await manager.save();
    // //给一个反馈
    // this.ctx.status = 200;
    // this.ctx.body = {
    //   msg: 'ok',
    //   data: '修改成功'
    // };

    ctx.apiSuccess('修改成功');

  }

app/extend/context.js 封装成功或者失败的api接口提示方法

  //api接口形式成功提示
  apiSuccess(data = '', msg = 'ok', code = 200) {
    this.body = { msg, data };
    this.status = code;
  },
  //api接口形式失败提示
  apiFail(data = '', msg = 'fail', code = 400) {
    this.body = { msg, data };
    this.status = code;
  },

# 2、优化公共表单模版

# 2.1 使用Vue中的v-model对文本框进行双向数据绑定,取消value属性

我们发现提交修改数据之后,给我们反馈的是api接口提示,最好是可以给我们返回前面的消息组件的提示,本节课我们来优化一下表单提示功能

# ① 思维引入:理解vue中的数据绑定v-model

app/view/admin/layout/_form.html表单模版

<div class="form-group row">
    <label class="col-form-label col-md-2">管理员账号</label>
    <div class="col-md-10">
        <input type="text" class="form-control" name="username" placeholder="管理员账号..." 
        {/* v-model="username" */}
        v-model="form.username"
        >
    </div>
</div>
<script>
    Vueapp = new Vue({
        el:'#vueapp',
        data(){
            return{
                {/* username:'迪丽热巴', */}
                form:{
                    username:'迪丽热巴',
                    password:'123456',
                }
            }
        },
        methods:{
            
        }
    });
</script>

# ② 模版引擎渲染出data中的字段和值

{% for item in form.fields %}
<div class="form-group row">
    <label class="col-form-label col-md-2">{{item.label}}</label>
    <div class="col-md-10">
        <input type="{{item.type}}" class="form-control" name="{{item.name}}"  
        placeholder="{{item.placeholder}}..."
        v-model="form.{{item.name}}">
    </div>
</div>    
{% endfor %}
<script>
    Vueapp = new Vue({
        el:'#vueapp',
        data(){
            return{
                /*
                form:{
                    username:'迪丽热巴',
                    password:'123456'
                }
                */
               //需要通过模版引擎拿这些值,字段名:form.fields.name 值:默认值传递过来的
               //模版引擎会最先渲染,不管你是什么代码,写完后大家可以查看源代码看到渲染后的代码
               form:{
                 {% for item in form.fields %}
                   {{item.name}} : "{{form.data[item.name]}}",
                {% endfor %}  
               },
                
            }
        },
        methods:{
            
        }
    });
</script>

# ③ 如果我们在新增表单中还希望给表单字段一个默认值

/app/controller/admin/manager.js 控制器

 //创建管理员---创建页面表单
  async create() {
    ....
    form: {
        //提交地址
        action: "/admin/manager/save",
        //  字段
        fields: [
          {
            label: '管理员账号',
            type: 'text',
            name: 'username',
            placeholder: '请输入管理员账号',
            default:'默认值测试', //新增时候默认值,可选
          },
          ...
        ],
      }
  }

则可以优化:app/view/admin/layout/_form.html表单模版
模版引擎的三元表单式:模版引擎的三元表单式(If 表达式)

<script>
    Vueapp = new Vue({
        el:'#vueapp',
        data(){
            return{
                /*
                form:{
                    username:'迪丽热巴',
                    password:'123456'
                }
                */
                //需要通过模版引擎拿这些值,字段名:form.fields.name 值:默认值传递过来的
                //模版引擎会最先渲染,不管你是什么代码,写完后大家可以查看源代码看到渲染后的代码
                //    form:{
                //      {% for item in form.fields %}
                //        {{item.name}} : "{{form.data[item.name]}}",
                //      {% endfor %}  
                //    },

                //当新增表单有默认值时候
                // 模版引擎的三元表达式语法: 
                // {{ true(真的代码) if 条件 else false(假的代码)}}
                   form:{
                     {% for item in form.fields %}
                       {{item.name}} : "{{ form.data[item.name]  if form.data[item.name] else item.default }}",
                     {% endfor %}  
                   },
                
            }
        },
        methods:{
            
        }
    });
</script>

# 2.2 完成表单提示消息功能

# ① 使用jquery的ajax来提交表单

app/view/admin/layout/_form.html表单模版

<div class="text-right mt-3">											
    <button type="submit" class="btn btn-primary" @click.stop.prevent="submit">提 交</button>
</div>
<script>
    Vueapp = new Vue({
        el:'#vueapp',
        data(){
           return {
              /*
              form:{
                username:'迪丽热巴',
              },
              */
              //需要通过模版引擎拿这些值,字段名:form.fields.name 值:默认值传递过来的
              //模版引擎会最先渲染,不管你是什么代码,写完后大家可以查看源代码看到渲染后的代码
            
              form:{
                {% for item in form.fields %}
                   {{item.name}} : "{{ form.data[item.name] if form.data[item.name] else  item.default  }}",
                {% endfor %}
              },
              
           }
        },
        methods:{
            submit(){
                // console.log('提交数据');
                $.ajax({
                    type: 'POST', //这里可以换成 GET
                    url: '{{form.action}}?_csrf={{ctx.csrf|safe}}',
                    // contentType:'application/x-www-form-urlencoded',
                    contentType:'application/json;chatset=UTF-8',
                    data:JSON.stringify(this.form),
                    success: function (response, stutas, xhr) {
                        // console.log(response)
                        Vueapp.$refs.toast.show({
                            msg:"{{'修改'  if id else '新增' }}成功",
                            type:'success',
                            delay:1000,
                            success:function(){
                                // 新增或者修改成功后,跳转到指定页面
                                window.location.href="{{successUrl}}";
                            }
                        });
                    },
                    error:function(e){
                        console.log(e);
                        Vueapp.$refs.toast.show({
                            msg:e.responseJSON.data,
                            type:'danger',
                            delay:3000
                        });
                    }
                });
            }
        }
    });
</script>

# ② 提交成功之后,重新跳转到指定页面

Vue自定义组件 app/public/assets/js/vue.component.js

// vue自定义组件

//消息提示组件
Vue.component('toast', {
    ...
    methods:{
        show:function(options){
           this.msg = options.msg || '提示';
           this.type = options.type || 'danger';
           this.toast = true;
           if(this.timer){
              clearTimeout(this.timer);
           }
           this.timer = setTimeout(()=>{
               this.hide();
               this.timer = null;
               //新增执行回调函数
               if(options.success && typeof options.success == 'function'){
                options.success();
               }
           },options.delay || 1500);
        },
        hide:function(){
            this.toast = false;
        }
    },
    ...
});

在控制器/app/controller/admin/manager.js 创建管理员---创建页面表单create()修改管理员界面edit()方法都加上成功后的跳转地址

//创建管理员---创建页面表单
  async create() {
    ...
    //渲染公共模版
    await ctx.renderTemplate({
      title: '创建管理员',//现在网页title,面包屑导航title,页面标题
      tempType: 'form', //模板类型:table表格模板 ,form表单模板
      form: {
        ...
      },
      //新增成功之后的跳转页面
      successUrl: '/admin/manager/index',
    });
  }
//修改管理员界面
  async edit() {
    ...
    //渲染公共模版
    await ctx.renderTemplate({
      id,
      title: '修改管理员',//现在网页title,面包屑导航title,页面标题
      tempType: 'form', //模板类型:table表格模板 ,form表单模板
      form: {
        ...
      },
      //修改成功之后的跳转页面
      successUrl: '/admin/manager/index',
    });

  }
更新时间: 2024年3月6日星期三晚上7点01分