@qxs-bns/components 0.0.82 → 0.0.84
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/README.md +566 -0
- package/es/package.json.mjs +1 -1
- package/es/src/icon/src/icon.vue.mjs +1 -1
- package/es/src/icon/src/icon.vue.mjs.map +1 -1
- package/lib/package.json.cjs +1 -1
- package/lib/src/icon/src/icon.vue.cjs +1 -1
- package/lib/src/icon/src/icon.vue.cjs.map +1 -1
- package/package.json +2 -2
- package/types/src/icon/src/icon.vue.d.ts.map +1 -1
- package/types/tsconfig.tsbuildinfo +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# @qxs-bns/components
|
|
2
|
+
|
|
3
|
+
QXS Business System 组件库,基于 Vue 3 + TypeScript + Element Plus 构建的企业级组件库。
|
|
4
|
+
|
|
5
|
+
## ✨ 特性
|
|
6
|
+
|
|
7
|
+
- 🎯 **企业级** - 专为企业级应用设计,提供丰富的业务组件
|
|
8
|
+
- 🔧 **二次封装** - 基于 Element Plus 进行增强,保持 API 一致性
|
|
9
|
+
- 📦 **按需加载** - 支持 Tree-shaking,减少打包体积
|
|
10
|
+
- 🎨 **主题定制** - 支持主题定制和暗黑模式
|
|
11
|
+
- 🌍 **国际化** - 内置国际化支持
|
|
12
|
+
- 📱 **响应式** - 移动端友好的响应式设计
|
|
13
|
+
- ♿ **无障碍** - 遵循 WAI-ARIA 标准
|
|
14
|
+
- 🎪 **TypeScript** - 完整的 TypeScript 类型定义
|
|
15
|
+
|
|
16
|
+
## 📦 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# npm
|
|
20
|
+
npm install @qxs-bns/components
|
|
21
|
+
|
|
22
|
+
# yarn
|
|
23
|
+
yarn add @qxs-bns/components
|
|
24
|
+
|
|
25
|
+
# pnpm
|
|
26
|
+
pnpm add @qxs-bns/components
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🚀 快速开始
|
|
30
|
+
|
|
31
|
+
### 完整引入
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { createApp } from 'vue'
|
|
35
|
+
import QxsComponents from '@qxs-bns/components'
|
|
36
|
+
import '@qxs-bns/components/dist/index.css'
|
|
37
|
+
import App from './App.vue'
|
|
38
|
+
|
|
39
|
+
const app = createApp(App)
|
|
40
|
+
app.use(QxsComponents)
|
|
41
|
+
app.mount('#app')
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 按需引入
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<template>
|
|
48
|
+
<QxsButton type="primary">按钮</QxsButton>
|
|
49
|
+
<QxsDataChart :data="chartData" show-type-name="bar" />
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script setup>
|
|
53
|
+
import { QxsButton, QxsDataChart } from '@qxs-bns/components'
|
|
54
|
+
|
|
55
|
+
const chartData = {
|
|
56
|
+
data: [
|
|
57
|
+
{ name: '一月', value: 100 },
|
|
58
|
+
{ name: '二月', value: 200 }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 🏗️ 项目结构
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
packages/components/
|
|
68
|
+
├── src/ # 源码目录
|
|
69
|
+
│ ├── button/ # 按钮组件
|
|
70
|
+
│ │ ├── src/ # 组件源码
|
|
71
|
+
│ │ │ └── button.vue
|
|
72
|
+
│ │ ├── style/ # 组件样式
|
|
73
|
+
│ │ │ └── index.scss
|
|
74
|
+
│ │ └── index.ts # 组件入口
|
|
75
|
+
│ ├── data-chart/ # 数据图表组件
|
|
76
|
+
│ ├── utils/ # 工具函数
|
|
77
|
+
│ ├── constants/ # 常量定义
|
|
78
|
+
│ └── index.ts # 总入口
|
|
79
|
+
├── dist/ # 构建产物
|
|
80
|
+
├── docs/ # 组件文档
|
|
81
|
+
└── package.json
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 🧩 核心组件
|
|
85
|
+
|
|
86
|
+
### 基础组件
|
|
87
|
+
|
|
88
|
+
| 组件名 | 说明 | 文档 |
|
|
89
|
+
|--------|------|------|
|
|
90
|
+
| Button | 按钮组件 | [查看文档](./src/button/README.md) |
|
|
91
|
+
| Input | 输入框组件 | [查看文档](./src/input/README.md) |
|
|
92
|
+
| Select | 选择器组件 | [查看文档](./src/select/README.md) |
|
|
93
|
+
|
|
94
|
+
### 数据展示
|
|
95
|
+
|
|
96
|
+
| 组件名 | 说明 | 文档 |
|
|
97
|
+
|--------|------|------|
|
|
98
|
+
| DataChart | 数据图表组件 | [查看文档](./src/data-chart/README.md) |
|
|
99
|
+
| Table | 表格组件 | [查看文档](./src/table/README.md) |
|
|
100
|
+
| Card | 卡片组件 | [查看文档](./src/card/README.md) |
|
|
101
|
+
|
|
102
|
+
### 导航组件
|
|
103
|
+
|
|
104
|
+
| 组件名 | 说明 | 文档 |
|
|
105
|
+
|--------|------|------|
|
|
106
|
+
| Menu | 菜单组件 | [查看文档](./src/menu/README.md) |
|
|
107
|
+
| Breadcrumb | 面包屑组件 | [查看文档](./src/breadcrumb/README.md) |
|
|
108
|
+
| Pagination | 分页组件 | [查看文档](./src/pagination/README.md) |
|
|
109
|
+
|
|
110
|
+
### 反馈组件
|
|
111
|
+
|
|
112
|
+
| 组件名 | 说明 | 文档 |
|
|
113
|
+
|--------|------|------|
|
|
114
|
+
| Dialog | 对话框组件 | [查看文档](./src/dialog/README.md) |
|
|
115
|
+
| Message | 消息提示组件 | [查看文档](./src/message/README.md) |
|
|
116
|
+
| Loading | 加载组件 | [查看文档](./src/loading/README.md) |
|
|
117
|
+
|
|
118
|
+
## 🛠️ 开发指南
|
|
119
|
+
|
|
120
|
+
### 环境要求
|
|
121
|
+
|
|
122
|
+
- Node.js >= 16
|
|
123
|
+
- pnpm >= 7
|
|
124
|
+
- Vue >= 3.3
|
|
125
|
+
- TypeScript >= 4.9
|
|
126
|
+
|
|
127
|
+
### 本地开发
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# 克隆项目
|
|
131
|
+
git clone <repository-url>
|
|
132
|
+
|
|
133
|
+
# 安装依赖
|
|
134
|
+
pnpm install
|
|
135
|
+
|
|
136
|
+
# 启动开发服务器
|
|
137
|
+
pnpm dev
|
|
138
|
+
|
|
139
|
+
# 构建组件库
|
|
140
|
+
pnpm build
|
|
141
|
+
|
|
142
|
+
# 运行测试
|
|
143
|
+
pnpm test
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 创建新组件
|
|
147
|
+
|
|
148
|
+
1. **创建组件目录**
|
|
149
|
+
```bash
|
|
150
|
+
mkdir src/my-component
|
|
151
|
+
cd src/my-component
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
2. **创建必要文件**
|
|
155
|
+
```
|
|
156
|
+
my-component/
|
|
157
|
+
├── src/
|
|
158
|
+
│ └── my-component.vue
|
|
159
|
+
├── style/
|
|
160
|
+
│ └── index.scss
|
|
161
|
+
├── index.ts
|
|
162
|
+
└── README.md
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
3. **组件模板**
|
|
166
|
+
```vue
|
|
167
|
+
<!-- src/my-component.vue -->
|
|
168
|
+
<template>
|
|
169
|
+
<div :class="ns.b()">
|
|
170
|
+
<slot />
|
|
171
|
+
</div>
|
|
172
|
+
</template>
|
|
173
|
+
|
|
174
|
+
<script setup lang="ts">
|
|
175
|
+
import { useNamespace } from '@qxs-bns/hooks'
|
|
176
|
+
|
|
177
|
+
defineOptions({
|
|
178
|
+
name: 'QxsMyComponent'
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const ns = useNamespace('my-component')
|
|
182
|
+
</script>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
4. **样式文件**
|
|
186
|
+
```scss
|
|
187
|
+
// style/index.scss
|
|
188
|
+
@use 'sass:map';
|
|
189
|
+
@use '@qxs-bns/theme-chalk/src/mixins/mixins' as *;
|
|
190
|
+
@use '@qxs-bns/theme-chalk/src/common/var' as *;
|
|
191
|
+
|
|
192
|
+
@include b(my-component) {
|
|
193
|
+
// 组件样式
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
5. **入口文件**
|
|
198
|
+
```typescript
|
|
199
|
+
// index.ts
|
|
200
|
+
import { withInstall } from '@qxs-bns/utils'
|
|
201
|
+
import MyComponent from './src/my-component.vue'
|
|
202
|
+
|
|
203
|
+
export const QxsMyComponent = withInstall(MyComponent)
|
|
204
|
+
export default QxsMyComponent
|
|
205
|
+
|
|
206
|
+
export * from './src/my-component'
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## 📋 开发规范
|
|
210
|
+
|
|
211
|
+
### 组件命名规范
|
|
212
|
+
|
|
213
|
+
#### Element Plus 二次封装组件
|
|
214
|
+
- **组件名**:保持与 Element Plus 一致,添加 `Qxs` 前缀
|
|
215
|
+
- **文件名**:使用 kebab-case
|
|
216
|
+
- **样式类名**:使用 Element Plus 的 BEM 命名规范
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// ✅ 正确示例
|
|
220
|
+
export const QxsButton = withInstall(Button)
|
|
221
|
+
export const QxsInput = withInstall(Input)
|
|
222
|
+
|
|
223
|
+
// ❌ 错误示例
|
|
224
|
+
export const QxsBtn = withInstall(Button)
|
|
225
|
+
export const QxsInputField = withInstall(Input)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### 自定义组件
|
|
229
|
+
- **组件名**:使用 `@qxs-bns/hooks` 中的 `useNamespace` 函数
|
|
230
|
+
- **文件名**:使用 kebab-case
|
|
231
|
+
- **样式类名**:使用 BEM 命名规范
|
|
232
|
+
|
|
233
|
+
```vue
|
|
234
|
+
<script setup lang="ts">
|
|
235
|
+
import { useNamespace } from '@qxs-bns/hooks'
|
|
236
|
+
|
|
237
|
+
defineOptions({
|
|
238
|
+
name: 'QxsDataChart' // 组件名使用 PascalCase
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const ns = useNamespace('data-chart') // 使用 kebab-case
|
|
242
|
+
</script>
|
|
243
|
+
|
|
244
|
+
<template>
|
|
245
|
+
<div :class="ns.b()"> <!-- 生成 .qxs-data-chart -->
|
|
246
|
+
<div :class="ns.e('header')"> <!-- 生成 .qxs-data-chart__header -->
|
|
247
|
+
<div :class="ns.em('header', 'title')"> <!-- 生成 .qxs-data-chart__header--title -->
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Props 设计规范
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
// 使用 TypeScript 接口定义 Props
|
|
254
|
+
interface ButtonProps {
|
|
255
|
+
/**
|
|
256
|
+
* 按钮类型
|
|
257
|
+
* @default 'default'
|
|
258
|
+
*/
|
|
259
|
+
type?: 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'default'
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* 按钮尺寸
|
|
263
|
+
* @default 'default'
|
|
264
|
+
*/
|
|
265
|
+
size?: 'large' | 'default' | 'small'
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 是否禁用
|
|
269
|
+
* @default false
|
|
270
|
+
*/
|
|
271
|
+
disabled?: boolean
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* 是否加载中
|
|
275
|
+
* @default false
|
|
276
|
+
*/
|
|
277
|
+
loading?: boolean
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 使用 withDefaults 设置默认值
|
|
281
|
+
const props = withDefaults(defineProps<ButtonProps>(), {
|
|
282
|
+
type: 'default',
|
|
283
|
+
size: 'default',
|
|
284
|
+
disabled: false,
|
|
285
|
+
loading: false
|
|
286
|
+
})
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 事件设计规范
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// 定义事件类型
|
|
293
|
+
interface ButtonEmits {
|
|
294
|
+
/**
|
|
295
|
+
* 点击事件
|
|
296
|
+
* @param event 原生事件对象
|
|
297
|
+
*/
|
|
298
|
+
click: [event: MouseEvent]
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 焦点事件
|
|
302
|
+
* @param event 原生事件对象
|
|
303
|
+
*/
|
|
304
|
+
focus: [event: FocusEvent]
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 失焦事件
|
|
308
|
+
* @param event 原生事件对象
|
|
309
|
+
*/
|
|
310
|
+
blur: [event: FocusEvent]
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const emit = defineEmits<ButtonEmits>()
|
|
314
|
+
|
|
315
|
+
// 事件处理
|
|
316
|
+
const handleClick = (event: MouseEvent) => {
|
|
317
|
+
if (props.disabled || props.loading) return
|
|
318
|
+
emit('click', event)
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 样式规范
|
|
323
|
+
|
|
324
|
+
#### SCSS 变量使用
|
|
325
|
+
|
|
326
|
+
```scss
|
|
327
|
+
// 使用主题变量
|
|
328
|
+
@use '@qxs-bns/theme-chalk/src/common/var' as *;
|
|
329
|
+
|
|
330
|
+
@include b(button) {
|
|
331
|
+
// 使用 CSS 变量
|
|
332
|
+
background-color: var(#{getCssVarName('button', 'bg-color')});
|
|
333
|
+
border-color: var(#{getCssVarName('button', 'border-color')});
|
|
334
|
+
color: var(#{getCssVarName('button', 'text-color')});
|
|
335
|
+
|
|
336
|
+
// 状态样式
|
|
337
|
+
@include when(disabled) {
|
|
338
|
+
opacity: var(#{getCssVarName('disabled-opacity')});
|
|
339
|
+
cursor: not-allowed;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 修饰符样式
|
|
343
|
+
@include m(primary) {
|
|
344
|
+
background-color: var(#{getCssVarName('color-primary')});
|
|
345
|
+
border-color: var(#{getCssVarName('color-primary')});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### BEM 命名规范
|
|
351
|
+
|
|
352
|
+
```scss
|
|
353
|
+
// Block(块)
|
|
354
|
+
.qxs-button { }
|
|
355
|
+
|
|
356
|
+
// Element(元素)
|
|
357
|
+
.qxs-button__icon { }
|
|
358
|
+
.qxs-button__text { }
|
|
359
|
+
|
|
360
|
+
// Modifier(修饰符)
|
|
361
|
+
.qxs-button--primary { }
|
|
362
|
+
.qxs-button--large { }
|
|
363
|
+
|
|
364
|
+
// State(状态)
|
|
365
|
+
.qxs-button.is-disabled { }
|
|
366
|
+
.qxs-button.is-loading { }
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## 🧪 测试规范
|
|
370
|
+
|
|
371
|
+
### 单元测试
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// button.test.ts
|
|
375
|
+
import { mount } from '@vue/test-utils'
|
|
376
|
+
import { describe, it, expect } from 'vitest'
|
|
377
|
+
import Button from '../src/button.vue'
|
|
378
|
+
|
|
379
|
+
describe('Button', () => {
|
|
380
|
+
it('should render correctly', () => {
|
|
381
|
+
const wrapper = mount(Button, {
|
|
382
|
+
props: { type: 'primary' },
|
|
383
|
+
slots: { default: 'Click me' }
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
expect(wrapper.classes()).toContain('qxs-button--primary')
|
|
387
|
+
expect(wrapper.text()).toBe('Click me')
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
it('should emit click event', async () => {
|
|
391
|
+
const wrapper = mount(Button)
|
|
392
|
+
await wrapper.trigger('click')
|
|
393
|
+
|
|
394
|
+
expect(wrapper.emitted('click')).toHaveLength(1)
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
it('should not emit click when disabled', async () => {
|
|
398
|
+
const wrapper = mount(Button, {
|
|
399
|
+
props: { disabled: true }
|
|
400
|
+
})
|
|
401
|
+
await wrapper.trigger('click')
|
|
402
|
+
|
|
403
|
+
expect(wrapper.emitted('click')).toBeUndefined()
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### 组件快照测试
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
// button.snap.test.ts
|
|
412
|
+
import { mount } from '@vue/test-utils'
|
|
413
|
+
import { describe, it, expect } from 'vitest'
|
|
414
|
+
import Button from '../src/button.vue'
|
|
415
|
+
|
|
416
|
+
describe('Button Snapshots', () => {
|
|
417
|
+
it('should match snapshot with different types', () => {
|
|
418
|
+
const types = ['primary', 'success', 'warning', 'danger', 'info']
|
|
419
|
+
|
|
420
|
+
types.forEach(type => {
|
|
421
|
+
const wrapper = mount(Button, {
|
|
422
|
+
props: { type },
|
|
423
|
+
slots: { default: 'Button' }
|
|
424
|
+
})
|
|
425
|
+
expect(wrapper.html()).toMatchSnapshot(`button-${type}`)
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
})
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## 📖 文档规范
|
|
432
|
+
|
|
433
|
+
### 组件文档模板
|
|
434
|
+
|
|
435
|
+
每个组件都应该包含完整的 README.md 文档:
|
|
436
|
+
|
|
437
|
+
```markdown
|
|
438
|
+
# Button 按钮
|
|
439
|
+
|
|
440
|
+
常用的操作按钮。
|
|
441
|
+
|
|
442
|
+
## 基础用法
|
|
443
|
+
|
|
444
|
+
基础的按钮用法。
|
|
445
|
+
|
|
446
|
+
:::demo 使用 `type`、`plain`、`round` 和 `circle` 属性来定义 Button 的样式。
|
|
447
|
+
|
|
448
|
+
button/basic
|
|
449
|
+
|
|
450
|
+
:::
|
|
451
|
+
|
|
452
|
+
## API
|
|
453
|
+
|
|
454
|
+
### Button Attributes
|
|
455
|
+
|
|
456
|
+
| 属性名 | 说明 | 类型 | 可选值 | 默认值 |
|
|
457
|
+
|--------|------|------|--------|--------|
|
|
458
|
+
| type | 类型 | string | primary / success / warning / danger / info / text | — |
|
|
459
|
+
| size | 尺寸 | string | large / small / mini | — |
|
|
460
|
+
| disabled | 是否禁用状态 | boolean | — | false |
|
|
461
|
+
|
|
462
|
+
### Button Events
|
|
463
|
+
|
|
464
|
+
| 事件名 | 说明 | 参数 |
|
|
465
|
+
|--------|------|------|
|
|
466
|
+
| click | 点击时触发 | (event: Event) |
|
|
467
|
+
|
|
468
|
+
### Button Slots
|
|
469
|
+
|
|
470
|
+
| 插槽名 | 说明 |
|
|
471
|
+
|--------|------|
|
|
472
|
+
| — | 默认插槽 |
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
## 🤝 贡献指南
|
|
476
|
+
|
|
477
|
+
### 提交规范
|
|
478
|
+
|
|
479
|
+
使用 [Conventional Commits](https://conventionalcommits.org/) 规范:
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
# 新功能
|
|
483
|
+
feat(button): add loading state support
|
|
484
|
+
|
|
485
|
+
# 修复 bug
|
|
486
|
+
fix(data-chart): resolve chart rendering issue
|
|
487
|
+
|
|
488
|
+
# 文档更新
|
|
489
|
+
docs(readme): update installation guide
|
|
490
|
+
|
|
491
|
+
# 样式调整
|
|
492
|
+
style(button): adjust padding and margin
|
|
493
|
+
|
|
494
|
+
# 重构代码
|
|
495
|
+
refactor(utils): optimize namespace function
|
|
496
|
+
|
|
497
|
+
# 性能优化
|
|
498
|
+
perf(table): improve virtual scrolling performance
|
|
499
|
+
|
|
500
|
+
# 测试相关
|
|
501
|
+
test(button): add unit tests for disabled state
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### 开发流程
|
|
505
|
+
|
|
506
|
+
1. **Fork 项目**
|
|
507
|
+
2. **创建功能分支**
|
|
508
|
+
```bash
|
|
509
|
+
git checkout -b feat/new-component
|
|
510
|
+
```
|
|
511
|
+
3. **开发组件**
|
|
512
|
+
- 编写组件代码
|
|
513
|
+
- 添加单元测试
|
|
514
|
+
- 编写文档
|
|
515
|
+
4. **提交代码**
|
|
516
|
+
```bash
|
|
517
|
+
git add .
|
|
518
|
+
git commit -m "feat(new-component): add new component"
|
|
519
|
+
```
|
|
520
|
+
5. **推送分支**
|
|
521
|
+
```bash
|
|
522
|
+
git push origin feat/new-component
|
|
523
|
+
```
|
|
524
|
+
6. **创建 Pull Request**
|
|
525
|
+
|
|
526
|
+
### 代码审查清单
|
|
527
|
+
|
|
528
|
+
- [ ] 组件命名符合规范
|
|
529
|
+
- [ ] TypeScript 类型定义完整
|
|
530
|
+
- [ ] 单元测试覆盖率 > 80%
|
|
531
|
+
- [ ] 文档完整且示例可运行
|
|
532
|
+
- [ ] 样式符合设计规范
|
|
533
|
+
- [ ] 无障碍性支持
|
|
534
|
+
- [ ] 国际化支持
|
|
535
|
+
|
|
536
|
+
## 🔧 常见问题
|
|
537
|
+
|
|
538
|
+
### Q: 如何自定义主题?
|
|
539
|
+
|
|
540
|
+
A: 通过 CSS 变量覆盖默认主题:
|
|
541
|
+
|
|
542
|
+
```css
|
|
543
|
+
:root {
|
|
544
|
+
--qxs-color-primary: #409eff;
|
|
545
|
+
--qxs-color-success: #67c23a;
|
|
546
|
+
--qxs-color-warning: #e6a23c;
|
|
547
|
+
--qxs-color-danger: #f56c6c;
|
|
548
|
+
--qxs-color-info: #909399;
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Q: 如何按需引入组件?
|
|
553
|
+
|
|
554
|
+
A: 使用 ES6 模块语法:
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
import { QxsButton, QxsInput } from '@qxs-bns/components'
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Q: 如何贡献新组件?
|
|
561
|
+
|
|
562
|
+
A: 请参考 [贡献指南](#🤝-贡献指南) 部分。
|
|
563
|
+
|
|
564
|
+
## 📄 许可证
|
|
565
|
+
|
|
566
|
+
MIT License - 详见 [LICENSE](./LICENSE) 文件。
|
package/es/package.json.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var r="0.0.
|
|
1
|
+
var r="0.0.84";export{r as version};
|
|
2
2
|
//# sourceMappingURL=package.json.mjs.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{defineComponent as e,useAttrs as t,computed as
|
|
1
|
+
import{defineComponent as e,useAttrs as t,computed as s,ref as i,createElementBlock as l,openBlock as r,Fragment as a,createCommentVNode as n,createBlock as u,resolveDynamicComponent as o,mergeProps as c,unref as v,createElementVNode as f,createVNode as p,normalizeClass as y}from"vue";import{Icon as d}from"../../../node_modules/.pnpm/@iconify_vue@5.0.0_vue@3.5.18_typescript@5.9.2_/node_modules/@iconify/vue/dist/iconify.mjs";import{useNamespace as h}from"@qxs-bns/hooks";const m=["src"],g={"aria-hidden":"true",class:"size-inherit shrink-0"},k=["xlink:href"];var $=e({name:"QxsIcon",inheritAttrs:!1,__name:"icon",props:{icon:{type:null,required:!0},flip:{type:null,required:!1,default:""},rotate:{type:Number,required:!1,default:0},color:{type:String,required:!1},size:{type:[String,Number],required:!1},localIconPrefix:{type:String,required:!1,default:"icon-"},fallback:{type:String,required:!1},loading:{type:String,required:!1}},setup(e){const $=t(),b=s(()=>$.class||""),z=h("icon"),S=s(()=>"object"==typeof e.icon||"function"==typeof e.icon),q=s(()=>{if(S.value)return"component";const t=e.icon;return/^https?:\/\//.test(t)||(/^\.{1,2}\//.test(s=t)||s.startsWith("/")||s.includes("/"))?"img":/^i-[^:]+:[^:]+/.test(t)?"unocss":t.startsWith("i-")&&!t.includes(":")?"css":t.includes(":")&&!t.startsWith("i-")?"iconify":/^[\w-]+$/.test(t)&&!t.startsWith("i-")?"svg":"css";var s}),x=s(()=>{if(S.value)return"";const t=e.icon;return"img"===q.value||"unocss"===q.value||q.value,t}),_=s(()=>"css"===q.value?x.value.split(" ").filter(e=>e.trim()):[]);function j(e){return e?"number"==typeof e||/^\d+(?:\.\d+)?$/.test(e)?`${e}px`:e:""}const w=s(()=>{const t=[];switch(e.flip){case"horizontal":t.push("rotateY(180deg)");break;case"vertical":t.push("rotateX(180deg)");break;case"both":t.push("rotateX(180deg)"),t.push("rotateY(180deg)")}e.rotate&&t.push(`rotate(${e.rotate%360}deg)`);let s=`${e.color?`color: ${e.color};`:""}${e.size?`font-size: ${j(e.size)};`:""}${t.length?`transform: ${t.join(" ")};`:""}`;const i=$.style;if(i)if("string"==typeof i)s+=i;else if("object"==typeof i){s+=Object.entries(i).map(([e,t])=>`${e.replace(/([A-Z])/g,"-$1").toLowerCase()}: ${t};`).join("")}return s}),I=i(!0),W=i(!1);function C(){I.value=!1,W.value=!1}function A(){I.value=!1,W.value=!0}return(e,t)=>(r(),l(a,null,[n(" Vue组件 "),"component"===q.value?(r(),u(o(e.icon),c({key:0,class:[v(z).b(),b.value],style:[w.value]},e.$attrs,{class:"size-inherit shrink-0"}),null,16,["class","style"])):"unocss"===q.value?(r(),l(a,{key:1},[n(" UnoCSS图标 "),f("i",c({class:[v(z).b(),x.value,b.value],style:[w.value]},e.$attrs),null,16)],2112)):"css"===q.value?(r(),l(a,{key:2},[n(" CSS类图标 "),f("i",c({class:["size-inherit shrink-0",[v(z).b(),..._.value,b.value]],style:[w.value]},e.$attrs),null,16)],2112)):"iconify"===q.value?(r(),l(a,{key:3},[n(" Iconify图标 "),p(v(d),c({class:["size-inherit shrink-0",[v(z).b(),b.value]],icon:x.value,style:[w.value]},e.$attrs),null,16,["icon","class","style"])],2112)):"img"===q.value?(r(),l(a,{key:4},[n(" 图片 "),n(" 加载中状态 "),I.value&&e.loading?(r(),l("i",{key:0,class:y([e.loading,b.value])},null,2)):W.value&&e.fallback?(r(),l(a,{key:1},[n(" 错误状态 "),f("i",{class:y([e.fallback,b.value])},null,2)],2112)):n("v-if",!0),n(" 图片本体 "),f("img",c({src:x.value,class:"size-inherit shrink-0"},e.$attrs,{class:[v(z).b(),b.value],style:[w.value,{width:e.size?j(e.size):"auto",height:"auto"}],onLoad:C,onError:A}),null,16,m)],64)):x.value?(r(),l(a,{key:5},[n(" SVG Sprite "),f("i",c({class:[v(z).b(),b.value],style:[w.value]},e.$attrs),[(r(),l("svg",g,[f("use",{"xlink:href":`#${e.localIconPrefix}${x.value}`},null,8,k)]))],16)],2112)):n("v-if",!0)],2112))}});export{$ as default};
|
|
2
2
|
//# sourceMappingURL=icon.vue.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon.vue.mjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component, CSSProperties } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, ref, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n color,\n size,\n localIconPrefix = 'icon-',\n fallback,\n loading,\n} = defineProps<PropsType>()\n\nconst attrs = useAttrs()\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n // 新增功能:支持图片URL\n fallback?: string // 加载失败时的备用图标\n loading?: string // 加载中显示的图标\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n\n const iconStr = icon as string\n\n // 检测是否为图片URL或路径\n const hasPathFeatures = (str: string) => {\n return /^\\.{1,2}\\//.test(str) || str.startsWith('/') || str.includes('/')\n }\n if (/^https?:\\/\\//.test(iconStr) || hasPathFeatures(iconStr)) {\n return 'img'\n }\n\n // 检测UnoCSS图标格式 (i-[provider]:[name])\n if (/^i-[^:]+:[^:]+/.test(iconStr)) {\n return 'unocss'\n }\n\n // 检测以i-开头的自定义CSS类图标(不包含冒号)\n if (iconStr.startsWith('i-') && !iconStr.includes(':')) {\n return 'css'\n }\n\n // 检测Iconify格式 (provider:name,但不以i-开头)\n if (iconStr.includes(':') && !iconStr.startsWith('i-')) {\n return 'iconify'\n }\n\n // 检测SVG sprite(单个单词,不包含空格和特殊字符,且不以i-开头)\n if (/^[\\w-]+$/.test(iconStr) && !iconStr.startsWith('i-')) {\n return 'svg'\n }\n\n // 其他情况都视为CSS类图标(包括多个类名等)\n return 'css'\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n\n const iconStr = icon as string\n\n // 对于图片类型,直接返回URL\n if (outputType.value === 'img') {\n return iconStr\n }\n\n // 对于UnoCSS图标,直接返回类名\n if (outputType.value === 'unocss') {\n return iconStr\n }\n\n // 对于传统CSS图标,处理多个类名的情况\n if (outputType.value === 'css') {\n return iconStr\n }\n\n // 其他情况直接返回\n return iconStr\n})\n\n// 对于CSS类图标,将字符串拆分为类名数组\nconst cssClasses = computed(() => {\n if (outputType.value === 'css') {\n return outputName.value.split(' ').filter(cls => cls.trim())\n }\n return []\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n\n // 构建基础样式\n let baseStyle = `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n\n // 拼接 attrs.style\n const attrsStyle = attrs.style\n if (attrsStyle) {\n if (typeof attrsStyle === 'string') {\n baseStyle += attrsStyle\n }\n else if (typeof attrsStyle === 'object') {\n // 处理对象形式的样式\n const styleEntries = Object.entries(attrsStyle as CSSProperties)\n const styleString = styleEntries\n .map(([key, value]) => `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)\n .join('')\n baseStyle += styleString\n }\n }\n\n return baseStyle\n})\n\n// 图片加载状态管理\nconst imageLoading = ref(true)\nconst imageError = ref(false)\n\nfunction handleImageLoad() {\n imageLoading.value = false\n imageError.value = false\n}\n\nfunction handleImageError() {\n imageLoading.value = false\n imageError.value = true\n}\n</script>\n\n<template>\n <!-- Vue组件 -->\n <component\n :is=\"icon\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- UnoCSS图标 -->\n <i\n v-else-if=\"outputType === 'unocss'\"\n :class=\"[ns.b(), outputName]\"\n :style=\"[style]\"\n />\n\n <!-- CSS类图标 -->\n <i\n v-else-if=\"outputType === 'css'\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b(), ...cssClasses]\"\n :style=\"[style]\"\n />\n\n <!-- Iconify图标 -->\n <Icon\n v-else-if=\"outputType === 'iconify'\"\n class=\"size-inherit shrink-0\"\n :icon=\"outputName\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n />\n\n <!-- 图片 -->\n <template v-else-if=\"outputType === 'img'\">\n <!-- 加载中状态 -->\n <i\n v-if=\"imageLoading && loading\"\n :class=\"loading\"\n />\n <!-- 错误状态 -->\n <i\n v-else-if=\"imageError && fallback\"\n :class=\"fallback\"\n />\n <!-- 图片本体 -->\n <img\n :src=\"outputName\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b()]\"\n :style=\"[style, { width: size ? formatSize(size) : 'auto', height: 'auto' }]\"\n @load=\"handleImageLoad\"\n @error=\"handleImageError\"\n >\n </template>\n\n <!-- SVG Sprite -->\n <i\n v-else-if=\"outputName\"\n :class=\"[ns.b()]\"\n :style=\"[style]\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-inherit shrink-0\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n </i>\n</template>\n"],"names":["attrs","useAttrs","ns","useNamespace","isComponentName","computed","__props","icon","outputType","value","iconStr","test","str","startsWith","includes","outputName","cssClasses","split","filter","cls","trim","formatSize","size","style","transform","flip","push","rotate","baseStyle","color","length","join","attrsStyle","Object","entries","map","key","replace","toLowerCase","imageLoading","ref","imageError","handleImageLoad","handleImageError","_createCommentVNode","_openBlock","_createBlock","_resolveDynamicComponent","class","_normalizeClass","_unref","b","_createElementBlock","_Fragment","_createElementVNode","_createVNode","Icon","loading","fallback","src","width","height","onLoad","onError","_hoisted_2","localIconPrefix"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"icon.vue.mjs","sources":["../../../../../../packages/components/src/icon/src/icon.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { Component, CSSProperties } from 'vue'\nimport { Icon } from '@iconify/vue'\nimport { useNamespace } from '@qxs-bns/hooks'\nimport { computed, ref, useAttrs } from 'vue'\n\ndefineOptions({\n name: 'QxsIcon',\n inheritAttrs: false,\n})\n\nconst {\n icon,\n flip = '',\n rotate = 0,\n color,\n size,\n localIconPrefix = 'icon-',\n fallback,\n loading,\n} = defineProps<PropsType>()\n\n// 支持 attrs 传递给图标组件\nconst attrs = useAttrs()\n\n// 手动提取 class,因为多根组件不会自动继承\nconst externalClass = computed(() => attrs.class || '')\n\nconst ns = useNamespace('icon')\n\nexport interface PropsType {\n icon: string | Component\n flip?: 'horizontal' | 'vertical' | 'both' | '' | undefined\n rotate?: number\n color?: string\n size?: string | number\n localIconPrefix?: string\n // 新增功能:支持图片URL\n fallback?: string // 加载失败时的备用图标\n loading?: string // 加载中显示的图标\n}\n\nconst isComponentName = computed(() => typeof icon === 'object' || typeof icon === 'function')\n\nconst outputType = computed(() => {\n if (isComponentName.value) {\n return 'component'\n }\n\n const iconStr = icon as string\n\n // 检测是否为图片URL或路径\n const hasPathFeatures = (str: string) => {\n return /^\\.{1,2}\\//.test(str) || str.startsWith('/') || str.includes('/')\n }\n if (/^https?:\\/\\//.test(iconStr) || hasPathFeatures(iconStr)) {\n return 'img'\n }\n\n // 检测UnoCSS图标格式 (i-[provider]:[name])\n if (/^i-[^:]+:[^:]+/.test(iconStr)) {\n return 'unocss'\n }\n\n // 检测以i-开头的自定义CSS类图标(不包含冒号)\n if (iconStr.startsWith('i-') && !iconStr.includes(':')) {\n return 'css'\n }\n\n // 检测Iconify格式 (provider:name,但不以i-开头)\n if (iconStr.includes(':') && !iconStr.startsWith('i-')) {\n return 'iconify'\n }\n\n // 检测SVG sprite(单个单词,不包含空格和特殊字符,且不以i-开头)\n if (/^[\\w-]+$/.test(iconStr) && !iconStr.startsWith('i-')) {\n return 'svg'\n }\n\n // 其他情况都视为CSS类图标(包括多个类名等)\n return 'css'\n})\n\nconst outputName = computed(() => {\n if (isComponentName.value) {\n return ''\n }\n\n const iconStr = icon as string\n\n // 对于图片类型,直接返回URL\n if (outputType.value === 'img') {\n return iconStr\n }\n\n // 对于UnoCSS图标,直接返回类名\n if (outputType.value === 'unocss') {\n return iconStr\n }\n\n // 对于传统CSS图标,处理多个类名的情况\n if (outputType.value === 'css') {\n return iconStr\n }\n\n // 其他情况直接返回\n return iconStr\n})\n\n// 对于CSS类图标,将字符串拆分为类名数组\nconst cssClasses = computed(() => {\n if (outputType.value === 'css') {\n return outputName.value.split(' ').filter(cls => cls.trim())\n }\n return []\n})\n\n// 用正则匹配 size 是不是 number 值,再判断是否有 px 结尾的单位,没有则拼接\nfunction formatSize(size: string | number | undefined): string {\n if (!size) {\n return ''\n }\n\n // 如果是数字,直接添加 px\n if (typeof size === 'number') {\n return `${size}px`\n }\n\n // 如果是纯数字字符串,添加 px\n if (/^\\d+(?:\\.\\d+)?$/.test(size)) {\n return `${size}px`\n }\n\n // 如果已经有单位(px、em、rem等)或者不是纯数字,直接返回\n return size\n}\n\n// 统一的样式计算属性,适用于所有图标类型\n// 包含颜色、尺寸和变换(旋转、翻转)\n// Iconify 图标通过外层 i 标签应用这些样式\nconst style = computed(() => {\n const transform = [] as string[]\n switch (flip) {\n case 'horizontal':\n transform.push('rotateY(180deg)')\n break\n case 'vertical':\n transform.push('rotateX(180deg)')\n break\n case 'both':\n transform.push('rotateX(180deg)')\n transform.push('rotateY(180deg)')\n break\n // 对于 '' 和 undefined,不做任何处理\n }\n if (rotate) {\n transform.push(`rotate(${rotate % 360}deg)`)\n }\n\n // 构建基础样式\n let baseStyle = `${color ? `color: ${color};` : ''}${size ? `font-size: ${formatSize(size)};` : ''}${transform.length ? `transform: ${transform.join(' ')};` : ''}`\n\n // 拼接 attrs.style\n const attrsStyle = attrs.style\n if (attrsStyle) {\n if (typeof attrsStyle === 'string') {\n baseStyle += attrsStyle\n }\n else if (typeof attrsStyle === 'object') {\n // 处理对象形式的样式\n const styleEntries = Object.entries(attrsStyle as CSSProperties)\n const styleString = styleEntries\n .map(([key, value]) => `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}: ${value};`)\n .join('')\n baseStyle += styleString\n }\n }\n\n return baseStyle\n})\n\n// 图片加载状态管理\nconst imageLoading = ref(true)\nconst imageError = ref(false)\n\nfunction handleImageLoad() {\n imageLoading.value = false\n imageError.value = false\n}\n\nfunction handleImageError() {\n imageLoading.value = false\n imageError.value = true\n}\n</script>\n\n<template>\n <!-- Vue组件 -->\n <component\n :is=\"icon\"\n v-if=\"outputType === 'component'\"\n :class=\"[ns.b(), externalClass]\"\n :style=\"[style]\"\n v-bind=\"$attrs\"\n class=\"size-inherit shrink-0\"\n />\n\n <!-- UnoCSS图标 -->\n <i\n v-else-if=\"outputType === 'unocss'\"\n :class=\"[ns.b(), outputName, externalClass]\"\n :style=\"[style]\"\n v-bind=\"$attrs\"\n />\n\n <!-- CSS类图标 -->\n <i\n v-else-if=\"outputType === 'css'\"\n class=\"size-inherit shrink-0\"\n :class=\"[ns.b(), ...cssClasses, externalClass]\"\n :style=\"[style]\"\n v-bind=\"$attrs\"\n />\n\n <!-- Iconify图标 -->\n <Icon\n v-else-if=\"outputType === 'iconify'\"\n class=\"size-inherit shrink-0\"\n :icon=\"outputName\"\n :class=\"[ns.b(), externalClass]\"\n :style=\"[style]\"\n v-bind=\"$attrs\"\n />\n\n <!-- 图片 -->\n <template v-else-if=\"outputType === 'img'\">\n <!-- 加载中状态 -->\n <i\n v-if=\"imageLoading && loading\"\n :class=\"[loading, externalClass]\"\n />\n <!-- 错误状态 -->\n <i\n v-else-if=\"imageError && fallback\"\n :class=\"[fallback, externalClass]\"\n />\n <!-- 图片本体 -->\n <img\n :src=\"outputName\"\n class=\"size-inherit shrink-0\"\n v-bind=\"$attrs\"\n :class=\"[ns.b(), externalClass]\"\n :style=\"[style, { width: size ? formatSize(size) : 'auto', height: 'auto' }]\"\n @load=\"handleImageLoad\"\n @error=\"handleImageError\"\n >\n </template>\n\n <!-- SVG Sprite -->\n <i\n v-else-if=\"outputName\"\n :class=\"[ns.b(), externalClass]\"\n :style=\"[style]\"\n v-bind=\"$attrs\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-inherit shrink-0\"\n >\n <use :xlink:href=\"`#${localIconPrefix}${outputName}`\" />\n </svg>\n </i>\n</template>\n"],"names":["attrs","useAttrs","externalClass","computed","class","ns","useNamespace","isComponentName","__props","icon","outputType","value","iconStr","test","str","startsWith","includes","outputName","cssClasses","split","filter","cls","trim","formatSize","size","style","transform","flip","push","rotate","baseStyle","color","length","join","attrsStyle","Object","entries","map","key","replace","toLowerCase","imageLoading","ref","imageError","handleImageLoad","handleImageError","_createCommentVNode","_createBlock","_resolveDynamicComponent","_mergeProps","_unref","b","$attrs","_createElementBlock","_Fragment","_createElementVNode","_createVNode","loading","_normalizeClass","fallback","src","width","height","onLoad","onError","_openBlock","_hoisted_2","localIconPrefix"],"mappings":"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"}
|
package/lib/package.json.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";exports.version="0.0.
|
|
1
|
+
"use strict";exports.version="0.0.84";
|
|
2
2
|
//# sourceMappingURL=package.json.cjs.map
|