作者:陈桔涛  历史版本:1  最后编辑:龚清  更新时间:2024-11-20 15:41

说明:
本示例中的代码和截图可能和您现在手中的版本不同,但操作思路一样。

一、概述

参照PC端数据模板列表中的自定义列,将移动端的显示字段通过配置保存至后端。注意,本示例中的后端请求参照自定义列的请求方法,需要根据自身需求再判断是否需要重新编写。

二、操作及示例

2.1、涉及的代码文件

1、template-list

目录:src\views\platform\data\template-list.vue
以下代码是在3.4.4的版本的基础上进行补充的,可根据自身代码还有下列示例有选择的进行补充

<template>
  <div
    :class="{
      'ibps-fixed-toolbar':checkMode
    }"
    class="data-template-list"
  >
    <van-sticky>
      <van-nav-bar
        v-if="!hiddenAppTitle"
        :title="generateTitle(title)"
        :left-text="$t('common.button.back')"
        :right-text="hasToolbar?rightText:''"
        left-arrow
        @click-left="onClickLeft"
        @click-right="toCheckMode()"
      />
      <!-- <van-search
        v-show="$utils.isNotEmpty(queryKey)"
        v-model="search"
        :placeholder="$utils.isNotEmpty(queryName)?'请输入'+queryName:''"
        show-action
        @search="onSearch"
      >
        <template v-if="$utils.isNotEmpty(queryColumns) && queryColumns.length >1" #left>
          <div style="width:100px;">
            <ibps-select
              v-model="queryKey"
              :label="queryName"
              :options="queryColumns"
              :clearable="false"
              value-key="prop"
              @change="changeQuery"
            />
          </div>
        </template>
        <template #action>
          <van-icon name="filter-o" :class="stateActive?'active':'disactive'" @click="clickMoreSarch" />
        </template>
      </van-search> -->

      <div v-show="$utils.isNotEmpty(queryKey)" class="data-template-list-search-wrapper">
        <div class="data-template-list-search">
          <div class="data-template-list-search__select">
            <ibps-select
              v-model="queryKey"
              :label="queryName"
              label-width="0px"
              input-align="left"
              :readonly="$utils.isEmpty(queryColumns) || queryColumns.length === 1"
              :options="queryColumns"
              :clearable="false"
              value-key="prop"
              @change="changeQuery"
            />
          </div>
          <div class="data-template-list-search__content">
            <van-search
              v-model="search"
              :type="fieldType === 'number'?fieldType:'text'"
              :placeholder="$utils.isNotEmpty(queryName)?'请输入'+queryName:''"
              @search="onSearch"
            />
          </div>
          <van-icon class="data-template-list-search__icon" name="filter-o" :class="stateActive?'active':'disactive'" @click="clickMoreSarch" />

        </div>
      </div>

      <!-- 设置过滤条件 -->
      <van-row>
        <van-col :span="hiddenAppTitle?'20':'24'">
          <van-dropdown-menu class="menus" active-color="#3388FF">
            <van-dropdown-item
              v-if="$utils.isNotEmpty(rangeData[rootIndex].options)"
              v-model="checkFilter"
              :options="rangeData[rootIndex].options"
            />
            <van-dropdown-item disabled title-class="menu-slot-text" class="menu-left">
              <template #title>
                <label
                  :class="$utils.isNotEmpty(rangeData[rootIndex].options)?null:'options-null'"
                >共{{ pagination?pagination.totalCount||rootIndex :0 }}条</label>
              </template>
            </van-dropdown-item>
            <van-dropdown-item
              class="menu-right"
              @open="onShowFieldsList('fields')"
            >
              <template #title>
                <span
                  class="menu-slot-right"
                  size="small"
                >
                  <van-icon name="password-view" class="custom-icon" /> 显示字段
                </span>
              </template>
            </van-dropdown-item>
          </van-dropdown-menu>
        </van-col>
        <van-col v-if="hiddenAppTitle" span="4">
          <van-button style="height:40px;width:100%;" plain type="primary" size="small" @click="toCheckMode()">
            <template #default>
              <i class="ibps-icon ibps-icon-user">
                管理
              </i></template>
          </van-button>
        </van-col>
      </van-row>

      <!-- <drop-menu
        ref="dropMenu"
        :async="async"
        :data="rangeData"
        :default-value="defaultValue"
        @change="changeFilterCondition"
      >
        <template v-slot:checks="scope">
          <div v-if="$utils.isNotEmpty(rangeData[rootIndex].options)" class="menu-slot-checks" :style="{'color':scope.color}">{{ scope.text||filterCondition }}</div>
        </template>
        <template v-slot:icon="scope">
          <span v-if="$utils.isNotEmpty(rangeData[rootIndex].options)" class="menu-slot-icon" :style="{'color':scope.color}">
            <van-icon :name="scope.icon" />
          </span>
        </template>
        <template slot="text">
          <label class="menu-slot-text" :class="$utils.isNotEmpty(rangeData[rootIndex].options)?null:'options-null'">&nbsp;&nbsp;共 {{ pagination?pagination.totalCount||rootIndex :0 }} 条</label>
        </template>
       设置显示字段 -->
      <!-- <template slot="right">
          <span class="menu-slot-right" size="small" @click="onShowFieldsList('fields')">
            <van-icon name="password-view" class="custom-icon" /> 显示字段
          </span>
        </template>
      </drop-menu> -->
    </van-sticky>
    <!-- 列 -->
    <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
      <van-list v-model="loading" :finished="finished" @load="onLoad">
        <van-cell
          v-for="(item,index) in listData"
          :key="item[idKey]+index"
          class="ibps-p-0"
        >
          <van-swipe-cell
            :right-width="manages.length>0?(manages.length>3?3*55:manages.length*55):0"
            :on-close="onClose"
            class="template-cell"
          >
            <van-checkbox-group v-model="checkedIds">
              <van-cell
                border
                style="padding:15px 16px"
                @click="onClick(item,index)"
              >
                <template slot="icon">
                  <van-checkbox
                    v-if="checkMode"
                    ref="checkboxes"
                    :name="item"
                    class="ibps-mr-10"
                  />
                </template>
                <!--标题-->
                <template #title>
                  <field-formatter
                    class="titles"
                    :is-text="true"
                    :label-key="labelField['name']"
                    :data="item"
                    :params="dataTemplate"
                    :template-fields="templateFields"
                  />
                </template>
                <!--描述展示-->
                <template #label>
                  <template v-if="$utils.isNotEmpty(descFields)">
                    <!-- v-if="descField&&descField['name']&&item[descField['name']]" -->
                    <div
                      v-for="descField in descFields"
                      :key="descField['name']"
                      class="van-card-list__desc"
                    >
                      <span class="van-card-list__desc__title desc">{{ descField['label'] }}:</span>
                      <field-formatter
                        :label-key="descField['name']"
                        :data="item"
                        :template-fields="templateFields"
                        :params="dataTemplate"
                        class="desc"
                        default-value
                      />
                    </div>
                  </template>
                </template>
              </van-cell>
            </van-checkbox-group>
            <!--管理列-->
            <template #right>
              <template-toolbar
                :buttons="manages"
                @action-event="button => handleAction(button,item)"
              />
            </template>
          </van-swipe-cell>
          <div v-if="listData.length-1 > index" class="ibps-blank-bar" />
        </van-cell>
      </van-list>
      <ibps-list-result-page
        :result-type="resultType"
        :error-type="errorType"
        :result-message="resultMessage"
      />
    </van-pull-refresh>
    <!--添加按钮-->
    <div v-if="$utils.isNotEmpty(addButton)" v-show="!checkMode" class="palette-button">
      <van-button
        round
        type="info"
        icon="plus"
        size="large"
        style="border-radius: 100%; width: 50px;"
        @click="handleAction(addButton)"
      />
    </div>
    <!--工具栏 -->
    <ibps-toolbar
      v-show="checkMode"
      icon-prefix="ibps-icon"
      :actions="toolbarButtons"
      :more-actions="moreToolbarButtons"
      @action-event="(button)=>handleAction(button)"
    />
    <!--更多查询 -->
    <ibps-more-search
      ref="ibpsMoreSearch"
      :show="popupShow"
      :search-forms="searchForms"
      @callback="onSearch"
      @close="callback => popupShow = callback"
      @resetForm="resetForm"
    >
      <template
        v-for="(slotForm,index) in searchForms.forms"
        :slot="slotForm.slotName"
      >
        <search-field
          ref="moreSearchField"
          :key="index"
          input-align="left"
          :forms="searchForms.forms"
          :item="slotForm"
          :data="searchFormData"
        />
      </template>
    </ibps-more-search>
    <!--显示字段 -->
    <van-popup
      v-model="display"
      position="bottom"
      :overlay="true"
    >
      <ibps-picker-toolbar
        :cancel-button-text="$t('cancel') "
        :confirm-button-text=" $t('confirm')"
        @cancel="onDisplayCancel"
        @confirm="onDisplayConfirm"
      />
      <div class="ibps-popup-wrapper">
        <van-checkbox-group v-model="chooseResult">
          <van-cell-group>
            <van-cell
              v-for="(item, index) in fieldsList"
              :key="item['name']+index"
              :title="item.name"
              size="large"
            >
              <template #right-icon>
                <van-checkbox :name="item.prop" />
              </template>
              <!-- <van-checkbox :name="item.name">{{ item.name }}</van-checkbox> -->
            </van-cell>
          </van-cell-group>
        </van-checkbox-group>
      </div>
    </van-popup>

    <data-template-formrender-dialog
      ref="formrender"
      :visible="formrenderVisible"
      :params="formrenderParams"
      :button-priority="buttonPriority"
      @callback="onRefreshCallback"
      @close="visible => formrenderVisible = visible"
    />

    <!-- 流程定义选择器 -->
    <bpm-def-dialog
      v-model="sefStartFlowDialogValue"
      :visible="sefStartFlowDialogVisible"
      :form-key="formKey"
      :multiple="false"
      :is-data-template-use="true"
      @close="visible => sefStartFlowDialogVisible = visible"
      @action-event="handleStartFlowDialogActionEvent"
    />
  </div>
</template>
<script>
import { queryDataByKey, removeFormData } from '@/api/platform/data/dataTemplate'
import { loadDataTemplateById, getDataTemplateListTemplate, buildDataTemplateFields } from '@/business/platform/data/utils'
import ButtonsConstants, { hasButton } from '@/business/platform/data/constants/buttons'
import { startFlowFromList } from '@/api/platform/bpmn/bpmInst'

import TreeUtils from '@/utils/tree'
import ActionUtils from '@/utils/action'
import i18n from '@/utils/i18n'
import storage from '@/utils/storage'
import IbpsMoreSearch from '@/components/ibps-more-search'
import IbpsListResultPage from '@/components/ibps-list-result-page'
import IbpsToolbar from '@/components/ibps-toolbar'
import FieldFormatter from '@/business/platform/data/data-template/field-formatter'
import FormUtils from '@/business/platform/form/utils/formUtil'
// import JTemplate from '@/business/platform/data/utils/JTemplate' // 自定义脚本
import eventsUtil from '@/utils/eventsUtil'// 自定义脚本
import TemplateToolbar from './template-toolbar'
// import DropMenu from './components/dropMenu'
import DateFormatUtil from '@/business/platform/form/utils/dateFormatUtil'

import SearchField from './components/search-field'
import { queryConditionOptions } from './constants'

import DataTemplateFormrenderDialog from '@/business/platform/data/form/dialog'
import IbpsPickerToolbar from '@/components/ibps-picker-toolbar'
import OptionMixin from '@/mixins/fieldOption'
import FormOptions from '@/business/platform/form/constants/formOptions'

import BpmDefDialog from '@/business/platform/bpmn/definition/dialog'
import CustomDataDisplayMixin from '@/business/platform/system/mixins/customDataDisplay'

export default {
  name: 'TemplateList',
  components: {
    TemplateToolbar,
    // DropMenu,
    SearchField,
    IbpsListResultPage,
    // IbpsPaletteButton,
    IbpsToolbar,
    IbpsMoreSearch,
    FieldFormatter,
    DataTemplateFormrenderDialog,
    IbpsPickerToolbar,
    BpmDefDialog
  },
  mixins: [OptionMixin, CustomDataDisplayMixin],
  props: {
    value: {
      type: [String, Number, Boolean, Array, Object, Date]
    }
  },

  data() {
    return {
      stateActive: false,
      popupShow: false,
      resetFields: false,
      dateFormat: 'yyyy-MM-dd',
      searchForms: {},
      firsthand: [],
      params: {},
      slotForms: [],
      paramsForm: {},
      searchParams: {},
      async: false,
      rootIndex: 0,
      filterCondition: '',

      action: '',
      position: '',
      actionData: {},

      fieldsList: [],
      display: false,
      chooseResult: [],
      checkFilter: '',
      configKey: '',
      defaultValue: [],
      rangeData: [{ options: [] }],
      search: '',
      title: '',
      listData: [],
      pagination: {},
      sorts: {},

      loading: false,
      finished: false,
      refreshing: false,
      resultType: 'init',
      buttonPriority: 'outside',
      errorType: null,
      resultMessage: null,

      idKey: '',
      labelField: '',
      descFields: [],

      dataTemplate: '',

      queryColumns: [],
      queryKey: '',
      queryName: '',
      queryLabel: '',
      fieldType: '',

      isTree: false,
      templateId: '',
      templateKey: '',
      formKey: '',
      detailFormKey: '',

      templateFields: {}, // 模版字段

      editButtons: '', // 编辑按钮
      cacheEditButtons: '', // 缓存编辑按钮
      functionButtons: [], // 功能按钮

      checkMode: false,
      checkedIds: [],

      toolbars: [],
      manages: [],

      defaultToolbars: [],
      defaultManages: [],

      isInitialization: false,

      // 表单
      formrenderVisible: false,
      formrenderParams: {},

      searchFormData: {},
      queryData: {},
      queryConditionOptions,
      combinationQueryFileds: {},
      eventsUtil: {},

      sefStartFlowDialogVisible: false,
      sefStartFlowDialogValue: {},
      sefStartFlowId: ''
    }
  },
  computed: {
    listIdentity() {
      return 'ibps_data_template_tpl' + '_' + this.dataTemplate.key
    },
    hasMessage() {
      return true
    },
    hiddenAppTitle() {
      return this.dataTemplate ? this.dataTemplate.default_list.attrs.hiddenAppTitle || false : false
    },
    toolbarButtons() {
      return this.toolbars.length > 4 ? this.toolbars.slice(0, 4) : this.toolbars
    },
    moreToolbarButtons() {
      return this.toolbars.length > 4 ? this.toolbars.slice(4, this.toolbars.length) : null
    },
    hasToolbar() {
      return this.toolbars.length > 0 && this.$utils.isNotEmpty(this.listData)
    },
    addButton() {
      for (let i = 0; i < this.defaultToolbars.length; i++) {
        const button = this.defaultToolbars[i]
        if (button.button_type === 'add') {
          return button
        }
      }
      return null
    },

    editButton() {
      return this.includesFunctionButton('edit')
    },
    rightText() {
      return this.checkMode
        ? this.$t('common.button.cancel')
        : this.$t('common.button.manage')
    },
    hasScript() {
      const tmpDef = this.dataTemplate
      return !!(tmpDef && tmpDef.attrs && tmpDef.attrs.mobile_script)
    }
  },
  created() {
    const params = this.$route.params
    this.templateId = params.id
    this.loadDataTemplate(this.templateId)
  },
  mounted() {
    // if (this.isInitialization && this.hasScript) {
    //   JTemplate._onLoad(this)
    // }
  },
  beforeDestroy() {
    eventsUtil.cleanEventsByName(this.eventsUtil._eventsUtilsID)
    this.eventsUtil = null
  },
  methods: {
    onLoadActions() {
      return false
    },
    onDisplayCancel() {
      this.display = false
    },
    onDisplayConfirm() {
      if (this.$utils.isEmpty(this.chooseResult) || this.chooseResult.length === 0) {
        this.$toast('显示字段至少选择一项!')
        return
      }
      ActionUtils.initListData(this)
      this.checkedIds = []
      this.loadDataTemplate(this.templateId)
      // storage.set('chooseFields', this.chooseResult)
      const _data = []
      this.fieldsList.forEach(el => {
        if (this.chooseResult.includes(el.prop)) {
          _data.push(el)
        }
      })
      this.saveCustomDataDisplay(_data, this.listIdentity).then((response) => {
        if (this.hasMessage) ActionUtils.success(response.message)
        this.display = false
      }).catch(() => {
        this.display = false
      })
    },
    onShowFieldsList(keys) {
      this.async = !this.async
      this.configKey = keys
      this.display = true
    },
    changeFilterCondition(barItem, listItem) {
      this.checkFilter = listItem.name
      storage.set('filterCondition', listItem)
      this.onSearch()
    },
    initFilterText() {
      const listItem = storage.get('filterCondition')
      if (this.$utils.isNotEmpty(listItem)) {
        this.checkFilter = listItem.value
      } else {
        this.checkFilter = this.$utils.isNotEmpty(this.rangeData[this.rootIndex].options) ? this.rangeData[this.rootIndex].options[this.rootIndex].value : ''
      }
    },

    loadDataTemplate(id) {
      const params = {}
      loadDataTemplateById(id, params)
        .then(data => {
          this.initFormData(data)
        })
        .catch(e => {
          console.error(e)
        })
    },
    initFormData(data) {
      this.title = data['name']
      this.dataTemplate = data
      // 初始化脚本
      if (!this.isInitialization) {
        // this.initJTemplate()
        if (this.dataTemplate.attrs && this.dataTemplate.attrs.mobile_script) {
          this.eventsUtil = eventsUtil._initEvents(this.dataTemplate.attrs.mobile_script, 'list_' + this.dataTemplate.key, true)
        }

        this.isInitialization = true
      }
      if (this.isInitialization && this.hasScript) {
        // JTemplate._onLoad(this)
        eventsUtil._eventCall(this.eventsUtil, 'onLoad', this)
      }
      i18n.setTitle(this.title)
      const template = getDataTemplateListTemplate(data)
      this.templateFields = buildDataTemplateFields(data.fields)
      this.setOptionsByFields(this.templateFields)
      this.buildKey(data, template)
      this.loadData()
      this.initFilterText()
    },
    async buildKey(dataTemplate, template) {
      this.idKey = dataTemplate['unique']
      this.templateKey = dataTemplate['key']
      this.formKey = dataTemplate.attrs ? dataTemplate.attrs.form_key || '' : ''
      this.detailFormKey = dataTemplate.attrs ? dataTemplate.attrs.detail_form_key || '' : ''

      this.buildQuerycolumns(template['query_columns'])
      const fieldsList = template['display_columns'] ? template['display_columns'] : []
      if (this.$utils.isNotEmpty(fieldsList)) {
        const displayFields = fieldsList.filter(i => i.field_type !== 'hidden')
        this.fieldsList = displayFields.map(i => {
          return {
            name: i.label,
            prop: i.name
          }
        })
      } else {
        this.$toast('显示字段不能为空!请在PC端中配置。')
      }
      this.initOptions(fieldsList)
      const colums = []
      // const chooseResult = storage.get('chooseFields')
      // if (chooseResult !== undefined) {
      //   this.chooseResult = chooseResult
      // }
      await this.getCustomDataDisplay(
        this.listIdentity
      ).then((data) => {
        if (this.$utils.isNotEmpty(data)) {
          this.chooseResult = data.map(el => {
            return el.prop
          })
        }
      })
      for (let i = 0; i < this.chooseResult.length; i++) {
        for (let l = 0; l < fieldsList.length; l++) {
          if (this.chooseResult[i] === fieldsList[l].name) {
            colums.push(fieldsList[l])
          }
        }
      }
      const displayColumns = colums
      const filterActions = this.$utils.isNotEmpty(template['filter_conditions']) ? template['filter_conditions'] : []
      if (this.$utils.isNotEmpty(filterActions)) {
        this.rangeData[this.rootIndex].options = filterActions.map(i => {
          return { text: i.label, value: i.key }
        })
      }
      this.initFilter(filterActions, this.rootIndex)

      if (dataTemplate.showType === 'tree') {
        this.isTree = true
        if (this.$utils.isNotEmpty(displayColumns)) {
          this.labelField = displayColumns['name_key']
          this.idKey = displayColumns['id_key']
          this.parentIdKey = displayColumns['pid_key']
        }
      } else if (dataTemplate.showType === 'list') {
        this.isTree = false
        if (this.$utils.isNotEmpty(displayColumns)) {
          // 显示字段顺序预处理
          const arr = []
          for (var i = 0; i < displayColumns.length; i++) {
            for (var l = 0; l < fieldsList.length; l++) {
              if (displayColumns[i].label === fieldsList[l].label) {
                arr[l] = fieldsList[l]
              }
            }
          }
          const columns = arr.filter(e => {
            if (e !== '' && e !== undefined) {
              return e
            }
          })

          this.labelField = columns[0] || {}
          this.descFields = columns.filter((e, i) => {
            return i > 0
          })
        }
        const button = template['buttons'] || {}
        const editButtons = button['edit_buttons'] || []

        this.cacheEditButtons = this.editButtons = editButtons
        let functionButtons = button['function_buttons'] || []
        // TODO:目前版本不支持按钮 ,'print', 'import', 'export', 'sefStartFlow',

        const allowButtons = ['add', 'remove', 'edit', 'detail', 'custom', 'sefStartFlow']

        functionButtons = functionButtons.filter((b) => {
          if (allowButtons.includes(b.button_type)) {
            return b
          }
        })

        this.functionButtons = functionButtons
        this.initToolbarAction()
      }
    },
    onSearch(data) {
      this.stateActive = false
      ActionUtils.initListData(this)
      this.checkedIds = []
      this.paramsForm = {}
      const moreSearchField = this.$refs.moreSearchField
      if (moreSearchField) {
        for (var i = 0; i < moreSearchField.length; i++) {
          const obj = moreSearchField[i].getParams()
          for (var j in obj) {
            if (moreSearchField[i].item.prop === j) {
              this.paramsForm[j] = obj[j]
            }
          }
        }
      }

      if (this.$utils.isNotEmpty(data)) {
        for (var d in data) {
          if (this.$utils.isNotEmpty(data[d])) {
            this.paramsForm[d] = data[d]
          }
        }
      }
      // 处理参数
      if (this.$utils.isNotEmpty(this.paramsForm)) {
        // const superParams = ['ISN', 'ISNN', 'SIE', 'SNE']
        for (var key in this.paramsForm) {
          const value = this.paramsForm[key]
          if (this.$utils.isEmpty(value)) {
            delete this.paramsForm[key]
            continue
          }
          // // 第三方参数
          // const upArrowsSymbolIndex = key.lastIndexOf('^')
          // const threeParams = key.slice(upArrowsSymbolIndex + 1)
          // if (this.$utils.isEmpty(value) && !superParams.includes(threeParams)) {
          //   delete this.paramsForm[key]
          //   continue
          // }
          // 多选的处理
          if (Array.isArray(value)) {
            this.paramsForm[key] = this.getMultiSelectParams(key, value)
          }
          // 组合字段处理
          if (this.combinationQueryFileds[key]) {
            this.paramsForm[key] = this.getCombinationQueryParams(this.combinationQueryFileds[key], value)
          }
        }
      }

      this.searchParams = Object.assign({}, this.paramsForm)

      this.loadData()
    },
    // 多值处理
    getMultiSelectParams(key, value) {
      const queryColumns = {
        relation: 'AND', // 组合条件类型
        parameters: [] // 拼接的字段集合
      }
      const relation = 'OR'
      const parameters = []
      for (let i = 0; i < value.length; i++) {
        const val = value[i]
        parameters.push({
          key: key,
          value: val,
          param: this.$utils.guid(),
          relation: relation
        })
      }
      queryColumns.parameters.push({
        relation: relation,
        parameters: parameters
      })
      return queryColumns
    },
    // 组合字段处理
    getCombinationQueryParams(combination, value) {
      const queryFileds = combination.queryFileds
      const conditionType = combination.conditionType || 'OR'

      const queryColumns = {
        relation: 'AND', // 组合条件类型
        parameters: [] // 拼接的字段集合
      }
      const parameters = []
      queryFileds.forEach(q => {
        parameters.push({
          relation: conditionType,
          key: q.queryfields,
          value: value,
          param: this.$utils.guid()
        })
      })
      queryColumns.parameters.push({
        relation: conditionType,
        parameters: parameters
      })
      return queryColumns
    },
    resetForm() {
      const moreSearchField = this.$refs.moreSearchField
      if (!moreSearchField) return
      for (var i = 0; i < moreSearchField.length; i++) {
        const def = moreSearchField[i].resetForm()
        for (var j in def) {
          this.paramsForm[j] = def[j]
        }
      }
    },
    clickMoreSarch() {
      this.popupShow = true
      this.stateActive = false
    },
    onLoad() {
      this.loadData()
    },
    loadData() {
      if (this.$utils.isEmpty(this.templateKey)) {
        return
      }
      if (this.checkFilter === '') {
        const listItem = storage.get('filterCondition')
        if (listItem !== undefined) {
          this.checkFilter = listItem.name
        }
      }
      const params = {
        key: this.templateKey,
        dynamicParams: '', // 动态参数,获取页面的值
        filterConditionKey: this.checkFilter || '' // 多个过滤条件
      }
      if (this.$utils.isNotEmpty(this.search) && this.queryKey) {
        params[this.queryKey] = this.search
      } else {
        Object.assign(params, this.searchParams)
      }
      // 加载数据
      queryDataByKey(ActionUtils.formatParams(params, this.pagination))
        .then(response => {
          const responseData = response.data
          if (this.isTree) {
            const dataResult = responseData.dataResult
            this.pagination = responseData.pageResult || {}
            this.listData = TreeUtils.transformToTreeFormat(dataResult, {
              idKey: this.idKey,
              parentIdKey: this.parentIdKey,
              childrenKey: this.childrenKey
            })

            // 初始化最顶级数据
            this.initRootTreeData(response.vars)
            this.finished = true
          } else {
            if (!this.finished) {
              // 处理数据
              ActionUtils.handleListData(this, responseData)
            }
          }
        })
        .catch(e => {
          ActionUtils.handleErrorData(this, e)
        })
    },
    // 默认显示字段列表
    initOptions(list) {
      list.forEach((val, index) => {
        if (this.configKey === '') {
          if (index < 4) {
            this.chooseResult.push(val.name)
          }
        }
      })
    },
    initFilter(list, index) {
      if (this.$utils.isNotEmpty(list)) {
        if (!this.$utils.isNotEmpty(this.defaultValue)) {
          const listItem = storage.get('filterCondition')
          if (this.$utils.isNotEmpty(listItem)) {
            const i = list.findIndex(i => i.label === listItem.text)
            this.defaultValue = [list[i].key]
          } else {
            this.defaultValue = [list[index].key]
          }
        }
      }
    },
    onRefreshCallback() {
      this.checkMode = false
      this.onRefresh()
    },
    onRefresh() {
      this.refreshing = true
      this.finished = false
      this.loading = true
      this.onSearch()
    },
    onClick(item, index) {
      if (this.checkMode) {
        this.$refs.checkboxes[index].toggle()
      } else {
        return
        // this.gotoForm({
        //   readonly: true,
        //   pkValue: item[this.idKey]
        // })
      }
    },

    generateTitle(name, title) {
      // generateTitle by vue-i18n
      return i18n.generateTitle(name, title)
    },

    onClickLeft() {
      this.$router.push({ name: 'dashboard' })
      storage.remove('filterCondition')
      storage.remove('chooseFields')
    },

    onClose(clickPosition, instance) {
      switch (clickPosition) {
        case 'left':
        case 'cell':
        case 'outside':
          instance.close()
          break
        case 'right':
          instance.open()
          break
      }
    },
    handleAction(button, data) {
      const buttonType = button.button_type || button.key
      const key = button.key || button.button_type
      const position = button.position
      const selection = this.getSelection(position, data)
      // 前置事件
      this.beforeScript(key, position, selection, data, () => {
        this.readonly = false
        this.action = buttonType
        this.position = position
        this.actionData = data
        switch (buttonType) {
          case 'add':// 添加
            this.handleEdit('', button, buttonType, position, data)
            break
          case 'edit':// 编辑
          case 'detail':// 明细
            ActionUtils.selectedRecord(selection).then((id) => {
              this.handleEdit(id, button, buttonType, position, data)
            }).catch(() => { })
            break
          case 'remove':// 删除
            ActionUtils.removeRecord(selection).then((ids) => {
              this.handleRemove(ids, button, buttonType, position, data)
            }).catch(() => { })

            break
          case 'sefStartFlow':// 启动自定义流程
            ActionUtils.selectedMultiRecord(selection).then((ids) => {
              if (button.deflow) {
                this.$dialog.confirm({
                  title: '消息',
                  message: '确定启动流程吗?'
                }).then(() => {
                  this.handleStartFlowFromList(ids, button.deflow, this.getFormKey())
                }).catch(() => {})
              } else {
                this.sefStartFlowDialogVisible = true
                this.sefStartFlowDialogValue = {}
                this.sefStartFlowId = ids
              }
            }).catch(() => {})
            break
          case 'custom':// 自定义按钮
            if (this.$utils.isNotEmpty(button['formKey'])) {
              ActionUtils.selectedRecord(selection, false).then((id) => {
                this.handleEdit(id, button, position, selection, data, button)
              }).catch(() => { })
            } else {
              ActionUtils.selectedMultiRecord(selection).then((ids) => {
                this.afterScript(key, position, ids, data, () => {
                  this.onRefreshCallback()
                })
              }).catch(() => {})
            }
            break
          default:
            break
        }
      })
    },
    handleStartFlowDialogActionEvent(key, data) {
      if (key === 'confirm') {
        this.handleStartFlowFromList(this.sefStartFlowId, data ? data.defKey : '', this.getFormKey(), (rtn) => {
          if (rtn) {
            this.sefStartFlowDialogVisible = false
          }
        })
      }
    },
    handleStartFlowFromList(id, defKey, formKey, callback) {
      startFlowFromList({
        ids: id,
        defKey: defKey,
        formKey: formKey
      }).then(response => {
        callback(true)
        this.$notify({
          message: '流程启动成功!',
          type: 'success'
        })
        this.search()
        this.afterScript(this.action, this.position, id, this.actionData, () => {
          this.onRefreshCallback()
        })
      }).catch(() => {
        callback(false)
      })
    },
    getFormKey() {
      return this.dataTemplate.attrs ? this.dataTemplate.attrs.form_key || '' : ''
    },
    getSelection(position, data) {
      const selection = []
      if (position === 'toolbar') {
        this.checkedIds.forEach((d) => {
          selection.push(d[this.idKey])
        })
      } else {
        selection.push(data[this.idKey])
      }
      return selection
    },
    getCustomDetailFormKey(button, key = 'detailFormKey') {
      if (button && this.$utils.isNotEmpty(button[key])) {
        return button[key]
      } else {
        return this.detailFormKey
      }
    },
    handleEdit(pkValue, button, code, position, data) {
      if (this.$utils.isEmpty(this.formKey)) {
        ActionUtils.warning('请绑定表单')
        return
      }
      this.editButtons = JSON.parse(JSON.stringify(this.cacheEditButtons))
      let formKey = this.formKey
      if (button.key === 'detail' || button.key === 'customDetail') {
        this.readonly = true
        this.buttonPriority = 'outside'
        const detailFormKey = button.key === 'customDetail' ? this.getCustomDetailFormKey(button) : this.detailFormKey
        if (this.$utils.isNotEmpty(detailFormKey)) {
          formKey = detailFormKey
        }
      }
      const editToolbars = []
      const action = this.action
      this.editButtons.forEach((rf, i) => {
        const buttonType = button.key === 'add' ? 'edit' : action
        // 编辑页顶部按钮
        if (hasButton(rf.button_type, buttonType, rf.position)) {
          editToolbars.push(rf)
        }
      })
      this.editButtons = editToolbars
      const buttonType = button.button_type || button.key
      // TODO: 左树右列数据处理
      // if (this.relatedTreeFields && this.$utils.isNotEmpty(this.defaultData)) {
      //   const selection = this.defaultData[this.relatedTreeFields]
      //   this.defaultFormData = action === 'add' ? this.getDefaultFormData(selection) : null
      // } else {
      //   this.defaultFormData = {}
      // }
      this.buttonPriority = 'outside'
      this.gotoForm({
        pkValue,
        readonly: buttonType === 'detail',
        formKey
      })
      this.afterScript(code, position, pkValue, data, () => {})
    },
    gotoForm({ pkValue = '', readonly = true, formKey = this.formKey }) {
      this.formrenderParams = {
        templateKey: this.templateKey,
        formKey: formKey,
        pkValue: pkValue,
        toolbars: this.editButtons,
        readonly: readonly
      }
      this.formrenderVisible = true
    },
    handleRemove(ids, button, code, position, data) {
      this.removeRecord(ids, r => {
        this.afterScript(code, position, ids, data, () => {
          this.checkMode = false
          this.checkedIds = []
          this.onRefreshCallback()
        })
      })
    },
    removeRecord(ids, callback) {
      removeFormData({
        ids: ids,
        idKey: this.idKey,
        formKey: this.formKey
      }).then(() => {
        this.$notify({
          type: 'success',
          message: '删除成功'
        })

        if (callback) {
          callback(true)
        }
        this.onRefresh()
      })
    },
    toCheckMode() {
      if (!this.hasToolbar) {
        return
      }
      if (this.checkMode) {
        this.show = true
        this.checkedIds = []
      }
      this.checkMode = !this.checkMode
    },
    /**
     * 初始化工具栏
     */
    initToolbarAction() {
      const functionButtons = this.functionButtons || []

      const toolbars = []
      const manages = []
      // 功能按钮
      functionButtons.forEach((rf, i) => {
        const btn = this.buildButton(rf, i)
        // 顶部按钮
        if (hasButton(rf.button_type, 'toolbar', rf.position)) {
          btn.position = 'toolbar'
          toolbars.push(JSON.parse(JSON.stringify(btn)))
        }

        // 管理列按钮
        if (hasButton(rf.button_type, 'manage', rf.position)) {
          btn.position = 'manage'
          manages.push(JSON.parse(JSON.stringify(btn)))
        }
      })

      const filterButtons = ['search', 'resetSearch', 'add']

      this.defaultToolbars = toolbars
      this.toolbars = toolbars.filter((b) => {
        if (!filterButtons.includes(b.button_type)) {
          return b
        }
      })
      this.defaultManages = manages
      this.manages = manages
      // this.manages = manages.filter((b) => {
      //   if (allowButtons.includes(b.button_type)) {
      //     return b
      //   }
      // })
    },
    /**
     * 构建按钮
     */
    buildButton(rf, i) {
      const defaultButton = ButtonsConstants[rf.button_type] || {}
      let key = rf.button_type
      let mode
      let rightIcon
      let menus
      if (key === 'custom' || key === 'sefStartFlow') {
        key = rf.code ? rf.code : key + i
      }
      if (rf.button_type === 'export') {
        mode = 'dropdown'
        rightIcon = true
        menus = ButtonsConstants[rf.button_type].menus
      }
      let disabled = false
      let hidden = false
      if (this.hasButtonAction(key, rf)) {
        hidden = (row, data) => {
          // return JTemplate._onLoadActions(this, key, rf, 'hidden', row, data)
          return eventsUtil._eventCall(this.eventsUtil, 'onLoadActions', this, key, rf, 'hidden', row, data)
        }
        disabled = (row, data) => {
          // return JTemplate._onLoadActions(this, key, rf, 'disabled', row, data)
          return eventsUtil._eventCall(this.eventsUtil, 'onLoadActions', this, key, rf, 'disabled', row, data)
        }
      }
      return {
        '$index': i,
        key: key,
        button_type: rf.button_type,
        code: rf.code,
        name: rf.label || defaultButton.label,
        icon: rf.icon ? rf.icon : defaultButton.icon,
        type: this.converType(rf.style || defaultButton.type),

        deflow: rf.deflow || null,
        formKey: rf.formKey || null,
        detailFormKey: rf.detailFormKey || null,
        mode: mode,
        confirm: rf.confirm || false,

        rightIcon: rightIcon,
        menus: menus,
        disabled: disabled,
        hidden: hidden
      }
    },
    converType(type) {
      if (type === 'primary') return 'info'
      if (type === 'info') return 'gray'
      if (type === 'success') return 'primary'
      return type
    },
    // 自定义格式数据事件
    hasButtonAction: function(key, button) {
      // const buttonActionResult = JTemplate._onLoadActions(this, key, button)
      const buttonActionResult = eventsUtil._eventCall(this.eventsUtil, 'onLoadActions', this, key, button)
      if (typeof (buttonActionResult) !== 'undefined' && buttonActionResult) {
        return true
      }
      return false
    },
    customCallback(item) {
      this.beforeSubmit('custom')
      // TODO 后面是自定义按钮的事
    },
    beforeSubmit(alias) {
      // 前置事件
      if (this.hasScript) {
        // const beforSubmitResult = JTemplate._beforeSubmit(
        //   this,
        //   alias,
        //   this.dataTemplate
        // )
        const beforSubmitResult = eventsUtil._eventCall(this.eventsUtil, 'beforeSubmit', this, alias, this.dataTemplate)
        if (typeof beforSubmitResult !== 'undefined' && !beforSubmitResult) {
          return
        }
      }
    },
    includesFunctionButton(key) {
      const btns = this.functionButtons.map(button => {
        return button['button_type']
      })
      return btns.includes(key)
    },
    convertField(column) {
      if (!column.name) return column
      const field = this.templateFields[column.name.toLowerCase()] || null
      // const same = this.$utils.toBoolean(column['same'], false)
      const same = !((column['same'] && column['same'] === 'N'))
      let fieldType = same ? (field ? (field['field_type'] || 'text') : 'text') : column['field_type'] || 'text'
      const fieldOptions = same ? (field ? (field['field_options'] || {}) : {}) : (column['field_options'] || {})
      const dataType = field ? field['type'] || 'varchar' : 'varchar'
      // 字段是日期类型
      if ((dataType === 'date' || dataType === 'timestamp' ||
      dataType === 'datetime' || dataType === 'currentTime' || dataType === 'currentDate') &&
       (fieldType !== 'datePicker' && fieldType !== 'dateRange')) {
        fieldType = !same ? fieldType : 'datePicker'
      }
      if (fieldType === 'datePicker' || fieldType === 'dateRange') {
        const datefmtType = fieldOptions['datefmt_type']
        if (datefmtType !== 'custom') {
          fieldOptions['datefmt'] = this.getDatefmt(fieldOptions)
        }
      }

      // 处理当前用户,当前组织控件
      if (fieldType === 'currentUser' || fieldType === 'currentOrg') {
        fieldType = 'selector'
      }
      column['field_type'] = fieldType
      column['field_options'] = fieldOptions
      column['dataType'] = dataType
      return column
    },
    getDatefmt(fieldOptions) {
      if (fieldOptions['datefmt_type'] && fieldOptions['datefmt_type'] !== 'custom') {
        return FormOptions.t.DATE_FORMATS[fieldOptions['datefmt_type']] || FormOptions.t.DATE_FORMATS['date']
      }
      return fieldOptions['datefmt'] || FormOptions.t.DATE_FORMATS['date']
    },
    buildQuerycolumns(queryColumns) {
      this.searchForms.forms = []
      if (this.$utils.isEmpty(queryColumns)) {
        return
      }
      // TODO:目前第一个字段查询字段只支持 文本,多行文本和自动编号、数字、日期范围
      const firstFieldTypes = ['text', 'textarea', 'daterange', 'autonumber', 'number']
      //
      const arr = [
        'text',
        'number',
        'numberRange',
        'hidden',
        'select',
        'dateRange',
        'datePicker',
        'autoNumber'
      ]

      const columns = []
      const queryColumnsData = []
      queryColumns.forEach(column => {
        if (column.common === 'N') return
        const field = this.convertField(column)
        const fd = this.buildSearchForm(field, !!column.same)
        columns.push(fd)
        const fieldType = field.field_type || ''
        if (firstFieldTypes.includes(fieldType.toLowerCase())) {
          queryColumnsData.push(fd)
        }
        if (!arr.includes(fieldType)) {
          this.slotForms.push(fd)
        }
        this.slotForms.forEach(v => {
          this.paramsForm[v.prop] = ''
        })
      })
      this.searchForms.forms = columns
      // 顶部查询条件
      this.queryColumns = queryColumnsData || []
      const queryColumn = this.$utils.isNotEmpty(this.queryColumns) ? this.queryColumns[0] : {}
      this.queryKey = queryColumn.prop || ''
      this.queryName = queryColumn.label || ''
      this.fieldType = queryColumn.fieldType || ''
    },
    buildSearchForm(field, same) {
      let querySuffix
      const datasetType = this.dataTemplate.datasetType
      const dataTypes = ['date', 'varchar', 'number']
      if (dataTypes.includes(field.dataType) &&
       field.queryCondition && this.$utils.isNotEmpty(field.queryCondition)) {
        const dataTypesSuffixs = datasetType === 'thirdparty' ? this.queryConditionOptions[field.fieldsTypes || 'varchar'] : this.queryConditionOptions[field.dataType]
        const suffixData = dataTypesSuffixs.filter(d => d.value === field.queryCondition)
        querySuffix = this.$utils.isNotEmpty(suffixData) ? suffixData[0].suffix : 'SL'
      } else {
        if (field.dataType === 'number') {
          querySuffix = 'SIN'
        }
      }
      // 1、日期/数字 条件为范围时得特殊处理。
      // 2、控件类型不一致时选的控件类型传递后缀是否以数据格式类型得对应条件后缀为准,还是单独保持原样。
      // 3、直接添加查询字段时得默认拼接后缀,保留目前写死得后缀将其转化在内部作为直接添加而不进入查询字段时得默认条件后缀。

      let searchColumn = {
        label: field.label,
        placeholder: field.placeholder
      }
      // 控件类型
      const fieldType = field['field_type'] || 'text'

      const fieldOptions = field['field_options']
      // 组合字段
      if (field.addType === 'combination') {
        const queryFileds = this.buildJointFileds(field.combinationKeyField).filter(f => f !== '')

        const combinationQueryFileds = []
        const queryCondition = field.queryCondition || 'include'
        queryFileds.forEach(name => {
          const column = this.fields[name.toLowerCase()] || { }
          const type = column.type || 'varchar'
          const dataType = this.dataTypes[type] || {}
          if (type === 'date') {
            combinationQueryFileds.push({
              queryfields: 'Q^' + name + dataType.start
            }, {
              queryfields: 'Q^' + name + dataType.end
            })
          } else {
            const fieldSuffix = dataType[queryCondition] ? dataType[queryCondition] : 'SL'
            combinationQueryFileds.push({ queryfields: 'Q^' + name + fieldSuffix })
          }
        })
        const prop = field.combinationName
        this.combinationQueryFileds[prop] = {
          prop: prop,
          conditionType: this.conditionTypeOptions[queryCondition].conditionType,
          queryFileds: combinationQueryFileds
        }
        // ---------------------------------------------------------
        searchColumn = Object.assign(searchColumn, {
          prop: prop,
          modelValue: prop,
          fieldType: fieldType,
          fieldOptions: fieldOptions
        })
      } else {
        if (fieldType === 'hidden') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'SL'
          }
          searchColumn = Object.assign(searchColumn, {
            prop: `Q^${field.name}^${querySuffix}`,
            modelValue: `Q^${field.name}^${querySuffix}`,
            fieldType: fieldType,
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'numberRange') { // 数字范围
          if (this.$utils.isEmpty(querySuffix)) {
            if (field.dataType === 'number') {
              querySuffix = ['DBL', 'DBG']
            } else {
              const dataTypesSuffixs = this.queryConditionOptions[field.dataType]
              querySuffix = [dataTypesSuffixs[0]['suffix'], dataTypesSuffixs[0]['suffix']]
            }
          }
          searchColumn = Object.assign(searchColumn, {
            prop: [`Q^${field.name}^${querySuffix[0]}`, `Q^${field.name}^${querySuffix[1]}`],
            modelValue: `Q^${field.name}^${querySuffix[0]}`,
            fieldType: 'numberRange',
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'radio' || fieldType === 'checkbox' || fieldType === 'select') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = fieldType !== 'checkbox' && !fieldOptions.multiple ? 'S' : 'SL'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            modelValue: `Q^${field.name}^${querySuffix}`,
            fieldType: fieldType,
            fieldOptions: fieldOptions,
            multiple: fieldOptions.multiple || fieldType === 'checkbox' || false,
            options: this.buildOptions(fieldOptions && fieldOptions.options ? fieldOptions.options : [])
          })
          this.setOptionsInField(searchColumn, fieldOptions)
        } else if (fieldType === 'switch') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'S'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            fieldType: 'select',
            fieldOptions: fieldOptions,
            options: this.buildSwitchOptions(fieldOptions)
          })
        } else if (fieldType === 'date') { // 日期
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'D'
          }
          const datefmt = fieldOptions.datefmt || ''
          const prop = `Q^${field.name}^${querySuffix}^${datefmt}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            fieldType: 'date',
            fieldOptions: fieldOptions,
            dateType: fieldOptions.datefmt_type ? fieldOptions.datefmt_type : 'date'
          })
        } else if (fieldType === 'datePicker' || fieldType.toLowerCase() === 'daterange') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'D'
          }
          const suffix = typeof querySuffix === 'object' && this.$utils.isNotEmpty(querySuffix) ? querySuffix : ['DL', 'DG']
          // const options = this.filterPickerOptions(field)
          // const pickerOptions = options.scopeData
          // const value = options.operationScriptDate
          const datefmt = fieldOptions.datefmt || ''
          const dateDealFmt = DateFormatUtil.dealFmt(fieldOptions.datefmt)
          const props = [`Q^${field.name}^${suffix[0]}^${datefmt}`, `Q^${field.name}^${suffix[1]}^${datefmt}`]
          searchColumn = Object.assign(searchColumn, {
            hasSame: same || false,
            datefmt: '^' + datefmt,
            prop: props,
            modelValue: `Q^${field.name}^${querySuffix}^${datefmt}`,
            fieldType: fieldType === 'datePicker' ? dateDealFmt.dateType : dateDealFmt.dateType + 'range',
            fieldOptions: fieldOptions
            // pickerOptions: pickerOptions || {}
          })
        } else if (fieldType === 'dictionary') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = fieldOptions.multiple ? 'SL' : 'S'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            slotName: prop + 'searchForm',
            fieldType: fieldType,
            placeholder: fieldOptions.placeholder,
            multiple: fieldOptions.multiple || false,
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'selector') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'SL'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            slotName: prop + 'searchForm',
            fieldType: fieldType,
            modelValue: prop,
            multiple: fieldOptions.multiple || false,
            selectorType: fieldOptions.selector_type || 'user',
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'customDialog') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'S'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            slotName: prop + 'searchForm',
            fieldType: fieldType,
            modelValue: prop,
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'linkdata') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'S'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            slotName: prop + 'searchForm',
            fieldType: fieldType,
            modelValue: prop,
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'address') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'SL'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            slotName: prop + 'searchForm',
            modelValue: prop,
            fieldType: fieldType,
            fieldOptions: fieldOptions
          })
        } else if (fieldType === 'number') {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'SIN'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            modelValue: prop,
            fieldType: fieldType,
            field_options: fieldOptions,
            dateType: field.dataType
          })
        } else {
          if (this.$utils.isEmpty(querySuffix)) {
            querySuffix = 'SL'
          }
          const prop = `Q^${field.name}^${querySuffix}`
          searchColumn = Object.assign(searchColumn, {
            prop: prop,
            modelValue: prop,
            slotName: prop + 'searchForm',
            fieldOptions: fieldOptions
          })
        }
      }
      return searchColumn
    },
    buildSwitchOptions(fieldOptions) {
      return FormUtils.getSwitchOptions(fieldOptions, 'value')
    },
    changeQuery(val, data) {
      this.queryName = data['label']
      this.fieldType = data['fieldType']
      this.search = ''
    },
    /**
     * 初始化脚本
     */
    initJTemplate() {
      const id = 'JTemplate'
      let el = document.getElementById(id)
      if (el) {
        el.parentNode.removeChild(el)
      }

      if (this.dataTemplate.attrs && this.dataTemplate.attrs.mobile_script) {
        const code = this.dataTemplate.attrs.mobile_script
        el = document.createElement('script')
        el.type = 'text/javascript'
        el.id = id
        document.body.appendChild(el)
        try {
          el.appendChild(document.createTextNode(code))
        } catch (ex) {
          el.text = code
        }
        document.body.appendChild(el)
      }
    },
    // 前置脚本
    beforeScript(action, position, selection, data, callback) {
      if (!this.hasScript) {
        const flag = true
        callback(flag)
        return
      }
      // JTemplate._beforeSubmit(this, action, position, selection, data, callback)
      eventsUtil._eventCall(this.eventsUtil, 'beforeSubmit', this, action, position, selection, data, callback)
    },
    // 后置脚本
    afterScript(action, position, selection, data, callback) {
      if (!this.hasScript) {
        const flag = true
        callback(flag)
        return
      }
      // JTemplate._afterSubmit(this, action, position, selection, data, callback)
      eventsUtil._eventCall(this.eventsUtil, 'afterSubmit', this, action, position, selection, data, callback)
    }
  }
}
</script>
<style lang="scss">
.data-template-list {
  .data-template-list-search-wrapper{
    padding:10px 0;
    background-color: #fff;
    .data-template-list-search{
      display: flex;
      align-items: center;
      height: 34px;
      .van-cell{
        padding: 5px 8px 5px 0;
        background-color: transparent;
      }

      .data-template-list-search__select{
        width: 120px;
        .van-cell{
          .van-field__label{
            padding-left: 10px;
            margin-right: 5px;
            text-overflow: ellipsis;
            white-space: nowrap;
            overflow: hidden;
          }
        }
      }
      .data-template-list-search__content{
        // padding-left: 12px;
        background-color: #f7f8fa;
        border-radius: 2px;
        flex: 1;
      }
      .data-template-list-search__icon{
        width: 24px;
      }
    }
  }

  .options-null{
    bottom: -9px !important;
  }
  .palette-button {
    position: fixed;
    right: 30px;
    bottom: 30px;
    z-index: 500;
  }
  .titles{
    font-weight: 600;
    font-size: 16px;
    display: block;
  }
  .desc{
    font-size: 14px;
    line-height: 22px;
  }
  .active {
    color: #4688f9;
  }
  .disactive {
    color: #666;
  }
  .menus{
    .van-dropdown-menu__bar{
      height: 40px;
       background-color: #f1f4f8;
    }
    .van-dropdown-menu__item--disabled{
      justify-content: end;
      margin: 0 7px;
    }
    .van-dropdown-menu__item--disabled + .van-dropdown-menu__item{
      justify-content: flex-end;
      margin: 0 20px;
    }
    .menu-slot-right{
      color: #3388FF;
    }
  }
}
.van-dropdown-menu__item--disabled{
  .van-dropdown-menu__title::after{
    border:0
  }
}

</style>

1、编写配置

1)混入对象的导入

对应代码

import CustomDataDisplayMixin from '@/business/platform/system/mixins/customDataDisplay'


CustomDataDisplayMixin

2)显示字段渲染修改

将多选绑定的name从item.name修改为item.prop

3)listIdentity以及是否显示保存成功提示

listIdentity:用于请求时传递的标识性参数,可根据自身需求重新编写
hasMessage:判断是否显示保存成功的提示

    listIdentity() {
      return 'ibps_data_template_tpl' + '_' + this.dataTemplate.key
    },
    hasMessage() {
      return true
    },

2、默认选择前4个字段进行显示

将之前的val.label换成val.name

    // 默认显示字段列表
    initOptions(list) {
      list.forEach((val, index) => {
        if (this.configKey === '') {
          if (index < 4) {
            this.chooseResult.push(val.name)
          }
        }
      })
    },

3、获取显示字段



代码:

    async buildKey(dataTemplate, template) {
      this.idKey = dataTemplate['unique']
      this.templateKey = dataTemplate['key']
      this.formKey = dataTemplate.attrs ? dataTemplate.attrs.form_key || '' : ''
      this.detailFormKey = dataTemplate.attrs ? dataTemplate.attrs.detail_form_key || '' : ''

      this.buildQuerycolumns(template['query_columns'])
      const fieldsList = template['display_columns'] ? template['display_columns'] : []
      if (this.$utils.isNotEmpty(fieldsList)) {
        const displayFields = fieldsList.filter(i => i.field_type !== 'hidden')
        this.fieldsList = displayFields.map(i => {
          return {
            name: i.label,
            prop: i.name
          }
        })
      } else {
        this.$toast('显示字段不能为空!请在PC端中配置。')
      }
      this.initOptions(fieldsList)
      const colums = []
      // const chooseResult = storage.get('chooseFields')
      // if (chooseResult !== undefined) {
      //   this.chooseResult = chooseResult
      // }
      await this.getCustomDataDisplay(
        this.listIdentity
      ).then((data) => {
        if (this.$utils.isNotEmpty(data)) {
          this.chooseResult = data.map(el => {
            return el.prop
          })
        }
      })
      for (let i = 0; i < this.chooseResult.length; i++) {
        for (let l = 0; l < fieldsList.length; l++) {
          if (this.chooseResult[i] === fieldsList[l].name) {
            colums.push(fieldsList[l])
          }
        }
      }
      const displayColumns = colums
      const filterActions = this.$utils.isNotEmpty(template['filter_conditions']) ? template['filter_conditions'] : []
      if (this.$utils.isNotEmpty(filterActions)) {
        this.rangeData[this.rootIndex].options = filterActions.map(i => {
          return { text: i.label, value: i.key }
        })
      }
      this.initFilter(filterActions, this.rootIndex)

      if (dataTemplate.showType === 'tree') {
        this.isTree = true
        if (this.$utils.isNotEmpty(displayColumns)) {
          this.labelField = displayColumns['name_key']
          this.idKey = displayColumns['id_key']
          this.parentIdKey = displayColumns['pid_key']
        }
      } else if (dataTemplate.showType === 'list') {
        this.isTree = false
        if (this.$utils.isNotEmpty(displayColumns)) {
          // 显示字段顺序预处理
          const arr = []
          for (var i = 0; i < displayColumns.length; i++) {
            for (var l = 0; l < fieldsList.length; l++) {
              if (displayColumns[i].label === fieldsList[l].label) {
                arr[l] = fieldsList[l]
              }
            }
          }
          const columns = arr.filter(e => {
            if (e !== '' && e !== undefined) {
              return e
            }
          })

          this.labelField = columns[0] || {}
          this.descFields = columns.filter((e, i) => {
            return i > 0
          })
        }
        const button = template['buttons'] || {}
        const editButtons = button['edit_buttons'] || []

        this.cacheEditButtons = this.editButtons = editButtons
        let functionButtons = button['function_buttons'] || []
        // TODO:目前版本不支持按钮 ,'print', 'import', 'export', 'sefStartFlow',

        const allowButtons = ['add', 'remove', 'edit', 'detail', 'custom', 'sefStartFlow']

        functionButtons = functionButtons.filter((b) => {
          if (allowButtons.includes(b.button_type)) {
            return b
          }
        })

        this.functionButtons = functionButtons
        this.initToolbarAction()
      }
    },

4、将选择的字段保存在后端

原先是将显示字段保存在本地:storage.set(‘chooseFields’, this.chooseResult)
现将显示字段通过请求保存在后端:

onDisplayConfirm() {
      if (this.$utils.isEmpty(this.chooseResult) || this.chooseResult.length === 0) {
        this.$toast('显示字段至少选择一项!')
        return
      }
      ActionUtils.initListData(this)
      this.checkedIds = []
      this.loadDataTemplate(this.templateId)
      // storage.set('chooseFields', this.chooseResult)
      const _data = []
      this.fieldsList.forEach(el => {
        if (this.chooseResult.includes(el.prop)) {
          _data.push(el)
        }
      })
      this.saveCustomDataDisplay(_data, this.listIdentity).then((response) => {
        if (this.hasMessage) ActionUtils.success(response.message)
        this.display = false
      }).catch(() => {
        this.display = false
      })
    },

2、customDataDisplay(列表混入对象)

目录:src\business\platform\system\mixins\customDataDisplay.js
注意:需在对应位置保存有以下代码,以下代码是用于在template-list中对应操作调用来对显示字段向后端进行保存或获取,也可保存在其他位置上,但是在template-list中必须对对应代码进行修改。

/**
 * 自定义展示数据
 */
import { getIdentityTypeAccount, save, reset } from '@/api/platform/system/customDataDisplay'
export default {
  methods: {

    getCustomDataDisplay(identity, type = 'display', account) {
      if (!account) {
        account = this.$store.getters.account
      }
      return new Promise((resolve, reject) => {
        getIdentityTypeAccount({
          identity: identity,
          account: account,
          type: type
        }).then(response => {
          const data = response.data
          if (this.$utils.isNotEmpty(data)) {
            resolve(JSON.parse(data.scheme))
          } else {
            resolve([])
          }
        }).catch((err) => {
          reject(err)
        })
      })
    },
    saveCustomDataDisplay(scheme, identity, type = 'display', account) {
      if (!account) {
        account = this.$store.getters.account
      }
      const customDataDisplay = {
        'account': account,
        'identity': identity,
        'type': type
      }
      return new Promise((resolve, reject) => {
        if (this.$utils.isNotEmpty(scheme)) {
          customDataDisplay['scheme'] = JSON.stringify(scheme)
          save(customDataDisplay).then(response => {
            resolve(response)
          }).catch((err) => {
            reject(err)
          })
        } else {
          reset(customDataDisplay).then(response => {
            resolve(response)
          }).catch((err) => {
            reject(err)
          })
        }
      })
    }
  }
}

在template-list中调用的位置如图:

3、customDataDisplay(显示字段请求Api)

目录:src\api\platform\system\customDataDisplay.js
注意:
1、需在对应位置保存有以下代码,以下代码是用于在第二点customDataDisplay(列表混入对象)中对应操作调用,也可保存在其他位置上,但是在customDataDisplay(列表混入对象)中必须对对应代码进行修改。
2、API是参照PC端自定义列的请求API复制选用的,可根据自身需求另写API后对该文件对应的请求方法进行修改。

import request from '@/utils/request'
import { SYSTEM_URL } from '@/api/baseUrl'
/**
 * 根据数据标识、数据类型及用户账号查询
 * @param {*} params
 */
export function getIdentityTypeAccount(params) {
  return request({
    url: SYSTEM_URL() + '/system/custom/data/display/scheme/get/identity/type/account',
    method: 'post',
    data: params
  })
}

/**
 * 保存数据
 * @param {*} params
 */
export function save(params) {
  return request({
    url: SYSTEM_URL() + '/system/custom/data/display/scheme/save',
    method: 'post',
    isLoading: true,
    data: params
  })
}
/**
 * 恢复默认数据
 * @param {*} params
 */
export function reset(params) {
  return request({
    url: SYSTEM_URL() + '/system/custom/data/display/scheme/reset',
    method: 'post',
    isLoading: true,
    data: params
  })
}

在customDataDisplay(列表混入对象)中调用的位置如图: