关于vue2 antd 碰到的问题总结下

news2024/11/6 3:49:30

1.关于vue2 antd 视图更新问题

1.一种强制更新 

Vue2是通过用Object…defineProperty来设置数据的getter和setter实现对数据和以及视图改变的监听的。对于数组和对象这种引用类型来说,getter和setter无法检测到它们内部的变化。用这种

 this.$set(this.form, "fileUrl", urlArr.join(","));

2.如果碰到

a-form :form="form"

 form: this.$form.createForm(this),

    <a-form :form="form" @submit="handleSubmit">
        <a-form-item
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          label="上级仓库"
          hasFeedback
        >
          <a-tree-select
            :treeData="dataSource"
            placeholder="请选择"
            :replaceFields="{
              title: 'name',
              key: 'id',
              value: 'id',
              children: 'children',
            }"
            v-decorator="['parentId', { initialValue: formData.parentId }]"
          />
        </a-form-item>
        <a-form-item
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          label="仓库名称"
          hasFeedback
        >
          <a-input
            placeholder="仓库名称"
            v-decorator="[
              'name',
              {
                initialValue: formData.name,
                rules: validatorRules.inputIf.rules,
              },
            ]"
          />
        </a-form-item>
        <a-form-item
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          label="所属机构"
          required
        >
          <vorganTree
            v-decorator="[
              'orgId',
              {
                initialValue: formData.orgId,
                rules: validatorRules.inputIf.rules,
              },
            ]"
          ></vorganTree>
        </a-form-item>
        <a-form-item
          :labelCol="labelCol"
          :wrapperCol="wrapperCol2"
          label="位置地点"
        >
          <div class="disflex">
            <a-input
              v-decorator="[
                'address',
                {
                  initialValue: formData.address,
                  rules: validatorRules.inputIf.rules,
                },
              ]"
            />
            <a-icon type="environment" @click="mapShow(true)" class="mapico" />
            <a-button type="link" @click="mapShow(true)">选择地点</a-button>
          </div>
        </a-form-item>
      </a-form>

a-form :form="form"

 form: this.$form.createForm(this),

那么使用

this.form.setFieldsValue({ address: val.address })来更新表单的值,以触发视图的更新。

清空的话

this.form.resetFields();

3.通常双向绑定如果不行试试前两种。

2.关于封装好了表格 使用

1.导入

import STable from "@/components/table/";

index.js

import T from "ant-design-vue/es/table/Table";
import get from "lodash.get"
export default {
  data() {
    return {
      needTotalList: [],
      selectedRows: [],
      selectedRowKeys: [],

      localLoading: false,
      localDataSource: [],
      localPagination: Object.assign({}, T.props.pagination)
    };
  },
  props: Object.assign({}, T.props, {
    rowKey: {
      type: [String, Function],
      default: 'id'
    },
    data: {
      type: Function,
      required: true
    },
    pageNum: {
      type: Number,
      default: 1
    },
    pageSize: {
      type: Number,
      default: 10
    },
    showSizeChanger: {
      type: Boolean,
      default: true
    },
    showAlertInfo: {
      type: Boolean,
      default: false
    },
    showPagination: {
      default: 'auto'
    },
    deleteShow: { // 删除键
      type: Boolean,
      default: false
    },
    bordered: {
      type: Boolean,
      default: true
    },
  }),
  watch: {
    'localPagination.current'(val) {
      this.$router.push({
        name: this.$route.name,
        params: Object.assign({}, this.$route.params, {
          current: val
        }),
      });
    },
    pageNum(val) {
      Object.assign(this.localPagination, {
        current: val
      });
    },
    pageSize(val) {
      Object.assign(this.localPagination, {
        pageSize: val
      });
    },
    showSizeChanger(val) {
      Object.assign(this.localPagination, {
        showSizeChanger: val
      });
    }
  },
  zzh() {
    const that = this
    return that;
  },
  created() {
    this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, {
      current: this.pageNum,
      pageSize: this.pageSize,
      showSizeChanger: this.showSizeChanger
    });
    this.needTotalList = this.initTotalList(this.columns)
    this.loadData();
  },
  methods: {
    loadingShow(value) {
      this.localLoading = value
    },
    refresh() {
      var mm = {
        current: 1,
        total: 0,
        size: this.pageSize,
      }
      this.loadData(mm);
    },
    deleteRefresh() {
      this.loadData()
    },
    loadData(pagination, filters, sorter) {
      this.localLoading = true
      var result = this.data(
        Object.assign({
          current: (pagination && pagination.current) ||
            this.localPagination.current,
          size: (pagination && pagination.pageSize) ||
            this.localPagination.pageSize
        },
          (sorter && sorter.field && {
            sortField: sorter.field
          }) || {},
          (sorter && sorter.order && {
            sortOrder: sorter.order
          }) || {}, {
          ...filters
        }
        )
      );

      if (result instanceof Promise) {
        result.then(r => {
          this.localPagination = Object.assign({}, this.localPagination, {
            current: (pagination && pagination.current) ||
              this.localPagination.current,  // 返回结果中的当前分页数
            total: r.totalCount, // 返回结果中的总记录数
            showSizeChanger: this.showSizeChanger,
            pageSize: (pagination && pagination.pageSize) ||
              this.localPagination.pageSize
          });

          !r.totalCount && ['auto', false].includes(this.showPagination) && (this.localPagination = false)
          this.localDataSource = r.data; // 返回结果中的数组数据
          this.localLoading = false
        });
      }
    },
    initTotalList(columns) {
      const totalList = []
      columns && columns instanceof Array && columns.forEach(column => {
        if (column.needTotal) {
          totalList.push({
            ...column,
            total: 0
          })
        }
      })
      return totalList
    },
    updateSelect(selectedRowKeys, selectedRows) {
      this.selectedRowKeys = selectedRowKeys
      this.selectedRows = selectedRows
      let list = this.needTotalList
      this.needTotalList = list.map(item => {
        return {
          ...item,
          total: selectedRows.reduce((sum, val) => {
            let total = sum + get(val, item.dataIndex)
            return isNaN(total) ? 0 : total
          }, 0)
        }
      })
    },
    updateEdit() {
      this.selectedRows = []
    },
    onClearSelected() {
      this.selectedRowKeys = []
      this.selectedRows = []
      this.updateSelect([], [])
      this.$emit('emptyData', { selectedRowKeys: [], selectedRows: [] })
      this.$emit('onSelect', { selectedRowKeys: [], selectedRows: [] })
    },
    onDeleteSelected() {
      this.$emit('DeleteData')
    },
    handleRowClick(record) {
      const selectedRowKeys = [...this.selectedRowKeys];
      const selectedRows = [...this.selectedRows];

      const rowKey = typeof this.rowKey === 'function' ? this.rowKey(record) : record[this.rowKey];

      const rowIndex = selectedRowKeys.indexOf(rowKey);
      if (rowIndex >= 0) {
        selectedRowKeys.splice(rowIndex, 1);
        selectedRows.splice(rowIndex, 1);
      } else {
        selectedRowKeys.push(rowKey);
        selectedRows.push(record);
      }

      this.updateSelect(selectedRowKeys, selectedRows);
      this.$emit('onSelect', { selectedRowKeys, selectedRows });
    },
    renderMsg(h) {
      const _vm = this
      this.that = this
      let d = []

      return d
    },
    renderAlert(h) {
      return h('span', {
        slot: 'message'
      }, this.renderMsg(h))
    },
  },

  render(h) {
    const _vm = this

    let props = {},
      localKeys = Object.keys(this.$data);

    Object.keys(T.props).forEach(k => {
      let localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`;
      if (localKeys.includes(localKey)) {
        return props[k] = _vm[localKey];
      }
      return props[k] = _vm[k];
    })


    // 显示信息提示
    if (this.showAlertInfo) {

      props.rowSelection = {
        selectedRowKeys: this.selectedRowKeys,
        onChange: (selectedRowKeys, selectedRows) => {
          _vm.updateSelect(selectedRowKeys, selectedRows)
          _vm.$emit('onSelect', { selectedRowKeys: selectedRowKeys, selectedRows: selectedRows })
        }
      };

      props.customRow = (record) => ({
        on: {
          click: () => {
            _vm.handleRowClick(record);
          },
        },
      });

      return h('div', {}, [
        h("a-table", {
          tag: "component",
          attrs: props,
          on: {
            change: _vm.loadData
          },
          scopedSlots: this.$scopedSlots
        }, this.$slots.default)
      ]);

    }

    props.customRow = (record) => ({
      on: {
        click: () => {
          _vm.handleRowClick(record);
        },
      },
    });

    return h("a-table", {
      tag: "component",
      attrs: props,
      on: {
        change: _vm.loadData
      },
      scopedSlots: this.$scopedSlots
    }, this.$slots.default);

  },
};

使用

 <s-table
            ref="tableAssets"
            size="default"
            :rowKey="(record) => record.id"
            :columns="columns1"
            :data="loadDataListAssets"
            :showAlertInfo="true"
            @onSelect="onChangeAssetsList"
          ></s-table>




 loadDataListAssets: (parameter) => {
        return getUserPage(Object.assign(parameter, this.listAssetsParam)).then(
          (res) => {
            var mm = { data: [] };
            mm.size = res.data.size;
            mm.current = res.data.current;
            mm.totalCount = res.data.total;
            mm.totalPage = res.data.pages;
            mm.data = res.data.records;
            return mm;
          }
        );
      },


    onChangeAssetsList(row) {
      console.log(row);
      let self = this;
      self.personList = [];
      let arr = [...new Set([...row.selectedRows])];
      let AssetsListRow = [
        ...new Set([...self.AssetsListRow.concat(arr), ...arr]),
      ];
      self.AssetsListRow = AssetsListRow.filter((item) =>
        row.selectedRowKeys.includes(item.id)
      );
      self.personList = [...new Set([...self.AssetsListRow, ...arr])];
      // self.personList = [...new Set([...self.personList, ...arr])];
      // const strings = self.personList.map((item) => JSON.stringify(item));
      // const removeDupList = Array.from(new Set(strings));
      // const result = removeDupList.map((item) => JSON.parse(item));
      // self.personList = result;
    },

有slot使用

 <s-table ref="tableAssets2" size="default" :columns="columns2" :data="loadDataListAssets2">
              <span slot="action" slot-scope="text, record" class="flx-row-c-c">
                <div>
                  <a @click="handleDel(record)">删除</a>
                </div>
              </span>
</s-table> 
           

3.关于单选封装好的组件使用

    <j-select-supplier
                  v-decorator="['supplierId']"
                  :multiple="false"
                  @accountData="accountData2"
                ></j-select-supplier>



import JSelectSupplier from "@/components/jeecgbiz/JSelectSupplier";



    accountData2(e) {
      if (e.length > 0) {
        this.editform.supplierName = e[0].name;
        this.editform.supplierId = e[0].id;
      } else {
        this.editform.supplierName = "";
        this.editform.supplierId = "";
      }
      console.log(e);
    },
<template>
  <!-- 定义在这里的参数都是不可在外部覆盖的,防止出现问题 -->
  <j-select-biz-component-two
    :value="value"
    :ellipsisLength="25"
    :listUrl="url.list"
    :columns="columns"
    :multiple="false"
    v-on="$listeners"
    v-bind="attrs"
    @commitData="commitData"
  />
</template>

<script>
import JSelectBizComponentTwo from "./JSelectBizComponentTwo";

export default {
  name: "JSelectSupplier",
  components: { JSelectBizComponentTwo },
  props: ["value"],
  data() {
    return {
      url: { list: "/asset/supplierinfo/page" },
      columns: [
        { title: "供应商", align: "center", dataIndex: "name" },
        {
          title: "联系人",
          align: "center",

          dataIndex: "linkman",
        },
        {
          title: "联系电话",
          align: "center",

          dataIndex: "phone",
        },
      ],
      // 定义在这里的参数都是可以在外部传递覆盖的,可以更灵活的定制化使用的组件
      default: {
        name: "供应商",
        width: "80%",
        displayKey: "name",
        returnKeys: ["id", "name"],
        rowKey: "id",
        valueKey: "id",
        queryParamText: "供应商名称", //左侧搜索框名称
        queryParamCode: "name", //左侧搜索框字段名
      },
    };
  },
  computed: {
    attrs() {
      return Object.assign(this.default, this.$attrs);
    },
  },
  methods: {
    // 提交数据
    commitData(e) {
      console.log(e);
      this.$emit("accountData", e);
    },
  },
};
</script>

<style lang="less" scoped></style>

外层也就是index

<template>
  <a-row class="j-select-biz-component-box" type="flex" :gutter="8">
    <a-col class="left" :class="{'full': !buttons}">
      <slot name="left">
        <a-select
          mode="multiple"
          :placeholder="placeholder"
          v-model="selectValue"
          :options="selectOptions"
          allowClear
          :disabled="disabled"
          :open="selectOpen"
          style="width: 100%;"
          @dropdownVisibleChange="handleDropdownVisibleChange"
          @click.native="visible=(buttons?visible:true)"
        />
      </slot>
    </a-col>

    <a-col v-if="buttons" class="right">
      <a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
    </a-col>

    <j-select-biz-component-modal
      v-model="selectValue"
      :visible.sync="visible"
      v-bind="modalProps"
      :multiple="multiple"
      @options="handleOptions"
      @commitData="commitData"
    />
  </a-row>
</template>

<script>
  import JSelectBizComponentModal from './JSelectBizComponentModal'

  export default {
    name: 'JSelectBizComponent',
    components: { JSelectBizComponentModal },
    props: {
      value: {
        type: String,
        default: ''
      },
      /** 是否返回 id,默认 false,返回 code */
      returnId: {
        type: Boolean,
        default: false
      },
      placeholder: {
        type: String,
        default: '请选择'
      },
      disabled: {
        type: Boolean,
        default: false
      },
      // 是否支持多选,默认 true
      multiple: {
        type: Boolean,
        default: true
      },
      // 是否显示按钮,默认 true
      buttons: {
        type: Boolean,
        default: true
      },
      // 显示的 Key
      displayKey: {
        type: String,
        default: null
      },
      // 返回的 key
      returnKeys: {
        type: Array,
        default: () => ['id', 'id']
      },
      // 选择按钮文字
      selectButtonText: {
        type: String,
        default: '选择'
      },

    },
    data() {
      return {
        selectValue: [],
        selectOptions: [],
        dataSourceMap: {},
        visible: false,
        selectOpen: false,
      }
    },
    computed: {
      valueKey() {
        return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
      },
      modalProps() {
        return Object.assign({
          valueKey: this.valueKey,
          multiple: this.multiple,
          returnKeys: this.returnKeys,
          displayKey: this.displayKey || this.valueKey
        }, this.$attrs)
      },
    },
    watch: {
      value: {
        immediate: true,
        handler(val) {
          console.log('cake撒',val)
          if (val) {
            this.selectValue = val.split(',')
          } else {
            this.selectValue = []
          }
        }
      },
      selectValue: {
        deep: true,
        handler(val) {
          console.log(this.materialUseTime)
          let rows = val.map(key => this.dataSourceMap[key])
          this.$emit('select', rows)
          let data = val.join(',')
          this.$emit('input', data)
          this.$emit('change', data)
        }
      }
    },
    methods: {
      commitData(e) {
        this.$emit('commitData',e)
      },
      handleOptions(options, dataSourceMap) {
        this.selectOptions = options
        this.dataSourceMap = dataSourceMap
      },
      handleDropdownVisibleChange() {
        // 解决antdv自己的bug —— open 设置为 false 了,点击后还是添加了 open 样式,导致点击事件失效
        this.selectOpen = true
        this.$nextTick(() => {
          this.selectOpen = false
        })
      },
    }
  }
</script>

<style lang="less" scoped>
  .j-select-biz-component-box {

    @width: 82px;

    .left {
      width: calc(100% - @width - 8px);
    }

    .right {
      width: @width;
    }

    .full {
      width: 100%;
    }

    /deep/ .ant-select-search__field {
      display: none !important;
    }
  }
</style>

 内层

<template>
  <a-modal
    centered
    :title="name + '选择'"
    :width="width"
    :visible="visible"
    @ok="handleOk"
    @cancel="close"
    cancelText="关闭"
  >
    <a-row :gutter="18">
      <a-col :span="16">
        <!-- 查询区域 -->
        <div class="table-page-search-wrapper">
          <a-form layout="inline">
            <a-row :gutter="24">
              <a-col :span="14">
                <a-form-item :label="queryParamText || name">
                  <a-input
                    v-model="queryParam[queryParamCode || valueKey]"
                    :placeholder="'请输入' + (queryParamText || name)"
                    @pressEnter="searchQuery"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="8">
                <span
                  style="float: left; overflow: hidden"
                  class="table-page-search-submitButtons"
                >
                  <a-button type="primary" @click="searchQuery" icon="search"
                    >查询</a-button
                  >
                  <a-button
                    type="primary"
                    @click="searchReset"
                    icon="reload"
                    style="margin-left: 8px"
                    >重置</a-button
                  >
                </span>
              </a-col>
            </a-row>
          </a-form>
        </div>

        <a-table
          size="small"
          bordered
          :rowKey="rowKey"
          :columns="innerColumns"
          :dataSource="dataSource"
          :pagination="ipagination"
          :loading="loading"
          :scroll="{ y: 240 }"
          :rowSelection="{
            selectedRowKeys,
            onChange: onSelectChange,
            type: multiple ? 'checkbox' : 'radio',
            width: '60px',
          }"
          :customRow="customRowFn"
          @change="handleTableChange"
        >
        </a-table>
      </a-col>
      <a-col :span="8">
        <a-card
          :title="'已选中的' + name"
          :bordered="false"
          :head-style="{ padding: 0 }"
          :body-style="{ padding: 0 }"
        >
          <a-table
            size="small"
            :rowKey="rowKey"
            bordered
            v-bind="selectedTable"
          >
            <span slot="action" slot-scope="text, record, index">
              <a @click="handleDeleteSelected(record, index)">删除</a>
            </span>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
  </a-modal>
</template>

<script>
// import { getAction } from '@/api/manage'
import Ellipsis from "@/components/Ellipsis";
import { JeecgListMixin } from "@/mixins/JeecgListMixin";
import { cloneObject, pushIfNotExist } from "@/utils/util";

export default {
  name: "JSelectBizComponentModal",
  mixins: [JeecgListMixin],
  components: { Ellipsis },
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    visible: {
      type: Boolean,
      default: false,
    },
    valueKey: {
      type: String,
      required: true,
    },
    multiple: {
      type: Boolean,
      default: true,
    },
    width: {
      type: [Number, String],
      default: "80%",
    },

    name: {
      type: String,
      default: "",
    },
    listUrl: {
      type: String,
      required: true,
      default: "",
    },
    // 根据 value 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname
    valueUrl: {
      type: String,
      default: "",
    },
    displayKey: {
      type: String,
      default: null,
    },
    columns: {
      type: Array,
      required: true,
      default: () => [],
    },
    // 查询条件Code
    queryParamCode: {
      type: String,
      default: null,
    },
    // 查询条件文字
    queryParamText: {
      type: String,
      default: null,
    },
    rowKey: {
      type: String,
      default: "id",
    },
    // 过长裁剪长度,设置为 -1 代表不裁剪
    ellipsisLength: {
      type: Number,
      default: 12,
    },
  },
  data() {
    return {
      innerValue: [],
      // 已选择列表
      selectedTable: {
        pagination: false,
        scroll: { y: 240 },
        columns: [
          {
            ...this.columns[0],
            width: this.columns[0].widthRight || this.columns[0].width,
          },
          {
            ...this.columns[1],
            width: this.columns[1].widthRight || this.columns[1].width,
          },
          {
            title: "操作",
            dataIndex: "action",
            align: "center",
            width: 60,
            scopedSlots: { customRender: "action" },
          },
        ],
        dataSource: [],
      },
      renderEllipsis: (value) => (
        <ellipsis length={this.ellipsisLength}>{value}</ellipsis>
      ),
      url: { list: this.listUrl },
      /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: 5,
        pageSizeOptions: ["5", "10", "20", "30"],
        showTotal: (total, range) => {
          return range[0] + "-" + range[1] + " 共" + total + "条";
        },
        showQuickJumper: true,
        showSizeChanger: true,
        total: 0,
      },
      options: [],
      dataSourceMap: {},
    };
  },
  computed: {
    // 表头
    innerColumns() {
      let columns = cloneObject(this.columns);
      columns.forEach((column) => {
        // 给所有的列加上过长裁剪
        if (this.ellipsisLength !== -1) {
          column.customRender = (text) => this.renderEllipsis(text);
        }
      });
      return columns;
    },
  },
  watch: {
    value: {
      deep: true,
      immediate: true,
      handler(val) {
        console.log(val);
        this.innerValue = cloneObject(val);
        this.selectedRowKeys = [];
        this.valueWatchHandler(val);
        this.queryOptionsByValue(val);
      },
    },
    dataSource: {
      deep: true,
      handler(val) {
        // 重置之后key恢复
        this.emitOptions(val);
        this.valueWatchHandler(this.innerValue);
      },
    },
    selectedRowKeys: {
      immediate: true,
      deep: true,
      handler(val) {
        this.selectedTable.dataSource = val.map((key) => {
          for (let data of this.dataSource) {
            if (data[this.rowKey] === key) {
              pushIfNotExist(this.innerValue, data[this.valueKey]);
              return data;
            }
          }
          for (let data of this.selectedTable.dataSource) {
            if (data[this.rowKey] === key) {
              pushIfNotExist(this.innerValue, data[this.valueKey]);
              return data;
            }
          }
          console.warn("未找到选择的行信息,key:" + key);
          return {};
        });
      },
    },
  },

  methods: {
    /** 关闭弹窗 */
    close() {
      this.$emit("update:visible", false);
    },

    valueWatchHandler(val) {
      console.log(val);
      val.forEach((item) => {
        this.dataSource
          .concat(this.selectedTable.dataSource)
          .forEach((data) => {
            if (data[this.valueKey] === item) {
              pushIfNotExist(this.selectedRowKeys, data[this.rowKey]);
            }
          });
      });
    },

    queryOptionsByValue(value) {
      console.log(value);
      if (!value || value.length === 0) {
        return;
      }
      // 判断options是否存在value,如果已存在数据就不再请求后台了
      let notExist = false;
      for (let val of value) {
        let find = false;
        for (let option of this.options) {
          if (val === option.value) {
            find = true;
            break;
          }
        }
        if (!find) {
          notExist = true;
          break;
        }
      }
      if (!notExist) return;
      // listDepartUser( {
      //   // 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
      //   // [this.valueKey]: value.join(',') + ',',
      //   pageNo: 1,
      //   pageSize: value.length
      // }).then((res) => {
      //   if (res.success) {
      //     let dataSource = res.result
      //     if (!(dataSource instanceof Array)) {
      //       dataSource = res.result.records
      //     }
      //     this.emitOptions(dataSource, (data) => {
      //       pushIfNotExist(this.innerValue, data[this.valueKey])
      //       pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
      //       pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
      //     })
      //   }
      // })
    },

    emitOptions(dataSource, callback) {
      dataSource.forEach((data) => {
        let key = data[this.valueKey];
        this.dataSourceMap[key] = data;
        pushIfNotExist(
          this.options,
          { label: data[this.displayKey || this.valueKey], value: key },
          "value"
        );
        typeof callback === "function" ? callback(data) : "";
      });
      this.$emit("options", this.options, this.dataSourceMap);
    },

    /** 完成选择 */
    handleOk() {
      let value = this.selectedTable.dataSource.map(
        (data) => data[this.valueKey]
      );
      this.$emit("input", value);
      this.$emit("commitData", this.selectedTable.dataSource);
      this.close();
    },

    /** 删除已选择的 */
    handleDeleteSelected(record, index) {
      this.selectionRows.splice(
        this.selectedRowKeys.indexOf(record[this.rowKey]),
        1
      );
      this.selectedRowKeys.splice(
        this.selectedRowKeys.indexOf(record[this.rowKey]),
        1
      );
      this.selectedTable.dataSource.splice(index, 1);
      console.log(this.selectedRowKeys, this.selectionRows);
    },

    customRowFn(record) {
      return {
        on: {
          click: () => {
            let key = record[this.rowKey];
            if (!this.multiple) {
              this.selectedRowKeys = [key];
              this.selectedTable.dataSource = [record];
            } else {
              let index = this.selectedRowKeys.indexOf(key);
              if (index === -1) {
                this.selectedRowKeys.push(key);
                this.selectedTable.dataSource.push(record);
              } else {
                this.handleDeleteSelected(record, index);
              }
            }
          },
        },
      };
    },
  },
};
</script>
<style lang="less" scoped>
</style>

JeecgListMixin的方法

/**
 * 新增修改完成调用 modalFormOk方法 编辑弹框组件ref定义为modalForm
 * 高级查询按钮调用 superQuery方法  高级查询组件ref定义为superQueryModal
 * data中url定义 list为查询列表  delete为删除单条记录  deleteBatch为批量删除
 */
import { filterObj } from "@/utils/util";
import { deleteAction, getAction, downFile, getFileAccessHttpUrl } from "@/api/manage";

export const JeecgListMixin = {
  data() {
    return {
      /* 查询条件-请不要在queryParam中声明非字符串值的属性 */
      queryParam: {},
      /* 数据源 */
      dataSource: [],
      /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: 10,
        pageSizeOptions: ["10", "20", "30"],
        showTotal: (total, range) => {
          return range[0] + "-" + range[1] + " 共" + total + "条";
        },
        showQuickJumper: true,
        showSizeChanger: true,
        total: 0,
      },
      /* 排序参数 */
      isorter: {
        column: "createTime",
        order: "desc",
      },
      /* 筛选参数 */
      filters: {},
      /* table加载状态 */
      loading: false,
      /* table选中keys*/
      selectedRowKeys: [],
      /* table选中records*/
      selectionRows: [],
      /* 查询折叠 */
      toggleSearchStatus: false,
      /* 高级查询条件生效状态 */
      superQueryFlag: false,
      /* 高级查询条件 */
      superQueryParams: "",
      /** 高级查询拼接方式 */
      superQueryMatchType: "and",
    };
  },
  created() {
    if (!this.disableMixinCreated) {
      console.log(" -- mixin created -- ");
      this.loadData();
      //初始化字典配置 在自己页面定义
      this.initDictConfig();
    }
  },
  computed: {},
  methods: {
    loadData(arg) {
      if (!this.url.list) {
        this.$message.error("请设置url.list属性!");
        return;
      }
      //加载数据 若传入参数1则加载第一页的内容
      if (arg === 1) {
        this.ipagination.current = 1;
      }
      var params = this.getQueryParams(); //查询条件
      this.loading = true;
      getAction(this.url.list, params)
        .then((res) => {
          if (res.ok) {
            //update-begin---author:zhangyafei    Date:20201118  for:适配不分页的数据列表------------
            this.dataSource = res.data.records || res.data;
            if (res.data.total) {
              this.ipagination.total = res.data.total;
            } else {
              this.ipagination.total = 0;
            }
            //update-end---author:zhangyafei    Date:20201118  for:适配不分页的数据列表------------
          } else {
            this.$message.warning(res.msg);
          }
        })
        .finally(() => {
          this.loading = false;
        });
    },
    initDictConfig() {
      console.log("--这是一个假的方法!");
    },
    handleSuperQuery(params, matchType) {
      //高级查询方法
      if (!params) {
        this.superQueryParams = "";
        this.superQueryFlag = false;
      } else {
        this.superQueryFlag = true;
        this.superQueryParams = JSON.stringify(params);
        this.superQueryMatchType = matchType;
      }
      this.loadData(1);
    },
    getQueryParams() {
      //获取查询条件
      let sqp = {};
      if (this.superQueryParams) {
        sqp["superQueryParams"] = encodeURI(this.superQueryParams);
        sqp["superQueryMatchType"] = this.superQueryMatchType;
      }
      var param = Object.assign(sqp, this.queryParam, this.isorter, this.filters);
      // param.field = this.getQueryField();
      param.current = this.ipagination.current;
      param.size = this.ipagination.pageSize;
      return filterObj(param);
    },
    getQueryField() {
      //TODO 字段权限控制
      var str = "id,";
      this.columns.forEach(function (value) {
        str += "," + value.dataIndex;
      });
      return str;
    },
    onSelectChange(selectedRowKeys, selectionRows) {
      this.selectedRowKeys = selectedRowKeys;
      this.selectionRows = selectionRows;
      console.log(selectedRowKeys, selectionRows);
    },
    onClearSelected() {
      this.selectedRowKeys = [];
      this.selectionRows = [];
    },
    searchQuery() {
      this.loadData(1);
    },
    superQuery() {
      this.$refs.superQueryModal.show();
    },
    searchReset() {
      this.queryParam = {};
      this.loadData(1);
    },
    batchDel: function () {
      if (!this.url.deleteBatch) {
        this.$message.error("请设置url.deleteBatch属性!");
        return;
      }
      if (this.selectedRowKeys.length <= 0) {
        this.$message.warning("请选择一条记录!");
        return;
      } else {
        var ids = "";
        for (var a = 0; a < this.selectedRowKeys.length; a++) {
          ids += this.selectedRowKeys[a] + ",";
        }
        var that = this;
        this.$confirm({
          title: "确认删除",
          content: "是否删除选中数据?",
          onOk: function () {
            that.loading = true;
            deleteAction(that.url.deleteBatch, { ids: ids })
              .then((res) => {
                if (res.success) {
                  //重新计算分页问题
                  that.reCalculatePage(that.selectedRowKeys.length);
                  that.$message.success(res.message);
                  that.loadData();
                  that.onClearSelected();
                } else {
                  that.$message.warning(res.message);
                }
              })
              .finally(() => {
                that.loading = false;
              });
          },
        });
      }
    },
    handleDelete: function (id) {
      if (!this.url.delete) {
        this.$message.error("请设置url.delete属性!");
        return;
      }
      var that = this;
      deleteAction(`${that.url.delete}/${id}`, { id: id }).then((res) => {
        if (res.ok) {
          //重新计算分页问题
          that.reCalculatePage(1);
          that.$message.success("操作成功");
          that.loadData();
        } else {
          that.$message.warning(res.msg);
        }
      });
    },
    reCalculatePage(count) {
      //总数量-count
      let total = this.ipagination.total - count;
      //获取删除后的分页数
      let currentIndex = Math.ceil(total / this.ipagination.pageSize);
      //删除后的分页数<所在当前页
      if (currentIndex < this.ipagination.current) {
        this.ipagination.current = currentIndex;
      }
      console.log("currentIndex", currentIndex);
    },
    handleEdit: function (record) {
      this.$refs.modalForm.edit(record);
      this.$refs.modalForm.title = "编辑";
      this.$refs.modalForm.disableSubmit = false;
    },
    handleAdd: function () {
      this.$refs.modalForm.add();
      this.$refs.modalForm.title = "新增";
      this.$refs.modalForm.disableSubmit = false;
    },
    handleTableChange(pagination, filters, sorter) {
      //分页、排序、筛选变化时触发
      //TODO 筛选
      console.log(pagination);
      if (Object.keys(sorter).length > 0) {
        // this.isorter.column = sorter.field;
        this.isorter.order = "ascend" == sorter.order ? "asc" : "desc";
      }
      this.ipagination = pagination;
      this.loadData();
    },
    handleToggleSearch() {
      this.toggleSearchStatus = !this.toggleSearchStatus;
    },
    // 给popup查询使用(查询区域不支持回填多个字段,限制只返回一个字段)
    getPopupField(fields) {
      return fields.split(",")[0];
    },
    modalFormOk() {
      // 新增/修改 成功时,重载列表
      this.loadData();
      //清空列表选中
      this.onClearSelected();
    },
    handleDetail: function (record) {
      this.$refs.modalForm.edit(record);
      this.$refs.modalForm.title = "详情";
      this.$refs.modalForm.disableSubmit = true;
    },
    /* 导出 */
    handleExportXls2() {
      let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
      let url = `${window._CONFIG["domianURL"]}/${this.url.exportXlsUrl}?paramsStr=${paramsStr}`;
      window.location.href = url;
    },
    handleExportXls(fileName) {
      if (!fileName || typeof fileName != "string") {
        fileName = "导出文件";
      }
      let param = this.getQueryParams();
      if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
        param["selections"] = this.selectedRowKeys.join(",");
      }
      console.log("导出参数", param);
      downFile(this.url.exportXlsUrl, param).then((data) => {
        if (!data) {
          this.$message.warning("文件下载失败");
          return;
        }
        if (typeof window.navigator.msSaveBlob !== "undefined") {
          window.navigator.msSaveBlob(new Blob([data], { type: "application/vnd.ms-excel" }), fileName + ".xls");
        } else {
          console.log(data);
          let url = window.URL.createObjectURL(new Blob([data.data], { type: "application/vnd.ms-excel" }));
          let link = document.createElement("a");
          link.style.display = "none";
          link.href = url;
          link.setAttribute("download", fileName + ".xls");
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link); //下载完成移除元素
          window.URL.revokeObjectURL(url); //释放掉blob对象
        }
      });
    },
    /* 图片预览 */
    getImgView(text) {
      if (text && text.indexOf(",") > 0) {
        text = text.substring(0, text.indexOf(","));
      }
      return getFileAccessHttpUrl(text);
    },
    /* 文件下载 */
    // update--autor:lvdandan-----date:20200630------for:修改下载文件方法名uploadFile改为downloadFile------
    downloadFile(text) {
      if (!text) {
        this.$message.warning("未知的文件");
        return;
      }
      if (text.indexOf(",") > 0) {
        text = text.substring(0, text.indexOf(","));
      }
      let url = getFileAccessHttpUrl(text);
      window.open(url);
    },
  },
};

 

Ellipsis的方法

import Ellipsis from './Ellipsis'

export default Ellipsis

Ellipsis.vue

<script>
  import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'

  export default {
    name: 'Ellipsis',
    props: {
      prefixCls: {
        type: String,
        default: 'ant-pro-ellipsis'
      },
      tooltip: {
        type: Boolean,
        default: true,
      },
      length: {
        type: Number,
        default: 25,
      },
      lines: {
        type: Number,
        default: 1
      },
      fullWidthRecognition: {
        type: Boolean,
        default: false
      }
    },
    methods: {},
    render() {
      const { tooltip, length } = this.$props
      let text = ''
      // 处理没有default插槽时的特殊情况
      if (this.$slots.default) {
        text = this.$slots.default.map(vNode => vNode.text).join('')
      }
      // 判断是否显示 tooltip
      if (tooltip && getStrFullLength(text) > length) {
        return (
          <a-tooltip>
            <template slot="title">{text}</template>
            <span>{cutStrByFullLength(text, this.length) + '…'}</span>
          </a-tooltip>
        )
      } else {
        return (<span>{text}</span>)
      }
    }
  }
</script>

4.关于封装好的复选表格

  <j-select-contract
                  :trigger-change="true"
                  v-decorator="['purchaseContractIds']"
                  customReturnField="purchaseContractIds"
                  @accountData="changeContract"
                  :multiple="true"
                  :status="2"
                ></j-select-contract>
import JSelectContract from "@/components/jeecgbiz/JSelectContract";

<template>
  <!-- 定义在这里的参数都是不可在外部覆盖的,防止出现问题 -->
  <j-select-biz-component
    :value="value"
    :ellipsisLength="25"
    :listUrl="url.list"
    :columns="columns"
    :multiple="multiple"
    v-on="$listeners"
    v-bind="attrs"
    @commitData="commitData"
  />
</template>

<script>
import JSelectBizComponent from "./JSelectBizComponent";

export default {
  name: "JSelectContract",
  components: { JSelectBizComponent },
  props: ["value", "multiple"],
  data() {
    return {
      url: { list: "/asset/purchasecontract/page?contractStatus=2&isAllTake=0" },
      columns: [
        { title: "合同单据号", align: "center", width: "25%", widthRight: "70%", dataIndex: "purchaseContractNo" },
        { title: "订购时间", align: "center", width: "25%", dataIndex: "orderDate" },
        { title: "采购员", align: "center", width: "20%", dataIndex: "purchasePersonName" },
        { title: "采购部门", align: "center", width: "20%", dataIndex: "organName" },
      ],
      // 定义在这里的参数都是可以在外部传递覆盖的,可以更灵活的定制化使用的组件
      default: {
        name: "采购合同",
        width: 1200,
        displayKey: "purchaseContractNo",
        returnKeys: ["id", "purchaseContractNo", "organName", "organId"],
        queryParamText: "合同单据号",
        queryParamCode: "purchaseContractNo",
      },
    };
  },
  computed: {
    attrs() {
      return Object.assign(this.default, this.$attrs);
    },
  },
  methods: {
    // 提交数据
    commitData(e) {
      this.$emit("accountData", e);
    },
  },
};
</script>

<style lang="less" scoped></style>

 index外层

<template>
  <a-row class="j-select-biz-component-box" type="flex" :gutter="8">
    <a-col class="left" :class="{'full': !buttons}">
      <slot name="left">
        <a-select
          mode="multiple"
          :placeholder="placeholder"
          v-model="selectValue"
          :options="selectOptions"
          allowClear
          :disabled="disabled"
          :open="selectOpen"
          style="width: 100%;"
          @dropdownVisibleChange="handleDropdownVisibleChange"
          @click.native="visible=(buttons?visible:true)"
        />
      </slot>
    </a-col>

    <a-col v-if="buttons" class="right">
      <a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
    </a-col>

    <j-select-biz-component-modal
      v-model="selectValue"
      :visible.sync="visible"
      v-bind="modalProps"
      :multiple="multiple"
      @options="handleOptions"
      @commitData="commitData"
    />
  </a-row>
</template>

<script>
  import JSelectBizComponentModal from './JSelectBizComponentModal'

  export default {
    name: 'JSelectBizComponent',
    components: { JSelectBizComponentModal },
    props: {
      value: {
        type: String,
        default: ''
      },
      /** 是否返回 id,默认 false,返回 code */
      returnId: {
        type: Boolean,
        default: false
      },
      placeholder: {
        type: String,
        default: '请选择'
      },
      disabled: {
        type: Boolean,
        default: false
      },
      // 是否支持多选,默认 true
      multiple: {
        type: Boolean,
        default: true
      },
      // 是否显示按钮,默认 true
      buttons: {
        type: Boolean,
        default: true
      },
      // 显示的 Key
      displayKey: {
        type: String,
        default: null
      },
      // 返回的 key
      returnKeys: {
        type: Array,
        default: () => ['id', 'id']
      },
      // 选择按钮文字
      selectButtonText: {
        type: String,
        default: '选择'
      },

    },
    data() {
      return {
        selectValue: [],
        selectOptions: [],
        dataSourceMap: {},
        visible: false,
        selectOpen: false,
      }
    },
    computed: {
      valueKey() {
        return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
      },
      modalProps() {
        return Object.assign({
          valueKey: this.valueKey,
          multiple: this.multiple,
          returnKeys: this.returnKeys,
          displayKey: this.displayKey || this.valueKey
        }, this.$attrs)
      },
    },
    watch: {
      value: {
        immediate: true,
        handler(val) {
          console.log('cake撒',val)
          if (val) {
            this.selectValue = val.split(',')
          } else {
            this.selectValue = []
          }
        }
      },
      selectValue: {
        deep: true,
        handler(val) {
          console.log(this.materialUseTime)
          let rows = val.map(key => this.dataSourceMap[key])
          this.$emit('select', rows)
          let data = val.join(',')
          this.$emit('input', data)
          this.$emit('change', data)
        }
      }
    },
    methods: {
      commitData(e) {
        this.$emit('commitData',e)
      },
      handleOptions(options, dataSourceMap) {
        this.selectOptions = options
        this.dataSourceMap = dataSourceMap
      },
      handleDropdownVisibleChange() {
        // 解决antdv自己的bug —— open 设置为 false 了,点击后还是添加了 open 样式,导致点击事件失效
        this.selectOpen = true
        this.$nextTick(() => {
          this.selectOpen = false
        })
      },
    }
  }
</script>

<style lang="less" scoped>
  .j-select-biz-component-box {

    @width: 82px;

    .left {
      width: calc(100% - @width - 8px);
    }

    .right {
      width: @width;
    }

    .full {
      width: 100%;
    }

    /deep/ .ant-select-search__field {
      display: none !important;
    }
  }
</style>

 内层

<template>
  <a-modal
    centered
    :title="name + '选择'"
    :width="width"
    :visible="visible"
    @ok="handleOk"
    @cancel="close"
    cancelText="关闭"
  >
    <a-row :gutter="18">
      <a-col :span="16">
        <!-- 查询区域 -->
        <div class="table-page-search-wrapper">
          <a-form :labelCol="{ span: 5 }" :wrapperCol="{ span: 18, offset: 1 }">
            <a-row :gutter="24">
              <a-col :span="14">
                <a-form-item :label="queryParamText || name">
                  <a-input
                    v-model="queryParam[queryParamCode || valueKey]"
                    :placeholder="'请输入' + (queryParamText || name)"
                    @pressEnter="searchQuery"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="8">
                <span
                  style="float: left; overflow: hidden"
                  class="table-page-search-submitButtons"
                >
                  <a-button type="primary" @click="searchQuery" icon="search"
                    >查询</a-button
                  >
                  <a-button
                    type="primary"
                    @click="searchReset"
                    icon="reload"
                    style="margin-left: 8px"
                    >重置</a-button
                  >
                </span>
              </a-col>
            </a-row>
          </a-form>
        </div>

        <a-table
          size="small"
          bordered
          :rowKey="rowKey"
          :columns="innerColumns"
          :dataSource="dataSource"
          :pagination="ipagination"
          :loading="loading"
          :scroll="{ y: 240 }"
          :rowSelection="{
            selectedRowKeys,
            onChange: onSelectChange,
            type: multiple ? 'checkbox' : 'radio',
          }"
          :customRow="customRowFn"
          @change="handleTableChange"
        >
        </a-table>
      </a-col>
      <a-col :span="8">
        <a-card
          :title="'已选' + name"
          :bordered="false"
          :head-style="{ padding: 0 }"
          :body-style="{ padding: 0 }"
        >
          <a-table
            size="small"
            :rowKey="rowKey"
            bordered
            v-bind="selectedTable"
          >
            <span slot="action" slot-scope="text, record, index">
              <a @click="handleDeleteSelected(record, index)">删除</a>
            </span>
          </a-table>
        </a-card>
      </a-col>
    </a-row>
  </a-modal>
</template>

<script>
// import { getAction } from '@/api/manage'
import Ellipsis from "@/components/Ellipsis";
import { JeecgListMixin } from "@/mixins/JeecgListMixin";
import { cloneObject, pushIfNotExist } from "@/utils/util";
import { listDepartUser } from "@/api/apis";

export default {
  name: "JSelectBizComponentModal",
  mixins: [JeecgListMixin],
  components: { Ellipsis },
  props: {
    value: {
      type: Array,
      default: () => [],
    },
    visible: {
      type: Boolean,
      default: false,
    },
    valueKey: {
      type: String,
      required: true,
    },
    multiple: {
      type: Boolean,
      default: true,
    },
    width: {
      type: Number,
      default: 900,
    },

    name: {
      type: String,
      default: "",
    },
    listUrl: {
      type: String,
      required: true,
      default: "",
    },
    // 根据 value 获取显示文本的地址,例如存的是 username,可以通过该地址获取到 realname
    valueUrl: {
      type: String,
      default: "",
    },
    displayKey: {
      type: String,
      default: null,
    },
    columns: {
      type: Array,
      required: true,
      default: () => [],
    },
    // 查询条件Code
    queryParamCode: {
      type: String,
      default: null,
    },
    // 查询条件文字
    queryParamText: {
      type: String,
      default: null,
    },
    rowKey: {
      type: String,
      default: "id",
    },
    // 过长裁剪长度,设置为 -1 代表不裁剪
    ellipsisLength: {
      type: Number,
      default: 12,
    },
  },
  data() {
    return {
      innerValue: [],
      // 已选择列表
      selectedTable: {
        pagination: false,
        scroll: { y: 240 },
        columns: [
          {
            ...this.columns[0],
            width: this.columns[0].widthRight || this.columns[0].width,
          },
          {
            title: "操作",
            dataIndex: "action",
            align: "center",
            width: 60,
            scopedSlots: { customRender: "action" },
          },
        ],
        dataSource: [],
      },
      renderEllipsis: (value) => (
        <ellipsis length={this.ellipsisLength}>{value}</ellipsis>
      ),
      url: { list: this.listUrl },
      /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: 5,
        pageSizeOptions: ["5", "10", "20", "30"],
        showTotal: (total, range) => {
          return range[0] + "-" + range[1] + " 共" + total + "条";
        },
        showQuickJumper: true,
        showSizeChanger: true,
        total: 0,
      },
      options: [],
      dataSourceMap: {},
    };
  },
  computed: {
    // 表头
    innerColumns() {
      let columns = cloneObject(this.columns);
      columns.forEach((column) => {
        // 给所有的列加上过长裁剪
        if (this.ellipsisLength !== -1) {
          column.customRender = (text) => this.renderEllipsis(text);
        }
      });
      return columns;
    },
  },
  watch: {
    value: {
      deep: true,
      immediate: true,
      handler(val) {
        console.log(val);
        this.innerValue = cloneObject(val);
        this.selectedRowKeys = [];
        this.valueWatchHandler(val);
        this.queryOptionsByValue(val);
      },
    },
    dataSource: {
      deep: true,
      handler(val) {
        // 重置之后key恢复
        this.emitOptions(val);
        this.valueWatchHandler(this.innerValue);
      },
    },
    selectedRowKeys: {
      immediate: true,
      deep: true,
      handler(val) {
        this.selectedTable.dataSource = val.map((key) => {
          for (let data of this.dataSource) {
            if (data[this.rowKey] === key) {
              pushIfNotExist(this.innerValue, data[this.valueKey]);
              return data;
            }
          }
          for (let data of this.selectedTable.dataSource) {
            if (data[this.rowKey] === key) {
              pushIfNotExist(this.innerValue, data[this.valueKey]);
              return data;
            }
          }
          console.warn("未找到选择的行信息,key:" + key);
          return {};
        });
      },
    },
  },

  methods: {
    /** 关闭弹窗 */
    close() {
      this.$emit("update:visible", false);
    },

    valueWatchHandler(val) {
      console.log(val);
      val.forEach((item) => {
        this.dataSource
          .concat(this.selectedTable.dataSource)
          .forEach((data) => {
            if (data[this.valueKey] === item) {
              pushIfNotExist(this.selectedRowKeys, data[this.rowKey]);
            }
          });
      });
    },

    queryOptionsByValue(value) {
      console.log(value);
      if (!value || value.length === 0) {
        return;
      }
      // 判断options是否存在value,如果已存在数据就不再请求后台了
      let notExist = false;
      for (let val of value) {
        let find = false;
        for (let option of this.options) {
          if (val === option.value) {
            find = true;
            break;
          }
        }
        if (!find) {
          notExist = true;
          break;
        }
      }
      if (!notExist) return;
      // listDepartUser( {
      //   // 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
      //   // [this.valueKey]: value.join(',') + ',',
      //   pageNo: 1,
      //   pageSize: value.length
      // }).then((res) => {
      //   if (res.success) {
      //     let dataSource = res.result
      //     if (!(dataSource instanceof Array)) {
      //       dataSource = res.result.records
      //     }
      //     this.emitOptions(dataSource, (data) => {
      //       pushIfNotExist(this.innerValue, data[this.valueKey])
      //       pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
      //       pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
      //     })
      //   }
      // })
    },

    emitOptions(dataSource, callback) {
      dataSource.forEach((data) => {
        let key = data[this.valueKey];
        this.dataSourceMap[key] = data;
        pushIfNotExist(
          this.options,
          { label: data[this.displayKey || this.valueKey], value: key },
          "value"
        );
        typeof callback === "function" ? callback(data) : "";
      });
      this.$emit("options", this.options, this.dataSourceMap);
    },

    /** 完成选择 */
    handleOk() {
      let value = this.selectedTable.dataSource.map(
        (data) => data[this.valueKey]
      );
      this.$emit("input", value);
      console.log(this.selectedTable.dataSource);
      this.$emit("commitData", this.selectedTable.dataSource);
      this.close();
    },

    /** 删除已选择的 */
    handleDeleteSelected(record, index) {
      this.selectionRows.splice(
        this.selectedRowKeys.indexOf(record[this.rowKey]),
        1
      );
      this.selectedRowKeys.splice(
        this.selectedRowKeys.indexOf(record[this.rowKey]),
        1
      );
      this.selectedTable.dataSource.splice(index, 1);
      console.log(this.selectedRowKeys, this.selectionRows);
    },

    customRowFn(record) {
      return {
        on: {
          click: () => {
            let key = record[this.rowKey];
            if (!this.multiple) {
              this.selectedRowKeys = [key];
              this.selectedTable.dataSource = [record];
            } else {
              let index = this.selectedRowKeys.indexOf(key);
              if (index === -1) {
                this.selectedRowKeys.push(key);
                this.selectedTable.dataSource.push(record);
              } else {
                this.handleDeleteSelected(record, index);
              }
            }
          },
        },
      };
    },
  },
};
</script>
<style lang="less" scoped>
</style>

这边的方法通单选框是一样的

5.关于没有封装的单选框表格同时要实现点击行增加单选框选中效果

<a-table
            style="width: 100%; margin-top: 50px"
            :rowKey="(record) => record.id"
            :dataSource="tableData"
            :columns="columns"
            :row-selection="{
              type: 'radio',
              onChange: onSelectChange,
              selectedRowKeys: selectedRowKeys,
            }"
            :customRow="customRowFn"
            @change="handleTableChange"
            :pagination="ipagination"
            :loading="tableLoading"
            bordered
          >
          </a-table>
data(){
return{
      tableSelectRow: {},
      selectedRowKeys: [],
   /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: 10,
        total: 0,
      },
}
}




method:{
//控制单选框
    onSelectChange(val, row) {
      console.log("val", val, "row", row);
      this.tableSelectRow = row[0];
      this.selectedRowKeys = val;
    },
//控制行customRowFn属性
 customRowFn(record) {
      return {
        on: {
          click: () => {
            // console.log("record", record);
            this.tableSelectRow = record;
            this.selectedRowKeys = [record.id];
            // console.log("this.tableSelectRow", this.tableSelectRow);
            // console.log("this.selectedRowKeys", this.selectedRowKeys);
          },
        },
      };
    },
    handleTableChange(pagination) {
      //分页、排序、筛选变化时触发
      this.ipagination = pagination;
      this.getlistassetstandardmodel();
    },
}


  // 资产型号确定
    chosmodelok() {
      console.log(this.assetsData);
      console.log(this.tableSelectRow);
      this.form.setFieldsValue({
        assetName: this.tableSelectRow.standardName,
        categoryId: this.tableSelectRow.categoryId,
        assetModel: this.tableSelectRow.standardModel,
        assetMetering: this.tableSelectRow.standardMetering,
      });
      (this.assetsData.categoryName = this.tableSelectRow.categoryName),
        (this.chosmodel = false);
    },

6.关于没有封装的单选框表格同时要实现点击行增加多选框选中效果

 <a-table
        :columns="columns"
        :data-source="dataSource"
        size="small"
        rowKey="id"
        :loading="loading"
        :pagination="ipagination"
        bordered
        :rowSelection="{
          selectedRowKeys: selectedRowKeys,
          onChange: onChangeTableSelect,
        }"
        :customRow="customRowFn"
        @change="handleTableChange"
      >
   selectedRowKeys: [],
    
   selectionRows: [],
 methods: {
    customRowFn(record) {
      return {
        on: {
          click: () => {
            // console.log("record", record);
            // this.tableSelectRow = record;
            // this.selectedRowKeys = [record.id];
            // console.log("this.tableSelectRow", this.tableSelectRow);
            // console.log("this.selectedRowKeys", this.selectedRowKeys);

            let key = record.id;
            let index = this.selectedRowKeys.indexOf(key);
            // console.log("index", index);
            if (index === -1) {
              this.selectedRowKeys.push(key);
              this.selectionRows.push(record);
            } else {
              this.selectionRows.splice(
                this.selectionRows.indexOf(record.id),
                1
              );
              this.selectedRowKeys.splice(
                this.selectedRowKeys.indexOf(record.id),
                1
              );
            }
            console.log("this.selectionRows", this.selectionRows);
          },
        },
      };
    },

    onChangeTableSelect(selectedRowKeys, selectionRows) {
      this.selectedRowKeys = selectedRowKeys;
      let mRow = JSON.parse(JSON.stringify(this.selectionRows));
      selectionRows.forEach((item) => {
        pushIfNotExist(mRow, item, "id");
      });
      this.selectionRows = this.selectedRowKeys.map((item) => {
        let mObj = {};
        mRow.forEach((items) => {
          if (items.id == item) {
            mObj = items;
          }
        });
        console.log(mObj);
        return mObj;
      });
      console.log(this.selectionRows);
    },
    clickShowModal(rows) {
      let mRows = JSON.parse(JSON.stringify(rows));
      this.onClearSelected();
      this.selectedRowKeys = mRows.map((item) => item.id);
      this.selectionRows = mRows;
      this.visible = true;
      this.loadData(1);
    },
    handleOk() {
      this.visible = false;
      this.$emit(
        "select",
        JSON.parse(JSON.stringify(this.selectedRowKeys)),
        JSON.parse(JSON.stringify(this.selectionRows))
      );
    },
  },
};

 这边表格分页效果是来自JeecgListMixin这个组件或者看5那边有单独写出来分页

/**
 * 新增修改完成调用 modalFormOk方法 编辑弹框组件ref定义为modalForm
 * 高级查询按钮调用 superQuery方法  高级查询组件ref定义为superQueryModal
 * data中url定义 list为查询列表  delete为删除单条记录  deleteBatch为批量删除
 */
import { filterObj } from "@/utils/util";
import { deleteAction, getAction, downFile, getFileAccessHttpUrl } from "@/api/manage";

export const JeecgListMixin = {
  data() {
    return {
      /* 查询条件-请不要在queryParam中声明非字符串值的属性 */
      queryParam: {},
      /* 数据源 */
      dataSource: [],
      /* 分页参数 */
      ipagination: {
        current: 1,
        pageSize: 10,
        pageSizeOptions: ["10", "20", "30"],
        showTotal: (total, range) => {
          return range[0] + "-" + range[1] + " 共" + total + "条";
        },
        showQuickJumper: true,
        showSizeChanger: true,
        total: 0,
      },
      /* 排序参数 */
      isorter: {
        column: "createTime",
        order: "desc",
      },
      /* 筛选参数 */
      filters: {},
      /* table加载状态 */
      loading: false,
      /* table选中keys*/
      selectedRowKeys: [],
      /* table选中records*/
      selectionRows: [],
      /* 查询折叠 */
      toggleSearchStatus: false,
      /* 高级查询条件生效状态 */
      superQueryFlag: false,
      /* 高级查询条件 */
      superQueryParams: "",
      /** 高级查询拼接方式 */
      superQueryMatchType: "and",
    };
  },
  created() {
    if (!this.disableMixinCreated) {
      console.log(" -- mixin created -- ");
      this.loadData();
      //初始化字典配置 在自己页面定义
      this.initDictConfig();
    }
  },
  computed: {},
  methods: {
    loadData(arg) {
      if (!this.url.list) {
        this.$message.error("请设置url.list属性!");
        return;
      }
      //加载数据 若传入参数1则加载第一页的内容
      if (arg === 1) {
        this.ipagination.current = 1;
      }
      var params = this.getQueryParams(); //查询条件
      this.loading = true;
      getAction(this.url.list, params)
        .then((res) => {
          if (res.ok) {
            //update-begin---author:zhangyafei    Date:20201118  for:适配不分页的数据列表------------
            this.dataSource = res.data.records || res.data;
            if (res.data.total) {
              this.ipagination.total = res.data.total;
            } else {
              this.ipagination.total = 0;
            }
            //update-end---author:zhangyafei    Date:20201118  for:适配不分页的数据列表------------
          } else {
            this.$message.warning(res.msg);
          }
        })
        .finally(() => {
          this.loading = false;
        });
    },
    initDictConfig() {
      console.log("--这是一个假的方法!");
    },
    handleSuperQuery(params, matchType) {
      //高级查询方法
      if (!params) {
        this.superQueryParams = "";
        this.superQueryFlag = false;
      } else {
        this.superQueryFlag = true;
        this.superQueryParams = JSON.stringify(params);
        this.superQueryMatchType = matchType;
      }
      this.loadData(1);
    },
    getQueryParams() {
      //获取查询条件
      let sqp = {};
      if (this.superQueryParams) {
        sqp["superQueryParams"] = encodeURI(this.superQueryParams);
        sqp["superQueryMatchType"] = this.superQueryMatchType;
      }
      var param = Object.assign(sqp, this.queryParam, this.isorter, this.filters);
      // param.field = this.getQueryField();
      param.current = this.ipagination.current;
      param.size = this.ipagination.pageSize;
      return filterObj(param);
    },
    getQueryField() {
      //TODO 字段权限控制
      var str = "id,";
      this.columns.forEach(function (value) {
        str += "," + value.dataIndex;
      });
      return str;
    },
    onSelectChange(selectedRowKeys, selectionRows) {
      this.selectedRowKeys = selectedRowKeys;
      this.selectionRows = selectionRows;
      console.log(selectedRowKeys, selectionRows);
    },
    onClearSelected() {
      this.selectedRowKeys = [];
      this.selectionRows = [];
    },
    searchQuery() {
      this.loadData(1);
    },
    superQuery() {
      this.$refs.superQueryModal.show();
    },
    searchReset() {
      this.queryParam = {};
      this.loadData(1);
    },
    batchDel: function () {
      if (!this.url.deleteBatch) {
        this.$message.error("请设置url.deleteBatch属性!");
        return;
      }
      if (this.selectedRowKeys.length <= 0) {
        this.$message.warning("请选择一条记录!");
        return;
      } else {
        var ids = "";
        for (var a = 0; a < this.selectedRowKeys.length; a++) {
          ids += this.selectedRowKeys[a] + ",";
        }
        var that = this;
        this.$confirm({
          title: "确认删除",
          content: "是否删除选中数据?",
          onOk: function () {
            that.loading = true;
            deleteAction(that.url.deleteBatch, { ids: ids })
              .then((res) => {
                if (res.success) {
                  //重新计算分页问题
                  that.reCalculatePage(that.selectedRowKeys.length);
                  that.$message.success(res.message);
                  that.loadData();
                  that.onClearSelected();
                } else {
                  that.$message.warning(res.message);
                }
              })
              .finally(() => {
                that.loading = false;
              });
          },
        });
      }
    },
    handleDelete: function (id) {
      if (!this.url.delete) {
        this.$message.error("请设置url.delete属性!");
        return;
      }
      var that = this;
      deleteAction(`${that.url.delete}/${id}`, { id: id }).then((res) => {
        if (res.ok) {
          //重新计算分页问题
          that.reCalculatePage(1);
          that.$message.success("操作成功");
          that.loadData();
        } else {
          that.$message.warning(res.msg);
        }
      });
    },
    reCalculatePage(count) {
      //总数量-count
      let total = this.ipagination.total - count;
      //获取删除后的分页数
      let currentIndex = Math.ceil(total / this.ipagination.pageSize);
      //删除后的分页数<所在当前页
      if (currentIndex < this.ipagination.current) {
        this.ipagination.current = currentIndex;
      }
      console.log("currentIndex", currentIndex);
    },
    handleEdit: function (record) {
      this.$refs.modalForm.edit(record);
      this.$refs.modalForm.title = "编辑";
      this.$refs.modalForm.disableSubmit = false;
    },
    handleAdd: function () {
      this.$refs.modalForm.add();
      this.$refs.modalForm.title = "新增";
      this.$refs.modalForm.disableSubmit = false;
    },
    handleTableChange(pagination, filters, sorter) {
      //分页、排序、筛选变化时触发
      //TODO 筛选
      console.log(pagination);
      if (Object.keys(sorter).length > 0) {
        // this.isorter.column = sorter.field;
        this.isorter.order = "ascend" == sorter.order ? "asc" : "desc";
      }
      this.ipagination = pagination;
      this.loadData();
    },
    handleToggleSearch() {
      this.toggleSearchStatus = !this.toggleSearchStatus;
    },
    // 给popup查询使用(查询区域不支持回填多个字段,限制只返回一个字段)
    getPopupField(fields) {
      return fields.split(",")[0];
    },
    modalFormOk() {
      // 新增/修改 成功时,重载列表
      this.loadData();
      //清空列表选中
      this.onClearSelected();
    },
    handleDetail: function (record) {
      this.$refs.modalForm.edit(record);
      this.$refs.modalForm.title = "详情";
      this.$refs.modalForm.disableSubmit = true;
    },
    /* 导出 */
    handleExportXls2() {
      let paramsStr = encodeURI(JSON.stringify(this.getQueryParams()));
      let url = `${window._CONFIG["domianURL"]}/${this.url.exportXlsUrl}?paramsStr=${paramsStr}`;
      window.location.href = url;
    },
    handleExportXls(fileName) {
      if (!fileName || typeof fileName != "string") {
        fileName = "导出文件";
      }
      let param = this.getQueryParams();
      if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
        param["selections"] = this.selectedRowKeys.join(",");
      }
      console.log("导出参数", param);
      downFile(this.url.exportXlsUrl, param).then((data) => {
        if (!data) {
          this.$message.warning("文件下载失败");
          return;
        }
        if (typeof window.navigator.msSaveBlob !== "undefined") {
          window.navigator.msSaveBlob(new Blob([data], { type: "application/vnd.ms-excel" }), fileName + ".xls");
        } else {
          console.log(data);
          let url = window.URL.createObjectURL(new Blob([data.data], { type: "application/vnd.ms-excel" }));
          let link = document.createElement("a");
          link.style.display = "none";
          link.href = url;
          link.setAttribute("download", fileName + ".xls");
          document.body.appendChild(link);
          link.click();
          document.body.removeChild(link); //下载完成移除元素
          window.URL.revokeObjectURL(url); //释放掉blob对象
        }
      });
    },
    /* 图片预览 */
    getImgView(text) {
      if (text && text.indexOf(",") > 0) {
        text = text.substring(0, text.indexOf(","));
      }
      return getFileAccessHttpUrl(text);
    },
    /* 文件下载 */
    // update--autor:lvdandan-----date:20200630------for:修改下载文件方法名uploadFile改为downloadFile------
    downloadFile(text) {
      if (!text) {
        this.$message.warning("未知的文件");
        return;
      }
      if (text.indexOf(",") > 0) {
        text = text.substring(0, text.indexOf(","));
      }
      let url = getFileAccessHttpUrl(text);
      window.open(url);
    },
  },
};

7.打印和导出写法

// 打印资产标签列表
const printAssetList = (fileName, params) => downloadFile("/asset/warehousing/printAssetList", fileName, params, "pdf"); // 打印资产标签列表
//导出资产列表
const warehousingExport = (fileName, params) => downloadFile("/asset/assetsinfo/export", fileName, params, "get"); // 导出资产列表
  // 打印资产
    clickAssetsPrint() {
      this.assetsPrintLoading = true;
      var mm = {};
      mm.id = this.selectedRows.map((item) => {
        return item.materialCode;
      });
      printAssetList("资产标签", mm)
        .then((res) => {
          this.assetsPrintLoading = false;
        })
        .catch((err) => {
          this.assetsPrintLoading = false;
        });

      // this.$refs.barCode.transferData(this.selectedRows)
    },
    // 导出资产
    getWarehousingExport() {
      var mm = {};
      mm.id = this.selectedRows.map((item) => {
        return item.materialCode;
      });
      warehousingExport("资产列表", mm).then((res) => {
        // let execlName = "资产文件名称";
        // const buf = Buffer.from(res),
        //   blob = new Blob([buf], { type: "application/vnd.ms-excel" }),
        //   downloadElement = document.createElement('a'),
        //   href = window.URL.createObjectURL(blob); // 创建下载的链接
        // downloadElement.href = href;
        // downloadElement.download = `${execlName}.xls`; // 下载后文件名
        // document.body.appendChild(downloadElement);
        // downloadElement.click(); // 点击下载
        // window.URL.revokeObjectURL(href); // 释放掉blob对象
      });
    },

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795064.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

T-Rex2: Towards Generic Object Detection via Text-Visual Prompt Synergy论文解读

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、引言二、文献综述1. Text-prompted Object Detection2. Visual-prompted Object Detection3. Interactive Object Detection 三、模型方法1. Visual-Text P…

在vscode 中使用npm的问题

当我装了 npm和nodejs后 跑项目在 文件中cmd的话可以直接运行但是在 vscode 中运行的时候就会报一下错误 解决方法就是在 vscode 中吧 power shell换成cmd 来运行就行了

JVM相关:Java内存区域

Java 虚拟机&#xff08;JVM)在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。 Java运行时数据区域是指Java虚拟机&#xff08;JVM&#xff09;在执行Java程序时&#xff0c;为了管理内存而划分的几个不同作用域。这些区域各自承担特定的任务&#xff0c…

知攻善防应急

知攻善防应急靶场一 小李在值守的过程中&#xff0c;发现有 CPU 占用飙升&#xff0c;出于胆子小&#xff0c;就立刻将服务器关机&#xff0c;并找你帮他分析&#xff0c;这是他的服务器系统&#xff0c;请你找出以下内容&#xff0c;并作为通关条件&#xff1a; 1.攻击者的 …

今日增长工具精选| 8个SaaS出海必备运营工具

一、SurveyMonkey 是一个灵活、方便、经济实惠的在线调查工具&#xff0c;可以通过自行设计定制化问卷&#xff0c;开展消费者调研&#xff0c;收集第一手数据&#xff0c;获取用户反馈。 客户涵盖财富100强公司以及其他不同规模和类型的组织&#xff0c;如公司、学术研究机构…

第二十六章CSS3续~

3.CSS3渐变属性 CSS3渐变(gradients)可以在两个或多个指定的颜色之间显示平稳的过渡。 以前&#xff0c;我们必须使用图像来实现这些效果。但是&#xff0c;通过使用CSS3渐变(gradients)&#xff0c;可以减少下载的事件和宽带的使用。由于渐变(gradient)是由浏览器生成的&…

首个文字生成手语模型来了!SignLLM通过文字描述来生成手语视频,目前已经支持八国手语!

SignLLM 是目前第一个通过文字描述生成手语视频的多语言手语模型。 该项目引入了首个多语言手语数据集 Prompt2Sign&#xff0c;它使用工具自动采集和处理网络上的手语视频&#xff0c;能够不断更新&#xff0c;且具有轻量化特点。 该模型当前支持 8 种手语类型。包括美国手语…

软件管理、rpm安装、yum安装、源码编译安装

目录 一、Windows安装/卸载 二、软件的卸载&#xff1a; 三、Linux的软件安装和卸载 3.1rpm安装 第一步&#xff1a;挂在光盘 第二步&#xff1a;查看/mnt 第三步&#xff1a;切换到/mnt/Packages 第四步&#xff1a;安装 3.2yum安装&#xff08;使用关盘作为yum源&…

29 - 买下所有产品的客户(高频 SQL 50 题基础版)

29 - 买下所有产品的客户 selectc.customer_id fromCustomer c group byc.customer_id havingcount(c.product_key)(select count(distinct product_key) from Product);

java版知识付费saas租户平台:剖析现代知识付费平台的功能架构与运营逻辑

在数字化学习的时代背景下&#xff0c;知识付费平台已经成为教育行业的一颗璀璨明星&#xff0c;以其用户需求为中心&#xff0c;提供便捷高效的学习途径。这些平台汇聚了众多专业知识&#xff0c;覆盖职业技能、生活兴趣和人文社科等多个领域&#xff0c;满足不同用户的学习需…

TikTok运营必看|7大广告类型及特点

TikTok广告是品牌或创作者付费向特定目标受众展示的推广内容&#xff08;通常是全屏视频&#xff09;。TikTok 上的广告是一种社交媒体营销形式&#xff0c;通常旨在提高广告商的知名度或销售特定产品或服务。 就 TikTok广告投放而言&#xff0c;其组织层级分为三个层级&#x…

【成品设计】基于红外线的目标跟踪无线测温系统设计

《基于红外线的目标跟踪无线测温系统设计》 整体功能&#xff1a; A端&#xff1a;无线跟踪端 主控&#xff1a;采用STM32F103C8T6单片机作为核心控制。360度编码模块数字脉冲输出红外解码编码模块OLED屏幕。 B端&#xff1a;无线待测端 主控&#xff1a;采用STM32F103C8T…

深入了解静态IP:基础知识与原理(固定IP地址解析)

在今天的数字化世界中&#xff0c;互联网连接已成为我们日常生活和工作中不可或缺的一部分。而在网络连接中&#xff0c;IP地址起着至关重要的作用。其中&#xff0c;静态IP地址因其独特的性质而备受关注。本文将深入探讨静态IP的基础知识、与动态IP的区别、工作原理以及为什么…

大模型创新企业集结!百度智能云千帆AI加速器Demo Day启动

新一轮技术革命风暴席卷而来&#xff0c;为创业带来源源不断的创新动力。过去一年&#xff0c;在金融、制造、交通、政务等领域&#xff0c;大模型正从理论到落地应用&#xff0c;逐步改变着行业的运作模式&#xff0c;成为推动行业创新和转型的关键力量。 针对生态伙伴、创业…

腾讯云Edgeone为我的网站保驾护航

文章目录 前言边缘安全加速平台介绍模拟网站被攻击攻击脚本攻击脚本执行 网站快速接入 EdgeOne前提条件&#xff08;注意事项&#xff09;添加站点添加加速域名 EdgeOne 防护效果EdgeOne 体验感受总结 前言 众所周知&#xff0c;网站如果没有安全防护&#xff0c;极易遭受恶意…

inBuilder 低代码平台新特性推荐 - 第二十期

今天来给大家带来的是 inBuilder 低代码平台特性推荐系列第二十期——菜单导航模式个性化示例。 场景介绍 目前平台提供了四种菜单导航模式&#xff0c;包括分组视图、列表视图、横向视图、平铺视图&#xff0c;均为横向导航&#xff0c;这些也是主流的菜单导航模式。 在某些…

UML实现图-组件图

概述 组件图(ComponentDiagram)描述了软件的各种组件和它们之间的依赖关系。组件图中通常包含4种元素:组件、程序、包、任务&#xff0c;各个组件之间还可以相互依赖。 一、组件的表示法 组件是定义了良好接口的物理实现单元&#xff0c;是系统中可替换的物理部件。在一般情…

JAVA-学习-2

一、类 1、类的定义 把相似的对象划分了一个类。 类指的就是一种模板&#xff0c;定义了一种特定类型的所有对象的属性和行为 在一个.java的问题件中&#xff0c;可以有多个class&#xff0c;但是智能有一个class是用public的class。被声明的public的class&#xff0c;必须和文…

AdSet通过审核并入驻全国SDK管理服务平台

SDK、API、H5是三种常见的APP广告接入方式&#xff0c;目前市面上使用最广泛的还是SDK对接&#xff0c;通过使用广告SDK&#xff0c;App开发者可以在App中展示广告商投放的广告&#xff0c;进而根据用户的点击赚取收益。具备一定规模流量、想快速获得收益的APP开发者都会考虑接…

创新实训2024.06.06日志:部署web服务

1. 运行web项目前后端服务 首先我们要先在服务器上运行客户端以及服务端的应用程序。随后再考虑如何通过公网/局域网访问的问题。 如何启动服务在仓库对应分支下的Readme文件中已经有详细描述了。 1.1. 启动服务端 对于服务端&#xff0c;即&#xff08;要求你在服务端子项…