Vue 弹框实现,防抖(useDebounceFn)

index.vue

<template>
  <div class="app-container"> 
    <el-card shadow="never">
      <el-table v-loading="loading" :data="pageData" highlight-current-row border>
        <el-table-column label="医院" width="150" prop="HospitalName" show-overflow-tooltip />
        <el-table-column label="科室" width="100" prop="DeptName" show-overflow-tooltip /> 
        <el-table-column
          fixed="right"
          label="操作"
          width="280"
          prop="describe"
        >
          <template #default="scope">
            <el-button 
              type="primary"
              size="small"
              @click="openDetailDialog(scope.row)"
            >
              详情
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <pagination
        v-if="total > 0"
        v-model:total="total"
        v-model:page="queryParams.PageNum"
        v-model:limit="queryParams.PageSize"
        @pagination="handleQuery"
      />
    </el-card>
 
    <!-- 添加 Detail 组件,并绑定 visible 属性 -->
    <Detail v-model="detailVisible" :detailData="currentDetailData" />
  </div>
</template>

<script setup lang="ts"> 
import Detail from "./components/Detail.vue";
import { ref, reactive } from "vue";

defineOptions({
  name: "Order",
  inheritAttrs: false,
});

import OrderAPI, { PageVO, PageQuery } from "@/api/order";
 

const queryFormRef = ref();
const loading = ref(false);
const total = ref(0);
const detailVisible = ref(false); // 控制 Detail 对话框的显示和隐藏
const currentDetailData = ref<any>(null); // 存储当前行的数据

const queryParams = reactive<PageQuery>({
  PageNum: 1,
  PageSize: 10,
});

// 日志表格数据
const pageData = ref<PageVO[]>();

/** 查询 */
function handleQuery() {
  loading.value = true;
  OrderAPI.getPage(queryParams)
    .then((data) => {
      console.log(data);
      pageData.value = data.BussinessData;
      total.value = data.Page.TotalCount;
    })
    .finally(() => {
      loading.value = false;
    });
}

const openDetailDialog = (row: any) => {
  currentDetailData.value = row; // 保存当前行的数据
  detailVisible.value = true; // 显示对话框
};

onMounted(() => {
  handleQuery();
});
</script>

detail.vue


<template>
  <el-dialog
    v-model="visible"
    :show-close="false"
    width="50%"
    append-to-body
    @close="closeDetailDialog"
  >
    <template #header>
      <div class="flex-x-between">
        <span>通知公告详情</span>
        <div class="dialog-toolbar">
          <el-button circle @click="closeDetailDialog">
            <template #icon>
              <Close />
            </template>
          </el-button>
        </div>
      </div>
    </template>
    <el-descriptions :column="1">
      <el-descriptions-item label="标题:">
        {{ props.detailData?.HospitalName || '无数据' }}
      </el-descriptions-item>

    </el-descriptions>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="primary" @click="handleSubmit">确 定</el-button>
      </span>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">

const props = defineProps({
  //update:modelValue 是 v-model 默认监听的事件,用于更新父组件的绑定值。
  modelValue: {
    type: Boolean,
    default: false,
  },
  detailData: {
    type: Object,
    default: () => ({}), // 默认空对象,避免 undefined
  },
});

//父页面,通过 update:modelValue 感知 <ChildComponent: modelValue="someValue" @update:modelValue="(newValue) => someValue = newValue"  />
const emit = defineEmits(["update:modelValue"]);
//从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 父组件可以用 v-model 绑定一个值:
const visible = defineModel("modelValue", {
  type: Boolean,
  required: true,
  default: false,
});


const closeDetailDialog = () => {
  visible.value = false;
};

const formRef = ref();

// 提交用户表单(防抖)
const handleSubmit = useDebounceFn(() => {
   
  formRef.value.validate((valid: boolean) => {
    if (valid) {
      const { Id } = props.detailData.Id
      const { MonitorHour } = props.detailData.MonitorHour
      OrderAPI.update(Id,MonitorHour)
        .then((data) => {
          ElMessage.success('设置成功')
          emit("update-sucess");
          handleClose();
        })
        .finally(() => {
          //loading.value = false;
        });
    }
  });
}, 1000);
</script>

关键点说明

  1. v-model 双向绑定

    • index.vue 中,Detail 组件通过 v-model="detailVisible" 绑定了一个布尔值,用于控制对话框的显示和隐藏。
    • Detail.vue 中,通过 props 接收 modelValue,并通过 emit("update:modelValue") 实现双向绑定。
  2. 传递数据

    • index.vue 通过 :detailData="currentDetailData" 将当前行的数据传递给 Detail.vue
    • Detail.vue 通过 props 接收 detailData 并显示在对话框中。
  3. 关闭对话框

    • 点击关闭按钮或对话框外部时,visible 会被设置为 false,并通过 emit 通知父组件更新状态。

效果

  • 点击“详情”按钮时,Detail 对话框会弹出并显示当前行的数据。
  • 点击关闭按钮或对话框外部时,对话框会关闭。

这样就实现了在 index.vue 中弹出 Detail.vue 的功能。

const emit = defineEmits(["update:modelValue"]); 是 Vue 3 的 <script setup> 语法中用于声明组件可以触发(emit)哪些自定义事件的写法。

1. 作用

  • 它告诉 Vue,这个组件可以触发一个名为 "update:modelValue" 的事件。
  • 这个事件通常用于实现 v-model 双向绑定

2. 为什么需要 update:modelValue

在 Vue 中,v-model 是语法糖,它实际上是 :modelValue(props) + @update:modelValue(emit)的组合。

例如:

<ChildComponent v-model="someValue" />

等价于:

<ChildComponent 
  :modelValue="someValue" 
  @update:modelValue="(newValue) => someValue = newValue" 
/>

所以,子组件需要通过 emit('update:modelValue', newValue) 来更新父组件的绑定值


3. defineEmits 详解

  • defineEmits 是 Vue 3 <script setup> 特有的 API,用于声明组件可以触发哪些事件
  • 它接收一个事件名数组(或更复杂的类型定义),并返回一个 emit 函数,用于触发这些事件。

示例:

const emit = defineEmits(["update:modelValue", "change", "close"]);

// 触发事件
emit("update:modelValue", false); // 通知父组件更新 v-model
emit("change", newValue); // 触发自定义事件
emit("close"); // 不带参数的事件

4. 在你的代码中的具体作用

Detail.vue 中:

const emit = defineEmits(["update:modelValue"]);

// 当 visible 变化时,通知父组件更新 v-model 绑定的值
watch(() => visible.value, (val) => {
  emit("update:modelValue", val); // 触发事件,让父组件的 detailVisible 同步更新
});

这样:

  • visible 变为 false(比如点击关闭按钮),emit('update:modelValue', false) 会通知父组件 index.vue 更新 detailVisiblefalse
  • 父组件的 v-model="detailVisible" 会自动同步这个变化,实现双向绑定。

5. 总结

代码 作用
defineEmits(["update:modelValue"]) 声明组件可以触发 update:modelValue 事件(用于 v-model 双向绑定)
emit("update:modelValue", newValue) 触发事件,通知父组件更新绑定的值

这样,Detail.vue 就可以通过 v-model 和父组件 index.vue 进行双向数据同步了。

posted @ 2025-08-15 10:17  VipSoft  阅读(82)  评论(0)    收藏  举报