一、jeecgboot项目前端vue3.0项目BasicTable组件实现父子组件之间的关联

1、需求说明

1:实现勾选父table的多选,子列表的多选全部选中
2:子列表的多选全部选中,勾选父table的多选
3:实现父列表和子列表的多选关联

2、代码实现

<template>
  <BasicTable
    @register="registerTable"
    :expandedRowKeys="expandedRowKeys"
    :rowSelection="parentRowSelection"
    :expand-column-width="80"
    :indentSize="0"
    :clickToRowSelect="false"
    @expand="handleExpand"
    :scroll="{ x: 1300, y: 1000 }"
  >
    <template #headerCell="{ column, title }">
      <template v-if="!column.customTitle">
        <span> 任务明细 </span>
      </template>
      <HeaderCell v-else :column="column" />
    </template>
    <template #emptyText>
      <i class="emptyTextImg">
        <img :src="emptyText" />
      </i>
      <div class="emptyTextTitle">暂无数据</div>
    </template>
    <template #expandedRowRender="{ record }">
      <BasicTable
        style="margin-left: 0px"
        :key="record.id"
        :ref="`tableRef${record.id}`"
        :columns="innerColumns"
        :data-source="record.taskCodingInfoList"
        :clickToRowSelect="false"
        :rowSelection="childRowSelection(record)"
        rowKey="childId"
        :canResize="false"
        :pagination="false"
        :showActionColumn="true"
        :showIndexColumn="true"
        :bordered="false"
        :actionColumn="{
          title: '管理任务',
          width: 200,
          slots: { customRender: 'actionChild' },
        }"
      >
        <template #actionChild="{ record: recordData }">
          <TableAction class="action" :actions="getTableActionChild(recordData)" :dropDownActions="getDropDownAction(recordData)" />
          <!--  -->
        </template>
        <template #emptyText>
          <i class="emptyTextImg">
            <img :src="emptyText" />
          </i>
          <div class="emptyTextTitle">暂无数据</div>
        </template>
      </BasicTable>
    </template>
    <template #action="{ record }">
      <TableAction class="action" :actions="getTableAction(record)" />
    </template>
  </BasicTable>
</template>

<script lang="ts" name="system-menu">
  import { ref, defineComponent, watch, onMounted } from 'vue';
  import { columns, innerColumns } from '../codeManagement.data';
  import { BasicTable, TableAction } from '/@/components/Table';
  import { useListPage } from '/@/hooks/system/useListPage';
  import HeaderCell from '/@/components/Table/src/components/HeaderCell.vue';
  import { useModal } from '/@/components/Modal';
  import { list, finishTask, cancelTask } from '../codeManagement.api';
  import { cancelOrder } from '/@/views/orderManaged/codedDocument/codedDocument.api';
  import emptyText from '/@/assets/images/emptyText.png';
  import { submitOrder } from '/@/views/orderManaged/codedDocument/codedDocument.api';
  export default defineComponent({
    components: { BasicTable, TableAction, HeaderCell},
    props: {
      searchData: {
        type: Object,
        default: () => ({}),
      },
    },
    setup(props, ctx) {
      let searchData = ref();
      onMounted(() => {
        searchData.value = props.searchData;
        setProps({ searchInfo: { tabNo: '1', ...searchData.value } });
        reload();
      });
      watch(
        () => props.searchData,
        (nValue) => {
          searchData.value = nValue;
          setProps({ searchInfo: { tabNo: '1', ...searchData.value } });
          reload();
        },
        {
          deep: true,
        }
      );
      let headers = {
        'Content-Type': 'application/x-www-form-urlencoded;boundary = ' + new Date().getTime(),
      };
    
      // 列表页面公共参数、方法
      const { prefixCls, tableContext } = useListPage({
        tableProps: {
          title: '',
          api: list,
          columns: columns,
          size: 'small',
          isTreeTable: false,
          striped: true,
          useSearchForm: false,
          showTableSetting: false,
          bordered: false,
          rowKey: 'id',
          tableSetting: { fullScreen: true },
          clickToRowSelect: false,
          searchInfo: { tabNo: '1', ...searchData.value },
          afterFetch: afterFetch,
          immediate: false,
          actionColumn: {
            width: 200,
          },
        },
      });
      function afterFetch(record) {
        record.map((item) => {
          if (!!item.taskCodingInfoList && item.taskCodingInfoList.length > 0) {
            item.taskCodingInfoList.forEach((i) => {
              i.childId = i.id;
              i.parentId = item.id;
            });
          }
        });
        console.log(record);
        return record;
      }
      //注册table数据
      const [registerTable, { reload, getDataSource, setProps }] = tableContext;

      // 父表选中状态
      const parentSelectedKeys = ref([]);
      const selectedChildKeys = ref([]);
      // 子表选中状态(每个父节点独立)
      const childSelectedKeys = ref({});
      const childRowSelection = (parent) => ({
        selectedRowKeys: childSelectedKeys.value[parent.id] || [],
        hideSelectAll: true,
        type: 'checkbox',
        columnWidth: 30,
        selectedSelect: (record, selected) => handleChildSelect(record, selected, parent),
      });
      // 父表选中事件(递归处理子节点)
      var handleParentSelect = (parent, selected) => {
        console.log(parent, selected);
        // 更新父表选中状态
        parentSelectedKeys.value = selected ? [...parentSelectedKeys.value, parent.id] : parentSelectedKeys.value.filter((k) => k !== parent.id);

        // 递归更新子表选中状态
        const updateChildren = (children) => {
          children.forEach((child) => {
            const childList = childSelectedKeys.value[parent.id] || [];
            childSelectedKeys.value[parent.id] = selected
              ? [...new Set([...childList, child.childId])]
              : childList.filter((k) => k !== child.childId);
            if (child.taskCodingInfoList) updateChildren(child.taskCodingInfoList);
          });
        };
        updateChildren(parent.taskCodingInfoList || []);
      };

      // 子表选中事件(联动父表)
      const handleChildSelect = (child, selected, parent) => {
        console.log(child, selected, parent, '1111');
        // 更新子表选中状态
        const childList = childSelectedKeys.value[parent.id] || [];
        childSelectedKeys.value[parent.id] = selected ? [...childList, child.childId] : childList.filter((k) => k !== child.childId);

        // 判断是否全选子节点
        const allChildrenSelected = parent.taskCodingInfoList.every((c) => childSelectedKeys.value[parent.id]?.includes(c.childId));

        // 更新父表选中状态
        if (allChildrenSelected && !parentSelectedKeys.value.includes(parent.id)) {
          parentSelectedKeys.value = [...parentSelectedKeys.value, parent.id];
        } else if (!allChildrenSelected && parentSelectedKeys.value.includes(parent.id)) {
          parentSelectedKeys.value = parentSelectedKeys.value.filter((k) => k !== parent.id);
        }
      };
      const parentRowSelection = {
        selectedRowKeys: parentSelectedKeys,
        selectedSelect: handleParentSelect,
        type: 'checkbox',
        columnWidth: 30,
        checkStrictly: false, // 开启父子自动联动‌:ml-citation{ref="1,3" data="citationList"}
      };
      /**
       * 展开事件
       * */
      // 展开key
      let expandedRowKeys = ref<any[]>([]);
      function handleExpand(expanded, record) {
        expandedRowKeys.value = [];
        if (expanded === true) {
          expandedRowKeys.value.push(record.id);
        }
      }
      /**
       * 新增
       */
      function handleCreate(record) {
        openModal1(true, {
          title: '分配赋码任务',
          type: 'add',
          record: record,
        });
      }
      /**
       * 操作栏
       */
      function getTableAction(record) {
        return [
          {
            label: '取消',
            popConfirm: {
              title: '确认取消',
              confirm: handleCancelOrder.bind(null, record),
            },
          },
        ];
      }
      async function handleCancelOrder(record) {
        let formData = new FormData();
        formData.append('id', record.id);
        await cancelOrder(formData, headers);
        reload();
      }
      function getTableActionChild(record) {
        return [
          {
            label: '完成',
            onClick: handleFinished.bind(null, record),
            ifShow: record.status == '1',
          },
        ];
      }
      function getDropDownAction(record) {
        return [
          {
            label: '终止',
            popConfirm: {
              title: '确认终止',
              confirm: handleStopTask.bind(null, record),
            },
            ifShow: false,
          },
        ];
      }
      /**
       * 终止任务
       */
      async function handleStopTask(record) {
        let formData = new FormData();
        formData.append('taskId', record.id);
        await cancelTask(formData, headers);
        reload();
      }
      /**
       * 任务完成
       */
      async function handleFinished(record) {
        console.log(record.id);
        let formData = new FormData();
        formData.append('taskId', record.id);
        await finishTask(formData, headers);
        reload();
      }
      return {
        innerColumns,
        emptyText,
        registerTable,
        handleChildSelect,
        handleParentSelect,
        childRowSelection,
        parentRowSelection,
        selectedChildKeys,
        handleExpand,
        expandedRowKeys,
        handleCreate,
        getTableActionChild,
        getDropDownAction,
        getTableAction,
      };
    },
  });
</script>
<style lang="less" scoped>
</style>

注:需要修改hooks文件中的文件内容实现selectedSelect方法,返回选中的数据,我选中的状态
修改的内容
在这里插入图片描述
修改的文件路径
在这里插入图片描述
在这里插入图片描述

二、 jeecgboot项目前端uni-app前端下程序项目打包

JeecgUniapp 移动框架,采用uniapp、vue2、colorui框架,一份代码多终端适配,同时支持APP、小程序、H5! 实现了与JeecgBoot低代码平台 完美对接! 目前已经实现登录、用户信息、通讯录、公告、移动首页、九宫格等基础功能,更多功能请自己扩展。

  1. 跨平台:产品采用H5开发,能跨平台适用各种移动终端;一次开发,处处使用,大大降低终端开发成本。
  2. 自适应:界面统一采用自适应的布局思想,自适应不同终端分辨率。
  3. 通用性:产品功能都围绕常用办公需求,普遍适用绝大多数企业的日常需要。
  4. 系统集成:让所有系统和应用集成在一起展现,让用户更方便找到所需功能,更自由地处理工作事宜。
  5. 极简开发:内置各类模板,只需按照业务逻辑进行简单配置,即可轻松实现在线生成表单、自定义门户等功能。
    下载源码
    在这里插入图片描述

1、项目简介

  1. 一个uni-app工程,默认包含如下目录及文件

┌─components uni-app组件目录
│ └─comp-a.vue 可复用的a组件
├─hybrid 存放本地网页的目录,详见
├─platforms 存放各平台专用页面的目录,详见
├─pages 业务页面文件存放的目录
│├─index
││ └─index.vue index页面
│├─list
││ └─list.vue list页面
├─static 存放应用引用静态资源(如图片、视频等)的目录,**注意:**静态资源只能存放于此
├─wxcomponents 存放小程序组件的目录,详见
├─main.js Vue初始化入口文件
├─App.vue 应用配置,用来配置App全局样式以及监听 应用生命周期
├─manifest.json 配置应用名称、appid、logo、版本等打包信息,详见
└─pages.json 配置页面路由、导航条、选项卡等页面类信息,详见

  1. jeecg-boot-uniapp项目,包含的非uni-app工程默认的其他目录

┌─_docs 项目的一些文档日志存放目录
├─api 请求服务端的接口文件存放目录
├─common 通用的文件
│├─luch-request 存放的是大神封装的request请求插件目录
│├─router 存放路由配置的目录
│├─service 存放请求拦截和开发环境配置
│├─util 存放的一些工具类
├─plugin 存放项目插件的目录
└─store 状态管理目录

2、启动项目

  1. 配置接口地址
    配置文件:env/.env.development
    在这里插入图片描述
  2. 项目运行H5

    pnpm run dev

  3. 运行小程序
    1. 下载 微信开发者工具
    2. 在.evn文件中 VITE_WX_APPID = ‘你的小程序appid’(也可使用测试号)
    3. 运行命令
      pnpm run dev:mp
    4. 把 dist/dev/mp-weixin 导入到工具
      在这里插入图片描述

3、项目打包

  1. 项目地址配置
    .env.production VITE_SERVER_BASEURL 改成生产地址

    // 变量必须以 VITE_ 为前缀才能暴露给外部读取
    NODE_ENV = ‘production’
    // 是否去除console 和 debugger
    VITE_DELETE_CONSOLE = true
    // 是否开启sourcemap
    VITE_SHOW_SOURCEMAP = false
    VITE_SERVER_BASEURL = ‘你线上的地址’
    在这里插入图片描述

  2. 打包h5
    1. 运行命令
      pnpm run build:h5 或 pnpm run build
    2. 产物放服务器
      产物地址:dist/build/h5
  3. 打包 Android
    1. uniapp 开发者中心实名认证
    2. 确认你是是该应用的成员或者拥有者
      在这里插入图片描述
  4. HBuilderX 编辑器 发行 – 云打包
    修改为自己的appId
    在这里插入图片描述
    在这里插入图片描述
    打包页面配置
    在这里插入图片描述等待 2-5 分钟,打包成功(产物地址:dist/release/apk/)
    在这里插入图片描述
Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐