zoukankan      html  css  js  c++  java
  • Vue公共结果页面实现

    需求

    我希望写一个公共结果页面,满足所有模块的结果展示,页面设计要素如下:

    1. 结果图标type(成功图标,失败图标)
    2. 标题title(如:提交成功)
    3. 描述descripton(如:您的工单已提交,请等待管理员审核)
    4. 内容content(内容不固定,样式不固定,可自定义)
    5. 操作action(提供默认按钮,可定制返回步数,具备自定义的能力)

    我希望的结果页面应该是这样的

    1. 只有一个路由页面,所有人模块跳转到当前页面展示结果。
    2. 除了typetitledescription只传入字符串即可,contentaction可扩展。

    方案

    框架采用vue,以下方案都是基于vue的实现思路

    方案一

    • 提供一个Result.vue组件
    • typetitledescription使用props传参
    • contentaction使用slot插槽进行扩展

    弊端:每个人都要自己配一个路由页面进行跳转,违背设计的初衷,只有一个路由页面满足所有结果展示的设计思路。

    方案二

    • 提供一个ResultPage.vue路由页面,所有模块都跳转到该页面
    • typetitledescription通过路由参数传递,结果页面接收参数进行展示
    • contentaction传递全局组件的标识,结果页面通过动态组件加载对应的标识

    动态组件API

    ResultPage.vue

    // 伪代码只是表达思路用
    <template>
      <div>
        <img
          v-if="$route.query.type === 'success'"
          src='success.png'
        />
        <img v-else src='fail.png'/>
      </div>
      <h1>{{ $route.query.title $}}</h1>
      <h2>{{ $route.query.description $}}</h2>
      <component 
        v-if="$route.query.content"
        is="$route.query.content"
      />
      <componet 
        v-if="$route.query.action"
        is="$route.query.action"
      >
      <button v-else>返回</button>
    </template>
    <script>
    export default {
      name: 'ResultPage'
    }
    </script>
    

    跳转示例

    <script>
    import Vue from 'vue'
    export default {
      methods: {
        // 创建全局组件,传递组件标识id字符串
        Vue.component('content', {
          template: `
            <table>
              <tr>
                <th>提交人:</th>
                <td>张三</td>
              </tr>
              <tr>
                <th>提交时间:</th>
                <td>2019-07-09</td>
              </tr>
            </table>
          `
        })
        this.$router.replace({
          name: 'ResultPage',
          query: {
            type: 'success'
            title: '提交成功',
            description: '提交等待审核',
            content: 'content'
          }
        })
      }
    }
    </script>
    

    弊端:无法模块化创建组件,无法传递组件,只能传递组件标识字符串
    优化:如果要传递组件构造器,可以用vuex传递参数

    方案三

    方案三是对方案二的优化升级,最大的难点是,怎么在路由页面间传递组件

    • 兼容方案二,路由传参+vuex传参
    • 组件支持模块化
    • 可传递局部组件
    • 组件可传参扩展

    需要了解的API

    • component 内置组件component
      • Props:
        • is - string | ComponentDefinition | ComponentConstructor
      • 用法:
        渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。
      <!-- 动态组件由 vm 实例的属性值 `componentId` 控制 -->
      <component :is="componentId"></component>
    
      <!-- 也能够渲染注册过的组件或 prop 传入的组件 -->
      <component :is="$options.components.child"></component>
    

    动态组件接收三个参数

    • string 组件标识
    • ComponentDefinition 组件定义
    • ComponentConstructor 组件构造器

    以下是对组件几个概念的示例

    // 返回组件定义
    import ComponentDefinition from './ComponentDefinition.vue'
    // 组件标识id
    const componentId = 'component-1' 
    // 返回组件构造器
     const ComponentConstructor = Vue.component(componentId, {
          data() {
            return {
              msg: '你好'
            }
          },
          template: `<h1>{{msg}}</h1>`
        })
    // 组件扩展返回组件构造器    
    const ComponentConstructor2 = Vue.extend(ComponentDefinition)
    // 创建组件实例,并给组件传递参数
    const componentInstance = new ComponentConstructor({
      propsData: {
        detail: {
          username: '张三'
        }
      }
    })
    

    如果要实现组件模块化,并可扩展就要用到以下方法
    1、导入本地组件
    2、对组件进行扩展
    3、创建组件实例,并传递propsData

    但是细心的朋友会发现,动态组件并不支持加载组件实例,组件实例需要手动挂载到页面才行

    // 创建实例
    const componentInstance = new ComponentConstructor({
      propsData: {
        detail: {
          username: '张三'
        }
      }
    })
    // 挂载实例
    componentInstance.$mount('#content');
    

    方案三要对结果页面进行扩展,支持组件模块化,在结果页面要对组件实例区分开,组件实例使用挂载的方式显示,其他用动态组件显示,好在Vue实例有_isVue属性能区分开。

    以上说明了原理,下面放出最终版本实现

    • ResultPage.vue
    <template>
      <q-content>
        <a-card
          :bordered="false"
        >
          <q-result
            :type="type"
            :title="title"
            :description="description"
          >
            <template
              v-if="!!content"
            >
              <template
                v-if="content._isVue"
              >
                <div id="content" />
              </template>
              <template
                v-else
              >
                <component
                  :is="content"
                />
              </template>
            </template>
            <template
              v-if="action"
              slot="action"
            >
              <div
                v-if="action._isVue"
                id="action"
              />
              <component
                :is="action"
                v-else
              />
            </template>
            <template
              v-else
              slot="action"
            >
              <a-button
                @click="backHandler"
              >
                {{ goTitle }}
              </a-button>
            </template>
          </q-result>
        </a-card>
      </q-content>
    </template>
    
    <script>
      import QContent from '../../components/page/QContent'
      import QResult from '../../components/result/QResult'
    
      export default {
        name: 'PageResult',
        components: { QResult, QContent },
        mixins: [],
        computed: {
          type () {
            return this.$route.query.type || this.result.type
          },
          title () {
            return decodeURIComponent(this.$route.query.title || this.result.title || '')
          },
          description () {
            return decodeURIComponent(this.$route.query.description || this.result.description || '')
          },
          content () {
            return this.$route.query.content || this.result.content
          },
          action () {
            return this.$route.query.action || this.result.action
          },
          go () {
            return this.$route.query.go || this.result.go
          },
          goTitle () {
            return this.go ? '返回' : '返回首页'
          },
        },
        beforeDestroy () {
          this.cacheResult({})
        },
        mounted () {
          // 挂载组件实例
          if (this.content && this.content._isVue) {
            this.content.$mount('#content')
          }
          if (this.action && this.action._isVue) {
            this.action.$mount('#action')
          }
        },
        methods: {
          backHandler: function () {
            if (this.go) {
              this.$router.go(this.go)
            } else {
              this.$router.replace('/')
            }
          },
        },
      }
    </script>
    
    <style scoped>
    
    </style>
    
    
    • result.js vuex传递参数
    import { RESULT_UPDATE } from '../mutation-types/result'
    
    export default {
      namespaced: true,
      state: {
        result: {},
      },
      mutations: {
        [RESULT_UPDATE] (state, obj) {
          state.result = obj
        },
      },
      getters: {
        getResult (state) {
          return state.result
        },
      },
      actions: {
        cacheResult ({ commit }, obj) {
          commit(RESULT_UPDATE, obj)
        },
      },
    }
    
    
    • result.js全局混入
    import Vue from 'vue'
    import { mapActions, mapState } from 'vuex'
    
    export default {
      computed: {
        ...mapState('result', [
          'result',
        ]),
      },
      methods: {
        ...mapActions('result', [
          'cacheResult',
        ]),
        changeResultPage: function (options = {}) {
          this.cacheResult(options)
          this.$router.replace({ name: 'pageResult' })
        },
        renderComponent: function (component, options) {
          if (options) { // 有参数
            if (typeof component !== 'string') {
              const componentConstructor = Vue.extend(component)
              return new componentConstructor(options)
            } else {
              return component
            }
          } else {
            return component
          }
        },
      },
    }
    
    
    • 模块调用
    <script>
    import TestResultContent from './components/TestResultContent'
    export default {
      methods: {
        toHandler: function() {
          this.changeResultPage({
              type: 'success',
              title: '创建成功',
              description: '使用默认方法跳转页面',
              content: this.renderComponent(TestResultContent, {
                propsData: {
                  detail: {
                    username: '张三',
                    production: 'office365',
                    datetime: '2019-09-09',
                    order: 'E128888292939293',
                    between: '2019-08-07 ~ 2022-12-11',
                    remark: '尽快购买',
                  },
                },
              })
            })
        }
      }
    }
    </script>
    
  • 相关阅读:
    svn的差异查看器和合并工具换成BCompare.exe
    Java经典编程题50道之十四
    Java经典编程题50道之十三
    Java经典编程题50道之十二
    Java经典编程题50道之十一
    Java经典编程题50道之十
    Java经典编程题50道之九
    Java经典编程题50道之八
    Java经典编程题50道之七
    Java经典编程题50道之六
  • 原文地址:https://www.cnblogs.com/iPing9/p/11390005.html
Copyright © 2011-2022 走看看