代码复用问题

你好,我想做一个商品入库功能,目前在表单开发中建了三张表,form_in, form_in_item, production, 三张表,参考了文档高级用法中https://doc.jeelowcode.com/doc/1800-33-0   实现了 主附表以及和产品表的交互联动(见图一)

现在遇到的问题和思考:

1)  系统中可能有多个相似功能,比如商品入库,出库,领用,归还,维修,变更等等,这些功能基本相似,都是要选择产品,那选择产品这里是否可以封装成一个公共组件?因为我发现我在from_in表的JS增强中写了选择产品的弹框代码,如果我现在要做出库,也是要选择产品,这时又要复制一遍弹框的代码到form_out增强代码中,这样似乎有点冗余了(见图二),有没有好的办法处理呢

2)以下是form_in表中的完整增强代码 ,可以简单帮我看一下,并提出优化吗,非常感谢

image.png

image.png


// form_in
//初始化控件 打开产品选择弹框
useFun.controlInit('TableView', 'tableView_32983', {
  tableId: '1925362580893753345', //表单开发id
  fixedSearch: {}, //固定表格搜索值(不会被覆盖)
  enhanceData: {
    type: 'enhance_select', // 选择时,隐藏新增编辑等按钮
    selectIds: []
  }, //传递给表格js增强内部调用配置
  showType: 'dialog', //弹窗类型 dialog | drawer
  popOption: { //弹窗配置
    title: '选择商品', //标题
    width: '90%', //弹窗宽度
    fullscreen: false, //是否全屏
    footerBtn: [ //底部按钮配置
      {
        params: {
          type: 'primary'
        }, //el-button 其他参数
        name: '确认选择', //按钮名称
        display: true, //是否显示
        loading: true, //点击时是否有loading
        icon: '', //图标
        clickFun: (loading) => {
          //点击事件
          // console.log('点击确定', tableView_32983.value,)
          // 当前弹窗表格 
          const currTable = componentRef.value.tableView_32983.controlRef
          // 附表实例
          const subTable = subTableRef.value.form_in_item
          if (!currTable.tableSelect?.length) {
            message.info('请勾选数据!')
            if (loading) loading() //关闭loading
            return;
          }

          // 拿到所有选中的附表data
          // console.error(tableForm.value.form_in_item)
          const selectIds = tableForm.value.form_in_item.map(k => k.sku_id)
          // 把勾选的数据赋值到附表
          setTimeout(() => {
            currTable.tableSelect.map(item => {
              // 没勾选过的
              if (!selectIds.includes(item.id)) {
                subTable.rowAdd({
                  sku_id: item.id,
                  epc: item.epc,
                  name: item.name,
                  num: 0,
                  $btn__signRowBtn: false,   // 自定义按钮隐藏
                })
              }
            })
          }, 100)

          if (loading) loading() //关闭loading
          tableView_32983.value.show = false  // 关闭弹框
        }
      }
    ],
    headerBtn: [], //顶部按钮配置(配置同上)
    dialogParams: {}, //弹窗其他配置
    handleClose: (done) => { //关闭弹窗前的回调
      done()
    }
  }
})

//控件调用
const { tableView_32983 } = Vue.toRefs(rendControlData.value)

const controlData = tableView_32983.value.params //TableView的配置

// 数组对象求和
const sumField = (arr, field) => {
  return arr?.reduce((total, item) => {
    const value = Number(item[field]);
    return total + (isNaN(value) ? 0 : value);
  }, 0) || 0;
}

// 设置主表表格顶部按钮是否可点击
const setTableBtnDisabled = (bool = true) => {
  // 审核按钮
  buttonObj.value.header.checkBtn = {
    ...buttonObj.value.header.checkBtn,
    params: {
      disabled: bool
    }
  }
}
// 监听主表表格勾选
watch(() => tableSelect.value.length, (length) => {
  setTableBtnDisabled(length == 0)
}, { immediate: true })
// 设置附表表格顶部按钮是否可点击
const setSubTableBtnDisabled = (bool = true) => {
  for (const key in subTableObj.value) {
    const headerBtn = subTableObj.value[key].buttonObj.header
    // console.error('headerBtn',headerBtn)
    // 审核按钮
    headerBtn.signBtn.params.disabled = bool
  }
}

// 监听附表表格勾选
watch(() => subTableRef.value?.form_in_item?.selectionSubChange(), (subTableSelect) => {
  // console.error('subTableRef.value.form_in_item.selectionSubChange', subTableRef.value)
  setSubTableBtnDisabled(subTableSelect?.length == 0)
}, { immediate: true })

// 监听附表数据,自动计算商品总数和金额
watch(() => tableForm.value.form_in_item, (subTable) => {
  tableForm.value.num = sumField(subTable, 'num')
  tableForm.value.amount = sumField(subTable, 'amount')
}, { immediate: true })


return {
  // 附表 自定义按钮  单个签收
  signRowBtn(row) {
    row.status = 1
    useFun.requestApi('put', '/jeelowcode/dbform-data/edit/1925363117659807746', {
      data: {
        ...row,
        form_type: 'form_in'
      }
    }).then(res => {
      row.$btn__signRowBtn = false
      message.success('操作成功')
      useFun.refreshChange()  // 刷新当前页表格数据
    })
  },

  // 附表 自定义按钮  批量签收
  signBtn() {
    if (!tableForm.value.form_in_item?.length) {
      message.info('请勾选数据!')
      return;
    }
    const data = tableForm.value.form_in_item.map(item => {
      item.status = 1
      item.form_type = 'form_in'
      return item
    })
    useFun.requestApi('put', '/jeelowcode/dbform-data/edit/batch/1925363117659807746', {
      data: data
    }).then(res => {
      message.success('操作成功')
      useFun.refreshChange()  // 刷新当前页表格数据
      subTableRef.value.form_in_item.crudRef.refreshTable()
    })
  },

  // 主表 自定义按钮 批量审核
  checkBtn() {
    if (!tableSelect.value?.length) {
      message.info('请勾选数据!')
      return;
    }
    const data = tableSelect.value.map(item => {
      item.status = 1
      return item
    })
    useFun.requestApi('put', '/jeelowcode/dbform-data/edit/batch/1925363085304946690', {
      data: data
    }).then(res => {
      message.success('操作成功')
      useFun.refreshChange()  // 刷新当前页表格数据
      tableSelect.value = []  // 重置勾选
      initSelectChange()
    })
  },

  //勾选表格数据时触发
  // selectionChange(selectData) {
  //   //selectData 表格勾选数据
  //   // console.error(selectData,buttonObj.value.header.checkBtn.display)
  //   if(selectData?.length){
  //     buttonObj.value.header.checkBtn.display = true
  //   }else{
  //     buttonObj.value.header.checkBtn.display = false
  //   }
  // },


  // 自定义按钮 选择产品
  selectAssetsBtn() {
    // 把已勾选的产品,勾选上
    controlData.enhanceData.selectIds = tableForm.value.form_in_item.map(k => k.sku_id)
    //显示TableView
    tableView_32983.value.show = true
    // componentRef.value.tableView_32983 TableView的Ref控件引用
  },
  beforeFormData(formData, type) { //表单打开前执行
    return new Promise(resolve => {
      const tableHideFields = ['order_user_id', 'confirm_user_id', 'status'] // 主表表单要隐藏的字段
      const tableDisabledFields = ['order_user_id', 'confirm_user_id', 'status','num','amount'] // 主表表单要禁止编辑的字段
      console.log('formData------------------', formData)

      // 设置主表部分字段不可见
      if (formData?.id) {
        // 编辑
        tableHideFields.forEach(item => {
          useFun.setPropConfig(item, { display: true })
        })
      } else {
        // 新增
        tableHideFields.forEach(item => {
          useFun.setPropConfig(item, { display: false })
        })
      }
      // 设置主表部分字段不可编辑
      for (const key in tableOption.value.column) {
        if(!tableDisabledFields.includes(key)){
          tableOption.value.column[key].disabled = false
        }
      }



      setTimeout(() => {
        // 隐藏附表头、操作列按钮
        for (const key in subTableObj.value) {
          const subTableButton = subTableObj.value[key].buttonObj
          // console.error('subTableButton------------------>', subTableButton)
          subTableButton.header.addBtn.display = false  // 隐藏新增按钮
          if (formData.status == 0 || !formData.status) {
            subTableButton.header.selectAssetsBtn.display = true   // 显示选择产品按钮
            subTableButton.header.batchDelBtn.display = true       // 显示批量删除按钮
            subTableButton.header.signBtn.display = false   // 隐藏批量签收按钮
            subTableButton.menu.signRowBtn.display = false  // 隐藏签收按钮
            subTableButton.menu.delBtn.display = true             // 显示行删除按钮
          } else if (formData.status == 1) {
            // 主表字段都禁用
            for (const key in tableOption.value.column) {
              tableOption.value.column[key].disabled = true
            }
            // 附表字段都禁用
            for (const key in subTableRef.value.form_in_item.tableOption.column) {
              subTableRef.value.form_in_item.tableOption.column[key].cell = false
            }
            subTableButton.header.selectAssetsBtn.display = false   // 隐藏选择产品按钮
            subTableButton.header.batchDelBtn.display = false       // 隐藏批量删除按钮
            subTableButton.header.signBtn.display = true   // 显示批量签收按钮
            subTableButton.menu.signRowBtn.display = true  // 显示行签收按钮
            subTableButton.menu.delBtn.display = false  // 隐藏行删除按钮
          }
        }
        // 隐藏附表行签收按钮
        tableForm.value.form_in_item = tableForm.value.form_in_item.map(item => {
          if (item.status == 1) {
            item.$btn__signRowBtn = false
          }
          return item;
        })
        console.warn('tableForm.value.form_in_item', tableForm.value.form_in_item)

        // 设置附表部分字段不可编辑和 不可见
        const subTableDisabledFields = ['order_id', 'epc', 'sku_id', 'assets_id', 'remark', 'status'] // 要禁止编辑的字段
        const subTableHideFields = ['order_id', 'epc'] // 要隐藏的字段
        subTableDisabledFields.forEach(item => {
          useFun.setSubPropConfig(item, { cell: false }, 'form_in_item')
        })
        subTableHideFields.forEach(item => {
          useFun.setSubPropConfig(item, { hide: true }, 'form_in_item')
        })

        // 设置附表选择禁用
        const subRef = subTableRef.value.form_in_item
        subRef.tableOption.selectable = (row, index) => { //控制行是否可勾选
          if (row.status == 1) {  // 状态为1禁用选择 false
            return false
          } else {
            return true
          }
        }
        subRef.crudRef.refreshTable()

        // 修改附表商品和价格,修改主表
        useFun.setSubPropConfig('num', {
          change: ({ value }) => {
            tableForm.value.num = sumField(tableForm.value.form_in_item, 'num')
          }
        })
        useFun.setSubPropConfig('amount', {
          change: ({ value }) => {
            tableForm.value.amount = sumField(tableForm.value.form_in_item, 'amount')
          }
        })

      }, 500)
      resolve(formData)
    })
  },
  initOption() { //表格显示前执行
    // 隐藏审核按钮
    //buttonObj.value.header.checkBtn.display = false

  },
}


评论区

超级管理员 2025-06-03 15:58

可以尝试一下通过打开自定义组件来实现image.png


思路

1.自行编写一个产品选择的控件

2.内部把所有数据处理获取的逻辑都写到组件里面,再把方法暴露出去

3. 表单开发 点击 选择产品的时候 弹出这个组件

4.确认选择这里的按钮都可以直接调用到 组件内部的方法 去获取数据处理数据之类的

5.表单开发增强只调用方法 和 网附表添加或删除数据


dtbadmin 2025-06-04 16:46

@超级管理员

你好,我写了一个自定义组件SelectSpu, 我想确认一下,1)取消和确认按钮是不是直接写在我的组件里呢,而不用写在footerBtn中? 2) 如果是写在我的组件里, 组件暴露了一个confirmSelect方法,在 JS增强中如何调用呢? 3)点击取消按钮,如何关闭弹框呢  4) 子组件如何获取/修改 附表form_in_item的值  5)是不是每一个业务单据都要写弹框初始化?

image.png

image.png

form_in   打开弹框代码如下

// 选择产品公共弹框  自定义控件初始化
useFun.controlInit('ControlView', 'controlView_37773', {
  controlName: 'SelectSku', //控件名称(全局注册的控件使用,例:el-transfer )
  controlPath: 'components/Select/SelectSku.vue', //控件相对路径(未全局注册的控件使用,例:components/IFrame/src/IFrame.vue)
  controlParams: {}, //控件配置
  showType: 'dialog', //弹窗类型 dialog | drawer
  popOption: { //弹窗配置
    title: '选择产品', //标题
    width: '', //弹窗宽度
    fullscreen: false, //是否全屏
    footerBtn: [ //底部按钮配置
      {
        params: {}, //el-button 其他参数
        name: '弹框的确认选择', //按钮名称
        display: true, //是否显示
        loading: true, //点击时是否有loading
        icon: '', //图标
        clickFun: (loading) => {
          // ??这里有个疑问 我是在这里对选中的数据处理吗?这样好像又回到了之前的问题,代码重复在这里了
          // ??我的理解是这里的  footerBtn 直接为 [], 在自定义组件中加取消和确认两个按钮,

          //点击事件
          if (loading) loading() //关闭loading
        }
      }
    ],
    headerBtn: [], //顶部按钮配置(配置同上)
    dialogParams: {}, //弹窗其他配置
    handleClose: (done) => { //关闭弹窗前的回调
      done()
    }
  }
})

return{
  // 自定义按钮 选择产品
  selectAssetsBtn() {
    // 自定义控件调用
    const { controlView_37773 } = Vue.toRefs(rendControlData.value)
    controlView_37773.value.show = true //显示ControlView
  },
}


自定义组件代码

<template>  <ContentWrap>    <avue-crud      ref="crudRef"      v-model="tableForm"      v-model:page="tablePage"      v-model:search="tableSearch"      :table-loading="loading"      :data="tableData"      :option="tableOption"      :before-open="beforeOpen"      :permission="permission"      @search-change="searchChange"      @search-reset="resetChange"      @refresh-change="getTableData"      @size-change="sizeChange"      @current-change="currentChange"      @selection-change="selectionChange"    >      <template #status="scope">        <dict-tag          v-if="scope.row.status !== undefined"          :type="DICT_TYPE.COMMON_STATUS"          :value="scope.row.status"        />      </template>    </avue-crud>  </ContentWrap>  <div class="footer-btns">    <el-button      @click="cancel"      :loading="exportLoading"      >取  消</el-button    >    <el-button      type="primary"      @click="confirmSelect"      :loading="exportLoading"      >确认选择</el-button    >  </div></template><script lang="ts" setup>import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'import { dateFormatter } from '@/utils/formatTime'import download from '@/utils/download'import * as PostApi from '@/api/system/post'import { CommonStatusEnum } from '@/utils/constants'
defineProps({  // isModal: {  //   type: Boolean,  //   default: true  // }})
defineOptions({ name: 'SystemPost' })
const message = useMessage() // 消息弹窗const { t } = useI18n() // 国际化const { getCurrPermi } = useCrudPermi()
const loading = ref(true) // 列表的加载中const tableOption = reactive({  align: 'center',  headerAlign: 'center',  searchMenuSpan: 6,  searchMenuPosition: 'left',  labelSuffix: ' ',  span: 12,  dialogWidth: '50%',  height:'50vh',  column: {    id: {      label: '商品编号',      display: false,      width: 90    },    name: {      label: '商品名称',      search: true,      minWidth: 90,      rules: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }]    },    sort: {      label: '商品顺序',      width: 90,      rules: [{ required: true, message: '商品顺序不能为空', trigger: 'blur' }]    },    status: {      label: '状态',      search: true,      type: 'select',      width: 90,      dicData: getIntDictOptions(DICT_TYPE.COMMON_STATUS),      rules: [{ required: true, message: '状态不能为空', trigger: 'blur' }],      value: CommonStatusEnum.ENABLE    },
    createTime: {      label: '创建时间',      searchRange: true,      display: false,      type: 'datetime',      width: 180,      startPlaceholder: '开始时间',      endPlaceholder: '结束时间',      formatter: (row, val, value, column) => {        return dateFormatter(row, column, val)      }    },    remark: {      label: '商品备注',      type: 'textarea',      minRows: 2,      maxRows: 4,      minWidth: 150,      span: 24    }  },  menu: false,    // 隐藏列操作  header: false,  // 隐藏表格头部操作  selection:true, // 开启选择  tip:false,      // 隐藏已选择提示
}) //表格配置const tableForm = ref<{ id?: number }>({})const tableData = ref([])const tableSearch = ref({})const tablePage = ref({  currentPage: 1,  pageSize: 10,  total: 0})const exportLoading = ref(false) // 导出的加载中const permission = getCurrPermi(['system:post'])const crudRef = ref()const selectedData = ref([])
useCrudHeight(crudRef)
/** 查询列表 */const getTableData = async () => {  loading.value = true  let searchObj = {    ...tableSearch.value,    pageNo: tablePage.value.currentPage,    pageSize: tablePage.value.pageSize  }  for (let key in searchObj) if (searchObj[key] === '') delete searchObj[key]  try {    const data = await PostApi.getPostPage(searchObj)    tableData.value = data.list    tablePage.value.total = data.total  } finally {    loading.value = false  }}
/** 搜索按钮操作 */const searchChange = (params, done) => {  tablePage.value.currentPage = 1  getTableData().finally(() => {    done()  })}
/** 清空按钮操作 */const resetChange = () => {  searchChange({}, () => {})}
const sizeChange = (pageSize) => {  tablePage.value.pageSize = pageSize  resetChange()}
const currentChange = (currentPage) => {  tablePage.value.currentPage = currentPage  getTableData()}
/** 表单打开前 */const beforeOpen = async (done, type) => {  console.log(11111111)  if (['edit', 'view'].includes(type) && tableForm.value.id) {    tableForm.value = await PostApi.getPost(tableForm.value.id)  }  done()}
// 勾选事件const selectionChange = (selection) => {  console.error('selection',selection)  selectedData.value = selection}

// 关闭弹框const cancel = () => {  } 
// 确认选择const confirmSelect = () => {  console.log('确认', selectedData.value)}
defineExpose({  confirmSelect})
/** 初始化 **/onMounted(async () => {  await getTableData()})</script><style lang="scss" scoped>.footer-btns{  display: flex;  flex-direction: row;  justify-content: flex-end;}</style>


dtbadmin 2025-06-04 16:57

image.pngimage.png

超级管理员 2025-06-04 17:26

@dtbadmin

表可以用 src\components\LowDesign\src\TableView.vue 组件去显示对应的表单开发 通过ref操作就可以了 不需要再用avue-crud

其他的就只要把需要的参数传递给组件用就可以了

image.png

image.png

dtbadmin 2025-06-05 10:13

@超级管理员

你好,我希望隐藏这些按钮和操作,但是无效

image.png

image.png

回复