Skip to content

AFormSwitch 开关组件

AFormSwitch 是一个功能完善的开关组件,基于 Element Plus 的 ElSwitch 封装,适用于布尔值的快速切换操作,支持自定义文本、颜色和加载状态。

基础用法

基本开关

vue
<template>
  <!-- 搜索栏中使用 -->
  <AFormSwitch 
    v-model="queryParams.enabled" 
    label="启用状态" 
    prop="enabled" 
    @change="handleQuery" 
  />
  
  <!-- 表单中使用 -->
  <AFormSwitch 
    v-model="form.isPublic" 
    label="是否公开" 
    prop="isPublic" 
    :span="12" 
  />
</template>

<script setup>
const queryParams = reactive({
  enabled: true
})

const form = reactive({
  isPublic: false
})
</script>

带文本的开关

vue
<template>
  <AFormSwitch 
    v-model="form.autoSave" 
    label="自动保存" 
    prop="autoSave"
    active-text="开启" 
    inactive-text="关闭" 
    :span="12" 
  />
</template>

组件属性

基础属性

属性类型默认值说明
modelValuestring | number | boolean-绑定值
labelstring''表单标签
propstring''表单字段名
spannumber-栅格占比
showFormItembooleantrue是否显示表单项包装

开关属性

属性类型默认值说明
activeValuestring | number | boolean'1'开关打开时的值
inactiveValuestring | number | boolean'0'开关关闭时的值
activeTextstring''开关打开时显示的文字
inactiveTextstring''开关关闭时显示的文字
disabledbooleanfalse是否禁用
loadingbooleanfalse是否显示加载中
sizeElSize''开关尺寸

样式属性

属性类型默认值说明
activeColorstring'#409eff'开关打开时的背景色
inactiveColorstring'#dcdfe6'开关关闭时的背景色
widthnumber-开关的宽度(像素)
inlinePromptbooleanfalse是否在按钮内显示文字

高级属性

属性类型默认值说明
beforeChangeFunction-切换前的钩子函数
validateEventbooleantrue是否触发表单验证
preventInitialChangebooleantrue是否阻止初始化时的 change 事件
labelWidthstring | number-标签宽度
tooltipstring''提示信息

使用示例

布尔值开关

vue
<template>
  <AFormSwitch 
    v-model="form.isEnabled" 
    label="功能开关" 
    prop="isEnabled"
    :active-value="true" 
    :inactive-value="false" 
    :span="12" 
  />
</template>

<script setup>
const form = reactive({
  isEnabled: false // 布尔类型
})
</script>

字符串值开关

vue
<template>
  <AFormSwitch 
    v-model="form.status" 
    label="状态" 
    prop="status"
    active-value="active" 
    inactive-value="inactive" 
    active-text="启用" 
    inactive-text="禁用"
    :span="12" 
  />
</template>

<script setup>
const form = reactive({
  status: 'inactive' // 字符串类型
})
</script>

数字值开关

vue
<template>
  <AFormSwitch 
    v-model="form.priority" 
    label="优先级" 
    prop="priority"
    :active-value="1" 
    :inactive-value="0" 
    active-text="高优先级" 
    inactive-text="普通优先级"
    :span="12" 
  />
</template>

<script setup>
const form = reactive({
  priority: 0 // 数字类型
})
</script>

自定义颜色开关

vue
<template>
  <AFormSwitch 
    v-model="form.dangerMode" 
    label="危险模式" 
    prop="dangerMode"
    active-color="#ff4949" 
    inactive-color="#13ce66" 
    active-text="危险" 
    inactive-text="安全"
    :span="12" 
  />
</template>

带提示信息的开关

vue
<template>
  <AFormSwitch 
    v-model="form.allowNotifications" 
    label="通知权限" 
    prop="allowNotifications"
    tooltip="开启后将接收系统通知消息" 
    :span="12" 
  />
</template>

内联文字开关

vue
<template>
  <AFormSwitch 
    v-model="form.darkMode" 
    label="主题模式" 
    prop="darkMode"
    inline-prompt 
    active-text="暗" 
    inactive-text="亮" 
    :span="12" 
  />
</template>

加载状态开关

vue
<template>
  <AFormSwitch 
    v-model="form.serviceEnabled" 
    label="服务状态" 
    prop="serviceEnabled"
    :loading="switchLoading" 
    @change="handleServiceToggle"
    :span="12" 
  />
</template>

<script setup>
const switchLoading = ref(false)

const handleServiceToggle = async (value) => {
  switchLoading.value = true
  try {
    // 模拟 API 调用
    await toggleService(value)
    showMsgSuccess('服务状态更新成功')
  } catch (error) {
    // 失败时恢复原状态
    form.serviceEnabled = !value
    showMsgError('服务状态更新失败')
  } finally {
    switchLoading.value = false
  }
}
</script>

不含表单项的纯开关

vue
<template>
  <div class="flex items-center">
    <span class="mr-2">实时更新</span>
    <AFormSwitch 
      v-model="realtimeUpdate" 
      :show-form-item="false" 
      @change="handleRealtimeToggle"
    />
  </div>
</template>

事件处理

基础事件

vue
<template>
  <AFormSwitch 
    v-model="form.notifications"
    label="通知设置"
    @change="handleNotificationChange"
    @focus="handleFocus"
    @blur="handleBlur"
  />
</template>

<script setup>
const handleNotificationChange = (value) => {
  console.log('通知设置变更:', value)
  
  if (value) {
    // 开启通知相关逻辑
    enableNotifications()
  } else {
    // 关闭通知相关逻辑
    disableNotifications()
  }
}

const handleFocus = (event) => {
  console.log('开关获得焦点')
}

const handleBlur = (event) => {
  console.log('开关失去焦点')
}
</script>

带确认的开关

vue
<template>
  <AFormSwitch 
    v-model="form.deleteMode" 
    label="删除模式" 
    prop="deleteMode"
    :before-change="confirmToggle" 
    active-text="批量删除" 
    inactive-text="单个删除"
    active-color="#f56c6c"
    :span="12" 
  />
</template>

<script setup>
const confirmToggle = () => {
  return new Promise((resolve) => {
    showConfirm('确定要切换删除模式吗?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    }).then(() => {
      resolve(true)
    }).catch(() => {
      resolve(false)
    })
  })
}
</script>

联动控制

vue
<template>
  <el-row :gutter="20">
    <AFormSwitch 
      v-model="form.autoBackup" 
      label="自动备份" 
      prop="autoBackup"
      @change="handleAutoBackupChange"
      :span="12" 
    />
    
    <AFormSwitch 
      v-model="form.backupEncryption" 
      label="备份加密" 
      prop="backupEncryption"
      :disabled="!form.autoBackup"
      :span="12" 
    />
  </el-row>
</template>

<script setup>
const handleAutoBackupChange = (enabled) => {
  if (!enabled) {
    // 关闭自动备份时,同时关闭加密
    form.backupEncryption = false
  }
}
</script>

高级用法

动态禁用

vue
<template>
  <AFormSwitch 
    v-model="form.advancedMode" 
    label="高级模式" 
    prop="advancedMode"
    :disabled="!canUseAdvancedMode" 
    tooltip="需要高级权限才能使用"
    :span="12" 
  />
</template>

<script setup>
const canUseAdvancedMode = computed(() => {
  return user.value?.level >= 5
})
</script>

批量开关控制

vue
<template>
  <div>
    <!-- 主控开关 -->
    <AFormSwitch 
      v-model="masterSwitch" 
      label="总开关" 
      @change="handleMasterToggle"
      :show-form-item="false"
    />
    
    <el-divider />
    
    <!-- 子开关组 -->
    <el-row :gutter="20">
      <AFormSwitch 
        v-model="form.email" 
        label="邮件通知" 
        :disabled="!masterSwitch"
        :span="8" 
      />
      <AFormSwitch 
        v-model="form.sms" 
        label="短信通知" 
        :disabled="!masterSwitch"
        :span="8" 
      />
      <AFormSwitch 
        v-model="form.push" 
        label="推送通知" 
        :disabled="!masterSwitch"
        :span="8" 
      />
    </el-row>
  </div>
</template>

<script setup>
const masterSwitch = ref(true)

const handleMasterToggle = (enabled) => {
  if (!enabled) {
    // 关闭主开关时,关闭所有子开关
    form.email = false
    form.sms = false
    form.push = false
  }
}
</script>

条件渲染开关

vue
<template>
  <div>
    <AFormSwitch 
      v-model="form.useCustomConfig" 
      label="使用自定义配置" 
      @change="handleConfigModeChange"
    />
    
    <template v-if="form.useCustomConfig">
      <AFormSwitch 
        v-model="form.enableCache" 
        label="启用缓存" 
        class="mt-4"
      />
      <AFormSwitch 
        v-model="form.enableCompression" 
        label="启用压缩" 
        class="mt-2"
      />
    </template>
  </div>
</template>

<script setup>
const handleConfigModeChange = (useCustom) => {
  if (!useCustom) {
    // 恢复默认配置
    resetToDefaultConfig()
  }
}
</script>

表单验证

基础验证

vue
<template>
  <el-form :model="form" :rules="rules" ref="formRef">
    <AFormSwitch 
      v-model="form.agreement" 
      label="用户协议" 
      prop="agreement"
      :active-value="true"
      :inactive-value="false"
      active-text="已同意"
      inactive-text="未同意"
    />
  </el-form>
</template>

<script setup>
const rules = {
  agreement: [
    { 
      required: true, 
      message: '请同意用户协议', 
      trigger: 'change' 
    },
    {
      validator: (rule, value, callback) => {
        if (value !== true) {
          callback(new Error('必须同意用户协议'))
        } else {
          callback()
        }
      },
      trigger: 'change'
    }
  ]
}
</script>

条件验证

vue
<template>
  <el-form :model="form" :rules="rules">
    <AFormSwitch 
      v-model="form.enableSSL" 
      label="启用SSL" 
      prop="enableSSL" 
    />
    
    <AFormInput 
      v-if="form.enableSSL"
      v-model="form.sslCertPath" 
      label="证书路径" 
      prop="sslCertPath" 
    />
  </el-form>
</template>

<script setup>
const rules = computed(() => ({
  enableSSL: [
    { required: true, message: '请选择是否启用SSL', trigger: 'change' }
  ],
  sslCertPath: form.enableSSL ? [
    { required: true, message: '启用SSL时必须提供证书路径', trigger: 'blur' }
  ] : []
}))
</script>

样式定制

自定义样式

vue
<template>
  <AFormSwitch 
    v-model="form.theme"
    label="暗黑主题"
    class="custom-switch"
    active-color="#1f2937"
    inactive-color="#f3f4f6"
  />
</template>

<style scoped>
.custom-switch :deep(.el-switch) {
  --el-switch-border-radius: 20px;
}

.custom-switch :deep(.el-switch__core) {
  height: 24px;
  min-width: 48px;
}

.custom-switch :deep(.el-switch__action) {
  width: 20px;
  height: 20px;
}
</style>

响应式尺寸

vue
<template>
  <AFormSwitch 
    v-model="form.enabled"
    label="启用状态"
    :size="isMobile ? 'small' : 'default'"
    :span="isMobile ? 24 : 12"
  />
</template>

<script setup>
import { useBreakpoint } from '@/composables/useBreakpoint'
const { isMobile } = useBreakpoint()
</script>

实际应用场景

系统设置

vue
<template>
  <el-card header="系统设置">
    <el-row :gutter="20">
      <AFormSwitch 
        v-model="settings.maintenance" 
        label="维护模式" 
        prop="maintenance"
        active-color="#f56c6c"
        tooltip="开启后系统将进入维护模式"
        :span="12" 
      />
      
      <AFormSwitch 
        v-model="settings.registration" 
        label="开放注册" 
        prop="registration"
        :span="12" 
      />
      
      <AFormSwitch 
        v-model="settings.guestAccess" 
        label="游客访问" 
        prop="guestAccess"
        :span="12" 
      />
      
      <AFormSwitch 
        v-model="settings.apiEnabled" 
        label="API接口" 
        prop="apiEnabled"
        :span="12" 
      />
    </el-row>
  </el-card>
</template>

<script setup>
const settings = reactive({
  maintenance: false,
  registration: true,
  guestAccess: false,
  apiEnabled: true
})
</script>

权限控制

vue
<template>
  <el-card header="权限设置">
    <AFormSwitch 
      v-model="permissions.canEdit" 
      label="编辑权限" 
      @change="handlePermissionChange('edit', $event)"
    />
    
    <AFormSwitch 
      v-model="permissions.canDelete" 
      label="删除权限" 
      :disabled="!permissions.canEdit"
      @change="handlePermissionChange('delete', $event)"
    />
    
    <AFormSwitch 
      v-model="permissions.canExport" 
      label="导出权限" 
      @change="handlePermissionChange('export', $event)"
    />
  </el-card>
</template>

<script setup>
const permissions = reactive({
  canEdit: false,
  canDelete: false,
  canExport: false
})

const handlePermissionChange = async (type, enabled) => {
  try {
    await updateUserPermission(type, enabled)
    showMsgSuccess('权限更新成功')
  } catch (error) {
    showMsgError('权限更新失败')
    // 恢复原状态
    permissions[`can${type.charAt(0).toUpperCase() + type.slice(1)}`] = !enabled
  }
}
</script>

功能开关

vue
<template>
  <div class="feature-toggles">
    <h3>实验性功能</h3>
    
    <AFormSwitch 
      v-for="feature in features" 
      :key="feature.key"
      v-model="feature.enabled"
      :label="feature.name"
      :tooltip="feature.description"
      :loading="feature.loading"
      @change="toggleFeature(feature)"
      class="mb-3"
    />
  </div>
</template>

<script setup>
const features = ref([
  {
    key: 'ai_assistant',
    name: 'AI助手',
    description: '智能助手功能,帮助提升工作效率',
    enabled: false,
    loading: false
  },
  {
    key: 'dark_mode',
    name: '暗黑模式',
    description: '切换到暗黑主题界面',
    enabled: false,
    loading: false
  },
  {
    key: 'real_time_sync',
    name: '实时同步',
    description: '数据实时同步到云端',
    enabled: true,
    loading: false
  }
])

const toggleFeature = async (feature) => {
  feature.loading = true
  try {
    await updateFeatureFlag(feature.key, feature.enabled)
    showMsgSuccess(`${feature.name}功能${feature.enabled ? '已启用' : '已禁用'}`)
  } catch (error) {
    feature.enabled = !feature.enabled
    showMsgError('功能切换失败')
  } finally {
    feature.loading = false
  }
}
</script>

通知设置

vue
<template>
  <el-card header="通知设置">
    <div class="notification-settings">
      <div v-for="category in notificationCategories" :key="category.key" class="mb-4">
        <h4 class="mb-2">{{ category.name }}</h4>
        <el-row :gutter="20">
          <el-col :span="8" v-for="type in category.types" :key="type.key">
            <AFormSwitch 
              v-model="notifications[category.key][type.key]"
              :label="type.name"
              :show-form-item="false"
              @change="saveNotificationSetting(category.key, type.key, $event)"
            />
          </el-col>
        </el-row>
      </div>
    </div>
  </el-card>
</template>

<script setup>
const notificationCategories = [
  {
    key: 'system',
    name: '系统通知',
    types: [
      { key: 'email', name: '邮件' },
      { key: 'sms', name: '短信' },
      { key: 'push', name: '推送' }
    ]
  },
  {
    key: 'social',
    name: '社交通知',
    types: [
      { key: 'comment', name: '评论' },
      { key: 'like', name: '点赞' },
      { key: 'follow', name: '关注' }
    ]
  }
]

const notifications = reactive({
  system: {
    email: true,
    sms: false,
    push: true
  },
  social: {
    comment: true,
    like: false,
    follow: true
  }
})

const saveNotificationSetting = async (category, type, enabled) => {
  try {
    await updateNotificationSetting(category, type, enabled)
    console.log(`${category}.${type} 设置为 ${enabled}`)
  } catch (error) {
    notifications[category][type] = !enabled
    showMsgError('设置保存失败')
  }
}
</script>

最佳实践

1. 明确的状态表示

vue
<template>
  <!-- 好的做法 - 状态清晰明确 -->
  <AFormSwitch 
    v-model="form.isPublic"
    label="文章可见性"
    active-text="公开"
    inactive-text="私有"
    :active-value="true"
    :inactive-value="false"
  />
  
  <!-- 避免的做法 - 状态含糊不清 -->
  <AFormSwitch 
    v-model="form.status"
    label="状态"
  />
</template>

2. 合适的默认值

vue
<template>
  <AFormSwitch 
    v-model="form.allowNotifications"
    label="接收通知"
    tooltip="默认开启,您可以随时关闭"
  />
</template>

<script setup>
const form = reactive({
  allowNotifications: true // 合理的默认值
})
</script>

3. 提供反馈

vue
<template>
  <AFormSwitch 
    v-model="form.autoSave"
    label="自动保存"
    :loading="savingSettings"
    @change="handleAutoSaveToggle"
  />
</template>

<script setup>
const savingSettings = ref(false)

const handleAutoSaveToggle = async (enabled) => {
  savingSettings.value = true
  try {
    await saveUserSetting('autoSave', enabled)
    showMsgSuccess(`自动保存已${enabled ? '开启' : '关闭'}`)
  } catch (error) {
    form.autoSave = !enabled
    showMsgError('设置保存失败')
  } finally {
    savingSettings.value = false
  }
}
</script>

4. 谨慎使用危险操作

vue
<template>
  <AFormSwitch 
    v-model="form.dangerousFeature"
    label="危险功能"
    active-color="#f56c6c"
    :before-change="confirmDangerousAction"
    tooltip="此功能可能对系统造成影响,请谨慎使用"
  />
</template>

<script setup>
const confirmDangerousAction = async () => {
  try {
    await showConfirm(
      '此操作可能对系统造成不可逆的影响,确定要继续吗?',
      '警告',
      { type: 'warning' }
    )
    return true
  } catch {
    return false
  }
}
</script>

注意事项

  1. 数据类型一致性:确保 activeValueinactiveValue 与绑定值类型一致
  2. 用户体验:重要的开关操作应该提供确认机制
  3. 状态反馈:异步操作时使用 loading 状态给用户反馈
  4. 默认值:为开关设置合理的默认状态
  5. 文本描述:使用 activeTextinactiveText 让状态更清晰
  6. 无障碍访问:确保开关有明确的标签和描述信息