vue3+ts实现20个前端常用组件

210 阅读2分钟

1. Button 组件

<template>
  <button class="btn" @click="onClick">
    <slot></slot>
  </button>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Button',
  methods: {
    onClick() {
      this.$emit('click');
    },
  },
});
</script>

<style scoped>
.btn {
  /* 样式 */
}
</style>

2. Input 组件

<template>
  <input class="input" :value="value" @input="onInput" />
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Input',
  props: {
    value: {
      type: String,
      required: true,
    },
  },
  methods: {
    onInput(event: InputEvent) {
      this.$emit('input', (event.target as HTMLInputElement).value);
    },
  },
});
</script>

<style scoped>
.input {
  /* 样式 */
}
</style>

3. Checkbox 组件

<template>
  <label class="checkbox">
    <input type="checkbox" :checked="checked" @change="onChange" />
    <span class="checkmark"></span>
    <slot></slot>
  </label>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Checkbox',
  props: {
    checked: {
      type: Boolean,
      default: false,
    },
  },
  methods: {
    onChange(event: InputEvent) {
      this.$emit('change', (event.target as HTMLInputElement).checked);
    },
  },
});
</script>

<style scoped>
.checkbox {
  /* 样式 */
}

.checkmark {
  /* 样式 */
}
</style>

4. Radio 组件

<template>
  <label class="radio">
    <input type="radio" :value="value" v-model="modelValue" @change="onChange" />
    <span class="radiomark"></span>
    <slot></slot>
  </label>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Radio',
  props: {
    value: {
      type: String,
      required: true,
    },
    modelValue: {
      type: String,
      required: true,
    },
  },
  methods: {
    onChange(event: InputEvent) {
      this.$emit('change', (event.target as HTMLInputElement).value);
    },
  },
});
</script>

<style scoped>
.radio {
  /* 样式 */
}

.radiomark {
  /* 样式 */
}
</style>

5. Select 组件

<template>
  <select class="select" v-model="modelValue" @change="onChange">
    <option v-for="option in options" :value="option.value" :key="option.value">{{ option.label }}</option>
  </select>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Select',
  props: {
    options: {
      type: Array,
      required: true,
    },
    modelValue: {
      type: String,
      required: true,
    },
  },
  methods: {
    onChange(event: InputEvent) {
      this.$emit('change', (event.target as HTMLInputElement).value);
    },
  },
});
</script>

<style scoped>
.select {
  /* 样式 */
}
</style>

6. Tabs 组件

<template>
  <div class="tabs">
    <div class="tab" v-for="tab in tabs" :key="tab.id" :class="{ active: tab.id === activeTab }" @click="activateTab(tab.id)">
      tab.label }}
    </div>
    <div class="tab-content">
      <slot></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Tabs',
  props: {
    tabs: {
      type: Array,
      required: true,
    },
  },
  setup() {
    const activeTab = ref('');

    const activateTab = (tabId: string) => {
      activeTab.value = tabId;
    };

    return {
      activeTab,
      activateTab,
    };
  },
});
</script>

<style scoped>
.tabs {
  /* 样式 */
}

.tab {
  /* 样式 */
}

.tab.active {
  /* 样式 */
}

.tab-content {
  /* 样式 */
}
</style>

7. Modal 组件

<template>
  <div class="modal" v-show="visible">
    <div class="modal-content">
      <slot></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Modal',
  props: {
    visible: {
      type: Boolean,
      default: false,
    },
  },
});
</script>

<style scoped>
.modal {
  /* 样式 */
}

.modal-content {
  /* 样式 */
}
</style>

8. Pagination 组件

<template>
  <ul class="pagination">
    <li v-for="page in pageCount" :key="page" :class="{ active: page === currentPage }" @click="changePage(page)">
      {{ page }}
    </li>
  </ul>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Pagination',
  props: {
    pageCount: {
      type: Number,
      required: true,
    },
    currentPage: {
      type: Number,
      required: true,
    },
  },
  methods: {
    changePage(page: number) {
      this.$emit('page-change', page);
    },
  },
});
</script>

<style scoped>
.pagination {
  /* 样式 */
}

.pagination li {
  /* 样式 */
}

.pagination li.active {
  /* 样式 */
}
</style>

9. Toast 组件

<template>
  <div class="toast" v-show="visible">
    {{ message }}
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Toast',
  props: {
    message: {
      type: String,
      required: true,
    },
    visible: {
      type: Boolean,
      default: false,
    },
  },
});
</script>

<style scoped>
.toast {
  /* 样式 */
}
</style>

10. Dropdown 组件

<template>
  <div class="dropdown" @click="toggleDropdown">
    <slot name="trigger"></slot>
    <div class="dropdown-menu" v-show="isOpen">
      <slot></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Dropdown',
  setup() {
    const isOpen = ref(false);

    const toggleDropdown = () => {
      isOpen.value = !isOpen.value;
    };

    return {
      isOpen,
      toggleDropdown,
    };
  },
});
</script>

<style scoped>
.dropdown {
  /* 样式 */
}

.dropdown-menu {
  /* 样式 */
}
</style>

11. Carousel 组件

<template>
  <div class="carousel">
    <div class="carousel-inner">
      <slot></slot>
    </div>
    <div class="carousel-controls">
      <button class="prev" @click="prevSlide">Prev</button>
      <button class="next" @click="nextSlide">Next</button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Carousel',
  methods: {
    prevSlide() {
      this.$emit('prev-slide');
    },
    nextSlide() {
      this.$emit('next-slide');
    },
  },
});
</script>

<style scoped>
.carousel {
  /* 样式 */
}

.carousel-inner {
  /* 样式 */
}

.carousel-controls {
  /* 样式 */
}

.carousel-controls button {
  /* 样式 */
}
</style>

12. Accordion 组件

<template>
  <div class="accordion">
    <div class="accordion-item" v-for="item in items" :key="item.id">
      <div class="accordion-header" @click="toggleItem(item.id)">
        {{ item.title }}
      </div>
      <div class="accordion-content" v-show="item.id === activeItem">
        {{ item.content }}
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Accordion',
  props: {
    items: {
      type: Array,
      required: true,
    },
  },
  setup() {
    const activeItem = ref('');

    const toggleItem = (itemId: string) => {
      activeItem.value = activeItem.value === itemId ? '' : itemId;
    };

    return {
      activeItem,
      toggleItem,
    };
  },
});
</script>

<style scoped>
.accordion {
  /* 样式 */
}

.accordion-item {
  /* 样式 */
}

.accordion-header {
  /* 样式 */
}

.accordion-content {
  /* 样式 */
}
</style>

13. Datepicker 组件

<template>
  <input class="datepicker" :value="value" @input="onInput" />
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Datepicker',
  props: {
    value: {
      type: String,
      required: true,
    },
  },
  methods: {
    onInput(event: InputEvent) {
      this.$emit('input', (event.target as HTMLInputElement).value);
    },
  },
});
</script>

<style scoped>
.datepicker {
  /* 样式 */
}
</style>

14. Table 组件

<template>
  <table class="table">
    <thead>
      <tr>
        <th v-for="column in columns" :key="column.field">{{ column.label }}</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in rows" :key="row.id">
        <td v-for="column in columns" :key="column.field">{{ row[column.field] }}</td>
      </tr>
    </tbody>
  </table>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Table',
  props: {
    columns: {
      type: Array,
      required: true,
    },
    rows: {
      type: Array,
      required: true,
    },
  },
});
</script>

<style scoped>
.table {
  /* 样式 */
}

.table th {
  /* 样式 */
}

.table td {
  /* 样式 */
}
</style>

15. Slider 组件

<template>
  <div class="slider">
    <div class="slider-bar">
      <div class="slider-handle" :style="{ left: handlePosition + '%' }" @mousedown="startDragging" @touchstart="startDragging"></div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Slider',
  setup() {
    const handlePosition = ref(0);
    const isDragging = ref(false);

    const startDragging = () => {
      isDragging.value = true;
    };

    return {
      handlePosition,
      startDragging,
    };
  },
});
</script>

<style scoped>
.slider {
  /* 样式 */
}

.slider-bar {
  /* 样式 */
}

.slider-handle {
  /* 样式 */
}
</style>

16. Progress 组件

<template>
  <div class="progress">
    <div class="progress-bar" :style="{ width: progress + '%' }"></div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Progress',
  props: {
    progress: {
      type: Number,
      required: true,
    },
  },
});
</script>

<style scoped>
.progress {
  /* 样式 */
}

.progress-bar {
  /* 样式 */
}
</style>

17. Tooltip 组件

<template>
  <div class="tooltip" @mouseenter="showTooltip" @mouseleave="hideTooltip">
    <slot></slot>
    <div class="tooltip-content" v-show="isTooltipVisible">{{ content }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Tooltip',
  props: {
    content: {
      type: String,
      required: true,
    },
  },
  setup() {
    const isTooltipVisible = ref(false);

    const showTooltip = () => {
      isTooltipVisible.value = true;
    };

    const hideTooltip = () => {
      isTooltipVisible.value = false;
    };

    return {
      isTooltipVisible,
      showTooltip,
      hideTooltip,
    };
  },
});
</script>

<style scoped>
.tooltip {
  /* 样式 */
}

.tooltip-content {
  /* 样式 */
}
</style>

18. Card 组件

<template>
  <div class="card">
    <div class="card-header">
      <slot name="header"></slot>
    </div>
    <div class="card-body">
      <slot></slot>
    </div>
    <div class="card-footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Card',
});
</script>

<style scoped>
.card {
  /* 样式 */
}

.card-header {
  /* 样式 */
}

.card-body {
  /* 样式 */
}

.card-footer {
  /* 样式 */
}
</style>

19. Alert 组件

<template>
  <div class="alert" :class="type">
    <slot></slot>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Alert',
  props: {
    type: {
      type: String,
      default: 'info',
    },
  },
});
</script>

<style scoped>
.alert {
  /* 样式 */
}

.alert.info {
  /* 样式 */
}

.alert.warning {
  /* 样式 */
}

.alert.error {
  /* 样式 */
}
</style>

20. Navbar 组件

<template>
  <nav class="navbar">
    <ul class="navbar-nav">
      <li class="nav-item" v-for="item in items" :key="item.id">
        <a :href="item.url" :class="{ active: item.url === activeItem }">{{ item.label }}</a>
      </li>
    </ul>
  </nav>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  name: 'Navbar',
  props: {
    items: {
      type: Array,
      required: true,
    },
    activeItem: {
      type: String,
      required: true,
    },
  },
});
</script>

<style scoped>
.navbar {
  /* 样式 */
}

.navbar-nav {
  /* 样式 */
}

.nav-item {
  /* 样式 */
}

.nav-item a {
  /* 样式 */
}

.nav-item a.active {
  /* 样式 */
}
</style>