@ecolovo/uniapp-components 1.0.10 → 2.0.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.
Files changed (130) hide show
  1. package/README.md +202 -66
  2. package/components/aspect/aspect.vue +24 -15
  3. package/components/badge/badge.scss +20 -0
  4. package/components/badge/badge.vue +49 -40
  5. package/components/button/button.scss +15 -0
  6. package/components/button/button.vue +121 -177
  7. package/components/checkbox/checkbox.scss +10 -0
  8. package/components/checkbox/checkbox.vue +118 -146
  9. package/components/checkbox-group/checkbox-group.vue +79 -85
  10. package/components/checkbox-group2/checkbox-group2.vue +111 -107
  11. package/components/checkbox-group2/checkbox2.vue +104 -0
  12. package/components/data-list/data-list.scss +9 -0
  13. package/components/data-list/data-list.vue +70 -79
  14. package/components/date-picker/date-picker-view.vue +125 -124
  15. package/components/date-picker/date-picker.vue +145 -146
  16. package/components/dialog/dialog-content.vue +39 -47
  17. package/components/dialog/dialog.scss +11 -0
  18. package/components/dialog/dialog.vue +125 -138
  19. package/components/dropdown/dropdown.scss +11 -0
  20. package/components/dropdown/dropdown.vue +55 -97
  21. package/components/empty/empty.scss +14 -0
  22. package/components/empty/empty.vue +42 -41
  23. package/components/flex/flex.vue +45 -54
  24. package/components/form/form.vue +29 -24
  25. package/components/form-item/form-item.scss +11 -0
  26. package/components/form-item/form-item.vue +43 -40
  27. package/components/icon/icon.vue +70 -85
  28. package/components/image/image.vue +35 -38
  29. package/components/input/input.vue +65 -79
  30. package/components/input/password-input.vue +34 -42
  31. package/components/list-page/list-page.vue +73 -99
  32. package/components/load-more/load-more.scss +12 -0
  33. package/components/load-more/load-more.vue +29 -29
  34. package/components/loading/animations/loading-animation1.vue +8 -8
  35. package/components/loading/animations/loading-animation2.vue +8 -9
  36. package/components/loading/animations/loading-animation3.vue +11 -11
  37. package/components/loading/animations/loading-animation4.vue +8 -8
  38. package/components/loading/loading.vue +19 -20
  39. package/components/message-box/message-box.vue +59 -41
  40. package/components/number-dialog/number-dialog.scss +9 -0
  41. package/components/number-dialog/number-dialog.vue +209 -204
  42. package/components/number-input/number-input.scss +9 -0
  43. package/components/number-input/number-input.vue +233 -232
  44. package/components/numpad/numpad.scss +9 -0
  45. package/components/numpad/numpad.vue +133 -150
  46. package/components/page/page-body.scss +15 -0
  47. package/components/page/page-body.vue +137 -119
  48. package/components/page/page-footer.scss +13 -0
  49. package/components/page/page-footer.vue +31 -33
  50. package/components/page/page-header.scss +17 -0
  51. package/components/page/page-header.vue +89 -94
  52. package/components/page/page.scss +9 -0
  53. package/components/page/page.vue +105 -81
  54. package/components/picker/picker-popup.vue +218 -209
  55. package/components/picker/picker.vue +99 -104
  56. package/components/popover/popover-content.vue +54 -63
  57. package/components/popover/popover-mask.vue +63 -63
  58. package/components/popover/popover.scss +28 -0
  59. package/components/popover/popover.vue +49 -47
  60. package/components/popup/popup-content.vue +49 -53
  61. package/components/popup/popup.scss +11 -0
  62. package/components/popup/popup.vue +117 -132
  63. package/components/price/price.scss +20 -0
  64. package/components/price/price.vue +31 -42
  65. package/components/search/search.scss +12 -0
  66. package/components/search/search.vue +93 -117
  67. package/components/searching/searching.vue +33 -40
  68. package/components/selector/selector.vue +43 -48
  69. package/components/switch/switch.scss +12 -0
  70. package/components/switch/switch.vue +99 -96
  71. package/components/tab/tab.scss +19 -0
  72. package/components/tab/tab.vue +151 -145
  73. package/components/textarea/textarea.vue +104 -101
  74. package/composable/component-name.d.ts +2 -0
  75. package/composable/component-name.js +8 -0
  76. package/composable/component-name.ts +10 -0
  77. package/configs/index.d.ts +25 -10
  78. package/configs/index.js +14 -23
  79. package/configs/index.ts +82 -0
  80. package/easyCom.d.ts +9 -0
  81. package/easyCom.js +1 -2
  82. package/easyCom.ts +31 -0
  83. package/index.d.ts +1 -0
  84. package/index.js +1 -0
  85. package/index.ts +1 -0
  86. package/init.d.ts +4 -7
  87. package/init.js +5 -4
  88. package/init.ts +34 -0
  89. package/package.json +14 -4
  90. package/styles/utils/css-var.scss +12 -15
  91. package/styles/utils/ellipsis.scss +0 -7
  92. package/styles/utils/z-index.scss +1 -1
  93. package/styles/var/component.scss +21 -245
  94. package/utils/date.d.ts +47 -0
  95. package/utils/date.js +23 -43
  96. package/utils/date.ts +153 -0
  97. package/utils/freezeDeep.d.ts +1 -0
  98. package/utils/freezeDeep.js +4 -14
  99. package/utils/freezeDeep.ts +9 -0
  100. package/utils/price.d.ts +24 -0
  101. package/utils/price.js +14 -8
  102. package/utils/price.ts +57 -0
  103. package/utils/round.d.ts +2 -0
  104. package/utils/round.js +2 -14
  105. package/utils/round.ts +11 -0
  106. package/utils/windowInfo.d.ts +24 -0
  107. package/utils/windowInfo.js +8 -36
  108. package/utils/windowInfo.ts +50 -0
  109. package/components/checkbox-group2/checkbox.vue +0 -109
  110. package/components/datetime-picker/datetime-picker-view.vue +0 -209
  111. package/components/datetime-picker/datetime-picker.vue +0 -235
  112. package/components/form/mixin.js +0 -13
  113. package/components/list-page/list-page.js +0 -10
  114. package/components/table/table.vue +0 -112
  115. package/components/time-picker/time-picker-view.vue +0 -127
  116. package/components/time-picker/time-picker.vue +0 -140
  117. package/components/uploader/download.js +0 -81
  118. package/components/uploader/file-uploader.vue +0 -52
  119. package/components/uploader/files.js +0 -225
  120. package/components/uploader/media-uploader.vue +0 -189
  121. package/components/uploader/mixin.js +0 -171
  122. package/components/uploader/uni.js +0 -38
  123. package/components/uploader/upload-button.vue +0 -29
  124. package/components/uploader/uploader copy.vue +0 -547
  125. package/components/uploader/uploader.vue +0 -117
  126. package/mixins/componentName.js +0 -32
  127. package/mixins/listPage.js +0 -1
  128. package/mixins/windowInfo.js +0 -1
  129. package/utils/fileSize.js +0 -39
  130. package/utils/floor.js +0 -20
@@ -1,8 +1,9 @@
1
1
  <template>
2
2
  <button
3
- :open-type="openType"
3
+ :open-type="isNotClickable ? undefined : openType"
4
4
  :class="[
5
- componentClassNames,
5
+ ...classNames,
6
+ $style['Button'],
6
7
  {
7
8
  // 不能点击,但不是禁用态,显示与激活态一致
8
9
  [$style['Button--not-clickable']]: !disabled && isNotClickable,
@@ -20,10 +21,8 @@
20
21
  buttonSize,
21
22
  ]"
22
23
  :hover-class="disabled ? 'none' : $style['Button--active']"
23
- :style="buttonTheme"
24
+ :style="inlineCssVar"
24
25
  @click="onClick"
25
- @getphonenumber="onOpenType('getphonenumber', $event)"
26
- @agreeprivacyauthorization="onOpenType('agreeprivacyauthorization', $event)"
27
26
  >
28
27
  <!-- 线性按钮边框:仅在线性按钮且不是禁用态时显示 -->
29
28
  <view v-if="!disabled && !text && plain" :class="$style['Button--plain-border']"></view>
@@ -31,16 +30,18 @@
31
30
  <template v-if="text">
32
31
  <icon-cpn
33
32
  v-if="icon || (text && isLoading && !disabled)"
34
- :class="$style['Button--icon']"
35
33
  :icon="text && isLoading ? 'loading-dot-spinner' : icon"
34
+ :spinning="text && isLoading"
35
+ :class="$style['Button--icon']"
36
36
  />
37
37
  <slot></slot>
38
38
  </template>
39
39
  <view v-else :class="$style['Button--title']">
40
40
  <icon-cpn
41
41
  v-if="icon"
42
- :class="$style['Button--icon']"
43
42
  :icon="text && isLoading ? 'loading-dot-spinner' : icon"
43
+ :spinning="text && isLoading"
44
+ :class="$style['Button--icon']"
44
45
  />
45
46
  <slot></slot>
46
47
  </view>
@@ -48,181 +49,124 @@
48
49
  <view v-if="!disabled && !text" :class="$style['Button--active-mask']"></view>
49
50
  <!-- 加载态样式:仅在非文本按钮处于加载态(不能处于禁用态)时显示 -->
50
51
  <view v-if="!disabled && !text && isLoading" :class="$style['Button--loading']">
51
- <icon-cpn icon="loading-dot-spinner" />
52
+ <icon-cpn icon="loading-dot-spinner" spinning />
52
53
  </view>
53
54
  </button>
54
55
  </template>
55
56
 
56
- <script>
57
- import componentName from '../../mixins/componentName';
57
+ <script lang="ts" setup>
58
+ import { computed, ref, useCssModule } from 'vue';
59
+ import useClassNames from '../../composable/component-name';
58
60
  import IconCpn from '../icon/icon.vue';
59
61
 
60
- /**
61
- * **组件使用说明:**
62
- *
63
- * 当 async 为 false 时,组件会按传入的 coolDown 控制两次点击之间的间隔
64
- * 当 async 为 true 时,需要在父组件监听 click 事件的回调函数中调用传入的 resolve 或 reject 方法,否则按钮将持续处于 loading 状态
65
- */
66
- export default {
67
- name: 'Button',
68
- components: { IconCpn },
69
- mixins: [componentName],
62
+ defineOptions({
70
63
  options: {
71
- virtualHost: true,
72
64
  styleIsolation: 'shared',
65
+ virtualHost: true,
73
66
  },
74
- props: {
75
- /** 是否禁用,禁用时按钮会显示禁用态(置灰) */
76
- disabled: {
77
- type: Boolean,
78
- default: false,
79
- },
80
- /** 是否禁止点击,为 `true` 时按钮会显白 */
81
- forbidClick: {
82
- type: Boolean,
83
- default: false,
84
- },
85
- /** 按钮主题 */
86
- type: {
87
- type: String,
88
- default: 'primary',
89
- },
90
- /** 对应小程序的 openType */
91
- openType: {
92
- type: String,
93
- default: '',
94
- },
95
- /** 按钮冷却时间,单位毫秒,两次点击之间的最小间隔,为 `0` 时可连续点击 */
96
- coolDown: {
97
- type: Number,
98
- default: 500,
99
- },
100
- /** 是否异步事件 */
101
- async: {
102
- type: Boolean,
103
- default: false,
104
- },
105
- /** 是否圆角 */
106
- round: {
107
- type: Boolean,
108
- default: false,
109
- },
110
- /** 是否线性 */
111
- plain: {
112
- type: Boolean,
113
- default: false,
114
- },
115
- /** 是否文本按钮 */
116
- text: {
117
- type: Boolean,
118
- default: false,
119
- },
120
- /** 按钮图标 */
121
- icon: {
122
- type: String,
123
- default: '',
124
- },
125
- /** 加载中 */
126
- loading: {
127
- type: Boolean,
128
- default: false,
129
- },
130
- /** 小尺寸 */
131
- small: {
132
- type: Boolean,
133
- default: false,
134
- },
135
- /** 大尺寸 */
136
- large: {
137
- type: Boolean,
138
- default: false,
139
- },
140
- },
141
- data() {
142
- return {
143
- /** 按钮是否冷却中 */
144
- isCoolingDown: false,
145
- /** 按钮是否处于请求状态 */
146
- isAsyncLoading: false,
147
- };
148
- },
149
- computed: {
150
- buttonTheme() {
151
- return `--theme-from-prop: var(--button-theme-${this.type});`;
152
- },
153
- /** 按钮尺寸样式 */
154
- buttonSize() {
155
- if (this.text) {
156
- return this.large
157
- ? this.$style['Button--text-large']
158
- : this.small
159
- ? this.$style['Button--text-small']
160
- : '';
161
- }
162
- return this.large
163
- ? this.$style['Button--block-large']
164
- : this.small
165
- ? this.$style['Button--block-small']
166
- : '';
167
- },
168
- /** 按钮 loading */
169
- isLoading() {
170
- return this.loading || this.isAsyncLoading;
171
- },
172
- /** 按钮是否可点击 */
173
- isNotClickable() {
174
- return this.disabled || this.forbidClick || this.isLoading || this.isCoolingDown;
175
- },
176
- },
177
- // emits: ['click', 'getphonenumber', 'agreeprivacyauthorization'],
178
- methods: {
179
- /** 进入冷却 */
180
- $_coolDown() {
181
- if (this.coolDown > 0) {
182
- this.isCoolingDown = true;
183
-
184
- setTimeout(() => {
185
- this.isCoolingDown = false;
186
- }, this.coolDown);
187
- }
188
- },
189
- /** 点击按钮响应 */
190
- onClick() {
191
- if (this.isNotClickable) {
192
- return;
193
- }
194
-
195
- this.$_coolDown();
196
-
197
- if (!this.async) {
198
- this.$emit('click');
199
- return;
200
- }
201
-
202
- this.isAsyncLoading = true;
203
- const { promise, resolve, reject } = Promise.withResolvers();
204
- promise
205
- .catch((err) => {
206
- if (process.env.NODE_ENV === 'development') {
207
- // eslint-disable-next-line no-console
208
- console.warn(`${this.componentClass} unresolved error:`, err);
209
- }
210
- })
211
- .finally(() => {
212
- this.isAsyncLoading = false;
213
- });
214
- this.$emit('click', { resolve, reject });
215
- },
216
- /** 开放能力 */
217
- onOpenType(eventType, event) {
218
- if (this.isNotClickable) {
219
- return;
220
- }
221
-
222
- this.$emit(eventType, event);
223
- this.$_coolDown();
224
- },
225
- },
67
+ });
68
+
69
+ const classNames = useClassNames('button');
70
+ const $style = useCssModule();
71
+
72
+ type _Props = {
73
+ /** 是否禁用,禁用时按钮会显示禁用态(置灰) */
74
+ disabled?: boolean;
75
+ /** 是否禁止点击,为 `true` 时按钮会显白 */
76
+ forbidClick?: boolean;
77
+ /** 按钮主题 */
78
+ type?: string;
79
+ /** 对应小程序的 openType */
80
+ openType?: 'contact' | 'getPhoneNumber' | 'getUserInfo' | 'chooseAvatar' | 'agreePrivacyAuthorization';
81
+ /** 按钮冷却时间,单位毫秒,两次点击之间的最小间隔,为 `0` 时可连续点击 */
82
+ coolDown?: number;
83
+ /** 是否异步事件 */
84
+ async?: boolean;
85
+ /** 是否圆角 */
86
+ round?: boolean;
87
+ /** 是否线性 */
88
+ plain?: boolean;
89
+ /** 是否文本按钮 */
90
+ text?: boolean;
91
+ /** 按钮图标 */
92
+ icon?: string;
93
+ /** 加载中 */
94
+ loading?: boolean;
95
+ /** 小尺寸 */
96
+ small?: boolean;
97
+ /** 大尺寸 */
98
+ large?: boolean;
99
+ };
100
+
101
+ const props = withDefaults(defineProps<_Props>(), {
102
+ disabled: false,
103
+ forbidClick: false,
104
+ type: 'primary',
105
+ openType: undefined,
106
+ coolDown: 500,
107
+ async: false,
108
+ round: false,
109
+ plain: false,
110
+ text: false,
111
+ icon: '',
112
+ loading: false,
113
+ small: false,
114
+ large: false,
115
+ });
116
+
117
+ const emit = defineEmits<{
118
+ (event: 'click', payload?: Omit<PromiseWithResolvers<unknown>, 'promise'>): void;
119
+ }>();
120
+
121
+ /** 按钮是否冷却中 */
122
+ const isCoolingDown = ref(false);
123
+ /** 按钮是否处于请求状态 */
124
+ const isAsyncLoading = ref(false);
125
+
126
+ /** 按钮 loading */
127
+ const inlineCssVar = computed(() => `--theme-from-prop: var(--button-theme-${props.type});`);
128
+ /** 按钮尺寸样式 */
129
+ const buttonSize = computed(() => {
130
+ if (props.text) {
131
+ return props.large ? $style['Button--text-large'] : props.small ? $style['Button--text-small'] : '';
132
+ }
133
+ return props.large ? $style['Button--block-large'] : props.small ? $style['Button--block-small'] : '';
134
+ });
135
+ /** 按钮 loading */
136
+ const isLoading = computed(() => props.loading || isAsyncLoading.value);
137
+ /** 按钮是否可点击 */
138
+ const isNotClickable = computed(() => props.disabled || props.forbidClick || isLoading.value || isCoolingDown.value);
139
+
140
+ /** 进入冷却 */
141
+ const $_coolDown = () => {
142
+ if (props.coolDown > 0) {
143
+ isCoolingDown.value = true;
144
+
145
+ setTimeout(() => {
146
+ isCoolingDown.value = false;
147
+ }, props.coolDown);
148
+ }
149
+ };
150
+ /** 点击按钮响应 */
151
+ const onClick = async () => {
152
+ if (isNotClickable.value) {
153
+ return;
154
+ }
155
+
156
+ $_coolDown();
157
+
158
+ if (!props.async) {
159
+ emit('click');
160
+ return;
161
+ }
162
+
163
+ isAsyncLoading.value = true;
164
+ const { promise, resolve, reject } = Promise.withResolvers();
165
+ promise.finally(() => {
166
+ isAsyncLoading.value = false;
167
+ });
168
+
169
+ emit('click', { resolve, reject });
226
170
  };
227
171
  </script>
228
172
 
@@ -230,13 +174,13 @@ export default {
230
174
  @use 'sass:color';
231
175
  @use 'sass:math';
232
176
  @use 'sass:map';
233
- @use '../../styles/index' as *;
177
+ @use '../../styles/utils' as *;
178
+ @use '../../styles/var' as *;
179
+ @use './button' as *;
234
180
 
235
181
  // 定义外部变量
236
- $component-name: 'button';
237
- $component-vars: $button;
238
182
  %external-vars {
239
- @include UTILS__DEFINE_COMPONENT_VARS();
183
+ @include UTILS__DEFINE_COMPONENT_VARS($button, 'button');
240
184
  }
241
185
 
242
186
  %button-themes {
@@ -0,0 +1,10 @@
1
+ @use 'sass:map';
2
+ @use '../../styles/utils' as *;
3
+
4
+ // 可配置的变量
5
+ $checkbox: () !default;
6
+ $checkbox-default: (
7
+ checked-color: UTILS__GLOBAL_VAR(theme, primary),
8
+ unchecked-color: UTILS__GLOBAL_VAR(color, foreground),
9
+ );
10
+ $checkbox: map.merge($checkbox-default, $checkbox);
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <view :class="componentClassNames" @click="toggle()">
2
+ <view :class="[...classNames, $style['Checkbox']]" @click="toggle()">
3
3
  <icon-cpn
4
4
  v-if="hasIcon"
5
5
  :icon="usingIcon || ''"
@@ -7,166 +7,141 @@
7
7
  :class="[$style['Checkbox--icon'], { [$style['Checkbox--checked']]: isChecked }]"
8
8
  />
9
9
  <view :class="$style['Checkbox--text']">
10
- <slot></slot>
10
+ <slot>{{ label }}</slot>
11
11
  </view>
12
12
  </view>
13
13
  </template>
14
14
 
15
- <script>
16
- import componentName from '../../mixins/componentName';
17
- import { isFormReadonly } from '../form/mixin';
15
+ <script lang="ts" setup>
16
+ import { computed, ComputedRef, inject, Ref, ref } from 'vue';
18
17
  import IconCpn from '../icon/icon.vue';
18
+ import useClassNames from '../../composable/component-name';
19
19
 
20
- export default {
21
- name: 'Checkbox',
20
+ defineOptions({
22
21
  options: {
23
- virtualHost: true,
24
22
  styleIsolation: 'shared',
23
+ virtualHost: true,
25
24
  },
26
- components: { IconCpn },
27
- mixins: [componentName, isFormReadonly],
28
- inject: {
29
- /** checkbox-group 传入的 check 方法 */
30
- checkGroup: {
31
- from: 'checkboxGroupCheck',
32
- default: null,
33
- },
34
- /** checkbox-group 组件的 data 对象,用于读取当前值 */
35
- checkboxGroupData: {
36
- from: 'checkboxGroupData',
37
- default: null,
38
- },
39
- /** checkbox-group 组件的 props 对象,用于读取 radio 和图标 */
40
- checkboxGroupProps: {
41
- from: 'checkboxGroupProps',
42
- default: { icon: true, radio: false },
43
- },
44
- },
45
- props: {
46
- /** 当在 checkbox-group 中时,这个值用于选项值;否则用于 v-model ,只能是 boolean 类型 */
47
- value: {
48
- type: [Number, String, Boolean, null],
49
- default: null,
50
- },
51
- /** 是否只读 */
52
- readonly: {
53
- type: Boolean,
54
- default: false,
55
- },
56
- /** 类型 */
57
- radio: {
58
- type: [Boolean, null],
59
- default: null,
60
- },
61
- /** 是否显示勾选图标 */
62
- icon: {
63
- type: [Boolean, null],
64
- default: null,
65
- },
66
- /** 选中时的图标 */
67
- checkedIcon: {
68
- type: String,
69
- default: '',
70
- },
71
- /** 未选中时的图标 */
72
- uncheckedIcon: {
73
- type: String,
74
- default: '',
75
- },
76
- },
77
- data() {
78
- return {
79
- /** 组件内部记录选中状态,仅在非 checkbox-group 中有效 */
80
- innerChecked: false,
81
- };
82
- },
83
- computed: {
84
- isReadonly() {
85
- return this.readonly || this.isFormReadonly;
86
- },
87
- /** 是否在 checkbox-group 中 */
88
- isInGroup() {
89
- return !!this.checkboxGroupData;
90
- },
91
- /** 是否显示勾选图标 */
92
- hasIcon() {
93
- return this.icon === null ? this.checkboxGroupProps.icon : this.icon;
94
- },
95
- /** 是否为单选 */
96
- isRadio() {
97
- return this.radio === null ? this.checkboxGroupProps.radio : this.radio;
98
- },
99
- /** checkbox-group 的值 */
100
- groupCheckedValue() {
101
- return this.checkboxGroupData?.checkedValue;
102
- },
103
- /** 最终使用的选中态的图标 */
104
- usingCheckedIcon() {
105
- return (
106
- this.checkboxGroupProps.checkedIcon ||
107
- this.checkedIcon ||
108
- (this.isRadio ? 'radio-checked' : 'checkbox-checked')
109
- );
110
- },
111
- /** 最终使用的未选中态的图标 */
112
- usingUncheckedIcon() {
113
- return (
114
- this.checkboxGroupProps.uncheckedIcon ||
115
- this.uncheckedIcon ||
116
- (this.isRadio ? 'radio-unchecked' : 'checkbox-unchecked')
117
- );
118
- },
119
- /** 最终使用的图标 */
120
- usingIcon() {
121
- return this.isChecked ? this.usingCheckedIcon : this.usingUncheckedIcon;
122
- },
123
- isChecked: {
124
- get() {
125
- if (this.isInGroup) {
126
- return this.isRadio
127
- ? this.value === this.groupCheckedValue
128
- : this.groupCheckedValue?.includes(this.value) || false;
129
- }
130
- return this.value === null ? this.innerChecked : this.value;
131
- },
132
- set(val) {
133
- this.$emit('input', val);
134
- this.innerChecked = val;
135
- this.$emit('change', val);
136
- },
137
- },
25
+ });
26
+
27
+ const classNames = useClassNames('checkbox');
28
+ /** 表单是否只读 */
29
+ const isFormReadonly = inject<ComputedRef<boolean>>(
30
+ 'ecolovo/form/readonly',
31
+ computed(() => false)
32
+ );
33
+ /** 勾选框组当前选中值 */
34
+ const groupCheckedValue = inject<Ref<undefined | string | number | boolean | Array<unknown>>>(
35
+ 'ecolovo/checkboxGroup/checkedValue'
36
+ );
37
+ /** 勾选框组的 props */
38
+ const groupProps = inject<null | Pick<_Props, 'radio' | 'icon' | 'checkedIcon' | 'uncheckedIcon'>>(
39
+ 'ecolovo/checkboxGroup/props',
40
+ null
41
+ );
42
+ /** 勾选框组勾选选项方法 */
43
+ const groupCheck =
44
+ inject<(val: undefined | string | number | boolean | Array<unknown>) => void>('ecolovo/checkboxGroup/check');
45
+
46
+ type _Props = {
47
+ /** 不在勾选框组中时使用,用于 v-model ,只能是 boolean 类型 */
48
+ modelValue?: boolean;
49
+ /** 在勾选框组时使用,用于定义当前勾选框的选项值 */
50
+ value?: number | string | boolean;
51
+ /** 选项标签 */
52
+ label?: string;
53
+ /** 是否只读 */
54
+ readonly?: boolean;
55
+ /** 是否单选 */
56
+ radio?: boolean;
57
+ /** 是否显示勾选图标 */
58
+ icon?: boolean;
59
+ /** 选中时的图标 */
60
+ checkedIcon?: string;
61
+ /** 未选中时的图标 */
62
+ uncheckedIcon?: string;
63
+ };
64
+
65
+ const props = withDefaults(defineProps<_Props>(), {
66
+ modelValue: undefined,
67
+ value: undefined,
68
+ label: undefined,
69
+ readonly: undefined,
70
+ radio: undefined,
71
+ icon: undefined,
72
+ checkedIcon: '',
73
+ uncheckedIcon: '',
74
+ });
75
+
76
+ const emit = defineEmits<{
77
+ (event: 'update:modelValue', value: boolean): void;
78
+ (event: 'change', value: boolean): void;
79
+ }>();
80
+
81
+ const innerChecked = ref(false);
82
+
83
+ /** 是否在 checkbox-group 中 */
84
+ const isInCheckboxGroup = computed<boolean>(() => !!groupProps);
85
+ /** 是否只读 */
86
+ const isReadonly = computed<boolean>(() => (props.readonly !== undefined ? props.readonly : isFormReadonly.value));
87
+ /** 是否显示勾选图标 */
88
+ const hasIcon = computed<boolean>(() => (props.icon !== undefined ? props.icon : groupProps?.icon ?? true));
89
+ /** 是否为单选 */
90
+ const isRadio = computed<boolean>(() => (props.radio !== undefined ? props.radio : groupProps?.radio || false));
91
+ /** 最终使用的选中态的图标 */
92
+ const usingCheckedIcon = computed<string>(
93
+ () => props.checkedIcon || groupProps?.checkedIcon || (isRadio.value ? 'radio-checked' : 'checkbox-checked')
94
+ );
95
+ /** 最终使用的未选中态的图标 */
96
+ const usingUncheckedIcon = computed(
97
+ () => props.uncheckedIcon || groupProps?.uncheckedIcon || (isRadio.value ? 'radio-unchecked' : 'checkbox-unchecked')
98
+ );
99
+ /** 最终使用的图标 */
100
+ const usingIcon = computed(() => (isChecked.value ? usingCheckedIcon.value : usingUncheckedIcon.value));
101
+ /** 是否勾选 */
102
+ const isChecked = computed<boolean>({
103
+ get() {
104
+ if (isInCheckboxGroup.value) {
105
+ return isRadio.value
106
+ ? props.value === groupCheckedValue!.value
107
+ : (groupCheckedValue as Ref<unknown[]>).value?.includes(props.value) || false;
108
+ }
109
+ return props.modelValue === undefined ? innerChecked.value : (props.modelValue as boolean);
138
110
  },
139
- methods: {
140
- /** 切换选择 */
141
- toggle(checked) {
142
- if (this.isReadonly) return;
143
-
144
- if (this.isInGroup) {
145
- this.checkGroup(this.value);
146
- return;
147
- }
148
-
149
- if (checked === undefined || checked === null) {
150
- this.isChecked = !this.isChecked;
151
- return;
152
- }
153
-
154
- if (this.isChecked !== !!checked) {
155
- this.isChecked = !!checked;
156
- }
157
- },
111
+ set(val) {
112
+ emit('update:modelValue', val);
113
+ innerChecked.value = val;
114
+ emit('change', val);
158
115
  },
116
+ });
117
+
118
+ /** 切换选择 */
119
+ const toggle = (checked?: boolean) => {
120
+ if (isReadonly.value) return;
121
+
122
+ if (isInCheckboxGroup.value) {
123
+ groupCheck?.(props.value);
124
+ return;
125
+ }
126
+
127
+ if (checked === undefined) {
128
+ isChecked.value = !isChecked.value;
129
+ return;
130
+ }
131
+
132
+ if (isChecked.value !== !!checked) {
133
+ isChecked.value = !!checked;
134
+ }
159
135
  };
160
136
  </script>
161
137
 
162
138
  <style lang="scss" module>
163
- @use '../../styles/index' as *;
139
+ @use '../../styles/utils' as *;
140
+ @use './checkbox' as *;
164
141
 
165
142
  // 定义外部变量
166
- $component-name: 'checkbox';
167
- $component-vars: $checkbox;
168
143
  %external-vars {
169
- @include UTILS__DEFINE_COMPONENT_VARS();
144
+ @include UTILS__DEFINE_COMPONENT_VARS($checkbox, 'checkbox');
170
145
  }
171
146
 
172
147
  .Checkbox {
@@ -187,9 +162,6 @@ $component-vars: $checkbox;
187
162
  }
188
163
  .Checkbox--checked {
189
164
  color: UTILS__LOCAL_VAR(checked-color);
190
- :global(.radio-checked) {
191
- fill: UTILS__LOCAL_VAR(checked-color);
192
- }
193
165
  }
194
166
  .Checkbox--text {
195
167
  flex: 0 1 auto;