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.
@@ -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
- <TCheckbox
224
+ <view
226
225
  v-for="opt in (item.componentProps?.options as FormOption[])"
227
226
  :key="opt.value"
228
- :label="opt.label"
229
- :value="opt.value"
230
- :checked="(model[item.field] || []).includes(opt.value)"
231
- v-bind="item.componentProps || {}"
232
- @change="(checked) => {
233
- const values = model[item.field] || []
234
- if (checked) {
235
- values.push(opt.value)
236
- } else {
237
- const index = values.indexOf(opt.value)
238
- if (index > -1) values.splice(index, 1)
239
- }
240
- onCheckboxChange(values, item)
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
- if (isOptionsMode.value) {
65
+ // 优先使用 model.value 判断
66
+ if (model.value !== undefined && model.value !== null) {
64
67
  return model.value === props.value
65
68
  }
66
- return props.checked || model.value === props.value
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
- handleChange(props.value!)
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 props.options"
108
+ v-for="option in options"
103
109
  :key="option.value"
104
110
  class="radio-item"
105
- :class="{ 'radio-disabled': props.disabled }"
106
- @click="!props.disabled && handleChange(option.value)"
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': props.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': props.disabled }]" @click="handleClick">
117
- <view class="radio-icon" :class="{ active: isChecked, 'radio-disabled': props.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="props.value !== undefined" class="radio-label">{{ props.value }}</text>
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
- &.radio-disabled {
140
- opacity: 0.5;
141
- cursor: not-allowed;
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
- &.active {
156
- border-color: var(--radio-active-color);
157
- }
167
+ .radio-icon.active {
168
+ border-color: var(--radio-active-color);
169
+ }
158
170
 
159
- &.radio-disabled {
160
- border-color: #dcdfe6;
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
- .radio-icon {
173
- width: 28rpx;
174
- height: 28rpx;
175
- }
182
+ .radio-size-small .radio-icon {
183
+ width: 28rpx;
184
+ height: 28rpx;
185
+ }
176
186
 
177
- .radio-dot {
178
- width: 14rpx;
179
- height: 14rpx;
180
- }
187
+ .radio-size-small .radio-dot {
188
+ width: 14rpx;
189
+ height: 14rpx;
190
+ }
181
191
 
182
- .radio-label {
183
- font-size: 24rpx;
184
- }
192
+ .radio-size-small .radio-label {
193
+ font-size: 24rpx;
185
194
  }
186
195
 
187
- .radio-size-medium {
188
- .radio-icon {
189
- width: 36rpx;
190
- height: 36rpx;
191
- }
196
+ .radio-size-medium .radio-icon {
197
+ width: 36rpx;
198
+ height: 36rpx;
199
+ }
192
200
 
193
- .radio-dot {
194
- width: 20rpx;
195
- height: 20rpx;
196
- }
201
+ .radio-size-medium .radio-dot {
202
+ width: 20rpx;
203
+ height: 20rpx;
204
+ }
197
205
 
198
- .radio-label {
199
- font-size: 28rpx;
200
- }
206
+ .radio-size-medium .radio-label {
207
+ font-size: 28rpx;
201
208
  }
202
209
 
203
- .radio-size-large {
204
- .radio-icon {
205
- width: 44rpx;
206
- height: 44rpx;
207
- }
210
+ .radio-size-large .radio-icon {
211
+ width: 44rpx;
212
+ height: 44rpx;
213
+ }
208
214
 
209
- .radio-dot {
210
- width: 26rpx;
211
- height: 26rpx;
212
- }
215
+ .radio-size-large .radio-dot {
216
+ width: 26rpx;
217
+ height: 26rpx;
218
+ }
213
219
 
214
- .radio-label {
215
- font-size: 32rpx;
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
- .radio-item.active & {
225
- color: var(--radio-active-color);
226
- }
230
+ .radio-item.active .radio-label {
231
+ color: var(--radio-active-color);
232
+ }
227
233
 
228
- .radio-disabled & {
229
- color: #c0c4cc;
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.0",
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
- "build": "echo 'Build completed'",
46
- "version": "pnpm run build"
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
  }