最近在做开发过程中,遇到了一个奇怪的bug,我也是Vue新手,从react转过来的,排查了好就终于解决了,明天可以继续联调啦。具体过程如下:
场景概括
<父组件>
<子组件 :record=“record”></子组件>
</父组件>
父组件
<template>
<a-table>…</a-table>
<子组件 :record=“record”></子组件>
</template>
<script></script>
<style></style>
子组件
<template></template>
<script>
// 获取props和emits
// 将record中的数据进行处理后在子组件中渲染数据
值得注意的是:处理的这些数据的字段最好是在副组件的表格中没有用到的字段,
否则会出现一种奇怪的现象:当点击父组件表格中的“查看”按钮并渲染出抽屉的同时,
父组件中的表格里的某个字段(也就是你在子组件中改动过相应字段值的字段)会变空,
也就是没有数据!!!
</script>
<style></style>
具体实践代码与截图
父组件
<template>
<PageWrapper class="people-container">
<a-card class="people-container_btngroup" :bodyStyle="bodyStyle">
<a-form ref="searchFormRef" :model="searchFormState" name="search-form" layout="inline" class="search-form">
<a-row :gutter="[4, 16]">
<a-col :span="24">
<a-form-item label="创建时间范围" name="createDate">
<a-range-picker v-model:value="searchFormState.createDate" show-time format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" class="mr-4"></a-range-picker>
<a-space>
<a @click="handlePeriod(0, 'endOf', 'create')">今天</a>
<a @click="handlePeriod(1, 'endOf', 'create')">昨天</a>
<a @click="handlePeriod(7, 'endOf', 'create')">近7天</a>
<a @click="handlePeriod(30, 'endOf', 'create')">近30天</a>
</a-space>
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="上次登录时间范围" name="lastLoginDate">
<a-range-picker v-model:value="searchFormState.lastLoginDate" show-time format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss" class="mr-4"></a-range-picker>
<a-space>
<a @click="handlePeriod(0, 'endOf', 'lastLogin')">今天</a>
<a @click="handlePeriod(1, 'endOf', 'lastLogin')">昨天</a>
<a @click="handlePeriod(7, 'endOf', 'lastLogin')">近7天</a>
<a @click="handlePeriod(30, 'endOf', 'lastLogin')">近30天</a>
</a-space>
</a-form-item>
</a-col>
<a-col :span="5">
<a-form-item label="账号" name="username">
<a-input placeholder="请输入账号" v-model:value="searchFormState.username"></a-input>
</a-form-item>
</a-col>
<a-col :span="3">
<a-form-item name="status">
<a-select :options="statusOptions" v-model:value="searchFormState.status" placeholder="状态" />
</a-form-item>
</a-col>
<a-col :span="3" :offset="13">
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">查询</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-card>
<a-row align="middle" class="people-table">
<a-button type="primary" @click="addPeopleBtnEvent">{{ '新增' + roleName }}</a-button>
<a class="people-table_total">共{{ totalCount }}个{{ roleName }}</a>
<a-divider type="vertical"></a-divider>
<SyncOutlined style="color: #909399" />
<a class="people-table_update" @click="handleReset">更新数据</a>
</a-row>
<a-table :data-source="tableData" :loading="isLoading" :columns="columns" :pagination="false">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === STATUS_NAME.to_be_enabled" color="processing">待启用</a-tag>
<a-tag v-if="record.status === STATUS_NAME.enabled" color="success">已启用</a-tag>
<a-tag v-if="record.status === STATUS_NAME.disabled" color="warning">已禁用</a-tag>
</template>
<template v-if="column.key === 'operation'">
<a-button type="link" @click="handleDetail(record)">查看</a-button>
<a-button
v-if="record.status == STATUS_NAME.disabled || (record.status == STATUS_NAME.to_be_enabled && record.businessStatus != '')"
type="link" @click="handleApply(record, STATUS_NAME.enabled)">
启用
</a-button>
<a-button v-if="record.status == STATUS_NAME.enabled" type="link"
@click="handleApply(record, STATUS_NAME.disabled)">
禁用
</a-button>
<a-tooltip placement="bottomRight"
v-if="record.status == STATUS_NAME.to_be_enabled && record.businessStatus == ''">
<template #title>
<span>请先授权后启用</span>
</template>
<a-button v-if="record.status == STATUS_NAME.to_be_enabled && record.businessStatus == ''" type="text"
disabled> 启用
</a-button>
</a-tooltip>
<a-popconfirm title="您确定删除该账号吗?" ok-text="确认" cancel="取消" @confirm="handleDelete(record.id, record)">
<a-button type="link">删除</a-button>
</a-popconfirm>
<a-button type="link" @click="resetPwd(record)">重置密码</a-button>
<a-tooltip placement="bottomRight">
<template #title>
<span>请先禁用后授权</span>
</template>
<a-button v-if="record.status == STATUS_NAME.enabled" type="text" disabled>授权</a-button>
</a-tooltip>
<a-button v-if="record.status != STATUS_NAME.enabled" type="link" @click="authApply(record)">授权</a-button>
</template>
</template>
</a-table>
</a-card>
<add-people-modal v-if="addPeopleModalVisible" :visible="addPeopleModalVisible"
:peopleType="peopleType[route.name as any]" @success="handleAddPeopleSuccess" @cancel="handleAddPropleCancel" />
<detail-drawer v-if="detailDrawerProps.visible" :peopleType="detailDrawerProps.peopleType"
:roleName="detailDrawerProps.roleName" :visible="detailDrawerProps.visible" :record="detailDrawerProps.record"
@close="handleDetailsDrawerClose" />
<UkeyVerifyModal v-if="ukeyVerifyModal.visible" :visible="ukeyVerifyModal.visible"
:roleName="ukeyVerifyModal.roleName" :editType="ukeyVerifyModal.editType" :record="ukeyVerifyModal.record"
@close="handleUkeyVerifyModalClose" />
<auth-modal v-if="authApplyModalProps.visible" :visible="authApplyModalProps.visible"
@success="handleAuthApplyModalSuccess" :record="authApplyModalProps.record"
:roleName="authApplyModalProps.roleName" @cancel="handleAuthApplyCancel" />
</PageWrapper>
</template>
<script setup lang="ts">
import { PageWrapper } from '@/components/Page';
import { ref, onBeforeMount, onMounted, computed, h } from 'vue';
import { useRoute } from 'vue-router';
import { type FormInstance, type SelectProps, Modal, Alert } from 'ant-design-vue';
import { SyncOutlined } from '@ant-design/icons-vue';
import { bodyStyle, peopleType, peopleTypeTitle, STATUS_NAME, STATUS, columns, } from './constants';
import AddPeopleModal from '@/views/people/components/AddPeopleModal.vue';
import DetailDrawer from '@/views/people/components/DetailDrawer.vue';
import UkeyVerifyModal from '@/views/people/components/UkeyVerifyModal.vue';
import AuthModal from '@/views/people/components/AuthModal.vue';
import { postPersonsApi } from '@/api/people'
import dayjs, { Dayjs } from 'dayjs';
type RangeValue = [Dayjs, Dayjs];
const searchFormRef = ref<FormInstance>();
const searchFormState = ref<Record<string, any>>({
createDate: ref<RangeValue>([] as any),
lastLoginDate: ref<RangeValue>([] as any),
username: ref<string | undefined>(undefined),
status: ref<string | undefined>(undefined),
pageNum: 1,
pageSize: 10
});
const statusOptions = ref<SelectProps['options']>([
{
label: '待启用',
value: STATUS_NAME.to_be_enabled,
},
{
label: '已启用',
value: STATUS_NAME.enabled,
},
{
label: '已禁用',
value: STATUS_NAME.disabled,
},
]);
// const TypographyText = Typography.Text;
const roleName = ref<string>('');
const route = useRoute();
const roleFromRoute = computed(() => peopleType[route.name as any]);
onBeforeMount(() => {
roleName.value = peopleTypeTitle[roleFromRoute.value] || '';
});
const isLoading = ref<boolean>(false);
const totalCount = ref<number>(0);
const tableData = ref([]);
const addPeopleModalVisible = ref<boolean>(false);
const ukeyVerifyModal = ref({
visible: false,
roleName: '',
editType: '',
record: {},
});
const authApplyModalProps = ref({
visible: false,
record: {},
roleName: '',
});
const detailDrawerProps = ref({
visible: false,
record: {},
roleName: '',
peopleType: peopleType[route.name as any]
})
// 获取分页数据
const getTableData = async () => {
try {
const params = {
...searchFormState.value,
roleName: roleName.value,
startTime: searchFormState.value.createDate ? searchFormState.value.createDate[0] : '',
endTime: searchFormState.value.createDate ? searchFormState.value.createDate[1] : '',
loginStartTime: searchFormState.value.lastLoginDate ? searchFormState.value.lastLoginDate[0] : '',
loginEndTime: searchFormState.value.lastLoginDate ? searchFormState.value.lastLoginDate[1] : '',
}
const data = await postPersonsApi(params as any)
tableData.value = data.records?.map((record: any, index: number) => {
let authBusiness = record.authorizedBusiness.map(item => item.name).join(';')
return {
...record,
authBusiness,
order: (data.pageNum - 1) * data.pageSize + index + 1
}
})
totalCount.value = data.total
} catch (e) {
console.log(e)
}
};
// 查询
const handleSearch = () => {
searchFormState.value.pageNum = 1;
getTableData();
}
// 重置
const handleReset = () => {
Object.keys(searchFormState.value).forEach((key) => {
searchFormState.value[key] = key === 'createDate' || key === 'lastLoginDate' ? [] : undefined;
})
searchFormState.value.pageNum = 1;
searchFormState.value.pageSize = 10;
getTableData();
}
const handleApply = (data: any, params: string) => {
console.log('data', data);
ukeyVerifyModal.value.visible = true;
ukeyVerifyModal.value.record = data;
ukeyVerifyModal.value.roleName = data.roleName;
// if (params) {
ukeyVerifyModal.value.editType = params;
};
const handleDelete = (id: number, record: any) => {
if (record.status === 1 || record.status === 2) {
// 警告Modal
Modal.warning({
title: `您确定要删除${record.role}吗?`,
content: h(Alert, {
message: `当前账号${STATUS[record.status]}, 请先禁用后删除`,
type: 'warning',
showIcon: true,
}),
});
} else {
// 删除Modal
handleApply(record, 'DELETE');
}
};
const resetPwd = (data: any) => {
handleApply(data, 'RESET');
};
// 授权
const authApply = (data: any) => {
if (data.status != STATUS_NAME.enabled) {
authApplyModalProps.value.visible = true;
authApplyModalProps.value.record = data;
authApplyModalProps.value.roleName = data.roleName;
}
};
const addPeopleBtnEvent = () => {
addPeopleModalVisible.value = true;
};
const handleAddPeopleSuccess = () => { };
const handleAddPropleCancel = () => {
getTableData();
addPeopleModalVisible.value = false;
};
const handleDetail = (data: any) => {
console.log('detail', data)
detailDrawerProps.value.visible = true;
detailDrawerProps.value.record = data;
detailDrawerProps.value.roleName = data.roleName;
};
const handleDetailsDrawerClose = () => {
detailDrawerProps.value.visible = false;
detailDrawerProps.value.record = {};
// getTableData()
};
const handleUkeyVerifyModalClose = () => {
getTableData()
ukeyVerifyModal.value.visible = false;
};
const handleAuthApplyModalSuccess = () => {
getTableData()
}
const handleAuthApplyCancel = () => {
authApplyModalProps.value.visible = false;
authApplyModalProps.value.record = {};
getTableData()
};
const handlePeriod = (daysOffset: number, periodEnd: string, status: string) => {
const start = dayjs().subtract(daysOffset, 'd').startOf('day').format('YYYY-MM-DD HH:mm:ss');
const end = dayjs().subtract(1, 'd')[periodEnd]().format('YYYY-MM-DD HH:mm:ss');
if (status === 'create') {
searchFormState.value.createDate = [start as any, end as any];
}
if (status === 'lastLogin') {
searchFormState.value.lastLoginDate = [start as any, end as any];
}
};
onMounted(() => {
getTableData();
});
</script>
<style scoped lang="less">
.people-container {
&_btngroup {
margin-bottom: 16px;
}
.people-table {
margin-bottom: 20px;
&_total {
margin-left: 20px;
color: #303133;
}
&_update {
margin-left: 5px;
color: #909399;
}
}
.ant-btn[disabled],
.ant-btn[disabled]:hover {
border: none;
background: transparent;
}
}
</style>
子组件
<template>
<a-drawer v-model:open="dialogVisible" :title="'查看' + peopleTypeTitle[props.peopleType]" placement="right"
@close="handleCancel" :width="480" class="details-container">
<a-descriptions bordered>
<a-descriptions-item v-for="(desc, index) in descItems" :key="index" :span="3">
<template #label>{{ desc.label }} </template>
<template v-if="desc.key === 'role'">
<a-tag v-if="['业务管理员', '业务操作员', '司法公证人员', '审计员', '超级管理员'].includes(
desc.value,
)
" :color="desc.value === '审计员' ? 'warning' : 'processing'">
{{ desc.value }}
</a-tag>
</template>
<template v-else-if="desc.key === 'status'">
<a-tag v-if="desc.value === STATUS_NAME.to_be_enabled" color="processing">待启用</a-tag>
<a-tag v-if="desc.value === STATUS_NAME.enabled" color="success">已启用</a-tag>
<a-tag v-if="desc.value === STATUS_NAME.disabled" color="warning">已禁用</a-tag>
</template>
<template v-else-if="desc.key === 'authorizedBusiness'">
<p v-for="(v, index) in desc.value" :key="index">
<CheckCircleFilled v-if="v.isChecked" style="margin: 0 5px 0 0; color: #52c41a; font-size: 14px" />
<CloseCircleFilled v-else="!v.isChecked" style="margin: 0 5px 0 0; color: #ff0033; font-size: 14px" />
{{ v.name }}
</p>
</template>
<span v-else>{{ desc.value }}</span>
</a-descriptions-item>
</a-descriptions>
</a-drawer>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { peopleTypeTitle, STATUS_NAME } from '@/views/people/constants';
import { getMenuOptionsApi } from '@/api/people'
const props = defineProps({
peopleType: {
type: String,
default: '',
},
visible: {
type: Boolean,
default: false,
},
roleName: {
type: String,
default: '',
},
record: {
type: Object,
default: () => {
return {};
},
},
});
let dialogVisible = ref<boolean>(props.visible);
let detailRecord = ref(props.record);
const emits = defineEmits(['close']);
const descItems = ref([
{ label: '账号', key: 'username', value: '' },
{ label: '角色', key: 'roleName', value: '' },
{ label: '创建时间', key: 'createTime', value: '' },
{ label: '上次登录时间', key: 'lastLoginTime', value: '' },
{ label: '状态', key: 'status', value: '' },
{ label: '授权业务', key: 'authorizedBusiness', value: '' },
]);
const roleIds = {
'超级管理员': 1,
'业务管理员': 2,
'业务操作员': 3,
'审计管理员': 4,
'审计员': 5
}
const handleCancel = () => {
emits('close');
};
const getDetailsRecord = async () => {
try {
console.log('record', detailRecord.value);
console.log(999, roleIds[props.roleName])
const data = await getMenuOptionsApi(roleIds[props.roleName])
let authBusinessHandled = detailRecord.value.authorizedBusiness
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < authBusinessHandled.length; j++) {
if (data[i].id == authBusinessHandled[j].id) {
data[i].isChecked = true
} else {
data[i].isChecked = false
}
}
}
detailRecord.value.authorizedBusiness = data
for (const descItem of descItems.value) {
descItem.value = detailRecord.value[descItem.key] || '';
}
} catch (e) {
console.log(e)
}
};
onMounted(() => {
getDetailsRecord();
});
</script>
<style lang="less" scoped>
.details-container {
&_label {
text-align: center;
}
}
</style>
截图