tang-ui-x 1.1.0 → 1.1.1
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/components/TForm/index.uvue +30 -17
- package/components/TRadioButton/README.md +117 -0
- package/components/TRadioButton/index.uvue +69 -64
- package/package.json +48 -47
|
@@ -213,32 +213,33 @@
|
|
|
213
213
|
<TRadioButton
|
|
214
214
|
v-for="opt in (item.componentProps?.options as FormOption[])"
|
|
215
215
|
:key="opt.value"
|
|
216
|
+
v-model="model[item.field]"
|
|
216
217
|
:label="opt.label"
|
|
217
218
|
:value="opt.value"
|
|
218
|
-
:checked="model[item.field] === opt.value"
|
|
219
|
-
v-bind="item.componentProps || {}"
|
|
220
219
|
@change="onRadioChange(opt.value, item)" />
|
|
221
220
|
</view>
|
|
222
221
|
|
|
223
222
|
<!-- 多选 -->
|
|
224
223
|
<view v-else-if="item.component === 'Checkbox'" class="checkbox-group">
|
|
225
|
-
<
|
|
224
|
+
<view
|
|
226
225
|
v-for="opt in (item.componentProps?.options as FormOption[])"
|
|
227
226
|
:key="opt.value"
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
227
|
+
class="checkbox-item">
|
|
228
|
+
<TCheckbox
|
|
229
|
+
:checked="(model[item.field] || []).includes(opt.value)"
|
|
230
|
+
v-bind="item.componentProps || {}"
|
|
231
|
+
@change="(checked) => {
|
|
232
|
+
const values = model[item.field] || []
|
|
233
|
+
if (checked) {
|
|
234
|
+
values.push(opt.value)
|
|
235
|
+
} else {
|
|
236
|
+
const index = values.indexOf(opt.value)
|
|
237
|
+
if (index > -1) values.splice(index, 1)
|
|
238
|
+
}
|
|
239
|
+
onCheckboxChange(values, item)
|
|
240
|
+
}" />
|
|
241
|
+
<text class="checkbox-label">{{ opt.label }}</text>
|
|
242
|
+
</view>
|
|
242
243
|
</view>
|
|
243
244
|
|
|
244
245
|
<!-- 开关 -->
|
|
@@ -338,6 +339,18 @@
|
|
|
338
339
|
gap: 16rpx;
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
.checkbox-item {
|
|
343
|
+
display: flex;
|
|
344
|
+
flex-direction: row;
|
|
345
|
+
align-items: center;
|
|
346
|
+
gap: 16rpx;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.checkbox-label {
|
|
350
|
+
font-size: 28rpx;
|
|
351
|
+
color: #333;
|
|
352
|
+
}
|
|
353
|
+
|
|
341
354
|
.error-message {
|
|
342
355
|
margin-top: 8rpx;
|
|
343
356
|
font-size: 24rpx;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# TRadioButton 单选按钮
|
|
2
|
+
|
|
3
|
+
单选按钮组件,支持单个按钮和选项组两种使用方式。
|
|
4
|
+
|
|
5
|
+
## 基础用法
|
|
6
|
+
|
|
7
|
+
```vue
|
|
8
|
+
<template>
|
|
9
|
+
<TRadioButton v-model="value" value="option1">选项一</TRadioButton>
|
|
10
|
+
<TRadioButton v-model="value" value="option2">选项二</TRadioButton>
|
|
11
|
+
<TRadioButton v-model="value" value="option3">选项三</TRadioButton>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import { ref } from 'vue'
|
|
16
|
+
const value = ref('option1')
|
|
17
|
+
</script>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 选项组模式
|
|
21
|
+
|
|
22
|
+
通过 `options` 属性传入选项数组,自动渲染多个单选按钮。
|
|
23
|
+
|
|
24
|
+
```vue
|
|
25
|
+
<template>
|
|
26
|
+
<TRadioButton
|
|
27
|
+
v-model="value"
|
|
28
|
+
:options="[
|
|
29
|
+
{ label: '选项一', value: 'option1' },
|
|
30
|
+
{ label: '选项二', value: 'option2' },
|
|
31
|
+
{ label: '选项三', value: 'option3' }
|
|
32
|
+
]"
|
|
33
|
+
/>
|
|
34
|
+
</template>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 禁用状态
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<TRadioButton v-model="value" value="option1" disabled>禁用选项</TRadioButton>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 不同尺寸
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<TRadioButton v-model="value" value="small" size="small">小尺寸</TRadioButton>
|
|
47
|
+
<TRadioButton v-model="value" value="medium" size="medium">中等尺寸</TRadioButton>
|
|
48
|
+
<TRadioButton v-model="value" value="large" size="large">大尺寸</TRadioButton>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 自定义颜色
|
|
52
|
+
|
|
53
|
+
```vue
|
|
54
|
+
<TRadioButton
|
|
55
|
+
v-model="value"
|
|
56
|
+
value="option1"
|
|
57
|
+
activeColor="#f56c6c"
|
|
58
|
+
inactiveColor="#909399"
|
|
59
|
+
>
|
|
60
|
+
自定义颜色
|
|
61
|
+
</TRadioButton>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
67
|
+
|------|------|------|--------|
|
|
68
|
+
| modelValue (v-model) | 绑定值 | `string \| number` | - |
|
|
69
|
+
| value | 单选按钮的值 | `string \| number` | - |
|
|
70
|
+
| label | 显示的标签文本 | `string` | - |
|
|
71
|
+
| options | 选项组模式的选项列表 | `FormOption[]` | `[]` |
|
|
72
|
+
| size | 按钮尺寸 | `'small' \| 'medium' \| 'large'` | `'medium'` |
|
|
73
|
+
| activeColor | 激活状态颜色 | `string` | `'#00bba7'` |
|
|
74
|
+
| inactiveColor | 非激活状态颜色 | `string` | `'#666666'` |
|
|
75
|
+
| disabled | 是否禁用 | `boolean` | `false` |
|
|
76
|
+
| checked | 是否选中(仅单个按钮模式) | `boolean` | `false` |
|
|
77
|
+
| name | 原生 name 属性 | `string` | - |
|
|
78
|
+
|
|
79
|
+
## Events
|
|
80
|
+
|
|
81
|
+
| 事件名 | 说明 | 回调参数 |
|
|
82
|
+
|--------|------|----------|
|
|
83
|
+
| change | 选中值改变时触发 | `(value: string \| number)` |
|
|
84
|
+
|
|
85
|
+
## FormOption 类型
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
type FormOption = {
|
|
89
|
+
label: string
|
|
90
|
+
value: string | number
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 在 TForm 中使用
|
|
95
|
+
|
|
96
|
+
TRadioButton 可以在 TForm 组件中使用:
|
|
97
|
+
|
|
98
|
+
```vue
|
|
99
|
+
<TForm v-model="formData" :schemas="schemas" />
|
|
100
|
+
|
|
101
|
+
<script setup>
|
|
102
|
+
const schemas = [
|
|
103
|
+
{
|
|
104
|
+
field: 'gender',
|
|
105
|
+
label: '性别',
|
|
106
|
+
component: 'Radio',
|
|
107
|
+
required: true,
|
|
108
|
+
componentProps: {
|
|
109
|
+
options: [
|
|
110
|
+
{ label: '男', value: 'male' },
|
|
111
|
+
{ label: '女', value: 'female' }
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
@@ -14,6 +14,8 @@ type Props = {
|
|
|
14
14
|
options?: FormOption[]
|
|
15
15
|
/** 单个按钮模式:选项值 */
|
|
16
16
|
value?: string | number
|
|
17
|
+
/** 单个按钮模式:显示标签 */
|
|
18
|
+
label?: string
|
|
17
19
|
/** 按钮尺寸 */
|
|
18
20
|
size?: 'small' | 'medium' | 'large'
|
|
19
21
|
/** 活动状态颜色 */
|
|
@@ -60,14 +62,16 @@ const isOptionsMode = computed(() => props.options && props.options.length > 0)
|
|
|
60
62
|
* 单个按钮模式:判断是否被选中
|
|
61
63
|
*/
|
|
62
64
|
const isChecked = computed(() => {
|
|
63
|
-
|
|
65
|
+
// 优先使用 model.value 判断
|
|
66
|
+
if (model.value !== undefined && model.value !== null) {
|
|
64
67
|
return model.value === props.value
|
|
65
68
|
}
|
|
66
|
-
|
|
69
|
+
// 如果没有 model.value,使用 checked 属性
|
|
70
|
+
return props.checked === true
|
|
67
71
|
})
|
|
68
72
|
|
|
69
73
|
/**
|
|
70
|
-
|
|
74
|
+
* 处理变化
|
|
71
75
|
*/
|
|
72
76
|
const handleChange = (value: string | number) => {
|
|
73
77
|
model.value = value
|
|
@@ -79,7 +83,9 @@ const handleChange = (value: string | number) => {
|
|
|
79
83
|
*/
|
|
80
84
|
const handleClick = () => {
|
|
81
85
|
if (props.disabled || isOptionsMode.value) return
|
|
82
|
-
|
|
86
|
+
if (props.value !== undefined) {
|
|
87
|
+
handleChange(props.value)
|
|
88
|
+
}
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
/**
|
|
@@ -99,26 +105,26 @@ const sizeClass = computed(() => {
|
|
|
99
105
|
<!-- 选项组模式:使用 options 属性 -->
|
|
100
106
|
<view v-if="isOptionsMode" class="radio-group" :style="cssVars">
|
|
101
107
|
<view
|
|
102
|
-
v-for="option in
|
|
108
|
+
v-for="option in options"
|
|
103
109
|
:key="option.value"
|
|
104
110
|
class="radio-item"
|
|
105
|
-
:class="{ 'radio-disabled':
|
|
106
|
-
@click="!
|
|
111
|
+
:class="{ 'radio-disabled': disabled }"
|
|
112
|
+
@click="!disabled && handleChange(option.value)"
|
|
107
113
|
>
|
|
108
|
-
<view class="radio-icon" :class="{ active: model === option.value, 'radio-disabled':
|
|
109
|
-
<view v-if="model === option.value" class="radio-dot"></view>
|
|
114
|
+
<view class="radio-icon" :class="{ active: model.value === option.value, 'radio-disabled': disabled }">
|
|
115
|
+
<view v-if="model.value === option.value" class="radio-dot"></view>
|
|
110
116
|
</view>
|
|
111
117
|
<text class="radio-label">{{ option.label }}</text>
|
|
112
118
|
</view>
|
|
113
119
|
</view>
|
|
114
120
|
|
|
115
121
|
<!-- 单个按钮模式:使用插槽或默认内容 -->
|
|
116
|
-
<view v-else class="radio-item" :class="[sizeClass, { 'radio-disabled':
|
|
117
|
-
<view class="radio-icon" :class="{ active: isChecked, 'radio-disabled':
|
|
122
|
+
<view v-else class="radio-item" :class="[sizeClass, { active: isChecked, 'radio-disabled': disabled }]" :style="cssVars" @tap="handleClick">
|
|
123
|
+
<view class="radio-icon" :class="{ active: isChecked, 'radio-disabled': disabled }">
|
|
118
124
|
<view v-if="isChecked" class="radio-dot"></view>
|
|
119
125
|
</view>
|
|
120
126
|
<slot>
|
|
121
|
-
<text v-if="
|
|
127
|
+
<text v-if="label || value !== undefined" class="radio-label">{{ label || value }}</text>
|
|
122
128
|
</slot>
|
|
123
129
|
</view>
|
|
124
130
|
</template>
|
|
@@ -126,24 +132,29 @@ const sizeClass = computed(() => {
|
|
|
126
132
|
<style lang="scss" scoped>
|
|
127
133
|
/* 选项组容器 */
|
|
128
134
|
.radio-group {
|
|
135
|
+
display: flex;
|
|
129
136
|
flex-direction: column;
|
|
130
137
|
gap: 16rpx;
|
|
131
138
|
}
|
|
132
139
|
|
|
133
140
|
/* 单个按钮容器 */
|
|
134
141
|
.radio-item {
|
|
142
|
+
display: flex;
|
|
135
143
|
flex-direction: row;
|
|
136
144
|
align-items: center;
|
|
137
145
|
gap: 16rpx;
|
|
146
|
+
padding: 8rpx 0;
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
}
|
|
138
149
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
150
|
+
.radio-item.radio-disabled {
|
|
151
|
+
opacity: 0.5;
|
|
152
|
+
cursor: not-allowed;
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/* 单选按钮图标 */
|
|
146
156
|
.radio-icon {
|
|
157
|
+
display: flex;
|
|
147
158
|
justify-content: center;
|
|
148
159
|
align-items: center;
|
|
149
160
|
border-radius: 50%;
|
|
@@ -151,14 +162,14 @@ const sizeClass = computed(() => {
|
|
|
151
162
|
border-style: solid;
|
|
152
163
|
border-color: var(--radio-inactive-color);
|
|
153
164
|
transition: all 0.2s ease;
|
|
165
|
+
}
|
|
154
166
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
.radio-icon.active {
|
|
168
|
+
border-color: var(--radio-active-color);
|
|
169
|
+
}
|
|
158
170
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
171
|
+
.radio-icon.radio-disabled {
|
|
172
|
+
border-color: #dcdfe6;
|
|
162
173
|
}
|
|
163
174
|
|
|
164
175
|
/* 单选点 */
|
|
@@ -168,65 +179,59 @@ const sizeClass = computed(() => {
|
|
|
168
179
|
}
|
|
169
180
|
|
|
170
181
|
/* 尺寸样式 */
|
|
171
|
-
.radio-size-small {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
182
|
+
.radio-size-small .radio-icon {
|
|
183
|
+
width: 28rpx;
|
|
184
|
+
height: 28rpx;
|
|
185
|
+
}
|
|
176
186
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
187
|
+
.radio-size-small .radio-dot {
|
|
188
|
+
width: 14rpx;
|
|
189
|
+
height: 14rpx;
|
|
190
|
+
}
|
|
181
191
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
192
|
+
.radio-size-small .radio-label {
|
|
193
|
+
font-size: 24rpx;
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
.radio-size-medium {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
196
|
+
.radio-size-medium .radio-icon {
|
|
197
|
+
width: 36rpx;
|
|
198
|
+
height: 36rpx;
|
|
199
|
+
}
|
|
192
200
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
.radio-size-medium .radio-dot {
|
|
202
|
+
width: 20rpx;
|
|
203
|
+
height: 20rpx;
|
|
204
|
+
}
|
|
197
205
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
206
|
+
.radio-size-medium .radio-label {
|
|
207
|
+
font-size: 28rpx;
|
|
201
208
|
}
|
|
202
209
|
|
|
203
|
-
.radio-size-large {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
210
|
+
.radio-size-large .radio-icon {
|
|
211
|
+
width: 44rpx;
|
|
212
|
+
height: 44rpx;
|
|
213
|
+
}
|
|
208
214
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
215
|
+
.radio-size-large .radio-dot {
|
|
216
|
+
width: 26rpx;
|
|
217
|
+
height: 26rpx;
|
|
218
|
+
}
|
|
213
219
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
220
|
+
.radio-size-large .radio-label {
|
|
221
|
+
font-size: 32rpx;
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
/* 文本标签 */
|
|
220
225
|
.radio-label {
|
|
221
226
|
color: var(--radio-inactive-color);
|
|
222
227
|
transition: color 0.2s ease;
|
|
228
|
+
}
|
|
223
229
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
230
|
+
.radio-item.active .radio-label {
|
|
231
|
+
color: var(--radio-active-color);
|
|
232
|
+
}
|
|
227
233
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
234
|
+
.radio-item.radio-disabled .radio-label {
|
|
235
|
+
color: #c0c4cc;
|
|
231
236
|
}
|
|
232
237
|
</style>
|
package/package.json
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "tang-ui-x",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "UniApp X UI 组件库 - 基于 uni-app x 的移动端 UI 组件库",
|
|
5
|
-
"main": "index.uts",
|
|
6
|
-
"module": "index.uts",
|
|
7
|
-
"types": "types/index.d.ts",
|
|
8
|
-
"files": [
|
|
9
|
-
"components",
|
|
10
|
-
"composables",
|
|
11
|
-
"utils",
|
|
12
|
-
"types",
|
|
13
|
-
"style",
|
|
14
|
-
"uni.scss",
|
|
15
|
-
"index.uts",
|
|
16
|
-
"README.md",
|
|
17
|
-
"LICENSE"
|
|
18
|
-
],
|
|
19
|
-
"keywords": [
|
|
20
|
-
"uniapp",
|
|
21
|
-
"uni-app-x",
|
|
22
|
-
"uniapp-x",
|
|
23
|
-
"ui",
|
|
24
|
-
"components",
|
|
25
|
-
"mobile",
|
|
26
|
-
"vue3",
|
|
27
|
-
"typescript",
|
|
28
|
-
"tang-ui"
|
|
29
|
-
],
|
|
30
|
-
"author": "sugar258596",
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"repository": {
|
|
33
|
-
"type": "git",
|
|
34
|
-
"url": "https://github.com/sugar258596/tang-ui.git"
|
|
35
|
-
},
|
|
36
|
-
"bugs": {
|
|
37
|
-
"url": "https://github.com/sugar258596/tang-ui/issues"
|
|
38
|
-
},
|
|
39
|
-
"homepage": "https://github.com/sugar258596/tang-ui#readme",
|
|
40
|
-
"peerDependencies": {
|
|
41
|
-
"vue": "^3.0.0"
|
|
42
|
-
},
|
|
43
|
-
"scripts": {
|
|
44
|
-
"preinstall": "npx only-allow pnpm",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "tang-ui-x",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "UniApp X UI 组件库 - 基于 uni-app x 的移动端 UI 组件库",
|
|
5
|
+
"main": "index.uts",
|
|
6
|
+
"module": "index.uts",
|
|
7
|
+
"types": "types/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"components",
|
|
10
|
+
"composables",
|
|
11
|
+
"utils",
|
|
12
|
+
"types",
|
|
13
|
+
"style",
|
|
14
|
+
"uni.scss",
|
|
15
|
+
"index.uts",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"uniapp",
|
|
21
|
+
"uni-app-x",
|
|
22
|
+
"uniapp-x",
|
|
23
|
+
"ui",
|
|
24
|
+
"components",
|
|
25
|
+
"mobile",
|
|
26
|
+
"vue3",
|
|
27
|
+
"typescript",
|
|
28
|
+
"tang-ui"
|
|
29
|
+
],
|
|
30
|
+
"author": "sugar258596",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/sugar258596/tang-ui.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/sugar258596/tang-ui/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/sugar258596/tang-ui#readme",
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"vue": "^3.0.0"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"preinstall": "npx only-allow pnpm",
|
|
45
|
+
"prepublishOnly": "pnpm run build",
|
|
46
|
+
"build": "echo 'Build completed'",
|
|
47
|
+
"version": "pnpm run build"
|
|
48
|
+
}
|
|
48
49
|
}
|