@ecolovo/uniapp-components 1.0.9 → 2.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 (117) hide show
  1. package/README.md +83 -51
  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 +125 -181
  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 -81
  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.ts +10 -0
  75. package/configs/index.ts +82 -0
  76. package/{easyCom.js → easyCom.ts} +1 -1
  77. package/index.ts +1 -0
  78. package/init.ts +34 -0
  79. package/package.json +12 -4
  80. package/styles/utils/css-var.scss +12 -15
  81. package/styles/utils/ellipsis.scss +0 -7
  82. package/styles/utils/z-index.scss +1 -1
  83. package/styles/var/component.scss +21 -245
  84. package/utils/{date.js → date.ts} +44 -42
  85. package/utils/freezeDeep.ts +9 -0
  86. package/utils/price.ts +57 -0
  87. package/utils/round.ts +11 -0
  88. package/utils/{windowInfo.js → windowInfo.ts} +12 -35
  89. package/components/checkbox-group2/checkbox.vue +0 -109
  90. package/components/datetime-picker/datetime-picker-view.vue +0 -209
  91. package/components/datetime-picker/datetime-picker.vue +0 -235
  92. package/components/form/mixin.js +0 -13
  93. package/components/list-page/list-page.js +0 -10
  94. package/components/table/table.vue +0 -112
  95. package/components/time-picker/time-picker-view.vue +0 -127
  96. package/components/time-picker/time-picker.vue +0 -140
  97. package/components/uploader/download.js +0 -81
  98. package/components/uploader/file-uploader.vue +0 -52
  99. package/components/uploader/files.js +0 -225
  100. package/components/uploader/media-uploader.vue +0 -179
  101. package/components/uploader/mixin.js +0 -171
  102. package/components/uploader/uni.js +0 -38
  103. package/components/uploader/upload-button.vue +0 -29
  104. package/components/uploader/uploader copy.vue +0 -547
  105. package/components/uploader/uploader.vue +0 -117
  106. package/configs/index.d.ts +0 -22
  107. package/configs/index.js +0 -68
  108. package/init.d.ts +0 -19
  109. package/init.js +0 -20
  110. package/mixins/componentName.js +0 -32
  111. package/mixins/listPage.js +0 -1
  112. package/mixins/windowInfo.js +0 -1
  113. package/utils/fileSize.js +0 -39
  114. package/utils/floor.js +0 -20
  115. package/utils/freezeDeep.js +0 -19
  116. package/utils/price.js +0 -32
  117. package/utils/round.js +0 -22
package/utils/price.ts ADDED
@@ -0,0 +1,57 @@
1
+ interface FormatPriceOption {
2
+ /** 精度,默认为 2 */
3
+ precision: number;
4
+ /** 是否补齐小数部分 0 */
5
+ padDecimal: boolean;
6
+ }
7
+ interface FormatPriceResult {
8
+ /** 格式化的价格字符串 */
9
+ price: string;
10
+ /** 是否负值 */
11
+ negative: boolean;
12
+ /** 整数部分价格 */
13
+ integer: string;
14
+ /** 小数部分价格 */
15
+ decimal: string;
16
+ /** 价格对应数值 */
17
+ number: number;
18
+ }
19
+
20
+ /**
21
+ * 格式化价格
22
+ * @param price 价格
23
+ */
24
+ export function formatPrice(price: number | string, option?: FormatPriceOption): FormatPriceResult {
25
+ if (isNaN(Number(price))) {
26
+ return {
27
+ price: '',
28
+ negative: false,
29
+ integer: '',
30
+ decimal: '',
31
+ number: NaN,
32
+ };
33
+ }
34
+ price = Number(price);
35
+ let negative = false;
36
+ if (price < 0) {
37
+ negative = true;
38
+ price = -price;
39
+ }
40
+ const integer = Math.floor(price).toString();
41
+ const decimalNum = Math.round(Number(price + 'e' + (option?.precision ?? 2))) % 100;
42
+ let decimal: string;
43
+ if (option?.padDecimal ?? true) {
44
+ decimal = '.' + decimalNum.toString().padStart(option?.precision ?? 2, '0');
45
+ } else {
46
+ if (decimalNum === 0) decimal = '';
47
+ else decimal = decimalNum.toString().slice(1);
48
+ }
49
+ const formattedPrice = `${negative ? '-' : ''}${integer}.${decimal}`;
50
+ return {
51
+ price: formattedPrice, // 价格字符串
52
+ negative,
53
+ integer, // 整数部分
54
+ decimal, // 小数部分
55
+ number: Number(formattedPrice),
56
+ };
57
+ }
package/utils/round.ts ADDED
@@ -0,0 +1,11 @@
1
+ export function round(val: number, precision = 0): number {
2
+ if (val.toString().indexOf('e') >= 0) {
3
+ const exponential = val.toString().split('e');
4
+ return Math.round(+`${exponential[0]}e${+exponential[1]! + precision}`) / 10 ** precision;
5
+ }
6
+ return Math.round(+`${val}e${precision}`) / 10 ** precision;
7
+ }
8
+
9
+ export function round2(val: number): number {
10
+ return round(val, 2);
11
+ }
@@ -1,22 +1,19 @@
1
1
  /** 默认导航栏高度 */
2
2
  const NAV_TITLE_HEIGHT = 44;
3
- /** 胶囊按钮位置信息
4
- * @type {UniApp.GetMenuButtonBoundingClientRectRes | null}
5
- */
6
- let menuButton = null;
7
- /** 系统信息
8
- * @type {UniApp.GetWindowInfoResult | null}
9
- */
10
- let windowInfo = null;
3
+
4
+ /** 胶囊按钮位置信息 */
5
+ let menuButton: UniApp.GetMenuButtonBoundingClientRectRes | null = null;
6
+ /** 胶囊按钮宽度,仅在 css 中使用,当有胶囊按钮时,表示胶囊按钮左边缘到屏幕右侧的宽度,否则为 `0px` */
7
+ let menuButtonWidthInCss: string = '0px';
8
+ /** 系统信息 */
9
+ let windowInfo: UniApp.GetWindowInfoResult | null = null;
11
10
  /** 导航栏高度 */
12
11
  let navBarHeight = NAV_TITLE_HEIGHT;
13
12
  /** 导航栏顶部距离 */
14
13
  let navBarTopOffset = 0;
15
14
  /** 导航栏底部距离 */
16
15
  let navBarBottomOffset = NAV_TITLE_HEIGHT;
17
- /** 底部安全区域高度
18
- * @type {number}
19
- */
16
+ /** 底部安全区域高度 */
20
17
  let safeAreaInsetBottom = uni.upx2px(50);
21
18
 
22
19
  /* #ifdef MP-WEIXIN || MP-ALIPAY */
@@ -30,7 +27,9 @@ if (menuButton) {
30
27
  if (navBarHeight < menuButton.height) {
31
28
  navBarHeight = menuButton.height + 12;
32
29
  }
33
- navBarTopOffset = menuButton.top - (navBarHeight - menuButton.height) / 3;
30
+ navBarTopOffset = menuButton.top - (navBarHeight - menuButton.height) / 2;
31
+
32
+ menuButtonWidthInCss = 'calc(100vw - ' + menuButton.left + 'px)';
34
33
  } else {
35
34
  navBarTopOffset = windowInfo.safeArea.top || windowInfo.statusBarHeight;
36
35
  }
@@ -40,34 +39,12 @@ if (_safeAreaBottom) {
40
39
  safeAreaInsetBottom = _safeAreaBottom;
41
40
  }
42
41
 
43
- /** mixin */
44
- const windowInfoMixin = {
45
- computed: {
46
- /** 胶囊按钮位置信息 */
47
- menuButton: () => menuButton,
48
- /** 胶囊按钮宽度 */
49
- menuButtonWidth() {
50
- return this.menuButton ? 'calc(100vw - ' + this.menuButton.left + 'px)' : '0px';
51
- },
52
- /** 系统信息 */
53
- windowInfo: () => windowInfo,
54
- /** 导航栏高度 */
55
- navBarHeight: () => navBarHeight,
56
- /** 导航栏顶部距离 */
57
- navBarTopOffset: () => navBarTopOffset,
58
- /** 导航栏底部距离 */
59
- navBarBottomOffset: () => navBarBottomOffset,
60
- /** 底部安全区域高度 */
61
- safeAreaInsetBottom: () => safeAreaInsetBottom,
62
- },
63
- };
64
-
65
42
  export {
66
43
  menuButton,
44
+ menuButtonWidthInCss,
67
45
  windowInfo,
68
46
  navBarHeight,
69
47
  navBarTopOffset,
70
48
  navBarBottomOffset,
71
49
  safeAreaInsetBottom,
72
- windowInfoMixin,
73
50
  };
@@ -1,109 +0,0 @@
1
- <template>
2
- <view :class="componentClassNames" @click="toggle()">
3
- <icon-cpn
4
- v-if="icon"
5
- :icon="currentValue ? checkedIcon || 'checkbox-checked' : uncheckedIcon || 'checkbox-unchecked'"
6
- tap-size="50rpx"
7
- :class="[$style['Checkbox2--icon'], { [$style['Checkbox2--checked']]: currentValue }]"
8
- />
9
- <view :class="$style['Checkbox2--text']">
10
- <slot></slot>
11
- </view>
12
- </view>
13
- </template>
14
-
15
- <script>
16
- import componentName from '../../mixins/componentName';
17
- import IconCpn from '../icon/icon.vue';
18
-
19
- export default {
20
- name: 'Checkbox2',
21
- options: {
22
- virtualHost: true,
23
- styleIsolation: 'shared',
24
- },
25
- components: { IconCpn },
26
- mixins: [componentName],
27
- props: {
28
- /** 值 */
29
- value: {
30
- type: [Boolean, null],
31
- default: null,
32
- },
33
- /** 是否显示勾选图标 */
34
- icon: {
35
- type: Boolean,
36
- default: true,
37
- },
38
- /** 选中时的图标 */
39
- checkedIcon: {
40
- type: String,
41
- default: '',
42
- },
43
- /** 未选中时的图标 */
44
- uncheckedIcon: {
45
- type: String,
46
- default: '',
47
- },
48
- },
49
- data() {
50
- return {
51
- innerValue: false,
52
- };
53
- },
54
- computed: {
55
- currentValue: {
56
- get: function () {
57
- return this.value === null ? this.innerValue : this.value;
58
- },
59
- set: function (val) {
60
- this.$emit('input', val);
61
- this.innerValue = val;
62
- this.$emit('change', val);
63
- },
64
- },
65
- },
66
- methods: {
67
- /** 切换选择 */
68
- toggle(checked) {
69
- if (checked === undefined || checked === null) {
70
- this.currentValue = !this.currentValue;
71
- return;
72
- }
73
-
74
- if (this.currentValue !== !!checked) {
75
- this.currentValue = !!checked;
76
- }
77
- },
78
- },
79
- };
80
- </script>
81
-
82
- <style lang="scss" module>
83
- @use '../../styles/index' as *;
84
-
85
- // 定义外部变量
86
- $component-name: 'checkbox';
87
- $component-vars: $checkbox;
88
- %external-vars {
89
- @include UTILS__DEFINE_COMPONENT_VARS();
90
- }
91
-
92
- .Checkbox2 {
93
- @extend %external-vars;
94
- display: flex;
95
- align-items: center;
96
-
97
- .Checkbox2--icon {
98
- flex: none;
99
- margin-right: 12rpx;
100
- color: UTILS__LOCAL_VAR(unchecked-color);
101
- }
102
- .Checkbox2--checked {
103
- color: UTILS__LOCAL_VAR(checked-color);
104
- }
105
- .Checkbox2--text {
106
- flex: 0 1 auto;
107
- }
108
- }
109
- </style>
@@ -1,209 +0,0 @@
1
- <template>
2
- <picker-view
3
- immediate-change
4
- :value="pickerValue"
5
- :class="componentClassNames"
6
- @change="handlePickerChange"
7
- @pickend="onPickEnd"
8
- >
9
- <picker-view-column>
10
- <view v-for="item in yearOptions" :key="item" :class="$style['DatetimePickerView--option']">
11
- {{ item }} 年
12
- </view>
13
- </picker-view-column>
14
- <picker-view-column>
15
- <view v-for="item in monthOptions" :key="item" :class="$style['DatetimePickerView--option']">
16
- {{ item }} 月
17
- </view>
18
- </picker-view-column>
19
- <picker-view-column>
20
- <view v-for="item in dateOptions" :key="item" :class="$style['DatetimePickerView--option']">
21
- {{ item }} 日
22
- </view>
23
- </picker-view-column>
24
- <picker-view-column>
25
- <view v-for="item in hourOptions" :key="item" :class="$style['DatetimePickerView--option']">
26
- {{ item }} 时
27
- </view>
28
- </picker-view-column>
29
- <picker-view-column v-if="minuteStep > 0">
30
- <view v-for="item in minuteOptions" :key="item" :class="$style['DatetimePickerView--option']">
31
- {{ item }} 分
32
- </view>
33
- </picker-view-column>
34
- </picker-view>
35
- </template>
36
-
37
- <script>
38
- import range from 'lodash-es/range';
39
- import componentName from '../../mixins/componentName';
40
- import { formatDate, toDate } from '../../utils/date';
41
-
42
- /** 年份选项 */
43
- const yearOptions = (function () {
44
- const arr = [];
45
- for (let i = 1900; i <= 2099; i++) {
46
- arr.push(i);
47
- }
48
- return arr;
49
- })();
50
-
51
- /** 获取指定日期所在月份的日期选项列表 */
52
- function getDateOptions(date) {
53
- date = new Date(date);
54
- date.setMonth(date.getMonth() + 1, 0);
55
- const max = date.getDate();
56
- return range(1, max + 1);
57
- }
58
-
59
- export default {
60
- name: 'DatetimePickerView',
61
- options: {
62
- virtualHost: true,
63
- styleIsolation: 'shared',
64
- },
65
- mixins: [componentName],
66
- props: {
67
- /** 绑定值 */
68
- value: {
69
- type: [String, null],
70
- default: null,
71
- },
72
- /** 是否在选择开始日期时,同步修改结束日期,且保持时间差 */
73
- keepDiff: {
74
- type: Boolean,
75
- default: false,
76
- },
77
- /** 结束日期是否可以与开始日期相同 */
78
- canSame: {
79
- type: Boolean,
80
- default: false,
81
- },
82
- /** 最小日期 */
83
- min: {
84
- type: String,
85
- default: '',
86
- },
87
- /** 最大日期 */
88
- max: {
89
- type: String,
90
- default: '',
91
- },
92
- /** 分钟步长 */
93
- minuteStep: {
94
- type: Number,
95
- default: 5,
96
- },
97
- },
98
- data() {
99
- return {
100
- innerValue: '',
101
- /** 年份选项列表 */
102
- yearOptions: yearOptions,
103
- /** 月份选项列表 */
104
- monthOptions: range(1, 13),
105
- /** 日期选项列表 */
106
- dateOptions: [],
107
- /** 小时选项列表 */
108
- hourOptions: range(0, 24),
109
- /** 分钟选项列表 */
110
- minuteOptions: [],
111
- /** picker-view 绑定值 */
112
- pickerValue: [0, 0, 0, 0, 0],
113
- };
114
- },
115
- computed: {
116
- currentValue: {
117
- get: function () {
118
- return this.value === null ? this.innerValue : this.value;
119
- },
120
- set: function (value) {
121
- this.$emit('input', value);
122
- this.innerValue = value;
123
- this.$emit('change', value);
124
- },
125
- },
126
- },
127
- created() {
128
- this.minuteOptions = range(0, 60, this.minuteStep);
129
- },
130
- mounted() {
131
- this.$nextTick(() => {
132
- this.syncPickerValue(this.currentValue || new Date());
133
- });
134
- },
135
- methods: {
136
- /** 同步 picker-view 选中索引 */
137
- syncPickerValue(date) {
138
- const dateObj = toDate(date || new Date());
139
- const [Y, M, D, h, m] = formatDate(dateObj, 'YYYY-MM-DD-HH-mm').split('-');
140
- this.dateOptions = getDateOptions(dateObj);
141
- const i1 = this.yearOptions.indexOf(Number(Y));
142
- const i2 = this.monthOptions.indexOf(Number(M));
143
- const i3 = this.dateOptions.indexOf(Number(D));
144
- const i4 = this.hourOptions.indexOf(Number(h));
145
- const i5 = Math.max(
146
- this.minuteOptions.indexOf(Math.round(Number(m) / this.minuteStep) * this.minuteStep),
147
- 0
148
- );
149
- this.$nextTick(() => {
150
- this.pickerValue =
151
- typeof this.pickerValue[0] === 'number'
152
- ? [i1, i2, i3, i4, i5].map((item) => item.toString())
153
- : [i1, i2, i3, i4, i5];
154
- });
155
- },
156
- /** picker-view 选项值变化处理 */
157
- handlePickerChange(e) {
158
- const newPickerValue = e.detail.value;
159
- /** 获取变化的列 */
160
- const col = this.pickerValue.findIndex((item, index) => Number(item) !== Number(newPickerValue[index]));
161
- const year = this.yearOptions[newPickerValue[0]];
162
- const month = this.monthOptions[newPickerValue[1]];
163
- // 如果改变的是年份或月份,需要重新获取日期列表
164
- if (col === 0 || col === 1) {
165
- this.dateOptions = getDateOptions(toDate(`${year}-${month}-01`));
166
- newPickerValue[2] = Math.min(
167
- newPickerValue[2] === -1 ? this.dateOptions.length - 1 : newPickerValue[2],
168
- this.dateOptions.length - 1
169
- );
170
- }
171
- const date = this.dateOptions[newPickerValue[2]];
172
- const hour = this.hourOptions[newPickerValue[3]];
173
- const minute = this.minuteStep >= 0 ? this.minuteOptions[newPickerValue[4]] : '00';
174
-
175
- this.pickerValue = newPickerValue;
176
- const dateValue = new Date(year, month - 1, date, hour, minute, 0, 0);
177
- let dateStr = formatDate(dateValue, formatDate.DATE_MINUTE_WITH_YEAR);
178
- if (this.min && dateStr < this.min) {
179
- dateStr = this.min;
180
- }
181
- if (this.max && dateStr > this.max) {
182
- dateStr = this.max;
183
- }
184
- this.currentValue = dateStr;
185
- },
186
- /** 滑动结束处理,保持选择器显示值与输入框值一致 */
187
- onPickEnd() {
188
- this.$nextTick(() => {
189
- this.syncPickerValue(this.currentValue);
190
- });
191
- },
192
- },
193
- };
194
- </script>
195
-
196
- <style lang="scss" module>
197
- @use '../../styles/index' as *;
198
-
199
- .DatetimePickerView {
200
- margin-top: 24rpx;
201
- height: 300rpx;
202
- }
203
- .DatetimePickerView--option {
204
- display: flex;
205
- align-items: center;
206
- justify-content: center;
207
- white-space: nowrap;
208
- }
209
- </style>
@@ -1,235 +0,0 @@
1
- <template>
2
- <view :class="componentClassNames">
3
- <selector-cpn
4
- :value="showText"
5
- :readonly="isReadonly"
6
- :disabled="disabled"
7
- :placeholder="placeholder"
8
- @click="showPicker"
9
- />
10
-
11
- <!-- 选择器弹框 -->
12
- <popup-cpn
13
- v-if="isShowPopup"
14
- ref="popup"
15
- v-model="isShowPopup"
16
- :cancel="false"
17
- title="选择时间"
18
- @click.native.stop.prevent="doNothing"
19
- @confirm="onConfirm"
20
- >
21
- <view v-show="checkedDateType === 'start'">
22
- <datetime-picker-view-cpn
23
- v-model="startStr"
24
- :min="min"
25
- :max="max"
26
- :minute-step="minuteStep"
27
- @change="onChangeStart"
28
- />
29
- </view>
30
- <view v-if="range" v-show="checkedDateType === 'end'">
31
- <datetime-picker-view-cpn
32
- v-model="endStr"
33
- :min="min"
34
- :max="max"
35
- :minute-step="minuteStep"
36
- @change="onChangeEnd"
37
- />
38
- </view>
39
- </popup-cpn>
40
- </view>
41
- </template>
42
-
43
- <script>
44
- import componentName from '../../mixins/componentName';
45
- import { isFormReadonly } from '../form/mixin';
46
- import { formatDate, toDate } from '../../utils/date';
47
- import PopupCpn from '../popup/popup.vue';
48
- import SelectorCpn from '../selector/selector.vue';
49
- import DatetimePickerViewCpn from './datetime-picker-view.vue';
50
-
51
- const DATE_FORMAT = formatDate.DATE_MINUTE_WITH_YEAR;
52
-
53
- export default {
54
- name: 'DatetimePicker',
55
- options: {
56
- virtualHost: true,
57
- styleIsolation: 'shared',
58
- },
59
- components: { PopupCpn, SelectorCpn, DatetimePickerViewCpn },
60
- mixins: [componentName, isFormReadonly],
61
- props: {
62
- /** 绑定值 */
63
- value: {
64
- type: [String, null, Array],
65
- default: null,
66
- },
67
- /** 是否禁用 */
68
- disabled: {
69
- type: Boolean,
70
- default: false,
71
- },
72
- /** 是否只读 */
73
- readonly: {
74
- type: Boolean,
75
- default: false,
76
- },
77
- /** 占位文本 */
78
- placeholder: {
79
- type: String,
80
- default: '请选择',
81
- },
82
- /** 是否日期范围选择器 */
83
- range: {
84
- type: Boolean,
85
- default: false,
86
- },
87
- /** 是否在选择开始日期时,同步修改结束日期,且保持时间差 */
88
- keepDiff: {
89
- type: Boolean,
90
- default: false,
91
- },
92
- /** 结束日期是否可以与开始日期相同 */
93
- canSame: {
94
- type: Boolean,
95
- default: true,
96
- },
97
- /** 在选择的日期后面拼接时间字符串,如果传入的是数组,会分别对应添加到范围日期值后 */
98
- timeAppend: {
99
- type: [String, Array],
100
- default: '',
101
- },
102
- /** 最小日期 */
103
- min: {
104
- type: String,
105
- default: '',
106
- },
107
- /** 最大日期 */
108
- max: {
109
- type: String,
110
- default: '',
111
- },
112
- /** 分钟步长 */
113
- minuteStep: {
114
- type: Number,
115
- default: 5,
116
- },
117
- },
118
- data() {
119
- return {
120
- /** 选择框是否可见 */
121
- isShowPopup: false,
122
- /** 开始日期 */
123
- startStr: '',
124
- /** 结束日期 */
125
- endStr: '',
126
- /** 当前选中的日期类型 */
127
- checkedDateType: 'start',
128
- };
129
- },
130
- computed: {
131
- isReadonly() {
132
- return this.readonly || this.isFormReadonly;
133
- },
134
- startDate() {
135
- return formatDate(
136
- toDate(this.range && Array.isArray(this.value) ? this.value[0] : this.value),
137
- DATE_FORMAT
138
- );
139
- },
140
- endDate() {
141
- return formatDate(toDate(this.range && Array.isArray(this.value) ? this.value[1] : ''), DATE_FORMAT);
142
- },
143
- showText() {
144
- return this.range && this.startDate && this.endDate
145
- ? `${this.startDate} 至 ${this.endDate}`
146
- : this.startDate;
147
- },
148
- },
149
- methods: {
150
- doNothing() {},
151
- /** 显示选择器弹出框 */
152
- showPicker() {
153
- if (this.disabled || this.isReadonly) {
154
- return;
155
- }
156
-
157
- this.checkedDateType = 'start';
158
-
159
- let [start, end] = [this.startDate, this.endDate];
160
- this.startStr = start || formatDate(new Date(), DATE_FORMAT);
161
- this.endStr = end || formatDate(new Date(Date.now() + (this.canSame ? 0 : 8.64e7)), DATE_FORMAT);
162
-
163
- this.isShowPopup = true;
164
- this.$emit('show');
165
- },
166
- /** 选中日期类型 */
167
- selectDateType(type) {
168
- if (this.checkedDateType !== type) this.checkedDateType = type;
169
- },
170
- onChangeStart(dateStr) {
171
- if (!this.range) return;
172
-
173
- const timeDiff = toDate(this.endStr).getTime() - toDate(dateStr).getTime();
174
- if (this.endStr <= dateStr || this.keepDiff) {
175
- if (this.keepDiff) {
176
- this.endStr = formatDate(new Date(toDate(dateStr).getTime() + timeDiff), DATE_FORMAT);
177
- } else if (!this.canSame) {
178
- this.endStr = formatDate(new Date(toDate(dateStr).getTime() + 8.64e7), DATE_FORMAT);
179
- } else {
180
- this.endStr = dateStr;
181
- }
182
- }
183
- },
184
- onChangeEnd(dateStr) {
185
- if (dateStr <= this.startStr) {
186
- if (this.canSame) {
187
- this.endStr = this.startStr;
188
- } else {
189
- this.endStr = formatDate(new Date(toDate(this.startStr).getTime() + 8.64e7), DATE_FORMAT);
190
- }
191
- }
192
- },
193
- onConfirm() {
194
- this.$refs.popup.close();
195
- const startAppend = Array.isArray(this.timeAppend) ? this.timeAppend[0] : this.timeAppend;
196
- const endAppend = Array.isArray(this.timeAppend) ? this.timeAppend[1] : this.timeAppend;
197
- const start = startAppend ? `${this.startStr} ${startAppend}` : this.startStr;
198
- const end = endAppend ? `${this.endStr} ${endAppend}` : this.endStr;
199
- this.$emit('input', this.range ? [start, end] : start);
200
- this.$emit('change', this.range ? [start, end] : start);
201
- },
202
- },
203
- };
204
- </script>
205
-
206
- <style lang="scss" module>
207
- @use '../../styles/index' as *;
208
-
209
- .DatetimePicker {
210
- width: 100%;
211
- height: 100%;
212
- }
213
- .DatetimePicker--date-input {
214
- flex: 1;
215
- display: flex;
216
- justify-content: center;
217
- align-items: center;
218
- height: 72rpx;
219
- background: UTILS__GLOBAL_VAR(color, background);
220
- border-radius: 12rpx;
221
- transition: color 0.3s, background 0.3s;
222
- &.DatetimePicker--date-checked {
223
- background: UTILS__GLOBAL_VAR(theme, primary);
224
- color: #fff;
225
- }
226
- }
227
- .DatetimePicker--date-separator {
228
- margin: 0 20rpx;
229
- }
230
- .DatetimePicker--date-selector {
231
- margin-top: 20rpx;
232
- display: flex;
233
- align-items: center;
234
- }
235
- </style>
@@ -1,13 +0,0 @@
1
- export const isFormReadonly = {
2
- inject: {
3
- formProps: {
4
- from: 'formProps',
5
- default: {},
6
- },
7
- },
8
- computed: {
9
- isFormReadonly() {
10
- return this.formProps.readonly;
11
- },
12
- },
13
- };