通用查询扩展报表的相关使用规范

相关介绍

我们的业务组件中已经实现了通用查询的功能,现在需要在该功能的基础上能够动态的去修改查询条件/工具栏/展示的结果等组件。 因此在原有的通用查询的基础上添加了能够扩展的功能,这篇文档主要是介绍如何基于通用查询模板去写报表

# 查询条件扩展

# Case1,重写查询条件展示

内置的查询条件展示形式有:text-input / number-input / select / date-picker / datetime-picker / switch,<query>组件使用查询模板配置的展示组件(不配置时根据条件的数据类型选用合适的展示组件)。如果业务需要其他的展示形式,则需要手写代码重写查询条件的展示。请看以下示例:

# 重写前的截图

重写前的截图

# 重新【车型】条件

<template>
  <query
    ref="query"
    <!-- 查询模板code -->
    custom-template-code="nio_part_project_QT_list"
    <!-- 条件初始化函数 -->
    :condition-init-fun="conditionInitFunc"
  >
    <!-- 通过作用域插槽暴露query内部的条件定义数据给业务页面使用 -->
    <template v-slot:condition="{ item }">
      <!-- 自定义的车型选择组件 -->
      <tag-list v-if="item.alias==='model'" v-model="item.value" :list="item.selectList"
        @on-change="onSelectedModelChanged"
      />
    </template>
  </query>
</template>

<script>
import TagList from './tagList.vue'
export default {
  components: {
    TagList
  },
  methods: {
    // 条件初始化方法
    conditionInitFunc (current, conditions) {
      // 是车型条件时
      if (current.alias === 'model') {
        // 不使用默认条件(原始的输入框);如果为true,则同时显示默认展示和自定义展示。
        current.useDefaultSlot = false
        // selectList是下拉框的选项定义,实际项目中可通过接口获取等。
        current.selectList = [{ label: 'ES1', value: 'ES1' }, { label: 'ES8', value: 'ES8' }]
      }
    },
    // 车型选中事件的处理函数
    onSelectedModelChanged () {
      // 调用query内部的refresh方法刷新数据---已经使用了选中的车型作为查询条件
      this.$refs.query.refresh()
    }
  }
}
</script>

代码说明

<query>是查询模板的前端实现组件,它通过Vue的作用域插槽(scoped slot)将查询模板中的条件定义暴露给query的调用者,使其能在具体的业务场景中对条件做个性化展示。如图所示,我们自定义了一个tag-list组件,替代默认的车型输入框。

同时还要传入 condition-init-fun 定义,即组件的初始化函数。

车型选中改变时,需要调用组件内部的刷新方法刷新表格

tag-list组件定义

<template>
  <ul class="tag-list">
    <li v-for="option in list" :key="option.value"
      :class="['tag', {'tag__checked': value === option.value}]"
      @click="chooseTag(option.value)"
    >{{option.label}}</li>
  </ul>
</template>
<script>
export default {
  name: 'tag-list',
  props: {
    disabled: Boolean,
    list: { type: Array, default: () => ([]) },
    value: String
  },
  methods: {
    chooseTag (value) {
      this.$emit('input', value)
      this.$emit('on-change', value)
    }
  }
}
</script>

代码说明

需要注意 chooseTag 方法的写法,这样写可以将选择的车型更新到组件内部的条件数据,方便调用组件内部的刷新方法刷新表格,而无需关注车型如何传值。

# 重写后的截图

重写前的截图

# Case2,重设条件宽度

场景说明

继续使用Case1的场景说明问题,如果车型选项较多,默认的宽度展示不全,会导致条件换行显示。见下图

# 重设前的截图

重设前的截图

# 宽度设置代码

<template>
  <query
    ...
    :condition-item-col="conditionItemCol"
  />
</template>
<script>
export default {
  ...
  data () {
    return {
      conditionItemCol: {
        // 条件区域宽度采用24等分,每个条件默认宽度为 6,以下代码将车型条件的宽度增加到12,即占行宽的50%
        model: 12
      }
    }
  }
}
</script>

# 工具栏扩展

# 结果展示为报表

# 截图示例

# 操作步骤及规范

# 1. 通用查询模板配置

注意:

红色框为必填项

配置切换组件的信息json字段如下:

{
  "templateReplace": {
    "condition": "",                     // 用来替换查询条件翻译组件的全局组件名称,为空时使用通用查询内置的转换组件
    "toolBar": "",                       // 用来替换工具栏的全局组件名称;如果不填时,默认使用 src/common/components/query/reportToolbarTemplate.vue注册的全局组件,ReportToolbarTemplate进行翻译
    "resultDisplay": "resultDisplay1",   // 查询结果展示组件,默认是table,只有指定了这个报表的功能才能实现
    "queryConditionAreaComp": ""         // 替换整个查询区域的全局组件名称
  },
  "appendConditions": [                 // 针对toolBar实现动态可配置逻辑,即作为参数信息结合ReportToolbarTemplate动态逻辑生成第二行查询条件
    {                                   // 以下是一个简单的例子,你可以针参数信息据自己补充其中的逻辑
      "key": "user",                    // 注意:value为默认显示的值,且必须
      "label": {
        "zh_CN": "用户名",
        "en_US": "Username"
      },
      "component": "radio-button",
      "options": [
        {
          "option_key": "张三",
          "option_desc": "张三"
        },
        {
          "option_key": "李四",
          "option_desc": "李四"
        }
      ],
      "value": ""
    },
    {
      "key": "tag",
      "label": {
        "zh_CN": "标签",
        "en_US": "Tag"
      },
      "component": "radio-button",
      "value": "工作日",
      "options": [
        {
          "option_key": "工作日",
          "option_desc": "工作日"
        },
        {
          "option_key": "周末",
          "option_desc": "周末"
        }
      ]
    },
    {
      "key": "date",
      "label": {
        "zh_CN": "时间段",
        "en_US": "Date Period"
      },
      "component": "daterange",
      "value": "lastWeek",
      "options": [
        {
          "option_key": "工作日",
          "option_desc": "工作日"
        },
        {
          "option_key": "周末",
          "option_desc": "周末"
        }
      ]
    }
  ]
}

# 2. toolBar组件配置

注意:

每一个form-item上需要绑定一个值改变时的事件changeConditions,从而通过this.$emit('append-conditions', {user, date, tag})将改变的值通知给query组件,进而达到刷新的效果;
append-conditions:这个事件名称固定,且不可变;用来toolbar组件与query组件的通信
conditions:用来接收第一行查询条件的搜索结果集,从而处理动态逻辑

<template>
  <div class="toolBar1 row">
    <div class="item" v-show="userList.length > 0">
      <RadioGroup v-model="user" type="button" @on-change="changeConditions">
        <Radio :label="item" :key="item" v-for="item in userList"></Radio>
      </RadioGroup>
    </div>
    <div class="item">
      <RadioGroup v-model="tag" type="button" @on-change="changeConditions">
        <Radio :label="item" :key="item" v-for="item in tagLists"></Radio>
      </RadioGroup>
    </div>
    <div class="item">
      <DatePicker placeholder="时间段" v-model="date" type="daterange" placement="bottom-end" style="width: 200px" @on-change="changeConditions"></DatePicker>
    </div>
  </div>
</template>

<script>
import dateUtil from 'UTILS/dateUtil'
export default {
  name: 'toolBar1',
  props: {
    conditions: Object                                                      // 接收查询条件的动态输入或者选择值
  },
  watch: {
    conditions: {
      handler (val) {
        let depObj = val.find(item => item.key === 'department')            // 监听有用的值,然后与本组件动态联动
        if (depObj) {
          this.dep = depObj.value[0]
        }
      },
      deep: true
    }
  },
  data () {
    return {
      dep: '',
      user: '',
      tag: '工作日',
      date: [],
      userLists: {
        OD: ['张三', '李四'],
        SD: ['王五']
      },
      tagLists: ['工作日', '周末']
    }
  },
  computed: {
    userList () {
      return this.userLists[this.dep] || []
    }
  },
  methods: {
    changeConditions () {
      let {user, date, tag} = this
      let formated = date.map(d => d ? dateUtil.format(new Date(d), 'yyyy-MM-dd') : '').join('~')
      date = formated === '~' ? '' : formated
      this.$emit('append-conditions', {user, date, tag})                        // 通过事件append-conditions通知query组件去将这些数据拼接到查询通用查询的查询条件中去,并且发出请求功能
    }
  },
  mounted () {
    this.changeConditions()
  }
}
</script>

# 3. resultDisplay组件配置

注意:

参数options用来接收通用查询的相关信息,包括列的定义,查询的结果数据;你需要用到的就是options.cData,即查询出来的最新数据来渲染你的报表

<template>
  <div class="query-bar-wrapper">
    <v-chart :options="chartOpt" ref="echarts" auto-resize></v-chart>
  </div>
</template>

<script>
  import ECharts from 'vue-echarts/components/ECharts'
  import 'echarts/lib/chart/bar'
  import 'echarts/lib/component/tooltip'
  import 'echarts/lib/component/legend'
  import 'echarts/lib/component/toolbox'
  import 'echarts/lib/component/title'

  export default {
    // 柱状图
    name: 'resultDisplay1',
    components: {
      'v-chart': ECharts
    },
    props: {
      options: {                                      // 接收query组件返回的一些参数,包括cData(即后台返回的查询结果)
        type: Object,
        default: () => {}
      }
    },
    mounted () {
      this.resetOptions(this.options)
    },
    watch: {
      options: {
        handler (opts) {
          this.resetOptions(opts)
          if (this.$refs.echarts) {
            opts.loadingSpinner ? this.$refs.echarts.showLoading() : this.$refs.echarts.hideLoading()
          }
        },
        immediate: true,
        deep: true
      }
    },
    data () {
      return {
        chartOpt: {
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'shadow'
            }
          },
          title: {
            text: '',
            x: 'center'
          },
          legend: {
            data: [],
            top: 30
          },
          toolbox: {
            show: true,
            orient: 'vertical',
            left: 'right',
            top: 'center',
            feature: {
              mark: {show: true},
              dataView: {show: true, readOnly: false},
              magicType: {show: true, type: ['stack', 'tiled', 'pie']},
              restore: {show: true},
              saveAsImage: {show: true}
            }
          },
          calculable: true,
          xAxis: [
            {
              type: 'category',
              axisTick: {show: false},
              data: []
            }
          ],
          yAxis: [
            {
              type: 'value'
            }
          ],
          series: []
        }
      }
    },
    methods: {
      resetOptions (opts) {
        let legendData = []
        let xAxisData = []
        let series = []

        const {cCols, cData} = opts
        cCols.forEach((col) => {
          if (!['selection', 'radio', 'index'].includes(col.type)) {
            // 排除特殊类型
            legendData.push(col.title)
            if (col.groupby) {
              xAxisData = cData.map(row => row[col.key])
            } else {
              series.push({
                name: col.title,
                type: 'bar',
                data: cData.map(row => row[col.key])
              })
            }
          }
        })

        if (this.chartOpt) {
          this.chartOpt.legend.data = legendData
          this.chartOpt.xAxis[0].data = xAxisData
          this.chartOpt.series = series
          // this.chartOpt.title.text = i18nName
        }
      }
    }
  }
</script>

# 4. query组件demo实例

在目录下src/modules/demo/query-report-extend有demo例子[只需要简单的指定code就行]

<template>
  <div slot="work-area" class="row">
    <div class="col-sm-12 col-md-12 col-lg-12">
      <query
        :customTemplateCode="customTemplateCode"
        :conditionsDefaultValue="conditionsDefaultValue"
        :showOperation="false"
        :contextPath="contextPath">
      </query>
    </div>
  </div>
</template>

<script>
  export default {
    name: '问题统计报表',
    data () {
      return {
        customTemplateCode: 'apiindex561-433',                // 配置的code参数
        conditionsDefaultValue: {                             // 查询条件的默认初始值
          issueSource: 'Body_CQA',
          vehicleModelId: 9
        },
        contextPath: 'cqa.report-service'                     // 替换掉通用查询默认的查询数据接口地址前缀
      }
    },
    methods: {
    },
    computed: {
    }
  }
</script>