父子组件之间数据交互和监听解决方案,和解决ant-design-vue复选框翻页后数据丢失的问题
以下是父组件代码
<template>
<AvicModal :visible="true" title="添加" width="960px" height="520px" :centered="true" @cancel="closeModal">
<a-spin :spinning="loading">
<h2 :style="{ 'font-weight': 'bold', 'text-align': 'center', 'margin-top': '0px', 'font-size': '34px' }">{{ addTypeText }}</h2>
<a-form ref="formRef" :model="form" :rules="rules" v-bind="layout" class="form-excel-style">
<a-row>
<a-col v-bind="colLayout.cols">
<a-form-item name="applyNo" label="申请单编码" has-feedback>
<AvicAutoCode
ref="autoCode"
v-model:value="form.applyNo"
code-type="ASSET_TRANSFER_CODE"
code-param="ASSET_TRANSFER"
:allow-clear="true"
:disabled="false"
placeholder="请输入编码"
/>
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="title" label="标题">
<a-input v-model:value="form.title" :maxlength="256" placeholder="请输入标题" />
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="manageDeptId" label="主管部门名称">
<AvicCommonSelect
v-model:value="form.manageDeptId"
type="deptSelect"
placeholder="请选择需求部门"
@callback="
(result) => {
form.manageDeptName = result.names;
form.manageDeptCode = result.deptNamePath;
}
"
/>
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="inDeptId" label="调入部门">
<AvicCommonSelect
v-model:value="form.inDeptId"
type="deptSelect"
placeholder="请选择调入部门"
@callback="
(result) => {
form.inDeptName = result.names;
form.inDeptCode = result.deptNamePath;
}
"
/>
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="outDeptId" label="调出部门">
<AvicCommonSelect
v-model:value="form.outDeptId"
type="deptSelect"
placeholder="请选择调出部门"
@callback="
(result) => {
form.outDeptName = result.names;
form.outDeptCode = result.deptNamePath;
}
"
/>
<!-- <a-input v-model:value="form.outDeptId" :maxlength="50" placeholder="请输入调出部门" /> -->
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="operatorId" label="经办人" has-feedback>
<AvicCommonSelect
v-model:value="form.operatorId"
type="userSelect"
placeholder="请选择经办人"
@callback="
(result) => {
form.operatorName = result.names;
form.operatorCode = result.deptNamePath;
}
"
/>
</a-form-item>
<!--
<a-form-item name="operatorId" label="经办人">
<a-input v-model:value="form.operatorId" :maxlength="50" placeholder="请输入经办人" />
</a-form-item> -->
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="applyDate" label="申请时间">
<a-date-picker v-model:value="form.applyDate" format="YYYY-MM-DD" value-format="YYYY-MM-DD" placeholder="请选择申请时间" />
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item name="secretLevel" label="单据密级" has-feedback>
<a-select
v-model:value="form.secretLevel"
:get-popup-container="(triggerNode) => triggerNode.parentNode"
:allow-clear="true"
placeholder="请选择单据密级"
>
<a-select-option v-for="item in secretLevelList" :key="item.sysLookupTlId" :value="item.lookupCode">
{{ item.lookupName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item name="transferReason" label="调入原因">
<a-input v-model:value="form.transferReason" :maxlength="4000" placeholder="请输入调入原因" />
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols2">
<a-form-item label="附件">
<AvicUploader
ref="uploadFile"
element-id="1"
form-type="add"
table-name="AMS_ASSET_TRANSFER_MAIN"
:form-id="form.id"
:allow-download="true"
:allow-preview="true"
:allow-delete="true"
:allow-update-secret-level="true"
:form-secret-level="form.secretLevel"
@afterUpload="afterUploadEvent"
/>
</a-form-item>
</a-col>
</a-row>
<div style="margin-top: 10px">
<a-button title="批量添加" type="primary" @click="showModal">
<template #icon>
<plus-outlined />
</template>
批量添加
</a-button>
<a-button title="删除" danger @click="handleDelete" :style="{ 'margin-left': '10px' }">
<template #icon>
<plus-outlined />
</template>
删除
</a-button>
<a-modal
v-model:visible="visible"
title=""
:okButtonProps="{ hidden: false }"
:cancelButtonProps="{ hidden: true }"
@ok="handleOk"
width="100%"
wrapClassName="full-modal"
>
<amsTeansferAssetDetailSelect
@select="selectFor"
v-if="props.addType === 'ASSET'"
:existsArr="selectList"
></amsTeansferAssetDetailSelect>
<amsTransferFurnitureDetailSelect
@select="selectFor"
v-if="props.addType === 'FURNITURE'"
:existsArr="selectList"
></amsTransferFurnitureDetailSelect>
</a-modal>
</div>
<div>
<a-table
v-if="props.addType === 'ASSET'"
:columns="assetColumns"
:data-source="selectList"
:row-selection="rowSelection"
row-key="id"
></a-table>
<a-table
v-if="props.addType === 'FURNITURE'"
:columns="furnitureColumns"
:data-source="selectList"
:row-selection="rowSelection"
row-key="id"
></a-table>
</div>
</a-form>
</a-spin>
<template #footer>
<a-button title="保存并启动流程" type="primary" :loading="loading" @click="saveAndStartProcess">保存并启动流程</a-button>
<a-button title="返回" type="primary" ghost @click="closeModal">返回</a-button>
</template>
</AvicModal>
</template>
<script lang="ts" setup>
import { useAmsAssetTransferMainForm, emits } from './ts/AmsAssetTransferMainForm'; // 引入表单ts
import amsTeansferAssetDetailSelect from './AmsTransferAssetDetailSelect.vue';
import amsTransferFurnitureDetailSelect from './AmsTransferFurnitureDetailSelect.vue';
const props = defineProps({
formId: {
type: String,
default: ''
},
bpmInstanceObject: {
type: Object,
default: null
},
params: {
type: Object,
required: false,
default: null
},
bpmOperatorRefresh: {
type: Function,
default: null
},
addType: {
type: String,
default: ''
}
});
const emit = defineEmits(emits);
const { form, formRef, rules, layout, colLayout, loading, secretLevelList, uploadFile, afterUploadEvent, saveAndStartProcess, closeModal } =
useAmsAssetTransferMainForm({
props,
emit
});
const visible = ref<boolean>(false);
const handleOk = (e: MouseEvent) => {
visible.value = false;
};
const showModal = () => {
visible.value = true;
};
const selectList = ref([]);
const selectedRowKeys = ref<string[]>([]); // 新增选中行key存储
// 行选择配置
const rowSelection = computed(() => ({
selectedRowKeys: selectedRowKeys.value,
onChange: (newSelectedRowKeys: string[]) => {
selectedRowKeys.value = newSelectedRowKeys;
},
// 可选:自定义选择列宽度
columnWidth: 60
}));
// 删除选中行
const handleDelete = () => {
console.log('handleDelete', selectedRowKeys.value);
selectList.value = selectList.value.filter((item) => !selectedRowKeys.value.includes(item.id));
selectedRowKeys.value = []; // 清空选中状态
if (props.addType === 'ASSET') {
form.value.amsTransferAssetDetailDto = selectList.value;
} else {
form.value.amsTransferFurnitureDetailDto = selectList.value;
}
};
const addTypeText = computed(() => (props.addType === 'ASSET' ? '资产调拨申请' : '办公家具调拨申请'));
form.value.transferType = props.addType;
const selectFor = (value) => {
console.log('selectFor', value);
selectList.value = value.value;
if (props.addType === 'ASSET') {
form.value.amsTransferAssetDetailDto = selectList.value;
} else {
form.value.amsTransferFurnitureDetailDto = selectList.value;
}
};
const assetColumns = [
{
title: '资产编码',
dataIndex: 'assetCode',
key: 'assetCode'
},
{
title: '资产名称',
dataIndex: 'assetName',
key: 'assetName'
},
{
title: '资产密级',
dataIndex: 'secretLevelName',
key: 'secretLevelName'
},
{
title: '责任人',
dataIndex: 'responsibleUserName',
key: 'responsibleUserName'
},
{
title: '资产型号',
dataIndex: 'assetModel',
key: 'assetModel'
},
{
title: '资产是否完好',
dataIndex: 'ifIntact',
key: 'ifIntact'
},
{
title: '资产原值(元)',
dataIndex: 'originalValue',
key: 'originalValue'
},
{
title: '出厂序列号',
dataIndex: 'serialNumber',
key: 'serialNumber'
},
{
title: '资产规格',
dataIndex: 'assetSpec',
key: 'assetSpec'
}
];
const furnitureColumns = [
{
title: '办公用品编号',
dataIndex: 'furnitureCode',
key: 'furnitureCode'
},
{
title: '办公用品类别',
dataIndex: 'category',
key: 'category'
},
{
title: '上架情况',
dataIndex: 'shelfStatus',
key: 'shelfStatus'
},
{
title: '办公用品名称',
dataIndex: 'furnitureName',
key: 'furnitureName'
},
{
title: '牌号',
dataIndex: 'brand',
key: 'brand'
},
{
title: '规格',
dataIndex: 'specification',
key: 'specification'
},
{
title: '计量单位',
dataIndex: 'unitName',
key: 'unitName'
},
{
title: '单价',
dataIndex: 'unitPrice',
key: 'unitPrice'
},
{
title: '库存数量',
dataIndex: 'stockQty',
key: 'stockQty'
}
];
</script>
点击批量添加,如果父组件有数据,子组件直接变成勾选状态,弹出子组件后,如果点击数据,就把数据传给父组件(鸡贼的把取消按钮去掉了),删除只能通过父组件删除(这样解决了翻页后数据丢失的问题)
以下是子组件代码
<template>
<div class="content-wrapper">
<div class="top-search-box">
<!-- 高级查询 -->
<a-form v-bind="layout" ref="formRef" :model="queryForm">
<a-row :gutter="16">
<a-col v-bind="colLayout.cols">
<a-form-item label="资产调拨主表ID">
<a-input
v-model:value="queryForm.amsAssetTransferMainId"
placeholder="请输入资产调拨主表ID"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item label="资产编码">
<a-input v-model:value="queryForm.assetCode" placeholder="请输入资产编码" :allow-clear="true" @pressEnter="handleQuery" />
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols">
<a-form-item label="资产名称">
<a-input v-model:value="queryForm.assetName" placeholder="请输入资产名称" :allow-clear="true" @pressEnter="handleQuery" />
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="责任人">
<a-input
v-model:value="queryForm.responsibleUserId"
placeholder="请输入责任人"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="责任人">
<a-input
v-model:value="queryForm.responsibleUserCode"
placeholder="请输入责任人"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="责任人">
<a-input
v-model:value="queryForm.responsibleUserName"
placeholder="请输入责任人"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="资产型号">
<a-input v-model:value="queryForm.assetModel" placeholder="请输入资产型号" :allow-clear="true" @pressEnter="handleQuery" />
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="资产是否完好">
<a-input v-model:value="queryForm.ifIntact" placeholder="请输入资产是否完好" :allow-clear="true" @pressEnter="handleQuery" />
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="出厂序列号">
<a-input
v-model:value="queryForm.serialNumber"
placeholder="请输入出厂序列号"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="资产规格">
<a-input v-model:value="queryForm.assetSpec" placeholder="请输入资产规格" :allow-clear="true" @pressEnter="handleQuery" />
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="存放地点">
<a-input
v-model:value="queryForm.storageLocation"
placeholder="请输入存放地点"
:allow-clear="true"
@pressEnter="handleQuery"
/>
</a-form-item>
</a-col>
<a-col v-show="advanced" v-bind="colLayout.cols">
<a-form-item label="SECRET_LEVEL">
<a-select
v-model:value="queryForm.secretLevel"
:get-popup-container="(triggerNode) => triggerNode.parentNode"
:allow-clear="true"
placeholder="请选择SECRET_LEVEL"
>
<a-select-option v-for="item in secretLevelList" :key="item.sysLookupTlId" :value="item.lookupCode">
{{ item.lookupName }}
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col v-bind="colLayout.cols" style="margin-left: auto">
<div class="table-page-search-submitButtons">
<a-space>
<AvicAdvancedQueryButton
table-key="amsTransferAssetDetail"
:query-form="queryForm"
:advanced="advanced"
@select="
(value, advancedFlag) => {
queryForm = value;
advanced = !advanced ? advancedFlag : advanced;
}
"
@search="handleQuery"
/>
<a-button type="primary" ghost @click="resetQuery">
<redo-outlined />
重置
</a-button>
<a-button type="link" style="margin: 0" @click="toggleAdvanced">
{{ advanced ? '收起' : '展开' }}
<up-outlined v-if="advanced" />
<down-outlined v-else />
</a-button>
</a-space>
</div>
</a-col>
</a-row>
</a-form>
</div>
<!-- 表格组件 -->
<div class="table-wrapper">
<AvicTable
ref="amsTransferAssetDetailSelect"
table-key="amsTransferAssetDetailSelect"
:columns="columns"
:row-key="(record) => record.id"
:data-source="list"
:loading="loading"
:show-table-setting="false"
:row-selection="{
selectedRowKeys: selectedRowKeys,
onChange: onSelectChange,
columnWidth: 40,
fixed: true
}"
:page-parameter="queryParam.pageParameter"
:total="totalPage"
:custom-row="customRow"
@change="handleTableChange"
@refresh="getList"
>
<template #toolBarRight>
<a-input-search
class="opt-btn-commonsearch"
style="width: 200px"
placeholder="请输入"
title="请输入"
:allow-clear="true"
@search="handleKeyWordQuery"
/>
</template>
<template #bodyCell="{ column, text, record, index }">
<template v-if="column.dataIndex === 'id'">
{{ index + 1 + queryParam.pageParameter.rows * (queryParam.pageParameter.page - 1) }}
</template>
</template>
</AvicTable>
</div>
</div>
</template>
<script lang="ts" setup>
import type { AmsTransferAssetDetailDto } from '@/api/avic/mms/ams/amsassettransfermain/AmsTransferAssetDetailApi'; // 引入模块DTO
import { listAmsTransferAssetDetailByPage } from '@/api/avic/mms/ams/amsassettransfermain/AmsTransferAssetDetailApi'; // 引入模块API
const $emit = defineEmits(['select', 'handleRowDblClick']);
const props = defineProps({
existsArr: {
type: Array,
default: () => []
}
});
watch(
() => props.existsArr,
(newValue) => {
if (newValue) {
console.log('existsArr', newValue);
selectedRowKeys.value = newValue.map((item) => item.id);
selectedRows.value = newValue;
} else {
selectedRowKeys.value = [];
selectedRows.value = [];
}
},
{ deep: true, immediate: false } // 如果需要初始执行可以加 immediate: true
);
const amsTransferAssetDetailSelect = ref();
const { proxy } = getCurrentInstance();
const layout = {
labelCol: { flex: '0 0 120px' },
wrapperCol: { flex: '1 1 0' }
};
const colLayout = proxy.$colLayout4; // 页面表单响应式布局对象
const columns = [
{
title: '资产编码',
dataIndex: 'assetCode',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '资产名称',
dataIndex: 'assetName',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '责任人',
dataIndex: 'responsibleUserName',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '资产型号',
dataIndex: 'assetModel',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '资产是否完好',
dataIndex: 'ifIntact',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '资产原值(元)',
dataIndex: 'originalValue',
ellipsis: true,
minWidth: 120,
resizable: true,
customRender: (text) => proxy.$formatZero(text.value, 7),
align: 'right'
},
{
title: '出厂序列号',
dataIndex: 'serialNumber',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '资产规格',
dataIndex: 'assetSpec',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: '存放地点',
dataIndex: 'storageLocation',
ellipsis: true,
sorter: true,
minWidth: 120,
resizable: true,
align: 'left'
},
{
title: 'SECRET_LEVEL',
dataIndex: 'secretLevelName',
ellipsis: true,
minWidth: 120,
resizable: true,
customHeaderCell() {
return {
['class']: 'required-table-title'
};
},
align: 'center'
},
{
title: '操作',
dataIndex: 'action',
ellipsis: true,
visible: false,
width: 120,
fixed: 'right'
}
];
const queryForm = ref<AmsTransferAssetDetailDto>({});
const queryParam = reactive({
// 请求表格数据参数
pageParameter: {
page: 1, // 页数
rows: proxy.$settings.table.pageSize || 20 // 每页条数
},
searchParams: {
...queryForm.value
},
keyWord: ref(''), // 快速查询数据
sidx: null, // 排序字段
sord: null // 排序方式: desc降序 asc升序
});
const advanced = ref(false); // 高级搜索 展开/关闭
const list = ref([]); // 表格数据集合
const selectedRowKeys = ref([]); // 选中数据主键集合
const selectedRows = ref([]); // 选中行集合
const loading = ref(false);
const totalPage = ref(0);
const secretLevelList = ref([]); // SECRET_LEVEL通用代码
onMounted(() => {
// 加载表格数据
getList();
// 获取当前用户对应的文档密级
getUserFileSecretList();
});
/** 查询数据 */
function getList() {
// selectedRowKeys.value = []; // 清空选中
// selectedRows.value = []; // 清空选中
loading.value = true;
listAmsTransferAssetDetailByPage(queryParam)
.then((response) => {
list.value = response.data.result;
totalPage.value = response.data.pageParameter.totalCount;
loading.value = false;
})
.catch(() => {
list.value = [];
totalPage.value = 0;
loading.value = false;
});
}
/** 获取当前用户对应的文档密级 */
function getUserFileSecretList() {
proxy.$getUserFileSecretLevelList((result) => {
secretLevelList.value = result;
});
}
/** 高级查询 查询按钮操作 */
function handleQuery() {
queryParam.searchParams = queryForm.value;
queryParam.keyWord = '';
queryParam.pageParameter.page = 1;
getList();
}
/** 高级查询 重置按钮操作 */
function resetQuery() {
queryForm.value = {};
handleQuery();
}
/** 高级查询 展开/收起 */
function toggleAdvanced() {
advanced.value = !advanced.value;
}
/** 快速查询逻辑 */
function handleKeyWordQuery(value) {
queryParam.keyWord = value;
queryParam.pageParameter.page = 1;
getList();
}
/** 勾选复选框时触发 */
function onSelectChange(rowKeys, rows) {
nextTick(() => {
// 1. 获取旧值(避免直接修改原数组)
const oldRowKeys = [...selectedRowKeys.value];
const oldRows = [...selectedRows.value];
// 2. 合并新值和旧值(根据需求选择是否去重)
// 方案一:直接追加(允许重复选择)
const mergedRowKeys = [...oldRowKeys, ...rowKeys];
const mergedRows = [...oldRows, ...rows];
const uniqueRowKeys = [...new Set(mergedRowKeys)]; // 对 rowKeys 去重
const uniqueRows = mergedRows.filter((item, index, self) => index === self.findIndex((t) => t.id === item.id)); // 对 rows 根据 id 去重
selectedRowKeys.value = uniqueRowKeys;
selectedRows.value = uniqueRows;
// 传出选中项
$emit('select', selectedRows);
});
}
// function onSelectChange(rowKeys, rows) {
// selectedRowKeys.value = rowKeys;
// selectedRows.value = rows;
// // 传出选中项
// $emit('select', selectedRows);
// }
/** 表格排序 */
function handleTableChange(pagination, _filters, sorter) {
queryParam.pageParameter.page = pagination.current;
queryParam.pageParameter.rows = pagination.pageSize;
if (proxy.$objIsNotBlank(sorter.field)) {
queryParam.sidx = sorter.field;
queryParam.sord = sorter.order === 'ascend' ? 'asc' : 'desc'; // 排序方式: desc降序 asc升序
}
getList();
}
/** 行双击事件 */
function customRow() {
return {
on: {
dblclick: (event, record) => {
$emit('handleRowDblClick', [record]);
}
}
};
}
onMounted(() => {
if (props.existsArr) {
selectedRowKeys.value = props.existsArr.map((item) => item.id);
selectedRows.value = props.existsArr;
}
});
</script>