mirror of https://github.com/halo-dev/halo-admin
parent
074d5b1fea
commit
6a960e573e
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { PropType } from "vue";
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import type { PropType } from "vue";
|
||||||
import type { Size } from "@/components/base/input/interface";
|
import type { Size } from "@/components/base/input/interface";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -65,6 +65,7 @@ function handleInput(e: Event) {
|
||||||
@apply text-black;
|
@apply text-black;
|
||||||
@apply block;
|
@apply block;
|
||||||
@apply transition-all;
|
@apply transition-all;
|
||||||
|
@apply appearance-none;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
@ -76,6 +77,11 @@ function handleInput(e: Event) {
|
||||||
border-color: #4ccba0;
|
border-color: #4ccba0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
@apply opacity-50;
|
||||||
|
@apply cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
&.input-lg {
|
&.input-lg {
|
||||||
@apply h-11;
|
@apply h-11;
|
||||||
@apply px-4;
|
@apply px-4;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`Input > should render 1`] = `
|
||||||
|
"<div class=\\"input-wrapper\\">
|
||||||
|
<!--v-if--><input class=\\"input-md\\" type=\\"text\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps({
|
||||||
|
value: {
|
||||||
|
type: [String, Number, Boolean],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<option :value="value">
|
||||||
|
<slot />
|
||||||
|
</option>
|
||||||
|
</template>
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
import type { Size } from "./interface";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import Option from "@/components/base/select/Option.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String as PropType<Size>,
|
||||||
|
default: "md",
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue"]);
|
||||||
|
|
||||||
|
const classes = computed(() => {
|
||||||
|
return [`select-${props.size}`];
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleChange(e: Event) {
|
||||||
|
const { value } = e.target as HTMLSelectElement;
|
||||||
|
emit("update:modelValue", value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="select-wrapper">
|
||||||
|
<select
|
||||||
|
:value="modelValue"
|
||||||
|
@change="handleChange"
|
||||||
|
:disabled="disabled"
|
||||||
|
:class="classes"
|
||||||
|
>
|
||||||
|
<option v-if="placeholder" value="" key="placeholder" disabled hidden>
|
||||||
|
{{ placeholder }}
|
||||||
|
</option>
|
||||||
|
<slot />
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.select-wrapper {
|
||||||
|
@apply box-border;
|
||||||
|
@apply relative;
|
||||||
|
@apply w-full;
|
||||||
|
@apply inline-flex;
|
||||||
|
|
||||||
|
select {
|
||||||
|
@apply outline-0;
|
||||||
|
@apply bg-white;
|
||||||
|
@apply antialiased;
|
||||||
|
@apply w-full;
|
||||||
|
@apply text-black;
|
||||||
|
@apply block;
|
||||||
|
@apply transition-all;
|
||||||
|
@apply appearance-none;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
border-color: #4ccba0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #4ccba0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
@apply opacity-50;
|
||||||
|
@apply cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-lg {
|
||||||
|
@apply h-11;
|
||||||
|
@apply px-4;
|
||||||
|
@apply text-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-md {
|
||||||
|
@apply h-9;
|
||||||
|
@apply px-3;
|
||||||
|
@apply text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-sm {
|
||||||
|
@apply h-7;
|
||||||
|
@apply px-3;
|
||||||
|
@apply text-xs;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.select-xs {
|
||||||
|
@apply h-6;
|
||||||
|
@apply px-2;
|
||||||
|
@apply text-xs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { VSelect } from "../index";
|
||||||
|
import { mount } from "@vue/test-utils";
|
||||||
|
|
||||||
|
describe("Select", () => {
|
||||||
|
it("should render", () => {
|
||||||
|
expect(VSelect).toBeDefined();
|
||||||
|
expect(mount(VSelect).html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with size prop", function () {
|
||||||
|
["lg", "md", "sm", "xs"].forEach((size) => {
|
||||||
|
const select = mount(VSelect, { props: { size } });
|
||||||
|
|
||||||
|
expect(select.html()).toMatchSnapshot();
|
||||||
|
expect(select.find("select").classes()).toContain(`select-${size}`);
|
||||||
|
select.unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`Select > should render 1`] = `
|
||||||
|
"<div class=\\"select-wrapper\\"><select class=\\"select-md\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</select></div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Select > should work with size prop 1`] = `
|
||||||
|
"<div class=\\"select-wrapper\\"><select class=\\"select-lg\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</select></div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Select > should work with size prop 2`] = `
|
||||||
|
"<div class=\\"select-wrapper\\"><select class=\\"select-md\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</select></div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Select > should work with size prop 3`] = `
|
||||||
|
"<div class=\\"select-wrapper\\"><select class=\\"select-sm\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</select></div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Select > should work with size prop 4`] = `
|
||||||
|
"<div class=\\"select-wrapper\\"><select class=\\"select-xs\\">
|
||||||
|
<!--v-if-->
|
||||||
|
</select></div>"
|
||||||
|
`;
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as VSelect } from "./Select.vue";
|
||||||
|
export { default as VOption } from "./Option.vue";
|
|
@ -0,0 +1 @@
|
||||||
|
export type Size = "lg" | "md" | "sm" | "xs";
|
|
@ -1,82 +1,85 @@
|
||||||
import type { RouteRecordRaw } from "vue-router";
|
import type { RouteRecordRaw } from "vue-router";
|
||||||
|
import HomeView from "../views/HomeView.vue";
|
||||||
|
import AboutView from "../views/AboutView.vue";
|
||||||
|
import ViewComponents from "../views/ViewComponents.vue";
|
||||||
|
|
||||||
export const routes: Array<RouteRecordRaw> = [
|
export const routes: Array<RouteRecordRaw> = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
component: () => import("../views/HomeView.vue"),
|
component: HomeView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/about",
|
path: "/about",
|
||||||
name: "about",
|
name: "about",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/posts",
|
path: "/posts",
|
||||||
name: "Posts",
|
name: "Posts",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: "/posts/categories",
|
path: "/posts/categories",
|
||||||
name: "Categories",
|
name: "Categories",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/posts/tags",
|
path: "/posts/tags",
|
||||||
name: "Tags",
|
name: "Tags",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/sheets",
|
path: "/sheets",
|
||||||
name: "Sheets",
|
name: "Sheets",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/comment",
|
path: "/comment",
|
||||||
name: "Comment",
|
name: "Comment",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/attachment",
|
path: "/attachment",
|
||||||
name: "Attachment",
|
name: "Attachment",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/themes",
|
path: "/themes",
|
||||||
name: "Themes",
|
name: "Themes",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/menus",
|
path: "/menus",
|
||||||
name: "Menus",
|
name: "Menus",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/visual",
|
path: "/visual",
|
||||||
name: "Visual",
|
name: "Visual",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/plugins",
|
path: "/plugins",
|
||||||
name: "Plugins",
|
name: "Plugins",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/users",
|
path: "/users",
|
||||||
name: "Users",
|
name: "Users",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
name: "Settings",
|
name: "Settings",
|
||||||
component: () => import("../views/AboutView.vue"),
|
component: AboutView,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/components",
|
path: "/components",
|
||||||
name: "Components",
|
name: "Components",
|
||||||
component: () => import("../views/ViewComponents.vue"),
|
component: ViewComponents,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -37,29 +37,71 @@
|
||||||
<VButton disabled type="danger"> Danger</VButton>
|
<VButton disabled type="danger"> Danger</VButton>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="box border-2 rounded p-2">
|
<section class="box border-2 rounded p-2 mb-3">
|
||||||
<h1 class="text-xl font-bold mb-2">Input</h1>
|
<h1 class="text-xl font-bold mb-2">Input</h1>
|
||||||
<h2 class="mb-1">Size:</h2>
|
<h2 class="mb-1">Size:</h2>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<VInput class="mb-2" placeholder="请输入邮箱" size="lg" />
|
<VInput
|
||||||
<VInput class="mb-2" placeholder="请输入邮箱" size="md" />
|
class="mb-2"
|
||||||
<VInput class="mb-2" placeholder="请输入邮箱" size="sm" />
|
v-model="inputValue"
|
||||||
<VInput placeholder="请输入邮箱" size="xs" />
|
placeholder="请输入邮箱"
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
<VInput
|
||||||
|
class="mb-2"
|
||||||
|
v-model="inputValue"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
size="md"
|
||||||
|
/>
|
||||||
|
<VInput
|
||||||
|
class="mb-2"
|
||||||
|
v-model="inputValue"
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
<VInput v-model="inputValue" placeholder="请输入邮箱" size="xs" />
|
||||||
</div>
|
</div>
|
||||||
<h2 class="mb-1">With Icon:</h2>
|
<!-- <h2 class="mb-1">With Icon:</h2>-->
|
||||||
|
<!-- <div class="mb-3">-->
|
||||||
|
<!-- <VInput v-model="inputValue" class="mb-2" size="md">-->
|
||||||
|
<!-- <template #prefix>-->
|
||||||
|
<!-- <IconDashboard />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <template #suffix>-->
|
||||||
|
<!-- <IconDashboard />-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </VInput>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<h2 class="mb-1">Disabled:</h2>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<VInput class="mb-2" size="md">
|
<VInput v-model="inputValue" disabled />
|
||||||
<template #prefix>
|
</div>
|
||||||
<IconDashboard />
|
</section>
|
||||||
</template>
|
<section class="box border-2 rounded p-2">
|
||||||
<template #suffix>
|
<h1 class="text-xl font-bold mb-2">Select</h1>
|
||||||
<IconDashboard />
|
<h2 class="mb-1">Size:</h2>
|
||||||
</template>
|
<div class="mb-3">
|
||||||
</VInput>
|
<VSelect v-model="selectValue">
|
||||||
|
<VOption
|
||||||
|
v-for="(option, index) in selectData"
|
||||||
|
:value="option.value"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</VOption>
|
||||||
|
</VSelect>
|
||||||
</div>
|
</div>
|
||||||
<h2 class="mb-1">Disabled:</h2>
|
<h2 class="mb-1">Disabled:</h2>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<VInput disabled />
|
<VSelect v-model="selectValue" disabled>
|
||||||
|
<VOption
|
||||||
|
v-for="(option, index) in selectData"
|
||||||
|
:value="option.value"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
{{ option.label }}
|
||||||
|
</VOption>
|
||||||
|
</VSelect>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,5 +112,20 @@
|
||||||
import { FilledLayout } from "../layouts";
|
import { FilledLayout } from "../layouts";
|
||||||
import { VButton } from "@/components/base/button";
|
import { VButton } from "@/components/base/button";
|
||||||
import { VInput } from "@/components/base/input";
|
import { VInput } from "@/components/base/input";
|
||||||
import { IconDashboard } from "@/core/icons";
|
import { VSelect, VOption } from "@/components/base/select";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
const inputValue = ref();
|
||||||
|
const selectValue = ref();
|
||||||
|
|
||||||
|
const selectData = [
|
||||||
|
{
|
||||||
|
value: "1",
|
||||||
|
label: "1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "2",
|
||||||
|
label: "2",
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue