@yxhl/specter-pui-vtk 1.0.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.
Files changed (49) hide show
  1. package/README.md +122 -0
  2. package/dist/specter-pui-vtk.css +1 -0
  3. package/dist/specter-pui.es.js +3289 -0
  4. package/dist/specter-pui.es.js.map +1 -0
  5. package/dist/specter-pui.umd.js +2 -0
  6. package/dist/specter-pui.umd.js.map +1 -0
  7. package/package.json +65 -0
  8. package/src/assets/css/globals.scss +250 -0
  9. package/src/assets/css/index.scss +271 -0
  10. package/src/assets/css/settings.scss +10 -0
  11. package/src/assets/css/variables.scss +0 -0
  12. package/src/assets/icon/logo.svg +9 -0
  13. package/src/assets/img/background.png +0 -0
  14. package/src/assets/img/dtx.png +0 -0
  15. package/src/commons/filters/dictionary.js +75 -0
  16. package/src/commons/filters/format.js +112 -0
  17. package/src/commons/filters/mask.js +25 -0
  18. package/src/commons/index.js +17 -0
  19. package/src/commons/request.js +89 -0
  20. package/src/commons/storage.js +41 -0
  21. package/src/commons/themes.js +153 -0
  22. package/src/commons/validation.js +72 -0
  23. package/src/components/README.md +35 -0
  24. package/src/components/assembly/VtkArea.vue +259 -0
  25. package/src/components/assembly/VtkCheckbox.vue +168 -0
  26. package/src/components/assembly/VtkCount.vue +403 -0
  27. package/src/components/assembly/VtkDatePicker.vue +326 -0
  28. package/src/components/assembly/VtkEmpty.vue +107 -0
  29. package/src/components/assembly/VtkFab.vue +78 -0
  30. package/src/components/assembly/VtkFormItem.vue +166 -0
  31. package/src/components/assembly/VtkImg.vue +372 -0
  32. package/src/components/assembly/VtkPage.vue +156 -0
  33. package/src/components/assembly/VtkPdf.vue +424 -0
  34. package/src/components/assembly/VtkProj.vue +539 -0
  35. package/src/components/assembly/VtkRadio.vue +82 -0
  36. package/src/components/assembly/VtkSearch.vue +145 -0
  37. package/src/components/assembly/VtkSelect.vue +104 -0
  38. package/src/components/assembly/VtkStepper.vue +160 -0
  39. package/src/components/message/alert.vue +31 -0
  40. package/src/components/message/confirm.vue +44 -0
  41. package/src/components/message/index.js +55 -0
  42. package/src/components/message/loading.vue +33 -0
  43. package/src/components/message/prompt.vue +57 -0
  44. package/src/components/message/toast.vue +45 -0
  45. package/src/components/message/vtkMessage.vue +27 -0
  46. package/src/composables/useMixins.js +2 -0
  47. package/src/composables/usePage.js +311 -0
  48. package/src/index.js +109 -0
  49. package/src/stores/message.js +79 -0
Binary file
Binary file
@@ -0,0 +1,75 @@
1
+ /**
2
+ * 将数值转换成中文 '0:未启/1:已启'
3
+ *
4
+ * @param {*} val 数值
5
+ * @param {*} map 值对
6
+ * @return {string} 中文值
7
+ */
8
+ export function dict(val, map) {
9
+ let value = "";
10
+ if (val && val.includes(",")) {
11
+ value = val.split(",").map(item => pre(item, map)).join();
12
+ } else {
13
+ value = pre(val, map)
14
+ }
15
+ return value;
16
+ }
17
+
18
+ function pre(value, map) {
19
+ if (value) {
20
+ var val = Array.isArray(value) ? value : value.split(",");
21
+ var maps = map?.split("/");
22
+ var results = []; // 创建一个空数组,用于存储匹配到的值
23
+ // 遍历 val 数组
24
+ for (var j = 0; j < val?.length; j++) {
25
+ var currentVal = val[j]; // 获取当前要查找的键
26
+ for (var i in maps) {
27
+ var kv = maps[i].split(":");
28
+ if (kv[0] == currentVal) {
29
+ results.push(kv[1]); // 将匹配到的值添加到结果数组中
30
+ break; // 找到匹配项后,跳出内层循环
31
+ }
32
+ }
33
+ }
34
+ return results.length > 0 ? results.join() : "其他";
35
+ }
36
+
37
+ }
38
+
39
+
40
+ /**
41
+ * 1,2字符残疾类别转换
42
+ *
43
+ * @param {*} val 数值
44
+ * @return {string} 中文值
45
+ */
46
+ export function analyType(val) {
47
+ if (!val) {
48
+ return ''
49
+ }
50
+ let newVal = ''
51
+ let arr = ['视力', '听力', '言语', '肢体', '智力', '精神', '多重']
52
+ for (let i = 1; i < 8; i++) {
53
+ if (i === 1) {
54
+ newVal = val.replace(i.toString(), arr[i - 1])
55
+ } else {
56
+ newVal = newVal.replace(i.toString(), arr[i - 1])
57
+ }
58
+ }
59
+ return newVal;
60
+ }
61
+ export function analyLevel(val) {
62
+ if (!val) {
63
+ return ''
64
+ }
65
+ let newVal = ''
66
+ let arr = ['一级', '二级', '三级', '四级', '不限等级']
67
+ for (let i = 1; i < 7; i++) {
68
+ if (i === 1) {
69
+ newVal = val.replace(i.toString(), arr[i - 1])
70
+ } else {
71
+ newVal = newVal.replace(i.toString(), arr[i - 1])
72
+ }
73
+ }
74
+ return newVal;
75
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ *
3
+ * @param {*} value 需格式化的时间
4
+ * @param {*} fmt 格式
5
+ */
6
+ export function date(value, fmt) {
7
+ if (!value || value == '无' || value == null) {
8
+ fmt = '无';
9
+ return fmt;
10
+ }
11
+ let getDate = new Date(value)
12
+ let o = {
13
+ 'M+': getDate.getMonth() + 1,
14
+ 'd+': getDate.getDate(),
15
+ 'h+': getDate.getHours(),
16
+ 'm+': getDate.getMinutes(),
17
+ 's+': getDate.getSeconds(),
18
+ 'q+': Math.floor((getDate.getMonth() + 3) / 3),
19
+ 'S': getDate.getMilliseconds()
20
+ }
21
+ if (/(y+)/.test(fmt)) {
22
+ fmt = fmt.replace(RegExp.$1, (getDate.getFullYear() + '').substr(4 - RegExp.$1.length))
23
+ }
24
+ for (let k in o) {
25
+ if (new RegExp('(' + k + ')').test(fmt)) {
26
+ fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)))
27
+ }
28
+ }
29
+ return fmt
30
+
31
+ }
32
+
33
+
34
+ /**
35
+ * 数字格式化 '0,000.00'
36
+ * @param {*} val 数值
37
+ * @param {*} fmt 格式
38
+ * @return {string} 数字字符串
39
+ */
40
+ export function num(val, fmt) {
41
+ if (isNaN(val) || val === '' || val === null) {
42
+ return val;
43
+ }
44
+
45
+ // 移除了对Strings和Datetime的依赖,这些在当前上下文中未定义
46
+ var numValue = parseFloat(val);
47
+ var neg = numValue < 0 ? "-" : ""; // 正负数
48
+ var absValue = Math.abs(numValue);
49
+
50
+ // 解析格式字符串,确定小数位数
51
+ var fix = 0;
52
+ if (fmt && fmt.includes('.')) {
53
+ fix = fmt.length - fmt.lastIndexOf('.') - 1;
54
+ }
55
+
56
+ // 是否需要千分位分隔符
57
+ var needSep = fmt && fmt.includes(',');
58
+
59
+ // 处理小数部分
60
+ var xs = fix > 0 ? absValue.toFixed(fix) : Math.round(absValue);
61
+ var decimalPart = fix > 0 ? '.' + xs.toString().split('.')[1] : '';
62
+
63
+ // 处理整数部分
64
+ var integerPart = fix > 0 ? Math.floor(absValue).toString() : xs.toString();
65
+
66
+ // 添加千分位分隔符
67
+ if (needSep && integerPart.length > 3) {
68
+ integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
69
+ }
70
+
71
+ return neg + integerPart + decimalPart;
72
+ }
73
+
74
+
75
+ /**
76
+ * 年龄
77
+ * @param {*} val 时间 yyyy-MM-dd
78
+ * @return {string} 年龄
79
+ */
80
+ export function age(val) {
81
+ let birthdays = new Date(val.slice(0, 10).replace(/-/g, "/"));
82
+ let d = new Date();
83
+ let age = d.getFullYear() - birthdays.getFullYear() - ((d.getMonth() < birthdays.getMonth() || (d.getMonth() === birthdays.getMonth() && d.getDate() < birthdays.getDate())) ? 1 : 0);
84
+ return age;
85
+ }
86
+
87
+ /**
88
+ * 指定中文大于len,用...省略号表示,达到美观效果
89
+ * @param {*} val 需格式的中文
90
+ * @param {*} len 需格式的长度
91
+ */
92
+ export function txt(val, len) {
93
+ if (val?.length > len) {
94
+ return val.slice(0, len) + "...";
95
+ } else {
96
+ return val;
97
+ }
98
+
99
+ }
100
+
101
+ /**
102
+ * 取数据绝对值
103
+ * @param {*} val 手机号
104
+ */
105
+ export function abs(val) {
106
+ return isNaN(val) ? 0 : Math.abs(val)
107
+ }
108
+
109
+
110
+
111
+
112
+
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 脱敏手机号码
3
+ * @param {*} val 手机号
4
+ * @return {string} 手机号码
5
+ */
6
+ export function mobile(val) {
7
+ return val.replace(/(\d{3})\d{4}(\d{4})/, "$1****$2");
8
+ }
9
+
10
+
11
+ /**
12
+ * 脱敏身份证号
13
+ * @param {*} val 身份证号
14
+ * @return {string} 身份证号
15
+ */
16
+ export function idcard(val) {
17
+ if (val) {
18
+ return val.replace(/^(.{4})(?:\d+)(.{4})$/, "$1** **** ****$2");
19
+ }
20
+ }
21
+
22
+
23
+
24
+
25
+
@@ -0,0 +1,17 @@
1
+ import request from "./request.js";
2
+ import storage from './storage.js';
3
+ import message from "../components/message/index.js";
4
+ import * as filters from './filters/dictionary.js';
5
+ import * as masks from './filters/mask.js';
6
+
7
+ const vtk = {
8
+ request,
9
+ storage,
10
+ message,
11
+ env: process.env,
12
+ ...filters, // 将所有filter方法展开到vtk对象中
13
+ ...masks // 将所有mask方法展开到vtk对象中
14
+ }
15
+
16
+ export default vtk;
17
+
@@ -0,0 +1,89 @@
1
+ import axios from 'axios'
2
+ import Storage from './storage.js'
3
+ import Message from "../components/message/index.js"
4
+
5
+ // create an axios instance
6
+ const service = axios.create({
7
+ baseURL: import.meta.env.VUE_APP_API_URL,
8
+ headers: {
9
+ 'X-Requested-With': 'XMLHttpRequest'
10
+ },
11
+ withCredentials: false,
12
+ timeout: 30000
13
+ })
14
+
15
+ // request interceptor
16
+ service.interceptors.request.use(
17
+ config => {
18
+ if (Storage.get("token")) {
19
+ config.headers['X-Token'] = "1b0679be72ad976ad5d491ad57a5eec0"
20
+ config.headers['Authorization'] = 'Bearer ' + Storage.get("token")
21
+ }
22
+ return config
23
+ },
24
+ error => {
25
+ Message.toast(error.message || '请求错误')
26
+ return Promise.reject(error)
27
+ }
28
+ )
29
+
30
+ // response interceptor
31
+ service.interceptors.response.use(
32
+ response => {
33
+ let res = response.data
34
+ let header = response.headers['content-disposition']
35
+
36
+ if (header != undefined && header.split(';')[0] == 'attachment') {
37
+ res = response
38
+ }
39
+
40
+ if (response.status !== 200) {
41
+ Message.toast(res.message || '请求失败')
42
+ return Promise.reject(new Error(res.message || '请求失败'))
43
+ } else {
44
+ return Promise.resolve(res)
45
+ }
46
+ },
47
+ error => {
48
+ console.log('err' + error)
49
+ Message.toast(error.message || '网络错误')
50
+ return Promise.reject(error)
51
+ }
52
+ )
53
+
54
+ const Request = {}
55
+
56
+ Request.http = (url, data = {}, method = 'GET', headers = { 'content-type': 'application/x-www-form-urlencoded' }, responseType = null) => {
57
+ if (headers['content-type'] === 'application/x-www-form-urlencoded') {
58
+ return service({ url: url, params: data, method: method, headers: headers, responseType: responseType })
59
+ } else {
60
+ return service({ url: url, data: data, method: method, headers: headers, responseType: responseType })
61
+ }
62
+ }
63
+
64
+ Request.getForm = (url, data = {}) => {
65
+ return Request.http(url, data)
66
+ }
67
+
68
+ Request.postForm = (url, data = {}) => {
69
+ return Request.http(url, data, 'POST')
70
+ }
71
+
72
+ Request.getJson = (url, data = {}) => {
73
+ return Request.http(url, data, 'POST', { 'content-type': 'application/json' })
74
+ }
75
+
76
+ Request.postJson = (url, data = {}) => {
77
+ console.log('POST request to:', url) // 简单的日志输出
78
+ return Request.http(url, data, 'POST', { 'content-type': 'application/json' })
79
+ }
80
+
81
+ Request.imp = (url, data = {}) => {
82
+ return Request.http(url, data, 'POST', { 'Content-Type': 'multipart/form-data' })
83
+ }
84
+
85
+ Request.exp = (url, data = {}) => {
86
+ return Request.http(url, data, 'POST', { 'content-Type': 'application/json' }, 'blob')
87
+ }
88
+
89
+ export default Request
@@ -0,0 +1,41 @@
1
+ const Storage = {};
2
+ /** 获取Storage对象 */
3
+ const storage = (type = 'local') => {
4
+ return type == "local" ? window.localStorage : window.sessionStorage;
5
+ }
6
+ /**
7
+ * 获取对应缓存值JSON对象
8
+ * @param {string} key 键
9
+ * @param {string} type 默认为localStorage, 'session'为sessionStorage
10
+ * @return {object} 对应的值JSON对象
11
+ */
12
+ Storage.get = (key, type = 'local') => {
13
+ var js = storage(type).getItem(key);
14
+ return /^[\\{|\\[].+[\\}|\\]]$/.test(js) ? JSON.parse(js) : js;
15
+ };
16
+ /**
17
+ * 设置存储缓存取对象JSON
18
+ * @param {string} key 键
19
+ * @param {string} value 值(JSON对象)
20
+ * @param {string} type 默认为localStorage, 'session'为sessionStorage
21
+ */
22
+ Storage.set = (key, value, type = 'local') => {
23
+ storage(type).setItem(key, JSON.stringify(value));
24
+ };
25
+ /**
26
+ * 删除对应的缓存对象JSON
27
+ * @param {string} key 键
28
+ * @param {string} type 默认为localStorage, 'session'为sessionStorage
29
+ */
30
+ Storage.remove = (key, type = 'local') => {
31
+ storage(type).removeItem(key);
32
+ };
33
+ /**
34
+ * 清空所有的缓存对象JSON
35
+ * @param {string} type 默认为localStorage, 'session'为sessionStorage
36
+ */
37
+ Storage.clear = (type = 'local') => {
38
+ storage(type).clear();
39
+ };
40
+
41
+ export default Storage;
@@ -0,0 +1,153 @@
1
+ import colors from 'vuetify/lib/util/colors';
2
+
3
+ const theme_colors = [
4
+ {
5
+ name: 'Blue-light',
6
+ scheme: 'md-blue-500-scheme',
7
+ dark: true,
8
+ primary: colors.blue.base,
9
+ secondary: colors.blue.lighten4, // #FFCDD2
10
+ accent: colors.blue.accent2, // #3F51B5
11
+ },
12
+ {
13
+ name: 'Yellow-light',
14
+ scheme: 'md-amber-a700-scheme',
15
+ dark: true,
16
+ primary: colors.amber.darken1,
17
+ secondary: colors.amber.lighten4, // #FFCDD2
18
+ accent: colors.amber.accent2, // #3F51B5
19
+ },
20
+ {
21
+ name: 'Red-light',
22
+ scheme: 'md-red-a400-scheme',
23
+ dark: true,
24
+ primary: colors.red.accent3,
25
+ secondary: colors.red.lighten4, // #FFCDD2
26
+ accent: colors.red.accent2, // #3F51B5
27
+ },
28
+ {
29
+ name: 'Pink-light',
30
+ dark: false,
31
+ scheme: 'md-pink-a100-scheme',
32
+ primary: colors.pink.accent1,
33
+ secondary: colors.pink.lighten4, // #FFCDD2
34
+ accent: colors.pink.accent2, // #3F51B5
35
+ },
36
+ {
37
+ name: 'Purple-light',
38
+ dark: false,
39
+ scheme: 'md-purple-a700-scheme',
40
+ primary: colors.purple.accent4,
41
+ secondary: colors.purple.lighten4, // #FFCDD2
42
+ accent: colors.purple.accent2, // #3F51B5
43
+ },
44
+ {
45
+ name: 'DeepPurple-light',
46
+ dark: false,
47
+ scheme: 'md-deep-purple-a700-scheme',
48
+ primary: colors.deepPurple.base,
49
+ secondary: colors.deepPurple.lighten4, // #FFCDD2
50
+ accent: colors.deepPurple.accent2, // #3F51B5
51
+ }
52
+ ];
53
+
54
+ const navi_colors = [
55
+ {
56
+ name: 'black',
57
+ class: 'black'
58
+ },
59
+ {
60
+ name: 'red-pink',
61
+ class: 'gradient-red-pink'
62
+ },
63
+ {
64
+ name: 'blue-grey-blue',
65
+ class: 'gradient-blue-grey-blue'
66
+ },
67
+ {
68
+ name: 'green-teal',
69
+ class: 'gradient-green-teal'
70
+ },
71
+ {
72
+ name: 'purple-deep-purple',
73
+ class: 'gradient-purple-deep-purple'
74
+ },
75
+ {
76
+ name: 'purple-amber',
77
+ class: 'gradient-purple-amber'
78
+ },
79
+ {
80
+ name: 'indigo-purple',
81
+ class: 'gradient-indigo-purple'
82
+ },
83
+ {
84
+ name: 'deep-purple-blue',
85
+ class: 'gradient-deep-purple-blue'
86
+ },
87
+ {
88
+ name: 'deep-orange-orange',
89
+ class: 'gradient-deep-orange-orange'
90
+ },
91
+ {
92
+ name: 'light-blue-cyan',
93
+ class: 'gradient-light-blue-cyan'
94
+ },
95
+ {
96
+ name: 'timber',
97
+ class: 'gradient-timber'
98
+ },
99
+ {
100
+ name: 'strawberry',
101
+ class: 'gradient-strawberry'
102
+ },
103
+ {
104
+ name: 'orange',
105
+ class: 'gradient-orange'
106
+ },
107
+ {
108
+ name: 'pomegranate',
109
+ class: 'gradient-pomegranate'
110
+ },
111
+ {
112
+ name: 'green-tea',
113
+ class: 'gradient-green-tea'
114
+ },
115
+ {
116
+ name: 'blackberry',
117
+ class: 'gradient-blackberry'
118
+ },
119
+ {
120
+ name: 'plum',
121
+ class: 'gradient-plum'
122
+ },
123
+ {
124
+ name: 'passion-fruit',
125
+ class: 'gradient-passion-fruit'
126
+ },
127
+ {
128
+ name: 'sublime-vivid',
129
+ class: 'gradient-sublime-vivid'
130
+ },
131
+ {
132
+ name: 'summer',
133
+ class: 'gradient-summer'
134
+ },
135
+ {
136
+ name: 'crystal-clear',
137
+ class: 'gradient-crystal-clear'
138
+ },
139
+ {
140
+ name: 'dawn',
141
+ class: 'gradient-dawn'
142
+ },
143
+ {
144
+ name: 'grapefruit-sunset',
145
+ class: 'gradient-grapefruit-sunset'
146
+ },
147
+ {
148
+ name: 'man-of-steel',
149
+ class: 'gradient-man-of-steel'
150
+ }
151
+ ]
152
+
153
+ export default { theme_colors, navi_colors }
@@ -0,0 +1,72 @@
1
+ /** https://github.com/shadowOfCode/bee.js */
2
+ const Validation = {
3
+ //手机号码
4
+ isMobile: function (input) {
5
+ return /^(?:\+86)?(?:13\d|14[57]|15[0-35-9]|17[35-8]|18\d)\d{8}$|^(?:\+86)?170[057-9]\d{7}$/.test(input);
6
+ },
7
+ //手机号码简单校验,只校验长度
8
+ isMobileSize: function (input) {
9
+ return /^(?:\+86)?1\d{10}$/.test(input);
10
+ },
11
+ isPhone: function (input) {
12
+ return /^(?:\(\d{3,4}\)|\d{3,4}-)?\d{7,8}(?:-\d{1,4})?$/.test(input);
13
+ },
14
+ isCall: (input) => {
15
+ return this.isPhone(input) || this.isMobile(input);
16
+ },
17
+ //邮箱格式校验
18
+ isEmail: function (input) {
19
+ return /^[-\w+]+(?:\.[-\w]+)*@[-a-z0-9]+(?:\.[a-z0-9]+)*(?:\.[a-z]{2,})$/i.test(input);
20
+ },
21
+ //18位身份证简单校验
22
+ isIdCardSimple: function (idCard) {
23
+ return /^(?:1[1-5]|2[1-3]|3[1-7]|4[1-6]|5[0-4]|6[1-5])\d{4}(?:1[89]|20)\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])\d{3}(?:\d|[xX])$/.test(idCard);
24
+ },
25
+ // 真实姓名校验
26
+ isName: function (input) {
27
+ return /^([\u4e00-\u9fa5]{1,20}|[a-zA-Z.\s]{1,20})$/.test(input);
28
+ },
29
+ //
30
+ //18位身份证严格校验
31
+ isIdCard: function (idCard) {
32
+ let checkCode = (input) => {
33
+ var multiplier = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
34
+ var ids = input.split("");
35
+ var len = 17, sum = 0;
36
+ for (var i = 0; i < len; i++) {
37
+ sum += ids[i] * multiplier[i];
38
+ }
39
+ var remainder = sum % 11;
40
+ var checkCodeArr = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
41
+ var result = checkCodeArr[remainder];
42
+ return result === input[17];
43
+ }
44
+ //先校验格式
45
+ if (this.isIdCardSimple(idCard)) {
46
+ //校验日期时间是否合法
47
+ var dateStr = idCard.substr(6, 8);
48
+ var dateStrNew = dateStr.replace(/(\d{4})(\d{2})(\d{2})/, "$1/$2/$3");
49
+ var dateObj = new Date(dateStrNew);
50
+ var month = dateObj.getMonth() + 1;
51
+ if (parseInt(dateStr.substr(4, 2)) === month) {
52
+ return checkCode(idCard);
53
+ }
54
+ }
55
+ return false;
56
+ },
57
+ getFromIdCard: (idCard) => {
58
+ var area = idCard.substr(0, 6);
59
+ var birth = idCard.substr(6, 8).replace(/(\d{4})(\d{2})(\d{2})/, '$1年$2月$3日');
60
+ var age = new Date().getFullYear() - idCard.substr(6, 4) + 1;
61
+ var sex = (idCard.substr(16, 1) % 2 === 0) ? '女' : '男';
62
+
63
+ return {
64
+ 'area': area,
65
+ 'birth': birth,
66
+ 'age': age,
67
+ 'sex': sex
68
+ };
69
+ }
70
+ };
71
+
72
+ export default Validation;
@@ -0,0 +1,35 @@
1
+ # Components
2
+
3
+ Vue template files in this folder are automatically imported.
4
+
5
+ ## 🚀 Usage
6
+
7
+ Importing is handled by [unplugin-vue-components](https://github.com/unplugin/unplugin-vue-components). This plugin automatically imports `.vue` files created in the `src/components` directory, and registers them as global components. This means that you can use any component in your application without having to manually import it.
8
+
9
+ The following example assumes a component located at `src/components/MyComponent.vue`:
10
+
11
+ ```vue
12
+ <template>
13
+ <div>
14
+ <MyComponent />
15
+ </div>
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ //
20
+ </script>
21
+ ```
22
+
23
+ When your template is rendered, the component's import will automatically be inlined, which renders to this:
24
+
25
+ ```vue
26
+ <template>
27
+ <div>
28
+ <MyComponent />
29
+ </div>
30
+ </template>
31
+
32
+ <script lang="ts" setup>
33
+ import MyComponent from '@/components/MyComponent.vue'
34
+ </script>
35
+ ```