Skip to content

特性

  • type: 类型danger、info
  • 主题: light、dark;
  • close按钮、同时可以触发自定义close事件;
  • 图标;
  • 描述

实现思路

整体实现思路上不难;

  • 对于不同类型、主题用 css 变量实现

代码

接下来看下代码实现:

类型 type.ts:

typescript
export type AlertType = "success" | "info" | "warning" | "danger";

export interface AlertProps {
  title?: string;
  type?: AlertType;
  description?: string;
  effect?: "light" | "dark";
  closable?: boolean;
  center?: boolean;
  showIcon?: boolean;
}

export interface AlertEmits {
  (e: "close"): void;
}

export interface AlertInstance {
  open(): void;
  close(): void;
}

主文件:

vue
<script setup lang="ts">
import type { AlertProps, AlertEmits, AlertInstance } from "./types";
import { typeIconMap } from "@toy-element/utils";
import { computed, ref } from "vue";
import ErIcon from "../Icon/Icon.vue";

defineOptions({
  name: "ErAlert",
});

const props = withDefaults(defineProps<AlertProps>(), {
  effect: "light",
  type: "info",
  closable: true,
});

const emits = defineEmits<AlertEmits>();
const slots = defineSlots();
const visible = ref(true);
const iconName = computed(() => typeIconMap.get(props.type) ?? "circle-info");
const withDescription = computed(() => props.description || slots.default);

function close() {
  visible.value = false;
  emits("close");
}

function open() {
  visible.value = true;
}

defineExpose<AlertInstance>({
  close,
  open,
});
</script>

<template>
  <transition name="er-alert-fade">
    <div
      v-show="visible"
      class="er-alert"
      role="alert"
      :class="{
        [`er-alert__${type}`]: type,
        [`er-alert__${effect}`]: effect,
        'text-center': center,
      }"
    >
      <er-icon
        v-if="showIcon"
        class="er-alert__icon"
        :class="{ 'big-icon': withDescription }"
        :icon="iconName"
      />
      <div class="er-alert__content">
        <span
          class="er-alert__title"
          :class="{ 'with-desc': withDescription }"
          :style="{ display: center && !showIcon ? 'flow' : 'inline' }"
        >
          <slot name="title">{{ title }}</slot>
        </span>
        <p class="er-alert__description">
          <slot>{{ description }}</slot>
        </p>
        <div class="er-alert__close" v-if="closable">
          <er-icon @click.stop="close" icon="xmark" />
        </div>
      </div>
    </div>
  </transition>
</template>
<style scoped>
@import "./style.css";
</style>

收获

关于代码测试的一些写法:

vue-test-utils 的 mount 函数