当前位置: 首页 > article >正文

使用vue3+element plus 的table自制的穿梭框(支持多列数据)

目录

一、效果图

二、介绍

三、代码区


一、效果图

话不多说,先上图

二、介绍

项目需要:通过穿梭框选择人员信息,可以根据部门、岗位进行筛选,需要显示多列(不光显示姓名,还包括人员的一些基础信息);项目前端用的是vue3+element plus(Transfer 组件 | Element Plus),原来打算使用element的组件 Transfer 穿梭框,发现它只实现简单的单列穿梭,无法满足需求。最后打算自己动手用table来实现:分为左右两个区域,左边查询区+待选数据列表table,右边选中的数据列表,中间两个按钮实现数据的转移。(在看的小伙伴有其他更合适的组件/组件库可以评论区分享)

三、代码区

html

    <el-dialog :title="title" :lock-scroll="false" v-model="open" :fullscreen="fullScreen" :close-on-click-modal="false"
      :close-on-press-escape="false" align-center>
      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
        <el-row :gutter="10">
          <el-col :lg="6">
            <el-form-item label="生产线" prop="lineCode">
              <el-select clearable v-model="form.lineCode" placeholder="请选择生产线" filterable
                @change="formLineSelectChange" style="width: 100%;">
                <el-option v-for="item in options.mes_line_list" :key="item.id" :label="item.lineName"
                  :value="item.lineCode">
                  <span class="fl">{{ item.innerLineCode }}</span>
                  <span class="fr" style="color: var(--el-text-color-secondary);">{{ item.lineName }}</span>
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :lg="4">
            <el-form-item label="标准工时" prop="standardWorkHours">
              <el-input-number v-model="form.standardWorkHours" :min="0" :max="24" :precision="1" placeholder="请输入标准工时"
                style="width: 100%;" />
            </el-form-item>
          </el-col>
          <el-col :lg="4">
            <el-form-item label="日期" prop="workDate">
              <el-date-picker v-model="form.workDate" type="date" placeholder="日期" value-format="YYYY-MM-DD"
                :shortcuts="shortcutsWorkDate" style="width: 100%;">
              </el-date-picker>
            </el-form-item>
          </el-col>

          <el-col :lg="4">
            <el-form-item label="班次" prop="workShift">
              <el-select clearable v-model="form.workShift" placeholder="请选择生产班次" style="width: 100%;">
                <el-option v-for="item in options.mes_classes_type" :key="item.dictValue" :label="item.dictLabel"
                  :value="item.dictValue">
                </el-option>
              </el-select>
            </el-form-item>
          </el-col>

          <el-col :lg="6">
            <el-form-item label="备注信息" prop="remark">
              <el-input v-model="form.remark" placeholder="请输入备注信息" style="width: 100%;" type="textarea" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-divider content-position="center">报工人员明细</el-divider>
        <!-- 查询条件 -->
        <el-form :model="queryParamsUserNav" label-position="right" inline ref="queryRefUserNav">
          <el-form-item label="部门" prop="deptId">
            <el-tree-select v-model="queryParamsUserNav.deptId" :data="deptOptions"
              :props="{ value: 'id', label: 'label', children: 'children' }" value-key="id" placeholder="请选择归属部门"
              check-strictly :render-after-expand="false" @change="changeDept($event)" filterable />
          </el-form-item>

          <el-form-item label="岗位" prop="postId">
            <el-select v-model="queryParamsUserNav.postId" placeholder="请选择岗位" filterable clearable
              @change="changePost">
              <el-option v-for="item in postOptions" :key="item.postId" :label="item.postName" :value="item.postId">
                <span class="fl">{{ item.postCode }}</span>
                <span class="fr" style="color: var(--el-text-color-secondary);">{{ item.postName }}</span>
              </el-option>
            </el-select>
          </el-form-item>

          <el-form-item label="用户" prop="userId">
            <el-select v-model="queryParamsUserNav.userId" placeholder="请选择用户" filterable clearable>
              <el-option v-for="item in userOptions" :key="item.userId" :label="item.nickName" :value="item.userId">
                <span class="fl">{{ item.nickName }}</span>
                <span class="fr" style="color: var(--el-text-color-secondary);">{{ item.userName }}</span>
              </el-option>
            </el-select>
          </el-form-item>

          <el-form-item>
            <el-button icon="search" type="primary" @click="handleQueryUserNav">{{ $t('btn.search') }}</el-button>
            <el-button icon="refresh" @click="resetQueryUserNav">{{ $t('btn.reset') }}</el-button>
          </el-form-item>
        </el-form>

        <!-- 自制穿梭框 -->
        <el-row>
          <!-- 左边table -->
          <el-col :lg="10" style="height: 100%;">

            <div class="table-container">
              <el-table :data="dataListLeft" v-loading="loadingLeft" ref="table" border
                header-cell-class-name="el-table-header-cell" highlight-current-row
                @selection-change="handleSelectionChangeLeft" :height="tableHeight - 40">
                <el-table-column type="selection" width="50" align="center" />
                <el-table-column type="index" width="60" label="序号" align="center" />
                <el-table-column prop="deptName" label="部门" align="center" />
                <el-table-column prop="userName" label="工号" align="center" />
                <el-table-column prop="nickName" label="姓名" align="center" />
                <el-table-column prop="postNames" label="岗位" align="center" />
              </el-table>
              <pagination :total="totalLeft" v-model:page="queryParamsUserNav.pageNum"
                v-model:limit="queryParamsUserNav.pageSize" @pagination="handleQueryUserNav" />
            </div>

          </el-col>

          <!-- 向左箭头 -->
          <el-col :lg="1" align="right"
            style="margin: 0.5%; display: flex; align-items: center; justify-content: center;">
            <el-button type="primary" icon="arrow-left" @click="handleLeftArrow"></el-button>
          </el-col>
          <!-- 向右箭头 -->
          <el-col :lg="1" align="left"
            style="margin: 0.5%; display: flex; align-items: center; justify-content: center;">
            <el-button type="primary" icon="arrow-right" @click="handleRightArrow"></el-button>
          </el-col>

          <!-- 右边table -->
          <el-col :lg="11" style="height: 100%;">

            <div class="table-container">
              <el-table :data="dataListRight" v-loading="loadingRight" ref="table" border
                header-cell-class-name="el-table-header-cell" highlight-current-row
                @selection-change="handleSelectionChangeRight" :height="tableHeight - 40">
                <el-table-column type="selection" width="50" align="center" />
                <el-table-column type="index" width="60" label="序号" align="center" />
                <el-table-column prop="deptName" label="部门" align="center" />
                <el-table-column prop="userName" label="工号" align="center" />
                <el-table-column prop="nickName" label="姓名" align="center" />
                <el-table-column prop="actualWorkHours" label="实际工时/h" align="center" width="150">
                  <template #default="scope">
                    <el-input-number v-model="scope.row.actualWorkHours" :min="0" :max="24" :precision="1"
                      placeholder="实际工时/h" style="width: 100%;" controls-position="right" />
                  </template>
                </el-table-column>
                <el-table-column prop="standardWorkHours" label="标准工时/h" align="center" width="150" />
                <el-table-column prop="postNames" label="岗位" align="center" />
              </el-table>
            </div>

          </el-col>

        </el-row>
      </el-form>
      <template #footer>
        <el-button text @click="cancel">{{ $t('btn.cancel') }}</el-button>
        <el-button type="primary" @click="submitForm">{{ $t('btn.submit') }}</el-button>
      </template>
    </el-dialog>

js

<script setup name="meshumanreport">
import {
  listMesHumanReport, getMesHumanReportList,
  addMesHumanReport, delMesHumanReport,
  updateMesHumanReport, updateMesHumanReportDetailPartial, getMesHumanReport,
}
  from '@/api/dataReport/meshumanreport.js'
import { getMesLineList } from '@/api/factoryManage/mesline.js'
import { treeselect } from '@/api/system/dept'
import { listPostOptionSelectLimit } from '@/api/system/post.js'
import { getUserListByPost } from '@/api/system/user.js'
import dayjs from 'dayjs';
import auth from '@/plugins/auth'

const { proxy } = getCurrentInstance()
const ids = ref([])
const loading = ref(false)
const loadingLeft = ref(false)
const loadingRight = ref(false)

const queryParamsUserNav = reactive({
  pageNum: 1,
  pageSize: 20,
  sortType: 'asc',
  deptId: undefined,
  postId: undefined,
  userId: undefined,
})

const total = ref(0)
const totalLeft = ref(0)
const totalRight = ref(0)
const dataList = ref([])
const dataListLeft = ref([])
const dataListRight = ref([])
const dataListLeftChosed = ref([])
const dataListRightChosed = ref([])
const queryRef = ref()
const queryRefUserNav = ref()

const deptOptions = ref([])
const postOptions = ref([])
const userOptions = ref([])
const standardDisabled = ref(true)
const tableHeight = ref(570)

var dictParams = [
  { dictType: "mes_classes_type" },
]

proxy.getDicts(dictParams).then((response) => {
  response.data.forEach((element) => {
    state.options[element.dictType] = element.list
  })
})

//当前时间
const now = new Date();

// 设置:subtract往前推 add往后
const dateRangeWorkDate = ref([dayjs().subtract(1, 'day').format('YYYY-MM-DD'), dayjs().format('YYYY-MM-DD')])


const shortcutsWorkDate = [
  {
    text: '今天',
    value: new Date(),
  },
  {
    text: '昨天',
    value: () => {
      const date = new Date()
      date.setTime(date.getTime() - 3600 * 1000 * 24)
      return date
    },
  },
  {
    text: '一周前',
    value: () => {
      const date = new Date()
      date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
      return date
    },
  },
]

//日期范围变化时,触发表单校验(因为el-date-picker 选中的值不会自动通知表单验证状态,所以这里要单独处理)
function handleDateChange() {
  dataList.value = []
  // console.log(dateRangeWorkDate.value)
  // queryParams.dateRangeWorkDate = dateRangeWorkDate.value
  // // 查询报表数据
  // getList()
}

// 动态表头生成
// const dateRangeWorkDate = ref([])
const dynamicHeaders = computed(() => {
  console.log(dateRangeWorkDate.value)
  if (!dateRangeWorkDate.value || dateRangeWorkDate.value.length !== 2) return []
  const dates = []
  const start = new Date(dateRangeWorkDate.value[0])
  const end = new Date(dateRangeWorkDate.value[1])

  while (start <= end) {
    dates.push({
      date: start.toISOString().split('T')[0],
      shifts: ['白班', '夜班']
    })
    start.setDate(start.getDate() + 1)
  }
  console.log(dates)
  return dates
})


/**************************************************** form操作 ****************************************************/
const formRef = ref()
const formRefEdit = ref()
const formRefUserNav = ref()
const title = ref('')
const titleUserNav = ref('')
// 操作类型 1、add 2、edit 3、view
const opertype = ref(0)
const open = ref(false)
const openUserNav = ref(false)
const openEditCell = ref(false)
const state = reactive({
  single: true,
  multiple: true,
  form: {},
  formUserNav: {},
  formEdit: {},
  rules: {
    //产线
    lineCode: [{ required: true, message: '请选择产线', trigger: 'change' },],
    //标准工时
    standardWorkHours: [{ required: true, message: '请输入标准工时', trigger: ['change', 'blur'] },],
},],
    workShift: [{ required: true, message: '请选择班次', trigger: 'change' },],
  },
  rulesQueryReport: {
    //产线
    // lineCode: [{ required: true, message: '请选择产线', trigger: 'change' },],
    //日期
    dateRangeWorkDate: [{ required: true, message: '请选择日期范围', trigger: 'blur' },],
  },
  rulesEdit: {
    lineName: [{ required: true, message: '请输入产线', trigger: 'blur' },],
    userName: [{ required: true, message: '请输入人员', trigger: 'blur' },],
    workDate: [{ required: true, message: '请选择日期', trigger: 'change' },],
    workShift: [{ required: true, message: '请选择班次', trigger: 'change' },],
    standardWorkHours: [{ required: true, message: '请输入标准工时', trigger: 'change' },],
    actualWorkHours: [{ required: true, message: '请输入实际工时', trigger: 'change' },],
  },
  options: {

    // 产线列表
    mes_line_list: [],

    // 班次 选项列表 格式 eg:{ dictLabel: '标签', dictValue: '0'}
    mes_classes_type: [],
  }
})

const { form, formUserNav, formEdit, rules, rulesQueryReport, rulesEdit, options, single, multiple } = toRefs(state)


// 添加按钮操作
function handleAdd() {
  reset();
  open.value = true
  title.value = '添加人力报工'
  opertype.value = 1
  form.value.standardWorkHours = 12


  //日期班次默认值
  var hour = dayjs().get('hour')
  if (hour >= 0 && hour < 8) {
    form.value.workDate = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
  }
  else {
    form.value.workDate = dayjs().format('YYYY-MM-DD')
  }
  if (hour >= 8 && hour < 20) {
    form.value.workShift = '白班'
  }
  else {
    form.value.workShift = '夜班'
  }

  //查询用户下拉列表
  getUserDropDownList()
}


// 添加&修改 表单提交
function submitForm() {
  // 校验表单
  proxy.$refs["formRef"].validate((valid) => {
    if (valid) {
      // 校验明细表
      if (dataListRight.value.length == 0) {
        proxy.$modal.msgError('请先选择人员信息')
        return
      }
      dataListRight.value.forEach(item => {
        item.workShopName = form.value.workShopName
        item.lineCode = form.value.lineCode
        item.lineName = form.value.lineName
        item.workDate = form.value.workDate
        item.workShift = form.value.workShift
        item.userCode = item.userName
        item.userName = item.nickName
      })
      // 提交表单
      console.log(dataListRight.value)
      form.value.mesHumanReportDetailNav = dataListRight.value
      addMesHumanReport(form.value).then((res) => {
        console.log(res)
        if (res.code == 200) {
          proxy.$modal.msgSuccess("操作成功!")
          open.value = false
          //刷新列表
          getList()
        } else {
          // proxy.$modal.msgError("操作失败")
        }
      }).catch(() => {
        proxy.$modal.msgError("操作失败")
      })
    }
  })
}

// 关闭dialog
function cancel() {
  open.value = false
  reset()
}

// 重置表单
function reset() {
  form.value = {
    id: null,
    workShopId: null,
    workShopCode: null,
    workShopName: null,
    lineId: null,
    lineCode: null,
    lineName: null,
    standardWorkHours: null,
    startTime: null,
    endTime: null,
    remark: null,
  };
  proxy.resetForm("formRef")
  resetQueryUserNav()
  dataListLeft.value = []
  dataListLeftChosed.value = []
  dataListRight.value = []
  dataListRightChosed.value = []
}



/**
 * 生产线下拉变更
 * @param {*} value
 */
function formLineSelectChange(value) {
  let line = state.options.mes_line_list.find(item => item.lineCode == value)
  console.log(line)

  form.value.lineId = line.id
  form.value.lineCode = line.lineCode
  form.value.lineName = line.lineName
  form.value.workShopId = line.workShopId
  form.value.workShopCode = line.workShopCode
  form.value.workShopName = line.workShopName
}



/********************************************  人力报工-明细子表信息  ***********************************************/
const mesHumanReportDetailList = ref([])
const checkedMesHumanReportDetail = ref([])
const fullScreen = ref(true)
const drawer = ref(false)

/** 人力报工-明细序号 */
function rowMesHumanReportDetailIndex({ row, rowIndex }) {
  row.index = rowIndex + 1;
}


/** 复选框选中数据 */
function handleMesHumanReportDetailSelectionChange(selection) {
  checkedMesHumanReportDetail.value = selection.map(item => item.index)
}


//查询用户信息
function handleQueryUserNav() {
  console.log(queryParamsUserNav)
  loadingLeft.value = true
  getUserListByPost(queryParamsUserNav).then((res) => {
    console.log(res)
    const { code, data } = res
    if (code == 200) {
      dataListLeft.value = data.result
      totalLeft.value = data.totalNum
      console.log(data.result)
      //dataListLeft去除掉右边dataListRight的人员
      dataListLeft.value = dataListLeft.value.filter(item => !dataListRight.value.some(item2 => item2.userId == item.userId))
    }
    loadingLeft.value = false
  }).catch(() => {
    loadingLeft.value = false
  })
}


//查询用户信息条件清空
function resetQueryUserNav() {
  queryParamsUserNav.deptId = undefined
  queryParamsUserNav.postId = undefined
  queryParamsUserNav.userId = undefined
  queryParamsUserNav.pageNum = 1
  queryParamsUserNav.pageSize = 20
  handleQueryUserNav()
}

//部门选择改变
function changeDept(e) {
  console.log(e)
  if (e) {
    queryParamsUserNav.deptId = e
    getUserDropDownList()
  }
}

//岗位选择改变
function changePost() {
  getUserDropDownList()
}

// 获取人员下拉列表
function getUserDropDownList() {
  queryParamsUserNav.userId = undefined
  var tmpQueryParams = {
    deptId: queryParamsUserNav.deptId,
    postId: queryParamsUserNav.postId,
    pageNum: 1,
    pageSize: 99999,
  }
  getUserListByPost(tmpQueryParams).then((res) => {
    console.log(res)
    const { code, data } = res
    if (code == 200) {
      userOptions.value = data.result
    }
  })

}

//箭头向左,取消选中数据
function handleLeftArrow() {
  if (dataListRightChosed.value.length == 0) {
    proxy.$modal.msgError('请先勾选要取消选中的人员')
  } else {
    dataListRight.value = dataListRight.value.filter(item => !dataListRightChosed.value.some(item2 => item2.userId == item.userId))
    dataListRightChosed.value = []
    //左边的数据重新查询
    handleQueryUserNav()
  }
}

//箭头向右,选中数据
function handleRightArrow() {
  if (form.value.standardWorkHours) {
    if (dataListLeftChosed.value.length > 0) {
      //右边的数据加上左边的选中数据,已经存在的不重复添加
      dataListRight.value = dataListRight.value.concat(dataListLeftChosed.value.filter(item => !dataListRight.value.some(item2 => item2.userId == item.userId)))
      console.log(form.value.workDate)
      dataListRight.value.forEach(item => {
        item.standardWorkHours = form.value.standardWorkHours
        item.actualWorkHours = form.value.standardWorkHours
      })

      dataListLeft.value = dataListLeft.value.filter(item => !dataListLeftChosed.value.some(item2 => item2.userId == item.userId))
      dataListLeftChosed.value = []
    } else {
      proxy.$modal.msgError('请先勾选要添加的人员')
    }
  } else {
    proxy.$modal.msgError('请先设置标准工时')
  }
}

//左侧选择改变
function handleSelectionChangeLeft(selection) {
  // dataListLeftChosed = selection.map(item => item.userId)
  dataListLeftChosed.value = selection
}

//右侧选择改变
function handleSelectionChangeRight(selection) {
  dataListRightChosed.value = selection
}


/** 查询部门下拉树结构 */
function getDeptTreeselect() {
  treeselect().then((response) => {
    deptOptions.value = response.data
  })
}
function getDropDownList() {
  // 获取生产线列表
  getMesLineList().then(res => {
    console.log(res)
    const { code, data } = res
    if (code == 200) {
      state.options.mes_line_list = data
    }
  })

  // 获取岗位列表
  listPostOptionSelectLimit().then((res) => {
    console.log(res)
    if (res.code == 200) {
      postOptions.value = res.data
    }
  })
}

handleQuery()
getDropDownList()
getDeptTreeselect()
</script>


http://www.kler.cn/a/570056.html

相关文章:

  • Kotlin函数式编程与Lambda表达式
  • 【Go原生】项目入口配置及路由配置写法
  • QEMU源码全解析 —— 内存虚拟化(24)
  • 华为飞腾D2000芯片(基于ARM架构)的欧拉操作系统(openEuler)上部署MySQL
  • springboot gradle 多项目创建
  • Python----线性代数(线性代数基础:标量,向量,矩阵,张量)
  • 【每日学点HarmonyOS Next知识】web内存异常问题、Web高度问题、图片按压效果、加载沙盒中html、感知路由返回传参
  • IP-----BGP协议
  • Ai-web 1.0靶场通关攻略
  • [特殊字符] Django 常用命令
  • MySql面试总结(二)
  • WireGuard搭建网络,供整个公司使用
  • 基于 RK3568 / IMX6ULL / STM32MP157 的智能车载系统
  • 基于YALMIP和cplex工具箱的IEEE33微电网故障处理电网重构matlab模拟与仿真
  • OceanBase接入DeepSeek,AI落地改写行业规则
  • 指针的进阶(提高篇)
  • 11.字符函数和字符串函数
  • js的简单介绍
  • 算法-回溯篇03-电话号码的字母组合
  • codewave初识