Vue响应式导致的表格个别字段从有到空的问题解决

最近在做开发过程中,遇到了一个奇怪的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>

截图

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值