Source: packages/QueryCard/index.vue

<!--  -->
<template>
  <P-PageCard v-bind="PageCardConfig">
    <!-- 此块内容暂不开启  s -->
    <div v-if="headerConfig.show" slot="header" class="pc-header">
      <div class="pc-header-con">
        <div class="pc-header-title">
          <a-icon v-if="headerConfig.title.icon" :type="headerConfig.title.icon" class="pc-header-title-icon" />
          <slot v-if="!headerConfig.title.icon" name="titleIcon"></slot>
          <span :class="['pc-header-title-label',headerConfig.title.labelClass]" :style="headerConfig.title.labelStyle">{{headerConfig.title.label}}</span>
        </div>
        <div v-if="headerConfig.backBtn.show" class="pc-header-btn">
          <a-button :type="headerConfig.backBtn.btnType" :icon="headerConfig.backBtn.icon" @click="btnEvent('backBtn')" class="header-btn-item">{{headerConfig.backBtn.label}}</a-button>
        </div>
      </div>
    </div>
     <!-- 此块内容暂不开启  e -->
    <div class="pc-body-con">
      <div class="pc-body-query">
        <div class="pc-query">
          <!-- <slot name="default"></slot> -->
          <p-PageForm ref="PPageFormDefault" :PEvent="formEvent" :formDataList="formConfig.formDataListDefault" :layout="formConfig.layouttDefault" :showHelp="showHelp">
            <!-- <slot name="defaultForm"></slot> -->
            <!-- <template v-slot:userName7="{value, itemData}" ></template> -->
          </p-PageForm>
        </div>
        <div class="pc-body-btn">
          <a-button type="primary" icon="search" class="body-btn-item" @click="btnEvent('query')">查询</a-button>
          <a-button icon="reload" class="body-btn-item" @click="btnEvent('reset')">重置</a-button>
          <a-button :key="btnItem.btnKey" v-for="(btnItem,index) in btnList" v-has="btnItem.vhas" @click="btnEvent(btnItem.btnKey,index,btnItem)" :type="btnItem.btnType" :disabled="btnItem.disabled" :icon="btnItem.icon" class="body-btn-item"  v-bind="otherAttr">{{btnItem.label}}</a-button>
          <a-button type="link" class="body-btn-item" @click="showPanel" v-if="formConfig.formDataListOther.length>0">
            更多<a-icon type="down" class="tb"/>
          </a-button>
        </div>
      </div>
      <div :class="['pc-body-panel',headerConfig.isDefaultHeadBottomBorder == true&&'show-border-top']">
        <!-- <slot name="other"></slot> -->
        <p-PageForm ref="PPageFormOther" v-if="formConfig.formDataListOther.length>0" :PEvent="formEvent" :formDataList="formConfig.formDataListOther" :layout="formConfig.layouttOther" :showHelp="showHelp">
          <!-- <template v-slot:userName7="{value, itemData}" >
            <component :is="'Pbutton'"></component>
          </template>  -->
        </p-PageForm>
      </div>
      <div class="pc-body-table">
        <slot v-if="showTableSlot==true" name="table"></slot>

        <a-table v-if="showTableSlot==false" :columns="columns" :data-source="dataSource" :pagination="pagination" @change="tableChangeEvent" v-bind="otherAttr">
          <template v-for="(item) in customRenderList" :slot="item.keyName" slot-scope="text, record,index">
            <slot :name="item.keyName" :data="{ text, record, index, indent: { [item.keyName]: text ,[item.title]:item.dataIndex} }"></slot>
          </template>
        </a-table>
        <!-- <span v-if="showTableHeader==true" >
          <template slot="title" slot-scope="currentPageData">
            <slot name="title" :data="currentPageData"></slot>
          </template>
        </span>
          <span v-if="showTableFooter==true" slot="footer" slot-scope="currentPageData">
            <slot name="footer" :data="currentPageData"></slot>
         </span> -->

      </div>
    </div>
  </P-PageCard>
</template>

<script>
import IS from './../utils/is'
import UUID from './../utils/uuid'
import anime from "animejs/lib/anime.js";
import PPageCard from './../Card'
// import Vue from 'Vue';
export default {
  // import引入的组件需要注入到对象中才能使用
  name: "PQueryCard",
  components: {PPageCard},
  props: {
    // 事件池
    eventPool: {
      type: Function,  // backBtn  query  reset  form-Panel   【queryBtnList按钮事件 btnKey = cloudDownload】
    },
    config:{
      type:Object,
      default: ()=>{
        return {
          /**
           * 整个组件的最外层设置  包括 外围样式 等 
           */
          PageCardConfig: {
            // 表示是否显示外围边框
            isCardBorder: false,
            // 头部下的边框
            isHeadBottomBorder: true,
            // card-body 的样式 class
            bodyClass: "",
            // card-body 的样式 style
            bodyStyle: {
              width: "100%",
              display: "flex",
              "justify-content": "center",
              "align-items": "center",
              "margin-top":"40px",
            },
            // 表示是否显示阴影  always 表示一直显示  hover 表示鼠标移动上面时显示  never 表示不显示
            shadow: "never",
            style: { margin: "10px", width: "100%" },
          },
          /**
           * 顶部标题设置
           */
          headerConfig:{
            show:true,
            title:{
              icon:'bars', // 支持 插槽  插槽名称【titleIcon】 当icon 的值为空时启用插槽
              label:'这是标题',
              labelClass:'',
              labelStyle:{},
            },
            backBtn:{
              show:true,
              icon:'left',
              btnType:'', //  组件库 Button 的 type 类型值
              label:'返回'
            },
            // 是否显示查询条件的分割线
            isDefaultHeadBottomBorder: false,
          },
          // 查询表单的布局设置  rowNum 表示行数  columnNum 表示列数     rowGap 行间距  rowHeight 行高  coliumnGap 列间距   labelCol 表单label的自适应设置   wrapperCol 表单的值部分的自适应设置
          // xs <576px    sm ≥576px    md	≥768px     lg	≥992px    xl ≥1200px      xxl	≥1600px
          layouttDefault:{ rowNum: 1, columnNum: 3, rowGap: "1px",rowHeight:'50px',  coliumnGap: "10px", labelCol: { xs: 12, sm: 12, md: 10, lg: 10, xl:10, xxl:10 }, wrapperCol: { xs: 12, sm: 12, md: 12, lg: 12, xl:12, xxl:12}},
          layouttOther:{ rowNum: 4, columnNum: 4, rowGap: "1px",rowHeight:'50px',  coliumnGap: "10px", labelCol: { xs: 12, sm: 12, md: 10, lg: 10, xl:10, xxl:10 }, wrapperCol: { xs: 12, sm: 12, md: 12, lg: 12, xl:12, xxl:12}},
        }
      },
    },
    // 和查询按钮并排放的按钮集合 如 下载  {btnKey:'11',label:'下载',icon:'cloud-download',btnType:'',vhas:'', disabled:false, otherAttr:{}}
    queryBtnList:{type:Array},
    // 查询表单集合 具体 参照表单属性   新增 componentName:'Pbutton', componentItem:Pbutton
    formDataList:{type:Array},
    // table 表设置  支持插槽 插槽名称 table
    tableConfig:{type:Object,default:()=>{
      return {
        // 用于控制是否启用自定义table的插槽, false 表示不起用 使用默认table    true 表示启用自定义table插槽
        showTableSlot:false,
        // 是否显示table  title ; 默认为false,不显示  true 表示显示 
        showTableHeader:false,
        // 是否显示table  footer ; 默认为false,不显示  true 表示显示 
        showTableFooter:false,
        columns:[],
        data:[],
        pagination:{ 
          current: 1,
          pageSize: 10,
          total: 0,
          pageSizeOptions: ['10', '20', '30'],
          showTotal: (total, range) => {
            return " 共" + total + "条"
          },
          showSizeChanger: true
        },
        // table 的其他属性
        otherAttr:{}
      }
    }},
    showHelp:{type:Boolean, default: () => { return false }}
  },
  data() {
    // 这里存放数据
    return {
      animeSwitch: false,
      configKey:'',
      /**
       * 此块内容暂不开启
       */
      PageCardConfig: {
        // 表示是否显示外围边框
        isCardBorder: false,
        // 头部下的边框
        isHeadBottomBorder: true,
        // card-body 的样式 class
        bodyClass: "",
        // card-body 的样式 style
        bodyStyle: {
          width: "100%",
          display: "flex",
          "justify-content": "center",
          "align-items": "center",
          "margin-top":"40px",
        },
        // 表示是否显示阴影  always 表示一直显示  hover 表示鼠标移动上面时显示  never 表示不显示
        shadow: "never",
        style: { padding: "10px", width: "100%" },
      },
      /**
       * 此块内容暂不开启
       */
      headerConfig:{
        show:true,
        // 是否显示查询条件的分割线
        isDefaultHeadBottomBorder: false,
        title:{
          icon:'bars',
          label:'这是标题',
          labelClass:'',
          labelStyle:{},
        },
        backBtn:{
          show:true,
          icon:'left',
          btnType:'',
          label:'返回'
        }
      },

      btnList:[
        // {btnKey:'cloudDownload',label:'下载',icon:'cloud-download',btnType:'',disabled:false}
      ],
      formConfig:{
        layouttDefault:{ rowNum: 1, columnNum: 3, rowGap: "1px",rowHeight:'50px',  coliumnGap: "10px",labelCol: { xs: 12, sm: 12, md: 10, lg: 10, xl:10, xxl:10 }, wrapperCol: { xs: 12, sm: 12, md: 12, lg: 12, xl:12, xxl:12}},
        layouttOther:{ rowNum: 4, columnNum: 4, rowGap: "1px",rowHeight:'50px',  coliumnGap: "10px",   labelCol: { xs: 12, sm: 12, md: 10, lg: 10, xl:10, xxl:10 }, wrapperCol: { xs: 12, sm: 12, md: 12, lg: 12, xl:12, xxl:12}},
        formDataListDefault:[],
        formDataListOther:[],
      },
      formData:{},
      formData_:{},

      // 用于处理值是否发生变化的key
      tableConfigKey:'',
      showTableSlot:true,
      // 是否显示table  title ; 默认为false,不显示  true 表示显示 
      showTableHeader:false,
      // 是否显示table  footer ; 默认为false,不显示  true 表示显示 
      showTableFooter:false,
      columns:[],
      dataSource:[],
      // table 的其他属性
      otherAttr:{
        bordered:true,
      },
      pagination:{
        current: 1,
        pageSize: 10,
        total: 0,
        pageSizeOptions: ['10', '20', '30'],
        showTotal: (total, range) => {
          return " 共" + total + "条"
        },
        showSizeChanger: true
      },
      customRenderList:[],
    }
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {
    queryBtnList: {
      handler(val) {
        if(IS.isArray(val)){
          this.btnList = val
        }
      },
      deep: true,
      immediate: true,
    },
    formDataList: {
      handler(val) {
        if(IS.isArray(val)){
          this.setFormData(val)
        }
      },
      deep: true,
      immediate: true,
    },
    config: {
      handler(val) {
        const newKey = UUID('MDUUID',JSON.stringify(val))
        if(!IS.isNullOrUnDef(val) && IS.isObject(val) && newKey != this.configKey){
          this.setConfig(val)
          this.configKey = newKey
        }
      },
      deep: true,
      immediate: true,
    },
    tableConfig: {
      handler(val) {
        const newKey = UUID('MDUUID',JSON.stringify(val))
        if(!IS.isNullOrUnDef(val) && IS.isObject(val) && newKey!=this.tableConfigKey){
          this.setTable(val)
          this.tableConfigKey = newKey
        }
      },
      deep: true,
      immediate: true,
    },
  },
  // 生命周期 - 创建完成(可以访问当前this实例)
  created() {},
  // 生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    this.$nextTick(()=>{
      this.showPanel()
    })
    if(this.$isShowHelp == true &&  this.showHelp==true){
        console.log(`
        组件名称:
        PQueryCard
        使用方法:样式【swiper-slide】必填
            <p-SwiperCon :onChange="onSWChange"> 
              <div class="swiper-slide">插槽1</div> 
              <div class="swiper-slide">插槽2</div> 
              <div class="swiper-slide">插槽3</div> 
            </p-SwiperCon>
        props参数说明:
            props:{
              // 此参数可选  用于相应组件的变化事件  此事件的参数为 当前切换的索引值,索引从0开始  
              onChange: {type: Function},
            },
        `)
      }
   
  },
  // 方法集合
  methods: {
    showPanel() {
      const {formConfig:{formDataListOther,layouttOther},animeSwitch,eventPool,formData} = this
      let height_ = "0px";
      let rotate_ = 0;
      let padding_ = ''
      // rowInfo
      const ys = formDataListOther.length % layouttOther.columnNum >0 ? 1 : 0;
      const rowSize = Math.floor(formDataListOther.length / layouttOther.columnNum)+ys;
      const btnSize = 3;
      const tempHeight = rowSize * 40 + 20 // rowSize * 65 + 20 + (btnSize > 0 ? 54 : 0);
      // 54

      if (animeSwitch) {
        height_ = tempHeight + "px"; // '180px'
        rotate_ = 180;
        padding_ = '10px 20px'
      }
      if (!animeSwitch) {
        height_ = "0px";
        rotate_ = 0;
        padding_ = '0px'
      }
      // console.log(rowSize,height_,this.formConfig.formDataListOther.length, this.formConfig.layouttOther.columnNum)
      anime({
        targets: ".pc-body-panel",
        height: height_,
        duration: 400,
        // backgroundColor: '#A4FF4F',
        padding:padding_,
        easing: "easeInOutQuad",
      });
      anime({
        targets: ".tb",
        rotate: {
          value: rotate_,
          duration: 400,
          easing: "easeInOutSine",
        },
      });
      this.getFormData()
      this.animeSwitch = !animeSwitch;
      eventPool && eventPool(`form-Panel`,{ItemData:null,formData:formData})
    },
    setConfig(config_){
      const {formConfig:{layouttDefault,layouttOther},headerConfig,PageCardConfig} = this
      const {PageCardConfig:PageCardConfig_={},headerConfig:headerConfig_={},layouttDefault:layouttDefault_={},layouttOther:layouttOther_={}} = config_
      
      if(!IS.isEmpty(PageCardConfig_)){
        this.PageCardConfig =Object.assign({},PageCardConfig,PageCardConfig_)
      }else{
        this.PageCardConfig =Object.assign({},PageCardConfig)
      }
      if(!IS.isEmpty(headerConfig_)){
        this.headerConfig = Object.assign({},headerConfig,headerConfig_)
      }else{
        this.headerConfig = Object.assign({},headerConfig)
      }
      if(!IS.isEmpty(layouttDefault_)){
        this.formConfig.layouttDefault = Object.assign({},layouttDefault,layouttDefault_)
      }else{
        this.formConfig.layouttDefault = Object.assign({},layouttDefault)
      }
      if(!IS.isEmpty(layouttOther_)){
        this.formConfig.layouttOther = Object.assign({},layouttOther,layouttOther_)
      }else{
        this.formConfig.layouttOther = Object.assign({},layouttOther)
      }
      // console.log('@@@@@@@@@@@@@@',config_,this.formConfig);

    },
    setFormData(formList){
      if(formList.length==0){
        return false
      }
      let defaultListArr = [], otherListArr = []
      formList.forEach(item=>{
        const {isDefault,component,...args} = item
        if(isDefault===true){
          defaultListArr.push({...args})
        }else{
          otherListArr.push({...args})
        }
      })
      // 当没有设置 isDefault 属性时,从otherListArr中选择前三个方进去
      const tempotherListArr = []
      if(defaultListArr.length==0){
        otherListArr.forEach((item,index)=>{
          if(index<3){
            defaultListArr.push(item)
          }else{
            tempotherListArr.push(item)
          }
        })
        otherListArr = tempotherListArr
      }

      this.formConfig.formDataListDefault = defaultListArr
      this.formConfig.formDataListOther = otherListArr
    },
    setCustomRender(columns){
      const tempArr = []
      if(!IS.isNullOrUnDef(columns) && IS.isArray(columns)){
        columns.forEach(item=>{
          const { scopedSlots,dataIndex,title } = item
          if (scopedSlots && scopedSlots.customRender) {
            tempArr.push({keyName:scopedSlots.customRender,dataIndex,title})
          }
        })
      }
      this.customRenderList = tempArr
    },
    setTable(config_){
      const {showTableSlot,columns,data,pagination,otherAttr,showTableHeader,showTableFooter} = config_
      this.showTableSlot = showTableSlot || false
      this.columns = columns
      this.dataSource = data
      this.showTableHeader = showTableHeader || false
      this.showTableFooter = showTableFooter || false
      this.setCustomRender(columns)
      if(!IS.isEmpty(otherAttr)){
        this.otherAttr = Object.assign({},this.otherAttr,otherAttr)
      }else{
        this.otherAttr = Object.assign({},this.otherAttr)
      }
      
      if(!IS.isEmpty(pagination)){
        this.pagination =  Object.assign({},this.pagination,pagination)
      }else{
        this.pagination =  Object.assign({},this.pagination)
      }
      if(IS.isBoolean(pagination) && pagination==false){
        this.pagination = pagination
      }
    },
    async getFormData(){
     const formRes = await this.$refs.PPageFormDefault.onSubmit()
     this.formData = {...this.formData,...formRes}
     this.formData_ = {...this.formData_,...this.$refs.PPageFormDefault.form}
     if(this.$refs.PPageFormOther){
       const formRes1 = await this.$refs.PPageFormOther.onSubmit()
       this.formData = {...this.formData,...formRes1} 
       this.formData_ = {...this.formData_,...this.$refs.PPageFormOther.form}
     }
      // this.$refs.PPageFormDefault.onSubmit((formRes)=>{
      //   this.formData = {...this.formData,...formRes}
      // })
      // this.$refs.PPageFormOther && this.$refs.PPageFormOther.onSubmit((formRes)=>{
      //   this.formData = {...this.formData,...formRes}
      // })
    },
    resetFormData(){
      this.formConfig.formDataListDefault.forEach(item=>{
        // item.value = 
      })
    },
    formEvent(...param){
      this.getFormData().then(item=>{
        this.eventPool && this.eventPool(`form-${param[0]}`,{ItemData:param,formData:{...this.formData,[param[1]]:param[2]}})
      })
    },
    btnEvent(key,item,data){
      // console.log('btnEvent 111===>',key,item,data);
      const queryAction = ()=>{
        this.getFormData().then((data)=>{
          this.eventPool && this.eventPool(key,{ItemData:data,formData:this.formData})
        })
      }

      const actionList={
        'query':(item_,data_)=>{
          queryAction()
        },
        'reset':()=>{
          this.$refs.PPageFormDefault && this.$refs.PPageFormDefault.resetForm()
          this.$refs.PPageFormOther && this.$refs.PPageFormOther.resetForm()
          this.getFormData().then(res=>{
            this.$nextTick().then(()=>{
              console.log('**********************reset**********************',JSON.stringify(this.formData),JSON.stringify(res));
              this.eventPool && this.eventPool('reset',{ItemData:data,formData:this.formData})
            })
          })
        }
      }
      actionList[key] && actionList[key](item,data)
      if(!actionList[key]){
        queryAction()
      }
    },
    tableChangeEvent(pagination, filters, sorter, { currentDataSource }){
      this.pagination.current = pagination.current
      this.pagination.pageSize = pagination.pageSize
      this.getFormData().then(res=>{
        this.eventPool && this.eventPool(`table-change`,{ItemData:{pagination, filters, sorter,currentDataSource},formData:{...this.formData}})
      })
    },
  },
}
</script>
<style lang="less" scoped>
.pc-header {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  flex-direction: column;
  .pc-header-con {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .pc-header-title {
      .pc-header-title-icon {
        margin-right: 5px;
      }
      .pc-header-title-label {
        font-weight: 600;
        color: rgba(48, 49, 51, 0.5);
      }
    }
    .pc-header-btn {
      .header-btn-item {
        margin: 0 3px;
      }
    }
  }
  .pc-header-panel {
    width: 100%;
    border: 1px solid #c3c3c3;
  }
}
.pc-body-con{
  width: 100%;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  flex-direction: column;
  .pc-body-query {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .pc-query {
      height: 100%;
      flex: 1;
      display: flex;
      justify-content:flex-start;
      align-items: center;
      /deep/.ant-form-item{
        margin-bottom: 0px;
      }
    }
    .pc-body-btn {
      .body-btn-item {
        margin: 0 3px;
      }
    }
  }
  .pc-body-panel {
    width: 100%;
    // border: 1px solid #c3c3c3;
    
    overflow: auto;
    margin: 10px 0;
    padding: 10px 20px;
    box-sizing: border-box;
    /deep/.ant-form-item{
        margin-bottom: 0px;
      }
    &.show-border-top{
      border-top: 1px solid #c3c3c3;
    }
    &.show-border{
      border: 1px solid #c3c3c3;
    }
  }
  .pc-body-table{
    width: 100%;
    // min-height: 500px;
    // border: 1px solid #c3c3c3;
  }
}
</style>