@dot-present/components 1.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/button/index.ts +4 -0
- package/button/src/button.ts +31 -0
- package/button/src/button.vue +40 -0
- package/button/test/button.test.ts +53 -0
- package/card/index.ts +4 -0
- package/card/src/card.ts +27 -0
- package/card/src/card.vue +31 -0
- package/card/test/card.test.ts +88 -0
- package/index.ts +28 -0
- package/package.json +22 -0
- package/vite.config.js +23 -0
- package/vitest.config.js +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":"4.1.0","results":[[":card/test/card.test.ts",{"duration":37.70300000000043,"failed":false}],[":button/test/button.test.ts",{"duration":32.867499999999836,"failed":false}]]}
|
package/button/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ExtractPropTypes } from "vue";
|
|
2
|
+
// ExtractPropTypes 是 vue3 所提供的一个工具类型用于从 vue 组件的 props 对象中提取 ts 类型
|
|
3
|
+
|
|
4
|
+
export const buttonProps = {
|
|
5
|
+
type: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: "default"
|
|
8
|
+
},
|
|
9
|
+
plain: {
|
|
10
|
+
type: Boolean,
|
|
11
|
+
default: false
|
|
12
|
+
},
|
|
13
|
+
round: {
|
|
14
|
+
type: Boolean,
|
|
15
|
+
default: false
|
|
16
|
+
},
|
|
17
|
+
circle: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
default: false
|
|
20
|
+
},
|
|
21
|
+
disabled: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
default: false
|
|
24
|
+
},
|
|
25
|
+
icon: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: ""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ButtonPropsType = ExtractPropTypes<typeof buttonProps>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
class="dot-button"
|
|
4
|
+
:class="[
|
|
5
|
+
`dot-button-${type}`,
|
|
6
|
+
{
|
|
7
|
+
'is-plain': plain,
|
|
8
|
+
'is-round': round,
|
|
9
|
+
'is-circle': circle,
|
|
10
|
+
'is-disabled': disabled,
|
|
11
|
+
},
|
|
12
|
+
]"
|
|
13
|
+
:disabled="disabled"
|
|
14
|
+
@click="handleClick"
|
|
15
|
+
>
|
|
16
|
+
<i v-if="icon" :class="[`dot-icon-${icon}`]"></i>
|
|
17
|
+
<span v-if="$slots.default">
|
|
18
|
+
<slot></slot>
|
|
19
|
+
</span>
|
|
20
|
+
</button>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { buttonProps } from "./button";
|
|
25
|
+
|
|
26
|
+
defineOptions({
|
|
27
|
+
name: "DotButton"
|
|
28
|
+
});
|
|
29
|
+
// 需要在 <script> 内部访问这些 prop 值(比如用于计算属性、watch 或其他逻辑)
|
|
30
|
+
// 将props才将props声明出来
|
|
31
|
+
defineProps(buttonProps);
|
|
32
|
+
|
|
33
|
+
const emit = defineEmits(["click"]);
|
|
34
|
+
|
|
35
|
+
const handleClick = (e: Event) => {
|
|
36
|
+
emit("click", e);
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<style scoped></style>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// 该文件用于书写测试用例
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect } from "vitest";
|
|
4
|
+
import { mount } from "@vue/test-utils";
|
|
5
|
+
import Button from "../src/button.vue";
|
|
6
|
+
|
|
7
|
+
describe("测试 Button 组件", () => {
|
|
8
|
+
// 一个一个的测试用例
|
|
9
|
+
// 3A 原则 arrange、action、assert
|
|
10
|
+
it("1.渲染按钮的时候有默认type", () => {
|
|
11
|
+
// 准备工作
|
|
12
|
+
const wrapper = mount(Button);
|
|
13
|
+
// 断言
|
|
14
|
+
expect(wrapper.classes()).toContain("dot-button");
|
|
15
|
+
expect(wrapper.classes()).toContain("dot-button-default");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("2.渲染plain属性的按钮", () => {
|
|
19
|
+
const wrapper = mount(Button, {
|
|
20
|
+
props: {
|
|
21
|
+
plain: true
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
expect(wrapper.classes()).toContain("is-plain");
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it("3.渲染disabled类型按钮", () => {
|
|
28
|
+
const wrapper = mount(Button, { props: { disabled: true } });
|
|
29
|
+
expect(wrapper.classes()).toContain("is-disabled");
|
|
30
|
+
expect(wrapper.attributes()).toHaveProperty("disabled");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("4.渲染icon类型的按钮", () => {
|
|
34
|
+
const wrapper = mount(Button, { props: { icon: "home" } });
|
|
35
|
+
expect(wrapper.find("i").classes()).toContain("dot-icon-home");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("5.测试slot插槽是否正常工作", () => {
|
|
39
|
+
const wrapper = mount(Button, {
|
|
40
|
+
slots: {
|
|
41
|
+
default: "点击我",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
expect(wrapper.text()).toContain("点击我");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// 测试事件是否工作正常
|
|
48
|
+
it("6.测试按钮事件", async ()=>{
|
|
49
|
+
const wrapper = mount(Button);
|
|
50
|
+
await wrapper.trigger("click");
|
|
51
|
+
expect(wrapper.emitted()).toHaveProperty("click");
|
|
52
|
+
});
|
|
53
|
+
})
|
package/card/index.ts
ADDED
package/card/src/card.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ExtractPropTypes } from "vue";
|
|
2
|
+
|
|
3
|
+
export const cardProps = {
|
|
4
|
+
// 卡片的宽度
|
|
5
|
+
width: {
|
|
6
|
+
type: Number,
|
|
7
|
+
default: 0,
|
|
8
|
+
},
|
|
9
|
+
// 卡片图片资源链接
|
|
10
|
+
imgSrc: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: "",
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
// 卡片图片高度
|
|
16
|
+
imgHeight: {
|
|
17
|
+
type: Number,
|
|
18
|
+
default: 0,
|
|
19
|
+
},
|
|
20
|
+
// 卡片概要
|
|
21
|
+
summary: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: "",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type CardPropsType = ExtractPropTypes<typeof cardProps>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { cardProps } from "./card";
|
|
3
|
+
|
|
4
|
+
defineOptions({
|
|
5
|
+
name: "DotCard"
|
|
6
|
+
});
|
|
7
|
+
defineProps(cardProps);
|
|
8
|
+
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<div class="dot-card" :style="width ? {width: width + 'px'} : {}">
|
|
13
|
+
<div class="dot-card-img" :style="imgHeight ? {height: imgHeight + 'px'} : {}">
|
|
14
|
+
<img :src="imgSrc" alt="">
|
|
15
|
+
</div>
|
|
16
|
+
<div v-if="summary" class="dot-card-summary">{{ summary }}</div>
|
|
17
|
+
<div v-else class="dot-card-summary"><slot></slot></div>
|
|
18
|
+
|
|
19
|
+
<div class="dot-card-footer">
|
|
20
|
+
<slot name="footer"></slot>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style scoped>
|
|
26
|
+
.card {
|
|
27
|
+
padding: 20px;
|
|
28
|
+
border-radius: 4px;
|
|
29
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
30
|
+
}
|
|
31
|
+
</style>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// 引入需要的库
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { mount } from "@vue/test-utils";
|
|
4
|
+
import Card from "../src/card.vue";
|
|
5
|
+
|
|
6
|
+
const imgSrc = "https://abc.com/abc.png";
|
|
7
|
+
|
|
8
|
+
describe("测试Card组件", () => {
|
|
9
|
+
// 在测试组件的时候,一般会针对每一个 prop 设计一个测试用例
|
|
10
|
+
|
|
11
|
+
// 测试卡片宽度
|
|
12
|
+
it("测试卡片宽度", () => {
|
|
13
|
+
// 3A 原则 arrange、action、assert
|
|
14
|
+
const wrapper = mount(Card, {
|
|
15
|
+
props: {
|
|
16
|
+
width: 300,
|
|
17
|
+
imgSrc,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
expect(wrapper.attributes("style")).toContain("width: 300px;");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 测试图片资源
|
|
25
|
+
it("测试图片资源", () => {
|
|
26
|
+
const wrapper = mount(Card, {
|
|
27
|
+
props: {
|
|
28
|
+
imgSrc,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(wrapper.find(".dot-card-img img").attributes("src")).toBe(imgSrc);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// 测试图片设置高度
|
|
36
|
+
it("测试图片设置高度", () => {
|
|
37
|
+
const wrapper = mount(Card, {
|
|
38
|
+
props: {
|
|
39
|
+
imgSrc,
|
|
40
|
+
imgHeight: 200,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(wrapper.find(".dot-card-img").attributes("style")).toContain(
|
|
45
|
+
"height: 200px"
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 测试卡片概要
|
|
50
|
+
it("测试卡片概要", () => {
|
|
51
|
+
const summary = "这是一个卡片的概要内容";
|
|
52
|
+
const wrapper = mount(Card, {
|
|
53
|
+
props: {
|
|
54
|
+
summary,
|
|
55
|
+
imgSrc,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
expect(wrapper.find(".dot-card-summary").text()).toBe(summary);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// 测试插槽
|
|
62
|
+
it("测试summary插槽", () => {
|
|
63
|
+
const summary = "这是一个卡片的概要内容";
|
|
64
|
+
const wrapper = mount(Card, {
|
|
65
|
+
props: {
|
|
66
|
+
imgSrc,
|
|
67
|
+
},
|
|
68
|
+
slots: {
|
|
69
|
+
default: summary,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
expect(wrapper.find(".dot-card-summary").text()).toBe(summary);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// 测试footer插槽
|
|
76
|
+
it("测试footer插槽", () => {
|
|
77
|
+
const footer = "这是 footer 内容";
|
|
78
|
+
const wrapper = mount(Card, {
|
|
79
|
+
props: {
|
|
80
|
+
imgSrc,
|
|
81
|
+
},
|
|
82
|
+
slots: {
|
|
83
|
+
footer: footer,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
expect(wrapper.find(".dot-card-footer").text()).toBe(footer);
|
|
87
|
+
});
|
|
88
|
+
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Button from "./button/src/button.vue";
|
|
2
|
+
import Card from "./card/src/card.vue";
|
|
3
|
+
|
|
4
|
+
import type { App, Plugin } from "vue";
|
|
5
|
+
|
|
6
|
+
// 之前是每个组件有一个入口文件,每个入口文件实际上所做的事情就是给这个组件添加一个 install 方法
|
|
7
|
+
// 之后在其他项目中就可以单独的引入这个组件:
|
|
8
|
+
// import DuyiButton from '@duyiui-plus/components/button'
|
|
9
|
+
// import DuyiCard from '@duyiui-plus/components/card'
|
|
10
|
+
|
|
11
|
+
// 现在整体项目的入口文件,要做的事情很简单:统一给所有组件添加上 install
|
|
12
|
+
// 之后在其他项目中使用的时候,就可以一次性导入所有的组件
|
|
13
|
+
|
|
14
|
+
const components = [Button, Card];
|
|
15
|
+
|
|
16
|
+
const install = (app: App) => {
|
|
17
|
+
components.forEach((component) => {
|
|
18
|
+
if (component.name) {
|
|
19
|
+
app.component(component.name, component);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const dot: Plugin = {
|
|
25
|
+
install,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default dot;
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dot-present/components",
|
|
3
|
+
"version": "1.0.1-alpha.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "types.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"test": "vitest"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"packageManager": "pnpm@10.26.2",
|
|
15
|
+
"peerDependencies": {
|
|
16
|
+
"vue": "^3.0.0"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"gitHead": "9e3a9aae5de9625e6567a0be190c830f8aee00a2"
|
|
22
|
+
}
|
package/vite.config.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// 从 node.js 内置的 url 模块中引入 fileURLToPath、URL
|
|
2
|
+
// 这两个函数用于做 URL 相关的处理
|
|
3
|
+
import { fileURLToPath, URL } from "node:url";
|
|
4
|
+
|
|
5
|
+
// 从 vite 里面引入 defineConfig,该方法用于定义 vite 配置
|
|
6
|
+
import { defineConfig } from "vite";
|
|
7
|
+
|
|
8
|
+
// 引入 @vitejs/plugin-vue 这个插件,让 vite 能够支持对 vue 的处理
|
|
9
|
+
import vue from "@vitejs/plugin-vue";
|
|
10
|
+
|
|
11
|
+
// https://vitejs.dev/config/
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
cacheDir: ".vite",
|
|
14
|
+
// 要使用的插件
|
|
15
|
+
plugins: [vue()],
|
|
16
|
+
resolve: {
|
|
17
|
+
// 定义了一个别名映射
|
|
18
|
+
// @ ---> src
|
|
19
|
+
alias: {
|
|
20
|
+
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
package/vitest.config.js
ADDED