@yxhl/specter-pui-vtk 1.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 (49) hide show
  1. package/README.md +122 -0
  2. package/dist/specter-pui-vtk.css +1 -0
  3. package/dist/specter-pui.es.js +3289 -0
  4. package/dist/specter-pui.es.js.map +1 -0
  5. package/dist/specter-pui.umd.js +2 -0
  6. package/dist/specter-pui.umd.js.map +1 -0
  7. package/package.json +65 -0
  8. package/src/assets/css/globals.scss +250 -0
  9. package/src/assets/css/index.scss +271 -0
  10. package/src/assets/css/settings.scss +10 -0
  11. package/src/assets/css/variables.scss +0 -0
  12. package/src/assets/icon/logo.svg +9 -0
  13. package/src/assets/img/background.png +0 -0
  14. package/src/assets/img/dtx.png +0 -0
  15. package/src/commons/filters/dictionary.js +75 -0
  16. package/src/commons/filters/format.js +112 -0
  17. package/src/commons/filters/mask.js +25 -0
  18. package/src/commons/index.js +17 -0
  19. package/src/commons/request.js +89 -0
  20. package/src/commons/storage.js +41 -0
  21. package/src/commons/themes.js +153 -0
  22. package/src/commons/validation.js +72 -0
  23. package/src/components/README.md +35 -0
  24. package/src/components/assembly/VtkArea.vue +259 -0
  25. package/src/components/assembly/VtkCheckbox.vue +168 -0
  26. package/src/components/assembly/VtkCount.vue +403 -0
  27. package/src/components/assembly/VtkDatePicker.vue +326 -0
  28. package/src/components/assembly/VtkEmpty.vue +107 -0
  29. package/src/components/assembly/VtkFab.vue +78 -0
  30. package/src/components/assembly/VtkFormItem.vue +166 -0
  31. package/src/components/assembly/VtkImg.vue +372 -0
  32. package/src/components/assembly/VtkPage.vue +156 -0
  33. package/src/components/assembly/VtkPdf.vue +424 -0
  34. package/src/components/assembly/VtkProj.vue +539 -0
  35. package/src/components/assembly/VtkRadio.vue +82 -0
  36. package/src/components/assembly/VtkSearch.vue +145 -0
  37. package/src/components/assembly/VtkSelect.vue +104 -0
  38. package/src/components/assembly/VtkStepper.vue +160 -0
  39. package/src/components/message/alert.vue +31 -0
  40. package/src/components/message/confirm.vue +44 -0
  41. package/src/components/message/index.js +55 -0
  42. package/src/components/message/loading.vue +33 -0
  43. package/src/components/message/prompt.vue +57 -0
  44. package/src/components/message/toast.vue +45 -0
  45. package/src/components/message/vtkMessage.vue +27 -0
  46. package/src/composables/useMixins.js +2 -0
  47. package/src/composables/usePage.js +311 -0
  48. package/src/index.js +109 -0
  49. package/src/stores/message.js +79 -0
@@ -0,0 +1,403 @@
1
+ <!--
2
+
3
+ // 基础用法
4
+ <VtkCount :start="0" :end="12345" :duration="3000" suffix="次" />
5
+
6
+ //带前缀和分隔符
7
+ <VtkCount
8
+ :start="0"
9
+ :end="999999"
10
+ :duration="2000"
11
+ prefix="¥"
12
+ suffix="元"
13
+ :separator="true"
14
+ />
15
+
16
+ //手动控制
17
+ <VtkCount
18
+ ref="countRef"
19
+ :start="0"
20
+ :end="100"
21
+ :autoplay="false"
22
+ @complete="onComplete"
23
+ />
24
+ <VBtn @click="startCount">开始计数</VBtn>
25
+ -->
26
+
27
+ <template>
28
+ <div class="vtk-count" :class="containerClass">
29
+ <div class="count-display" :style="{ color: color }">
30
+ <!-- 前缀 -->
31
+ <span v-if="prefix" class="prefix">{{ prefix }}</span>
32
+
33
+ <!-- 数字容器 -->
34
+ <div class="digits-container">
35
+ <div
36
+ v-for="(digitChar, index) in maxDigitString"
37
+ :key="`digit-${index}-${digitKey}`"
38
+ class="digit-wrapper"
39
+ :class="{ 'digit-separator': isNaN(digitChar) }"
40
+ >
41
+ <div
42
+ v-if="!isNaN(digitChar)"
43
+ class="digit-scroll"
44
+ >
45
+ <div
46
+ v-for="n in 10"
47
+ :key="n"
48
+ class="digit-item"
49
+ :style="getDigitStyle(getCurrentDigit(index), n-1)"
50
+ >
51
+ {{ n-1 }}
52
+ </div>
53
+ </div>
54
+ <div v-else class="separator">{{ digitChar }}</div>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- 后缀 -->
59
+ <span v-if="suffix" class="suffix">{{ suffix }}</span>
60
+ </div>
61
+ </div>
62
+ </template>
63
+
64
+ <script setup>
65
+ import { ref, computed, onMounted, watch, nextTick } from 'vue'
66
+
67
+ // 定义组件名称
68
+ defineOptions({
69
+ name: "VtkCount",
70
+ inheritAttrs: false,
71
+ });
72
+
73
+ // 定义 props
74
+ const props = defineProps({
75
+ // 初始值
76
+ start: {
77
+ type: Number,
78
+ default: 0
79
+ },
80
+ // 目标值
81
+ end: {
82
+ type: Number,
83
+ default: 0
84
+ },
85
+ // 动画持续时间(毫秒)
86
+ duration: {
87
+ type: Number,
88
+ default: 2000
89
+ },
90
+ // 是否自动开始动画
91
+ autoplay: {
92
+ type: Boolean,
93
+ default: true
94
+ },
95
+ // 数字前缀
96
+ prefix: {
97
+ type: String,
98
+ default: ''
99
+ },
100
+ // 数字后缀
101
+ suffix: {
102
+ type: String,
103
+ default: ''
104
+ },
105
+ // 容器自定义类名
106
+ containerClass: {
107
+ type: String,
108
+ default: ''
109
+ },
110
+ // 是否使用分隔符(千分位)
111
+ separator: {
112
+ type: Boolean,
113
+ default: false
114
+ },
115
+ // 小数位数
116
+ decimals: {
117
+ type: Number,
118
+ default: 0
119
+ },
120
+ // 文字颜色
121
+ color: {
122
+ type: String,
123
+ default: '#1976d2'
124
+ }
125
+ })
126
+
127
+ // 定义 emits
128
+ const emit = defineEmits(['complete'])
129
+
130
+ // 当前显示的数值
131
+ const currentNumber = ref(props.start)
132
+
133
+ // 当前每个位置的数字值
134
+ const currentDigits = ref([])
135
+
136
+ // 用于强制重新渲染的key
137
+ const digitKey = ref(0)
138
+
139
+ // 是否正在动画中
140
+ const isAnimating = ref(false)
141
+
142
+ // 格式化数字(处理小数和分隔符)
143
+ const formatNumber = (num) => {
144
+ let result = num.toFixed(props.decimals)
145
+ if (props.separator && props.decimals === 0) {
146
+ result = result.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
147
+ }
148
+ return result
149
+ }
150
+
151
+ // 获取目标数字字符串
152
+ const targetNumberString = computed(() => {
153
+ return formatNumber(props.end)
154
+ })
155
+
156
+ // 获取起始数字字符串
157
+ const startNumberString = computed(() => {
158
+ return formatNumber(props.start)
159
+ })
160
+
161
+ // 获取最大位数的字符串(用于保持DOM结构稳定)
162
+ const maxDigitString = computed(() => {
163
+ const target = targetNumberString.value
164
+ const start = startNumberString.value
165
+ return target.length >= start.length ? target : start
166
+ })
167
+
168
+ // 获取当前指定位置的数字
169
+ const getCurrentDigit = (index) => {
170
+ if (index < currentDigits.value.length) {
171
+ return currentDigits.value[index]
172
+ }
173
+ return '0'
174
+ }
175
+
176
+ // 初始化数字数组
177
+ const initDigitsArray = () => {
178
+ const targetStr = targetNumberString.value
179
+ const startStr = startNumberString.value
180
+
181
+ // 初始化currentDigits数组,确保长度与maxDigitString一致
182
+ currentDigits.value = maxDigitString.value.split('').map((char, index) => {
183
+ if (isNaN(char)) {
184
+ return char // 分隔符保持原样
185
+ }
186
+
187
+ // 从起始字符串对应位置获取数字
188
+ if (index < startStr.length) {
189
+ const startChar = startStr[index]
190
+ return isNaN(startChar) ? '0' : startChar
191
+ }
192
+ return '0'
193
+ })
194
+ }
195
+
196
+ // 获取数字样式
197
+ const getDigitStyle = (currentValue, digitValue) => {
198
+ // 如果是分隔符,直接返回
199
+ if (isNaN(currentValue) || isNaN(digitValue)) {
200
+ return {
201
+ transform: 'translateY(0)'
202
+ }
203
+ }
204
+
205
+ const current = parseInt(currentValue) || 0
206
+ const target = digitValue
207
+ const offset = target - current
208
+
209
+ // 处理循环滚动(9->0 或 0->9)
210
+ let translateY
211
+ if (offset > 5) {
212
+ // 向下滚动(如 9->0)
213
+ translateY = -(10 - offset) * 100
214
+ } else if (offset < -5) {
215
+ // 向上滚动(如 0->9)
216
+ translateY = (10 + offset) * 100
217
+ } else {
218
+ translateY = -offset * 100
219
+ }
220
+
221
+ return {
222
+ transform: `translateY(${translateY}%)`,
223
+ transition: isAnimating.value ? 'transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94)' : 'none',
224
+ color: props.color
225
+ }
226
+ }
227
+
228
+ // 开始数字动画
229
+ const startAnimation = () => {
230
+ if (isAnimating.value) return
231
+
232
+ isAnimating.value = true
233
+ const startTime = performance.now()
234
+ const startValue = props.start
235
+ const endValue = props.end
236
+ const diff = endValue - startValue
237
+
238
+ // 初始化数字数组
239
+ initDigitsArray()
240
+
241
+ const animate = (currentTime) => {
242
+ const elapsed = currentTime - startTime
243
+ const progress = Math.min(elapsed / props.duration, 1)
244
+
245
+ // 使用缓动函数使动画更自然
246
+ const easeOutQuart = 1 - Math.pow(1 - progress, 4)
247
+ currentNumber.value = startValue + diff * easeOutQuart
248
+
249
+ // 更新每个数字位的当前值
250
+ updateDigitArray(currentNumber.value)
251
+
252
+ if (progress < 1) {
253
+ requestAnimationFrame(animate)
254
+ } else {
255
+ currentNumber.value = endValue
256
+ // 确保最终值准确
257
+ updateDigitArray(endValue)
258
+ isAnimating.value = false
259
+ emit('complete')
260
+ }
261
+ }
262
+
263
+ requestAnimationFrame(animate)
264
+ }
265
+
266
+ // 更新数字数组
267
+ const updateDigitArray = (value) => {
268
+ const valueStr = formatNumber(value)
269
+ const maxStr = maxDigitString.value
270
+
271
+ // 保持数组长度不变,只更新值
272
+ for (let i = 0; i < maxStr.length; i++) {
273
+ if (i < valueStr.length) {
274
+ currentDigits.value[i] = valueStr[i]
275
+ } else {
276
+ // 超出部分保持为'0'或分隔符
277
+ currentDigits.value[i] = isNaN(maxStr[i]) ? maxStr[i] : '0'
278
+ }
279
+ }
280
+ }
281
+
282
+ // 监听 end 值变化,重新开始动画
283
+ watch(() => props.end, (newVal, oldVal) => {
284
+ if (newVal !== oldVal) {
285
+ // 当位数可能发生变化时,更新key强制重新渲染
286
+ const newStr = formatNumber(newVal)
287
+ const oldStr = formatNumber(oldVal)
288
+ if (newStr.length !== oldStr.length) {
289
+ digitKey.value += 1
290
+ nextTick(() => {
291
+ startAnimation()
292
+ })
293
+ } else {
294
+ startAnimation()
295
+ }
296
+ }
297
+ })
298
+
299
+ // 组件挂载时开始动画
300
+ onMounted(() => {
301
+ if (props.autoplay) {
302
+ // 检查初始值和目标值位数是否不同,如果不同则更新key
303
+ const startStr = startNumberString.value
304
+ const endStr = targetNumberString.value
305
+ if (startStr.length !== endStr.length) {
306
+ digitKey.value += 1
307
+ }
308
+
309
+ nextTick(() => {
310
+ startAnimation()
311
+ })
312
+ }
313
+ })
314
+
315
+ // 提供外部调用方法
316
+ defineExpose({
317
+ startAnimation
318
+ })
319
+ </script>
320
+
321
+ <style scoped>
322
+ .vtk-count {
323
+ display: inline-block;
324
+ }
325
+
326
+ .count-display {
327
+ display: flex;
328
+ align-items: center;
329
+ font-family: 'Roboto Mono', monospace;
330
+ font-size: 24px;
331
+ font-weight: bold;
332
+ }
333
+
334
+ .digits-container {
335
+ display: flex;
336
+ align-items: center;
337
+ }
338
+
339
+ .digit-wrapper {
340
+ position: relative;
341
+ width: 1em;
342
+ height: 1em;
343
+ overflow: hidden;
344
+ text-align: center;
345
+ margin: 0 1px;
346
+ }
347
+
348
+ .digit-wrapper.digit-separator {
349
+ width: auto;
350
+ animation: none;
351
+ }
352
+
353
+ .digit-scroll {
354
+ position: relative;
355
+ height: 100%;
356
+ }
357
+
358
+ .digit-item {
359
+ position: absolute;
360
+ top: 0;
361
+ left: 0;
362
+ width: 100%;
363
+ height: 100%;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ }
368
+
369
+ .separator {
370
+ display: flex;
371
+ align-items: center;
372
+ justify-content: center;
373
+ height: 100%;
374
+ font-weight: normal;
375
+ }
376
+
377
+ .prefix, .suffix {
378
+ font-size: 0.8em;
379
+ align-self: flex-end;
380
+ margin-bottom: 0.1em;
381
+ }
382
+
383
+ .prefix {
384
+ margin-right: 4px;
385
+ }
386
+
387
+ .suffix {
388
+ margin-left: 4px;
389
+ }
390
+
391
+ /* 响应式设计 */
392
+ @media (max-width: 768px) {
393
+ .count-display {
394
+ font-size: 20px;
395
+ }
396
+ }
397
+
398
+ @media (max-width: 480px) {
399
+ .count-display {
400
+ font-size: 18px;
401
+ }
402
+ }
403
+ </style>
@@ -0,0 +1,326 @@
1
+ <template>
2
+ <div class="vtk-date-range-picker">
3
+ <VMenu
4
+ v-model="menu"
5
+ :close-on-content-click="false"
6
+ transition="scale-transition"
7
+ offset-y
8
+ min-width="auto"
9
+ eager
10
+ >
11
+ <template #activator="{ props: activatorProps }">
12
+ <VTextField
13
+ v-bind="{ ...activatorProps, ...textFieldProps }"
14
+ v-model="displayValue"
15
+ :placeholder="placeholder"
16
+ :disabled="disabled"
17
+ readonly
18
+ clearable
19
+ density="compact"
20
+ variant="outlined"
21
+ @click:clear="clearValue"
22
+ >
23
+ <template #prepend-inner>
24
+ <VIcon size="small">mdi-calendar</VIcon>
25
+ </template>
26
+ </VTextField>
27
+ </template>
28
+
29
+ <VCard style="width: 650px;">
30
+ <VCardText class="pa-0">
31
+ <div class="date-picker-container">
32
+ <VDatePicker
33
+ v-model="startDateRange"
34
+ hide-header
35
+ :min="minDate"
36
+ :max="maxDate"
37
+ :show-current="showCurrent"
38
+ @update:model-value="onStartDateChange"
39
+ class="date-picker-item"
40
+ />
41
+ <VDatePicker
42
+ v-model="endDateRange"
43
+ hide-header
44
+ :min="minDate"
45
+ :max="maxDate"
46
+ :show-current="showCurrent"
47
+ @update:model-value="onEndDateChange"
48
+ class="date-picker-item"
49
+ />
50
+ </div>
51
+ </VCardText>
52
+
53
+ <VDivider></VDivider>
54
+
55
+ <VCardActions>
56
+ <VSpacer></VSpacer>
57
+ <VBtn text @click="cancel">取消</VBtn>
58
+ <VBtn color="primary" variant="tonal" @click="confirm">确定</VBtn>
59
+ </VCardActions>
60
+ </VCard>
61
+ </VMenu>
62
+ </div>
63
+ </template>
64
+
65
+ <script setup>
66
+ import { ref, computed, watch, useAttrs } from 'vue';
67
+
68
+ // 定义组件名称
69
+ defineOptions({
70
+ name: "VtkDatePicker",
71
+ inheritAttrs: false,
72
+ });
73
+
74
+ // 定义props
75
+ const props = defineProps({
76
+ // v-model绑定值
77
+ modelValue: {
78
+ type: [Array, String],
79
+ default: () => []
80
+ },
81
+ // 占位符
82
+ placeholder: {
83
+ type: String,
84
+ default: '请选择日期范围'
85
+ },
86
+ // 禁用状态
87
+ disabled: {
88
+ type: Boolean,
89
+ default: false
90
+ },
91
+ // 最小日期
92
+ min: {
93
+ type: String,
94
+ default: undefined
95
+ },
96
+ // 最大日期
97
+ max: {
98
+ type: String,
99
+ default: undefined
100
+ },
101
+ // 是否显示当前日期
102
+ showCurrent: {
103
+ type: [Boolean, String],
104
+ default: true
105
+ },
106
+ // 显示格式
107
+ format: {
108
+ type: String,
109
+ default: 'YYYY-MM-DD'
110
+ },
111
+ // 分隔符
112
+ separator: {
113
+ type: String,
114
+ default: ' 至 '
115
+ }
116
+ });
117
+
118
+ // 定义emit事件
119
+ const emit = defineEmits(['update:modelValue', 'change']);
120
+
121
+ // 获取透传属性
122
+ const attrs = useAttrs();
123
+
124
+ // 菜单显示控制
125
+ const menu = ref(false);
126
+
127
+ // 日期范围值
128
+ const dateRange = ref([]);
129
+
130
+ // 为了横向排列,需要分别管理开始和结束日期选择器的值
131
+ const startDateRange = ref(null);
132
+ const endDateRange = ref(null);
133
+
134
+
135
+ // 显示值
136
+ const displayValue = computed(() => {
137
+ if (!dateRange.value || dateRange.value.length === 0) {
138
+ return '';
139
+ }
140
+
141
+ if (dateRange.value.length === 1) {
142
+ return formatDate(dateRange.value[0]);
143
+ }
144
+
145
+ return `${formatDate(dateRange.value[0])}${props.separator}${formatDate(dateRange.value[1])}`;
146
+ });
147
+
148
+ // 文本框属性
149
+ const textFieldProps = computed(() => {
150
+ // 排除我们自己使用的属性
151
+ const {
152
+ modelValue,
153
+ placeholder,
154
+ disabled,
155
+ min,
156
+ max,
157
+ showCurrent,
158
+ format,
159
+ separator,
160
+ ...rest
161
+ } = attrs;
162
+
163
+ return rest;
164
+ });
165
+
166
+ // 格式化日期
167
+ const formatDate = (date) => {
168
+ if (!date) return '';
169
+
170
+ // 如果是字符串直接返回
171
+ if (typeof date === 'string') {
172
+ return date;
173
+ }
174
+
175
+ // 如果是 Date 对象
176
+ if (date instanceof Date) {
177
+ const year = date.getFullYear();
178
+ const month = String(date.getMonth() + 1).padStart(2, '0');
179
+ const day = String(date.getDate()).padStart(2, '0');
180
+ return `${year}-${month}-${day}`;
181
+ }
182
+
183
+ return date;
184
+ };
185
+
186
+ // 最小日期
187
+ const minDate = computed(() => props.min);
188
+
189
+ // 最大日期
190
+ const maxDate = computed(() => props.max);
191
+
192
+ // 监听外部modelValue变化
193
+ watch(
194
+ () => props.modelValue,
195
+ (newValue) => {
196
+ if (Array.isArray(newValue) && newValue.length > 0) {
197
+ dateRange.value = [...newValue];
198
+ // 同步到单独的日期选择器
199
+ if (newValue[0]) startDateRange.value = newValue[0];
200
+ if (newValue[1]) endDateRange.value = newValue[1];
201
+ } else if (typeof newValue === 'string' && newValue) {
202
+ // 处理字符串形式的日期范围,如 "2023-01-01 至 2023-01-31"
203
+ const dates = newValue.split(props.separator);
204
+ if (dates.length === 2) {
205
+ dateRange.value = [dates[0].trim(), dates[1].trim()];
206
+ startDateRange.value = dates[0].trim();
207
+ endDateRange.value = dates[1].trim();
208
+ } else {
209
+ dateRange.value = [];
210
+ startDateRange.value = null;
211
+ endDateRange.value = null;
212
+ }
213
+ } else {
214
+ dateRange.value = [];
215
+ startDateRange.value = null;
216
+ endDateRange.value = null;
217
+ }
218
+ },
219
+ { immediate: true }
220
+ );
221
+
222
+
223
+ // 开始日期变化处理
224
+ const onStartDateChange = (value) => {
225
+ // 更新日期范围的第一个值
226
+ if (value) {
227
+ dateRange.value = [value, dateRange.value[1] || null];
228
+ } else {
229
+ // 如果开始日期被清除,清除整个范围或只清除开始日期
230
+ if (dateRange.value.length > 0) {
231
+ dateRange.value = [null, dateRange.value[1] || null];
232
+ }
233
+ }
234
+ };
235
+
236
+ // 结束日期变化处理
237
+ const onEndDateChange = (value) => {
238
+ // 更新日期范围的第二个值
239
+ if (value) {
240
+ dateRange.value = [dateRange.value[0] || null, value];
241
+ } else {
242
+ // 如果结束日期被清除,只清除结束日期
243
+ if (dateRange.value.length > 1) {
244
+ dateRange.value = [dateRange.value[0] || null, null];
245
+ }
246
+ }
247
+ };
248
+
249
+ // 清除值
250
+ const clearValue = () => {
251
+ dateRange.value = [];
252
+ startDateRange.value = null;
253
+ endDateRange.value = null;
254
+ emit('update:modelValue', []);
255
+ emit('change', []);
256
+ menu.value = false;
257
+ };
258
+
259
+ // 取消
260
+ const cancel = () => {
261
+ // 恢复到原始值
262
+ if (Array.isArray(props.modelValue)) {
263
+ dateRange.value = [...props.modelValue];
264
+ if (props.modelValue[0]) startDateRange.value = props.modelValue[0];
265
+ if (props.modelValue[1]) endDateRange.value = props.modelValue[1];
266
+ } else {
267
+ dateRange.value = [];
268
+ startDateRange.value = null;
269
+ endDateRange.value = null;
270
+ }
271
+ menu.value = false;
272
+ };
273
+
274
+ // 确认
275
+ const confirm = () => {
276
+ // 确保日期按顺序排列
277
+ let sortedRange = [...dateRange.value].filter(date => date !== null);
278
+ if (sortedRange.length === 2) {
279
+ const date1 = new Date(sortedRange[0]);
280
+ const date2 = new Date(sortedRange[1]);
281
+ if (date1 > date2) {
282
+ sortedRange = [sortedRange[1], sortedRange[0]];
283
+ }
284
+ } else if (sortedRange.length === 1) {
285
+ // 如果只有一个日期,复制为范围
286
+ sortedRange = [sortedRange[0], sortedRange[0]];
287
+ }
288
+
289
+ emit('update:modelValue', sortedRange);
290
+ emit('change', sortedRange);
291
+ menu.value = false;
292
+ };
293
+ </script>
294
+
295
+ <style scoped>
296
+ .vtk-date-range-picker {
297
+ width: 100%;
298
+ }
299
+
300
+ .date-picker-container {
301
+ display: flex;
302
+ flex-direction: row;
303
+ gap: 0;
304
+ }
305
+
306
+ .date-picker-item {
307
+ flex: 1;
308
+ max-width: 50%;
309
+ }
310
+
311
+ .date-picker-item:first-child {
312
+ border-right: 1px solid rgba(0, 0, 0, 0.12);
313
+ }
314
+
315
+ :deep(.v-picker) {
316
+ border-radius: 0;
317
+ }
318
+
319
+ :deep(.v-date-picker-month__day--adjacent) {
320
+ opacity: 0.5;
321
+ }
322
+
323
+ :deep(.v-date-picker-title) {
324
+ display: none;
325
+ }
326
+ </style>