Skip to content

弹窗服务 (pop)

@erp/biz 提供的统一弹窗服务,包含对话框、抽屉、消息提示、通知等功能。

功能概览

  • 消息提示 - 轻量级反馈:success / error / warning / info / loading
  • 通知提示 - 丰富通知:succ / err / warn / inf
  • 确认对话框 - 用户确认:confirm
  • Modal 弹窗 - 自定义内容:createModal
  • Drawer 抽屉 - 侧边面板:createDrawer
  • Page 页面 - 全屏组件:createPage
  • 全局加载 - 加载遮罩:spin

引入方式

typescript
import { pop } from '@erp/biz'

初始化配置

在应用入口处初始化 pop 服务:

typescript
import { createApp } from 'vue'
import { pop } from '@erp/biz'
import router from './router'
import App from './App.vue'

const app = createApp(App)

// 初始化 pop 服务
pop.install(app, router, {
  popupContainer: '#app',  // 弹窗挂载容器
  exchangeTime: true,
  scrollToClose: false
})

app.mount('#app')


消息提示

轻量级的消息反馈,显示在页面顶部,适用于操作结果的即时反馈。

pop.success

显示成功消息。

typescript
pop.success('操作成功')

// 自定义配置
pop.success({
  content: '保存成功',
  duration: 3000
})

pop.error

显示错误消息。

typescript
pop.error('操作失败')

pop.error({
  content: '网络错误,请重试',
  duration: 5000
})

pop.warning

显示警告消息。

typescript
pop.warning('请注意数据安全')

pop.info

显示信息消息。

typescript
pop.info('这是一条提示信息')

pop.loading

显示加载中消息,需手动关闭。

typescript
// 显示加载
const loading = pop.loading('正在处理...')

// 处理完成后关闭
await doSomething()
loading.close()

// 自定义配置
const loading = pop.loading({
  content: '正在上传...',
  duration: 0  // 0 表示不自动关闭
})

MessageConfig

参数类型默认值说明
contentstring-消息内容
durationnumber3000显示时长(ms),0 表示不自动关闭
resetOnHoverbooleantrue鼠标悬停时是否重置计时
closablebooleanfalse是否显示关闭按钮


通知提示

更丰富的通知消息,显示在页面角落,适用于需要用户关注的重要信息。

pop.succ

显示成功通知。

typescript
pop.succ('订单提交成功')

pop.succ({
  title: '提交成功',
  content: '您的订单已提交,请等待审核'
})

pop.err

显示错误通知。

typescript
pop.err('系统异常')

pop.err({
  title: '操作失败',
  content: '服务器繁忙,请稍后重试',
  duration: 5000
})

pop.warn

显示警告通知。

typescript
pop.warn('库存不足')

pop.inf

显示信息通知。

typescript
pop.inf('您有新的消息')

NotificationConfig

参数类型默认值说明
titlestring'消息通知'通知标题
contentstring-通知内容
durationnumber3000显示时长(ms)
closablebooleantrue是否显示关闭按钮
positionstring'topRight'显示位置


确认对话框

用于需要用户确认的操作场景,如删除、提交等危险或重要操作。

pop.confirm

显示确认对话框,返回 Promise。

typescript
// 基础用法
try {
  await pop.confirm('确定要删除吗?')
  // 用户点击确定
  await deleteItem()
  pop.success('删除成功')
} catch {
  // 用户点击取消
}

// 自定义配置
await pop.confirm('此操作不可恢复,确定继续?', {
  title: '警告',
  okText: '确定删除',
  cancelText: '取消',
  okButtonProps: { status: 'danger' }
})

// 手动关闭
const dialog = pop.confirm('处理中...')
await doSomething()
dialog.close()

ModalConfig

参数类型默认值说明
titlestring'温馨提示'对话框标题
contentstring | VNode-对话框内容
okTextstring'确定'确定按钮文字
cancelTextstring'取消'取消按钮文字
closablebooleantrue是否显示关闭按钮
maskClosablebooleantrue点击遮罩是否关闭
widthnumber | string420对话框宽度
okButtonPropsobject-确定按钮属性
cancelButtonPropsobject-取消按钮属性


用于展示自定义内容的模态对话框,支持异步加载组件。

pop.createModal

打开 Modal 弹窗,加载自定义组件。

typescript
// 基础用法
const result = await pop.createModal(MyComponent, {
  // 传递给组件的 props
  id: 123,
  name: '张三'
}, {
  // Modal 配置
  title: '编辑用户',
  width: 600
})

// 异步加载组件
const result = await pop.createModal(
  import('./EditUser.vue'),
  { id: 123 },
  { title: '编辑用户' }
)

// 使用 id 防止重复打开
await pop.createModal(MyComponent, params, {
  id: 'edit-user-modal',
  title: '编辑用户'
})

// 手动关闭
const modal = pop.createModal(MyComponent)
modal.close()

组件内部使用

vue
<!-- EditUser.vue -->
<template>
  <div>
    <FkForm :model="form" />
  </div>
</template>

<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'

// 接收 pop.createModal 传入的参数
const props = defineProps<{
  id: number
  name: string
}>()

// 定义事件
const emit = defineEmits<{
  (e: 'ok', data: any): void
  (e: 'close'): void
  (e: 'loading', loading: boolean): void
}>()

// 提交数据
const handleSubmit = async () => {
  emit('loading', true)
  try {
    const result = await saveUser(form)
    emit('ok', result)  // 关闭弹窗并返回数据
  } finally {
    emit('loading', false)
  }
}

// 取消
const handleCancel = () => {
  emit('close')
}
</script>


Drawer 抽屉

从屏幕边缘滑出的面板,适用于详情展示、表单编辑等场景。

pop.createDrawer

打开抽屉面板。

typescript
// 基础用法
const result = await pop.createDrawer(MyComponent, {
  title: '详情',
  width: 500
})

// 异步加载组件
const result = await pop.createDrawer(
  import('./UserDetail.vue'),
  {
    title: '用户详情',
    width: 600,
    id: 'user-detail'  // 防止重复打开
  }
)

// 手动关闭
const drawer = pop.createDrawer(MyComponent)
drawer.close()

pop.closeDrawer

根据 id 关闭抽屉。

typescript
pop.closeDrawer('user-detail')

pop.hasDrawer

检查抽屉是否存在。

typescript
if (pop.hasDrawer('user-detail')) {
  pop.closeDrawer('user-detail')
}

DrawerConfig

参数类型默认值说明
titlestring-抽屉标题
widthnumber | string'auto'抽屉宽度
heightnumber | string-抽屉高度(placement 为 top/bottom 时)
placementstring'right'抽屉位置:top/right/bottom/left
closablebooleantrue是否显示关闭按钮
maskbooleantrue是否显示遮罩
maskClosablebooleantrue点击遮罩是否关闭
idstring-唯一标识,用于防止重复打开


Page 页面

创建独立的页面组件,适用于全屏弹窗或复杂业务页面。

pop.createPage

创建独立的页面组件,适用于全屏弹窗或复杂页面。

typescript
// 基础用法
const result = await pop.createPage(MyPage, {
  title: '新建订单',
  bizType: 'create'
})

// 异步加载
const result = await pop.createPage(
  import('./OrderPage.vue'),
  {
    id: 'order-page',
    bizId: '123',
    bizType: 'edit',
    bizModel: { name: '订单1' }
  }
)

// 手动关闭
const page = pop.createPage(MyPage)
page.close()

PageConfig

参数类型说明
idstring页面唯一标识
titlestring页面标题
bizIdstring业务 ID(编辑/查看时使用)
bizTypestring业务类型(create/edit/view)
bizModelobject业务数据模型
bizParamsobject其他业务参数
onOkfunction确定回调
onCancelfunction取消回调
onClosefunction关闭回调


全局加载

显示全屏加载遮罩,阻止用户操作,适用于页面级别的加载状态。

pop.spin

显示全局加载遮罩。

typescript
// 显示加载
const spin = pop.spin()

// 处理完成后关闭
await doSomething()
spin.close()

// 自定义配置
const spin = pop.spin({
  tip: '正在加载数据...',
  size: 32
})

SpinConfig

参数类型默认值说明
tipstring'正在加载...'加载提示文字
sizenumber-加载图标大小
dotbooleanfalse是否使用点状加载
hideIconbooleanfalse是否隐藏加载图标


路由集成

pop 服务支持与 Vue Router 集成,在路由切换时自动管理弹窗:

typescript
// main.ts
import { pop } from '@erp/biz'
import router from './router'

pop.install(app, router)

// 路由配置中添加 router_id
const routes = [
  {
    path: '/order',
    component: OrderPage,
    meta: {
      router_id: 'order-page'  // 用于弹窗关联
    }
  }
]

pop.destroyByRouterId

销毁指定路由下的所有弹窗。

typescript
pop.destroyByRouterId('order-page')


完整示例

列表页 CRUD

vue
<template>
  <div>
    <FkButton @click="handleCreate">新建</FkButton>
    <FkTable :data="list">
      <FkTableColumn label="操作">
        <template #default="{ row }">
          <FkButton @click="handleEdit(row)">编辑</FkButton>
          <FkButton @click="handleDelete(row)">删除</FkButton>
        </template>
      </FkTableColumn>
    </FkTable>
  </div>
</template>

<script setup>
import { pop } from '@erp/biz'

// 新建
const handleCreate = async () => {
  const result = await pop.createModal(
    import('./EditForm.vue'),
    { type: 'create' },
    { title: '新建', width: 600 }
  )
  if (result) {
    pop.success('创建成功')
    refreshList()
  }
}

// 编辑
const handleEdit = async (row) => {
  const result = await pop.createDrawer(
    import('./EditForm.vue'),
    {
      title: '编辑',
      width: 500,
      id: `edit-${row.id}`
    }
  )
  if (result) {
    pop.success('保存成功')
    refreshList()
  }
}

// 删除
const handleDelete = async (row) => {
  try {
    await pop.confirm(`确定删除 "${row.name}" 吗?`, {
      title: '删除确认',
      okButtonProps: { status: 'danger' }
    })
    
    const loading = pop.loading('正在删除...')
    await deleteApi(row.id)
    loading.close()
    
    pop.success('删除成功')
    refreshList()
  } catch {
    // 用户取消
  }
}
</script>

基于 MIT 许可发布