@yxhl/specter-pui-vtk 1.0.91 → 1.0.93
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/dist/specter-pui-vtk.css +1 -1
- package/dist/specter-pui.es.js +2803 -2729
- package/dist/specter-pui.es.js.map +1 -1
- package/dist/specter-pui.umd.js +1 -1
- package/dist/specter-pui.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/components/assembly/VtkDateSelector.vue +47 -17
- package/src/components/assembly/VtkEmptyNew.vue +16 -18
- package/src/components/assembly/VtkUpload.vue +59 -29
- package/src/index.js +15 -11
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="vtk-date-selector">
|
|
2
|
+
<div class="vtk-date-selector" :style="rootStyle">
|
|
3
3
|
<VMenu
|
|
4
4
|
v-if="!inline"
|
|
5
5
|
v-model="showPicker"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
</VTextField>
|
|
27
27
|
</div>
|
|
28
28
|
</template>
|
|
29
|
-
<div class="date-picker-dropdown" @click.stop @mousedown.stop>
|
|
29
|
+
<div class="date-picker-dropdown" :style="popupStyle" @click.stop @mousedown.stop>
|
|
30
30
|
<!-- 头部控制区(非日期/月份范围选择模式时显示) -->
|
|
31
31
|
<div v-if="!(mode === 'range' && (displayMode === 'day' || displayMode === 'month'))" class="picker-header">
|
|
32
32
|
<button class="nav-btn" @click="prevPeriod">‹</button>
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
</div>
|
|
223
223
|
</div>
|
|
224
224
|
</VMenu>
|
|
225
|
-
<div v-if="inline" class="date-picker-dropdown inline-mode" @click.stop @mousedown.stop>
|
|
225
|
+
<div v-if="inline" class="date-picker-dropdown inline-mode" :style="popupStyle" @click.stop @mousedown.stop>
|
|
226
226
|
<!-- 头部控制区(非日期/月份范围选择模式时显示) -->
|
|
227
227
|
<div v-if="!(mode === 'range' && (displayMode === 'day' || displayMode === 'month'))" class="picker-header">
|
|
228
228
|
<button class="nav-btn" @click="prevPeriod">‹</button>
|
|
@@ -449,10 +449,20 @@ const props = defineProps({
|
|
|
449
449
|
validator: (value) => ['single', 'range'].includes(value)
|
|
450
450
|
},
|
|
451
451
|
// 占位符
|
|
452
|
-
placeholder: {
|
|
453
|
-
type: String,
|
|
454
|
-
default: '请选择日期'
|
|
455
|
-
},
|
|
452
|
+
placeholder: {
|
|
453
|
+
type: String,
|
|
454
|
+
default: '请选择日期'
|
|
455
|
+
},
|
|
456
|
+
// 输入框宽度,数字按 px 处理,也支持 100%、12rem 等 CSS 宽度
|
|
457
|
+
width: {
|
|
458
|
+
type: [String, Number],
|
|
459
|
+
default: ''
|
|
460
|
+
},
|
|
461
|
+
// 日期弹层宽度,数字按 px 处理,也支持 100%、24rem 等 CSS 宽度
|
|
462
|
+
popupWidth: {
|
|
463
|
+
type: [String, Number],
|
|
464
|
+
default: ''
|
|
465
|
+
},
|
|
456
466
|
// 禁用状态
|
|
457
467
|
disabled: {
|
|
458
468
|
type: Boolean,
|
|
@@ -533,12 +543,29 @@ const YEAR_PANEL_CENTER_OFFSET = 4;
|
|
|
533
543
|
const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
|
|
534
544
|
const monthList = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
|
|
535
545
|
|
|
536
|
-
// 显示值
|
|
537
|
-
const menuAttach = computed(() => (props.noTeleport ? true : false));
|
|
538
|
-
|
|
539
|
-
const menuLocation = computed(() => props.placement === 'right' ? 'bottom end' : 'bottom start');
|
|
540
|
-
|
|
541
|
-
const menuOrigin = computed(() => props.placement === 'right' ? 'top end' : 'top start');
|
|
546
|
+
// 显示值
|
|
547
|
+
const menuAttach = computed(() => (props.noTeleport ? true : false));
|
|
548
|
+
|
|
549
|
+
const menuLocation = computed(() => props.placement === 'right' ? 'bottom end' : 'bottom start');
|
|
550
|
+
|
|
551
|
+
const menuOrigin = computed(() => props.placement === 'right' ? 'top end' : 'top start');
|
|
552
|
+
|
|
553
|
+
const normalizeCssSize = (value) => {
|
|
554
|
+
if (value === null || value === undefined || value === '') return '';
|
|
555
|
+
if (typeof value === 'number') return `${value}px`;
|
|
556
|
+
const nextValue = String(value).trim();
|
|
557
|
+
return /^\d+(\.\d+)?$/.test(nextValue) ? `${nextValue}px` : nextValue;
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const rootStyle = computed(() => {
|
|
561
|
+
const width = normalizeCssSize(props.width);
|
|
562
|
+
return width ? { width } : {};
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const popupStyle = computed(() => {
|
|
566
|
+
const width = normalizeCssSize(props.popupWidth);
|
|
567
|
+
return width ? { width } : {};
|
|
568
|
+
});
|
|
542
569
|
|
|
543
570
|
const displayValue = computed(() => {
|
|
544
571
|
if (!selectedValue.value) return '';
|
|
@@ -1206,10 +1233,13 @@ watch(() => props.modelValue, (newValue) => {
|
|
|
1206
1233
|
color: rgb(var(--v-theme-on-surface), 0.87);
|
|
1207
1234
|
}
|
|
1208
1235
|
|
|
1209
|
-
.date-picker-dropdown {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1236
|
+
.date-picker-dropdown {
|
|
1237
|
+
box-sizing: border-box;
|
|
1238
|
+
max-width: calc(100vw - 24px);
|
|
1239
|
+
overflow-x: auto;
|
|
1240
|
+
background: rgb(var(--v-theme-surface));
|
|
1241
|
+
border-radius: 8px;
|
|
1242
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
1213
1243
|
border: 1px solid rgb(var(--v-theme-on-surface), 0.12);
|
|
1214
1244
|
}
|
|
1215
1245
|
|
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card class="pa-4 mx-auto text-center pt-10" color="transparent" elevation="0" style="border: none !important;box-shadow: none !important;">
|
|
3
|
-
<v-img contain width="120" class="mx-auto" src="
|
|
4
|
-
<p class="text-grey mt-1">{{ text || "暂无数据" }}</p>
|
|
5
|
-
</v-card>
|
|
6
|
-
</template>
|
|
7
|
-
|
|
8
|
-
<script setup>
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
type: String,
|
|
17
|
-
default: null,
|
|
1
|
+
<template>
|
|
2
|
+
<v-card class="pa-4 mx-auto text-center pt-10" color="transparent" elevation="0" style="border: none !important;box-shadow: none !important;">
|
|
3
|
+
<v-img contain width="120" class="mx-auto" :src="emptyImg" alt="" />
|
|
4
|
+
<p class="text-grey mt-1">{{ text || "暂无数据" }}</p>
|
|
5
|
+
</v-card>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup>
|
|
9
|
+
import emptyImg from '@/assets/img/empty.svg';
|
|
10
|
+
|
|
11
|
+
// 定义 props
|
|
12
|
+
defineProps({
|
|
13
|
+
text: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: null,
|
|
18
16
|
},
|
|
19
17
|
items: {
|
|
20
18
|
type: Array,
|
|
@@ -23,4 +21,4 @@ const props = defineProps({
|
|
|
23
21
|
});
|
|
24
22
|
</script>
|
|
25
23
|
|
|
26
|
-
<style lang="scss" scoped></style>
|
|
24
|
+
<style lang="scss" scoped></style>
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
:key="file.uid"
|
|
27
27
|
class="vtk-upload__picture-card-item"
|
|
28
28
|
>
|
|
29
|
-
<v-img :src="file.url || file.preview" cover class="fill-height" />
|
|
29
|
+
<v-img :key="file.url || file.preview || file.uid" :src="file.url || file.preview" cover class="fill-height" />
|
|
30
30
|
<div class="vtk-upload__picture-card-mask">
|
|
31
31
|
<VBtn icon size="x-small" variant="text" color="white" title="查看" @click.stop="handlePreview(file)">
|
|
32
32
|
<VIcon>mdi-eye</VIcon>
|
|
@@ -297,9 +297,10 @@ const previewTranslateX = ref(0);
|
|
|
297
297
|
const previewTranslateY = ref(0);
|
|
298
298
|
const previewDragState = ref(null);
|
|
299
299
|
|
|
300
|
-
// 内部维护文件列表
|
|
301
|
-
const fileList = ref([]);
|
|
302
|
-
let isSyncingModel = false;
|
|
300
|
+
// 内部维护文件列表
|
|
301
|
+
const fileList = ref([]);
|
|
302
|
+
let isSyncingModel = false;
|
|
303
|
+
const localPreviewUrls = new Set();
|
|
303
304
|
|
|
304
305
|
watch(
|
|
305
306
|
() => props.modelValue,
|
|
@@ -309,10 +310,11 @@ watch(
|
|
|
309
310
|
return;
|
|
310
311
|
}
|
|
311
312
|
|
|
312
|
-
// Accept external URL lists or file object lists.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
313
|
+
// Accept external URL lists or file object lists.
|
|
314
|
+
revokeAllLocalPreviews();
|
|
315
|
+
fileList.value = normalizeFileList(val || []);
|
|
316
|
+
},
|
|
317
|
+
);
|
|
316
318
|
|
|
317
319
|
/* -------------------- 工具函数 -------------------- */
|
|
318
320
|
let uidCounter = 0;
|
|
@@ -384,7 +386,21 @@ const fileIcon = (file) => {
|
|
|
384
386
|
return 'mdi-file-outline';
|
|
385
387
|
};
|
|
386
388
|
|
|
387
|
-
const getPreviewSrc = (file) => file?.url || file?.preview || '';
|
|
389
|
+
const getPreviewSrc = (file) => file?.url || file?.preview || '';
|
|
390
|
+
|
|
391
|
+
const revokeLocalPreview = (file) => {
|
|
392
|
+
const preview = file?.preview;
|
|
393
|
+
if (preview && localPreviewUrls.has(preview)) {
|
|
394
|
+
URL.revokeObjectURL(preview);
|
|
395
|
+
localPreviewUrls.delete(preview);
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const revokeAllLocalPreviews = () => {
|
|
400
|
+
fileList.value.forEach(revokeLocalPreview);
|
|
401
|
+
localPreviewUrls.forEach((url) => URL.revokeObjectURL(url));
|
|
402
|
+
localPreviewUrls.clear();
|
|
403
|
+
};
|
|
388
404
|
|
|
389
405
|
const previewImageList = computed(() => fileList.value.filter((file) => isImage(file) && getPreviewSrc(file)));
|
|
390
406
|
|
|
@@ -444,12 +460,20 @@ const processFiles = (rawFiles) => {
|
|
|
444
460
|
|
|
445
461
|
const file = buildFile(raw, 'ready');
|
|
446
462
|
|
|
447
|
-
//
|
|
448
|
-
if (isImage(raw)) {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
463
|
+
// 生成本地预览地址,保证 picture-card 选图后立即回显缩略图。
|
|
464
|
+
if (isImage(raw)) {
|
|
465
|
+
if (typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
466
|
+
file.preview = URL.createObjectURL(raw);
|
|
467
|
+
localPreviewUrls.add(file.preview);
|
|
468
|
+
} else {
|
|
469
|
+
const reader = new FileReader();
|
|
470
|
+
reader.onload = (e) => {
|
|
471
|
+
file.preview = e.target.result;
|
|
472
|
+
emit('change', file, fileList.value);
|
|
473
|
+
};
|
|
474
|
+
reader.readAsDataURL(raw);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
453
477
|
|
|
454
478
|
const beforeHook = props.beforeUpload ? props.beforeUpload(raw) : true;
|
|
455
479
|
Promise.resolve(beforeHook).then((result) => {
|
|
@@ -512,9 +536,12 @@ const uploadFile = (file) => {
|
|
|
512
536
|
Request.http(props.action, formData, 'POST', headers).then((res) => {
|
|
513
537
|
file.status = 'success';
|
|
514
538
|
file.response = res;
|
|
515
|
-
// 兼容 { data: 'url' } 和 { data: { url: '...' } } 两种结构
|
|
516
|
-
const url = typeof res?.data === 'string' ? res.data : (res?.data?.url || res?.url || '');
|
|
517
|
-
if (url)
|
|
539
|
+
// 兼容 { data: 'url' } 和 { data: { url: '...' } } 两种结构
|
|
540
|
+
const url = typeof res?.data === 'string' ? res.data : (res?.data?.url || res?.url || '');
|
|
541
|
+
if (url) {
|
|
542
|
+
file.url = url;
|
|
543
|
+
revokeLocalPreview(file);
|
|
544
|
+
}
|
|
518
545
|
console.log('[VtkUpload] res:', res, '| file.url:', file.url, '| fileList:', JSON.parse(JSON.stringify(fileList.value)));
|
|
519
546
|
syncModel();
|
|
520
547
|
emit('success', res, file, fileList.value);
|
|
@@ -529,10 +556,11 @@ const uploadFile = (file) => {
|
|
|
529
556
|
|
|
530
557
|
/* -------------------- 移除文件 -------------------- */
|
|
531
558
|
const handleRemove = (file) => {
|
|
532
|
-
const doRemove = () => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
559
|
+
const doRemove = () => {
|
|
560
|
+
revokeLocalPreview(file);
|
|
561
|
+
fileList.value = fileList.value.filter((f) => f.uid !== file.uid);
|
|
562
|
+
syncModel();
|
|
563
|
+
emit('remove', file, fileList.value);
|
|
536
564
|
};
|
|
537
565
|
|
|
538
566
|
if (props.beforeRemove) {
|
|
@@ -687,9 +715,10 @@ watch(previewVisible, (visible) => {
|
|
|
687
715
|
resetPreviewTransform();
|
|
688
716
|
});
|
|
689
717
|
|
|
690
|
-
onBeforeUnmount(() => {
|
|
691
|
-
window.removeEventListener('keydown', handlePreviewKeydown);
|
|
692
|
-
|
|
718
|
+
onBeforeUnmount(() => {
|
|
719
|
+
window.removeEventListener('keydown', handlePreviewKeydown);
|
|
720
|
+
revokeAllLocalPreviews();
|
|
721
|
+
});
|
|
693
722
|
|
|
694
723
|
/* -------------------- 对外暴露方法 -------------------- */
|
|
695
724
|
/** 手动触发未上传文件的上传 */
|
|
@@ -700,10 +729,11 @@ const submit = () => {
|
|
|
700
729
|
};
|
|
701
730
|
|
|
702
731
|
/** 清空文件列表 */
|
|
703
|
-
const clearFiles = () => {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
732
|
+
const clearFiles = () => {
|
|
733
|
+
revokeAllLocalPreviews();
|
|
734
|
+
fileList.value = [];
|
|
735
|
+
syncModel();
|
|
736
|
+
};
|
|
707
737
|
|
|
708
738
|
/** 手动移除某个文件 */
|
|
709
739
|
const remove = (file) => handleRemove(file);
|
package/src/index.js
CHANGED
|
@@ -11,9 +11,10 @@ export { default as VtkDatePicker } from "./components/assembly/VtkDatePicker.vu
|
|
|
11
11
|
export { default as VtkDateSelector } from "./components/assembly/VtkDateSelector.vue";
|
|
12
12
|
export { default as VtkDateTimePicker } from "./components/assembly/VtkDateTimePicker.vue";
|
|
13
13
|
export { default as VtkDept } from "./components/assembly/VtkDept.vue";
|
|
14
|
-
export { default as VtkEmpty } from "./components/assembly/VtkEmpty.vue";
|
|
15
|
-
export { default as
|
|
16
|
-
export { default as
|
|
14
|
+
export { default as VtkEmpty } from "./components/assembly/VtkEmpty.vue";
|
|
15
|
+
export { default as VtkEmptyNew } from "./components/assembly/VtkEmptyNew.vue";
|
|
16
|
+
export { default as VtkFab } from "./components/assembly/VtkFab.vue";
|
|
17
|
+
export { default as VtkFormItem } from "./components/assembly/VtkFormItem.vue";
|
|
17
18
|
export { default as VtkImg } from "./components/assembly/VtkImg.vue";
|
|
18
19
|
export { default as VtkPage } from "./components/assembly/VtkPage.vue";
|
|
19
20
|
export { default as VtkPdf } from "./components/assembly/VtkPdf.vue";
|
|
@@ -55,9 +56,10 @@ import VtkDatePicker from "./components/assembly/VtkDatePicker.vue";
|
|
|
55
56
|
import VtkDateSelector from "./components/assembly/VtkDateSelector.vue";
|
|
56
57
|
import VtkDateTimePicker from "./components/assembly/VtkDateTimePicker.vue";
|
|
57
58
|
import VtkDept from "./components/assembly/VtkDept.vue";
|
|
58
|
-
import VtkEmpty from "./components/assembly/VtkEmpty.vue";
|
|
59
|
-
import
|
|
60
|
-
import
|
|
59
|
+
import VtkEmpty from "./components/assembly/VtkEmpty.vue";
|
|
60
|
+
import VtkEmptyNew from "./components/assembly/VtkEmptyNew.vue";
|
|
61
|
+
import VtkFab from "./components/assembly/VtkFab.vue";
|
|
62
|
+
import VtkFormItem from "./components/assembly/VtkFormItem.vue";
|
|
61
63
|
import VtkImg from "./components/assembly/VtkImg.vue";
|
|
62
64
|
import VtkPage from "./components/assembly/VtkPage.vue";
|
|
63
65
|
import VtkPdf from "./components/assembly/VtkPdf.vue";
|
|
@@ -80,9 +82,10 @@ function install(app, options = {}) {
|
|
|
80
82
|
VtkDateSelector,
|
|
81
83
|
VtkDateTimePicker,
|
|
82
84
|
VtkDept,
|
|
83
|
-
VtkEmpty,
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
VtkEmpty,
|
|
86
|
+
VtkEmptyNew,
|
|
87
|
+
VtkFab,
|
|
88
|
+
VtkFormItem,
|
|
86
89
|
VtkImg,
|
|
87
90
|
VtkPage,
|
|
88
91
|
VtkPdf,
|
|
@@ -128,8 +131,9 @@ export default {
|
|
|
128
131
|
VtkDateSelector,
|
|
129
132
|
VtkDateTimePicker,
|
|
130
133
|
VtkDept,
|
|
131
|
-
VtkEmpty,
|
|
132
|
-
|
|
134
|
+
VtkEmpty,
|
|
135
|
+
VtkEmptyNew,
|
|
136
|
+
VtkFab,
|
|
133
137
|
VtkFormItem,
|
|
134
138
|
VtkImg,
|
|
135
139
|
VtkPage,
|