@jzt-packages/components 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.
- package/package.json +68 -0
- package/src/JztBackTop/index.vue +255 -0
- package/src/JztButtonList/index.vue +88 -0
- package/src/JztChart/index.vue +95 -0
- package/src/JztCharts/index.vue +317 -0
- package/src/JztClassTabs/index.vue +156 -0
- package/src/JztDateSelect/dateSelect.vue +186 -0
- package/src/JztDateSelect/dateType.vue +54 -0
- package/src/JztDateSelect/index.ts +135 -0
- package/src/JztDateSelect/interface/index.ts +13 -0
- package/src/JztDialog/index.vue +249 -0
- package/src/JztEllipsisTooltip/index.vue +61 -0
- package/src/JztEmpty/index.vue +45 -0
- package/src/JztErrorPage/403.vue +30 -0
- package/src/JztErrorPage/404.vue +19 -0
- package/src/JztErrorPage/500.vue +18 -0
- package/src/JztErrorPage/assets/401.png +0 -0
- package/src/JztErrorPage/assets/403.png +0 -0
- package/src/JztErrorPage/assets/404.png +0 -0
- package/src/JztErrorPage/assets/500.png +0 -0
- package/src/JztErrorPage/index.scss +35 -0
- package/src/JztErrorPage/index.vue +35 -0
- package/src/JztFilePreview/components/pdfViewer.vue +221 -0
- package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
- package/src/JztFilePreview/index.scss +171 -0
- package/src/JztFilePreview/index.vue +68 -0
- package/src/JztFilePreview/interface/index.ts +18 -0
- package/src/JztFilePreview/previewFile.vue +371 -0
- package/src/JztFormGrid/README.md +520 -0
- package/src/JztFormGrid/components/formItem.vue +209 -0
- package/src/JztFormGrid/components/formItemValue.vue +384 -0
- package/src/JztFormGrid/components/showDetailForm.vue +172 -0
- package/src/JztFormGrid/index.scss +60 -0
- package/src/JztFormGrid/index.vue +513 -0
- package/src/JztFormGrid/interface/index.ts +106 -0
- package/src/JztGrid/components/GridItem.vue +68 -0
- package/src/JztGrid/index.vue +179 -0
- package/src/JztGrid/interface/index.ts +6 -0
- package/src/JztImportExcel/assets/delete.png +0 -0
- package/src/JztImportExcel/index.scss +46 -0
- package/src/JztImportExcel/index.vue +430 -0
- package/src/JztImportExcel/interface/index.ts +25 -0
- package/src/JztLabelTitle/index.vue +65 -0
- package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
- package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
- package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
- package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
- package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
- package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
- package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
- package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
- package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
- package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
- package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
- package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
- package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
- package/src/JztLeftRightMode/index.ts +3 -0
- package/src/JztLeftRightMode/index.vue +494 -0
- package/src/JztLeftRightMode/types/index.ts +457 -0
- package/src/JztLoading/fullScreen.ts +45 -0
- package/src/JztLoading/index.scss +67 -0
- package/src/JztLoading/index.vue +18 -0
- package/src/JztLogin/components/LoginFooter.vue +17 -0
- package/src/JztLogin/components/LoginForm.vue +99 -0
- package/src/JztLogin/hooks/useLogin.ts +186 -0
- package/src/JztLogin/index.scss +142 -0
- package/src/JztLogin/index.vue +31 -0
- package/src/JztLogin/interface/index.ts +47 -0
- package/src/JztNumericalRange/index.vue +81 -0
- package/src/JztPageCard/comm/datePicker.vue +151 -0
- package/src/JztPageCard/comm/details.vue +60 -0
- package/src/JztPageCard/comm/export.vue +24 -0
- package/src/JztPageCard/comm/tabs.vue +94 -0
- package/src/JztPageCard/comm/tooltip.vue +31 -0
- package/src/JztPageCard/index.vue +287 -0
- package/src/JztPagination/index.vue +70 -0
- package/src/JztProductInfo/components/imagePreview.vue +275 -0
- package/src/JztProductInfo/components/qxUnique.vue +101 -0
- package/src/JztProductInfo/components/records.vue +265 -0
- package/src/JztProductInfo/hooks/useParams.ts +143 -0
- package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
- package/src/JztProductInfo/images/defaultProduct.png +0 -0
- package/src/JztProductInfo/index.ts +116 -0
- package/src/JztProductInfo/index.vue +108 -0
- package/src/JztProductInfo/interface/index.ts +15 -0
- package/src/JztQueryDetailTable/index.scss +100 -0
- package/src/JztQueryDetailTable/index.vue +400 -0
- package/src/JztQueryDetailTable/interface/index.ts +10 -0
- package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
- package/src/JztQueryTable/README.md +567 -0
- package/src/JztQueryTable/components/ColSetting.vue +67 -0
- package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
- package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
- package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
- package/src/JztQueryTable/components/Pagination.vue +54 -0
- package/src/JztQueryTable/components/TableColumn.vue +109 -0
- package/src/JztQueryTable/const.ts +1 -0
- package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
- package/src/JztQueryTable/hooks/useSelection.ts +47 -0
- package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
- package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
- package/src/JztQueryTable/index.scss +91 -0
- package/src/JztQueryTable/index.vue +1445 -0
- package/src/JztQueryTable/interface/index.ts +185 -0
- package/src/JztRegionSelect/index.vue +134 -0
- package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
- package/src/JztSearchForm/index.vue +530 -0
- package/src/JztSearchForm/interface/index.ts +100 -0
- package/src/JztSelectFilter/index.scss +63 -0
- package/src/JztSelectFilter/index.vue +110 -0
- package/src/JztSelectTable/index.vue +257 -0
- package/src/JztTable/index.scss +72 -0
- package/src/JztTable/index.vue +353 -0
- package/src/JztTable/interface/index.ts +1 -0
- package/src/JztTime/comm/agencySelect.vue +112 -0
- package/src/JztTime/comm/collapseRow.vue +132 -0
- package/src/JztTime/comm/dateSelect.vue +292 -0
- package/src/JztTime/comm/deptSelect.vue +193 -0
- package/src/JztTime/comm/typeSelect.vue +97 -0
- package/src/JztTime/index.ts +216 -0
- package/src/JztTime/index.vue +303 -0
- package/src/JztTime/interface/index.ts +23 -0
- package/src/JztTreeFilter/index.scss +44 -0
- package/src/JztTreeFilter/index.vue +177 -0
- package/src/JztUploadFile/interface/index.ts +21 -0
- package/src/JztUploadFile/multiple.scss +215 -0
- package/src/JztUploadFile/multiple.vue +318 -0
- package/src/JztUploadFile/single.scss +226 -0
- package/src/JztUploadFile/single.vue +274 -0
- package/src/JztUploadImg/Img.vue +294 -0
- package/src/JztUploadImg/Imgs.vue +411 -0
- package/src/JztUploadImg/index.scss +138 -0
- package/src/JztUploadImg/interface/index.ts +22 -0
- package/src/SelectIcon/index.scss +39 -0
- package/src/SelectIcon/index.vue +106 -0
- package/src/SvgIcon/index.vue +22 -0
- package/src/hooks/useAuthButtons.ts +58 -0
- package/src/hooks/useFormByUserType.ts +90 -0
- package/src/hooks/useTableEvents.ts +30 -0
- package/src/hooks/useUploadFileHook.ts +262 -0
- package/src/index.ts +91 -0
- package/src/typings/global.d.ts +101 -0
- package/src/utils/index.ts +107 -0
- package/src/utils/tree.ts +57 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style="style">
|
|
3
|
+
<slot></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts" name="Grid">
|
|
8
|
+
import {
|
|
9
|
+
ref,
|
|
10
|
+
watch,
|
|
11
|
+
useSlots,
|
|
12
|
+
computed,
|
|
13
|
+
provide,
|
|
14
|
+
onBeforeMount,
|
|
15
|
+
onMounted,
|
|
16
|
+
onUnmounted,
|
|
17
|
+
onDeactivated,
|
|
18
|
+
onActivated,
|
|
19
|
+
VNodeArrayChildren,
|
|
20
|
+
VNode
|
|
21
|
+
} from 'vue'
|
|
22
|
+
import type { BreakPoint } from './interface/index'
|
|
23
|
+
|
|
24
|
+
type Props = {
|
|
25
|
+
cols?: number | Record<BreakPoint, number>
|
|
26
|
+
collapsed?: boolean
|
|
27
|
+
collapsedRows?: number
|
|
28
|
+
gap?: [number, number] | number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
32
|
+
cols: () => ({ xs: 1, sm: 2, md: 3, lg: 4, xl: 5, xxl: 6 }),
|
|
33
|
+
collapsed: false,
|
|
34
|
+
collapsedRows: 2,
|
|
35
|
+
gap: 0
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
onBeforeMount(() => props.collapsed && findIndex())
|
|
39
|
+
onMounted(() => {
|
|
40
|
+
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
|
|
41
|
+
window.addEventListener('resize', resize)
|
|
42
|
+
})
|
|
43
|
+
onActivated(() => {
|
|
44
|
+
resize({ target: { innerWidth: window.innerWidth } } as unknown as UIEvent)
|
|
45
|
+
window.addEventListener('resize', resize)
|
|
46
|
+
})
|
|
47
|
+
onUnmounted(() => {
|
|
48
|
+
window.removeEventListener('resize', resize)
|
|
49
|
+
})
|
|
50
|
+
onDeactivated(() => {
|
|
51
|
+
window.removeEventListener('resize', resize)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// 监听屏幕变化
|
|
55
|
+
const resize = (e: UIEvent) => {
|
|
56
|
+
let width = (e.target as Window).innerWidth
|
|
57
|
+
// 768 992 1200 1920 2560 / 768 1100 1440 1920 2560
|
|
58
|
+
switch (!!width) {
|
|
59
|
+
case width < 768:
|
|
60
|
+
breakPoint.value = 'xs'
|
|
61
|
+
break
|
|
62
|
+
case width >= 768 && width < 1130:
|
|
63
|
+
breakPoint.value = 'sm'
|
|
64
|
+
break
|
|
65
|
+
case width >= 1130 && width < 1440:
|
|
66
|
+
breakPoint.value = 'md'
|
|
67
|
+
break
|
|
68
|
+
case width >= 1440 && width < 1920:
|
|
69
|
+
breakPoint.value = 'lg'
|
|
70
|
+
break
|
|
71
|
+
case width >= 1920 && width < 2560:
|
|
72
|
+
breakPoint.value = 'xl'
|
|
73
|
+
break
|
|
74
|
+
case width >= 2560:
|
|
75
|
+
breakPoint.value = 'xxl'
|
|
76
|
+
break
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 注入 gap 间距
|
|
81
|
+
provide('gap', Array.isArray(props.gap) ? props.gap[0] : props.gap)
|
|
82
|
+
|
|
83
|
+
// 注入响应式断点
|
|
84
|
+
let breakPoint = ref<BreakPoint>('xl')
|
|
85
|
+
provide('breakPoint', breakPoint)
|
|
86
|
+
|
|
87
|
+
// 注入要开始折叠的 index
|
|
88
|
+
const hiddenIndex = ref(-1)
|
|
89
|
+
provide('shouldHiddenIndex', hiddenIndex)
|
|
90
|
+
|
|
91
|
+
// 注入 cols
|
|
92
|
+
const gridCols = computed(() => {
|
|
93
|
+
if (typeof props.cols === 'object') return props.cols[breakPoint.value] ?? props.cols
|
|
94
|
+
return props.cols
|
|
95
|
+
})
|
|
96
|
+
provide('cols', gridCols)
|
|
97
|
+
|
|
98
|
+
// 寻找需要开始折叠的字段 index
|
|
99
|
+
const slots = useSlots().default!()
|
|
100
|
+
// emit
|
|
101
|
+
const emit = defineEmits<{
|
|
102
|
+
hiddenIndex: [value: number]
|
|
103
|
+
}>()
|
|
104
|
+
const findIndex = () => {
|
|
105
|
+
let fields: VNodeArrayChildren = []
|
|
106
|
+
let suffix: VNode | null = null
|
|
107
|
+
slots.forEach((slot: any) => {
|
|
108
|
+
// suffix
|
|
109
|
+
if (typeof slot.type === 'object' && slot.type.name === 'GridItem' && slot.props?.suffix !== undefined)
|
|
110
|
+
suffix = slot
|
|
111
|
+
// slot children
|
|
112
|
+
if (typeof slot.type === 'symbol' && Array.isArray(slot.children)) fields.push(...slot.children)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
// 计算 suffix 所占用的列
|
|
116
|
+
let suffixCols = 0
|
|
117
|
+
if (suffix) {
|
|
118
|
+
suffixCols =
|
|
119
|
+
((suffix as VNode).props![breakPoint.value]?.span ?? (suffix as VNode).props?.span ?? 1) +
|
|
120
|
+
((suffix as VNode).props![breakPoint.value]?.offset ?? (suffix as VNode).props?.offset ?? 0)
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
let find = false
|
|
124
|
+
fields.reduce((prev = 0, current, index) => {
|
|
125
|
+
prev +=
|
|
126
|
+
((current as VNode)!.props![breakPoint.value]?.span ?? (current as VNode)!.props?.span ?? 1) +
|
|
127
|
+
((current as VNode)!.props![breakPoint.value]?.offset ?? (current as VNode)!.props?.offset ?? 0)
|
|
128
|
+
if (Number(prev) > props.collapsedRows * gridCols.value - suffixCols) {
|
|
129
|
+
hiddenIndex.value = index
|
|
130
|
+
find = true
|
|
131
|
+
// 不能注释,会导致表单自定隐藏出问题
|
|
132
|
+
throw 'find it'
|
|
133
|
+
}
|
|
134
|
+
return prev
|
|
135
|
+
}, 0)
|
|
136
|
+
if (!find) hiddenIndex.value = -1
|
|
137
|
+
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.warn(e)
|
|
140
|
+
}finally {
|
|
141
|
+
emit('hiddenIndex', hiddenIndex.value)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 断点变化时执行 findIndex
|
|
146
|
+
watch(
|
|
147
|
+
() => breakPoint.value,
|
|
148
|
+
() => {
|
|
149
|
+
if (props.collapsed) findIndex()
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
// 监听 collapsed
|
|
154
|
+
watch(
|
|
155
|
+
() => props.collapsed,
|
|
156
|
+
value => {
|
|
157
|
+
if (value) return findIndex()
|
|
158
|
+
hiddenIndex.value = -1
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
// 设置间距
|
|
163
|
+
const gridGap = computed(() => {
|
|
164
|
+
if (typeof props.gap === 'number') return `${props.gap}px`
|
|
165
|
+
if (Array.isArray(props.gap)) return `${props.gap[1]}px ${props.gap[0]}px`
|
|
166
|
+
return 'unset'
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// 设置 style
|
|
170
|
+
const style = computed(() => {
|
|
171
|
+
return {
|
|
172
|
+
display: 'grid',
|
|
173
|
+
gridGap: gridGap.value,
|
|
174
|
+
gridTemplateColumns: `repeat(${gridCols.value}, minmax(0, 1fr))`
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
defineExpose({ breakPoint })
|
|
179
|
+
</script>
|
|
Binary file
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
.tips {
|
|
2
|
+
margin-left: 30px;
|
|
3
|
+
margin-bottom: 20px;
|
|
4
|
+
|
|
5
|
+
.el-icon {
|
|
6
|
+
margin-top: 3px;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
.el-upload__tip {
|
|
10
|
+
color: #666666;
|
|
11
|
+
margin-top: 0px;
|
|
12
|
+
font-weight: normal;
|
|
13
|
+
}
|
|
14
|
+
.upload {
|
|
15
|
+
width: 100%;
|
|
16
|
+
:deep(.el-upload-dragger) {
|
|
17
|
+
width: 100%;
|
|
18
|
+
padding: 24px 10px;
|
|
19
|
+
}
|
|
20
|
+
:deep(.el-upload-dragger .el-icon--upload) {
|
|
21
|
+
color: #666666;
|
|
22
|
+
}
|
|
23
|
+
:deep(.el-icon--close) {
|
|
24
|
+
background: url('./assets/delete.png') 100% 100%;
|
|
25
|
+
width: 16px;
|
|
26
|
+
height: 16px;
|
|
27
|
+
}
|
|
28
|
+
:deep(.el-icon--close svg) {
|
|
29
|
+
display: none;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
.box {
|
|
33
|
+
padding: 16px 16px;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
background: #f7f9fc;
|
|
36
|
+
font-family: PingFang SC;
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
font-weight: 600;
|
|
39
|
+
line-height: 22px;
|
|
40
|
+
.result {
|
|
41
|
+
font-weight: 400;
|
|
42
|
+
&-failure {
|
|
43
|
+
color: #fa2c2d;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- <span class="ml10">请上传 .xls , .xlsx 标准格式文件,文件最大为 {{ parameter.fileSize }}M</span> -->
|
|
3
|
+
<jzt-dialog
|
|
4
|
+
v-model="dialogVisible"
|
|
5
|
+
:title="parameter.title"
|
|
6
|
+
:destroy-on-close="true"
|
|
7
|
+
:width="parameter.width"
|
|
8
|
+
:confirm-text="parameter.confirmText"
|
|
9
|
+
:cancel-text="parameter.cancelText"
|
|
10
|
+
:disabled="disabled"
|
|
11
|
+
@close="handleClose"
|
|
12
|
+
@confirm="handleConfirm"
|
|
13
|
+
>
|
|
14
|
+
<!-- <div class="tips flx-left-center">
|
|
15
|
+
<el-icon :size="16" color="#888888"><WarningFilled /></el-icon>
|
|
16
|
+
<span style="margin-left: 5px; color: red">
|
|
17
|
+
{{ parameter.tips }}
|
|
18
|
+
</span>
|
|
19
|
+
</div> -->
|
|
20
|
+
<div v-loading="uploadLoading" class="box">
|
|
21
|
+
<p class="mb8 flx-left-center">
|
|
22
|
+
<label>1.下载模板</label>
|
|
23
|
+
<el-button type="primary" link @click="downloadTemp">点击下载模板</el-button>
|
|
24
|
+
</p>
|
|
25
|
+
<p class="mb8 flx-left-center">2.上传完善后的模板</p>
|
|
26
|
+
<el-upload
|
|
27
|
+
action="#"
|
|
28
|
+
class="upload"
|
|
29
|
+
ref="uploadRef"
|
|
30
|
+
v-model:file-list="fileList"
|
|
31
|
+
:drag="parameter.isDrag"
|
|
32
|
+
:limit="excelLimit"
|
|
33
|
+
:multiple="true"
|
|
34
|
+
:show-file-list="true"
|
|
35
|
+
:http-request="uploadExcel"
|
|
36
|
+
:before-upload="beforeExcelUpload"
|
|
37
|
+
:on-exceed="handleExceed"
|
|
38
|
+
:on-success="excelUploadSuccess"
|
|
39
|
+
:on-error="excelUploadError"
|
|
40
|
+
:on-remove="excelUploadRemove"
|
|
41
|
+
:accept="parameter.fileType!.join(',')"
|
|
42
|
+
>
|
|
43
|
+
<slot name="empty">
|
|
44
|
+
<div v-if="parameter.isDrag">
|
|
45
|
+
<el-icon class="el-icon--upload">
|
|
46
|
+
<upload-filled />
|
|
47
|
+
</el-icon>
|
|
48
|
+
<div class="el-upload__text">
|
|
49
|
+
将文件拖到此处或
|
|
50
|
+
<em>点击上传</em>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div v-else class="flx-left-center">
|
|
54
|
+
<el-button type="primary" icon="UploadFilled">选择文件</el-button>
|
|
55
|
+
<div class="el-upload__tip flx-left-center ml10">
|
|
56
|
+
<el-icon :size="16" color="#888888"><WarningFilled /></el-icon>
|
|
57
|
+
<span class="ml6">{{ parameter.tips }}</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div v-if="parameter.isDrag" class="el-upload__tip flx-center-center mt4">
|
|
61
|
+
<el-icon v-if="parameter.isIcon" :size="16" color="#888888"><WarningFilled /></el-icon>
|
|
62
|
+
<span class="ml6">{{ parameter.tips }}</span>
|
|
63
|
+
<span></span>
|
|
64
|
+
</div>
|
|
65
|
+
</slot>
|
|
66
|
+
<!-- <template #tip>
|
|
67
|
+
<slot v-if="!isDrag" name="tip">
|
|
68
|
+
<div class="el-upload__tip flx-left-center">
|
|
69
|
+
<el-icon :size="16" color="#888888"><WarningFilled /></el-icon>
|
|
70
|
+
<span class="ml6">{{ parameter.tips }}</span>
|
|
71
|
+
</div>
|
|
72
|
+
</slot>
|
|
73
|
+
</template> -->
|
|
74
|
+
</el-upload>
|
|
75
|
+
<div
|
|
76
|
+
class="result pl8 flx-between-center"
|
|
77
|
+
v-show="parameter.isShowCount && fileList.length && fileList.length > 0"
|
|
78
|
+
>
|
|
79
|
+
<div>
|
|
80
|
+
<span v-if="allCount > 0">
|
|
81
|
+
导入数据 {{ allCount }} 条,
|
|
82
|
+
<el-text class="mx-1" type="danger">失败 {{ importData.failCount }} 条</el-text>
|
|
83
|
+
</span>
|
|
84
|
+
<el-text v-else class="mx-1" type="danger">导入失败</el-text>
|
|
85
|
+
</div>
|
|
86
|
+
<div>
|
|
87
|
+
<el-button v-if="importData.errorMsg && importData.errorMsg != ''" type="info" link @click="downErrorFile">
|
|
88
|
+
下载错误数据
|
|
89
|
+
<i class="iconfont icon-xiazai"></i>
|
|
90
|
+
</el-button>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
<div v-if="tableConfig.columns && tableConfig.columns.length" class="table-main" style="margin-top: -10px">
|
|
95
|
+
<JztTable
|
|
96
|
+
ref="jztTableRef"
|
|
97
|
+
:pagination="false"
|
|
98
|
+
:tool-button="false"
|
|
99
|
+
:table-data="tableData"
|
|
100
|
+
:table-config="tableConfig"
|
|
101
|
+
@radioChange="radioChange"
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<slot name="footer"></slot>
|
|
105
|
+
</jzt-dialog>
|
|
106
|
+
</template>
|
|
107
|
+
|
|
108
|
+
<script setup lang="tsx" name="ImportExcel">
|
|
109
|
+
import { isFullUrl, useDownload, localGetServer } from '@jzt-spd/utils'
|
|
110
|
+
import { ElNotification, genFileId, UploadInstance, UploadRawFile, UploadRequestOptions } from 'element-plus'
|
|
111
|
+
import { computed, nextTick, ref } from 'vue'
|
|
112
|
+
import JztDialog from '../JztDialog/index.vue'
|
|
113
|
+
import JztTable from '../JztTable/index.vue'
|
|
114
|
+
import type { TableConfigProps } from '../JztTable/interface/index'
|
|
115
|
+
|
|
116
|
+
export interface ExcelParameterProps {
|
|
117
|
+
title?: string // 标题
|
|
118
|
+
tips: string // 说明
|
|
119
|
+
fileSize?: number // 上传文件的大小
|
|
120
|
+
fileType?: File.ExcelMimeType[] // 上传文件的类型
|
|
121
|
+
tempApi?: (params: any) => Promise<any> // 下载模板的Api,
|
|
122
|
+
tempParams?: any // 下载模板的入参
|
|
123
|
+
importApi?: (params: any) => Promise<any> // 批量导入的Api
|
|
124
|
+
importParams?: any // 批量导入的入参
|
|
125
|
+
getTableList?: (response?: Record<string, any>) => void // 获取表格数据的Api
|
|
126
|
+
columns?: any[]
|
|
127
|
+
tableData?: any[]
|
|
128
|
+
isDrag?: boolean // 是否可拖拽上传
|
|
129
|
+
isIcon?: boolean // 是否展示提示图标
|
|
130
|
+
width?: string // 弹框宽度
|
|
131
|
+
fileName: string // 文件流的名称 excelFormData
|
|
132
|
+
confirmText?: string | boolean // 确定按钮的文字
|
|
133
|
+
cancelText?: string | boolean // 取消按钮的文字
|
|
134
|
+
confirmFun?: (params: any) => void // 确定按钮的执行函数
|
|
135
|
+
autoClose?: boolean // 导入成功后是否自动关闭弹框
|
|
136
|
+
importCallBack?: (params: any) => void // 导入成功的回调函数
|
|
137
|
+
onRemove?: (params: { val: any; fileList: any[] }) => void // 文件移除时的函数
|
|
138
|
+
isReplace?: boolean // 是否可以在选中时自动替换上一个文件,必须设置limit=1
|
|
139
|
+
suffix?: string // 后缀
|
|
140
|
+
isNotify?: boolean
|
|
141
|
+
isShowCount?: false // 是否展示导入结果
|
|
142
|
+
downErrorFile?: (params: any) => void // 下载错误数据的回调函数
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 父组件传过来的参数
|
|
146
|
+
const parameter = ref<ExcelParameterProps>({
|
|
147
|
+
title: '文件导入',
|
|
148
|
+
tips: '请上传.xls,.xlsx 标准格式文件,文件最大为 5 M',
|
|
149
|
+
fileSize: 5,
|
|
150
|
+
fileType: ['application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
|
|
151
|
+
isDrag: true,
|
|
152
|
+
width: '520px',
|
|
153
|
+
fileName: 'file',
|
|
154
|
+
autoClose: true,
|
|
155
|
+
isReplace: false,
|
|
156
|
+
confirmText: '确认',
|
|
157
|
+
isShowCount: false,
|
|
158
|
+
cancelText: false
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// 是否覆盖数据
|
|
162
|
+
// const isCover = ref(false)
|
|
163
|
+
// 最大文件上传数
|
|
164
|
+
const excelLimit = ref(1)
|
|
165
|
+
// 文件列表
|
|
166
|
+
const fileList = ref([])
|
|
167
|
+
// 表格实例
|
|
168
|
+
const jztTableRef = ref()
|
|
169
|
+
// dialog 禁用状态
|
|
170
|
+
const disabled = ref<boolean>(true)
|
|
171
|
+
// dialog状态
|
|
172
|
+
const dialogVisible = ref(false)
|
|
173
|
+
|
|
174
|
+
const tableData = ref<any[]>([])
|
|
175
|
+
const uploadRef = ref<UploadInstance>()
|
|
176
|
+
|
|
177
|
+
const tableConfig = ref<TableConfigProps>({
|
|
178
|
+
topList: [],
|
|
179
|
+
columns: []
|
|
180
|
+
})
|
|
181
|
+
const importData = ref({
|
|
182
|
+
successCount: 0,
|
|
183
|
+
failCount: 0,
|
|
184
|
+
errorMsg: ''
|
|
185
|
+
})
|
|
186
|
+
// 导出结果数量
|
|
187
|
+
const allCount = computed(() => importData.value.successCount + importData.value.failCount)
|
|
188
|
+
|
|
189
|
+
// const emit = defineEmits([''])
|
|
190
|
+
// 接收父组件参数
|
|
191
|
+
const acceptParams = (params: ExcelParameterProps) => {
|
|
192
|
+
Object.keys(params).forEach(key => {
|
|
193
|
+
parameter.value[key] = params[key] ?? parameter.value[key]
|
|
194
|
+
})
|
|
195
|
+
dialogVisible.value = true
|
|
196
|
+
if (params.columns && params.columns.length) {
|
|
197
|
+
tableConfig.value.columns = params.columns || []
|
|
198
|
+
} else {
|
|
199
|
+
tableConfig.value.columns = []
|
|
200
|
+
}
|
|
201
|
+
tableData.value = []
|
|
202
|
+
}
|
|
203
|
+
// 关闭dialog,并清空数据
|
|
204
|
+
const handleClose = () => {
|
|
205
|
+
dialogVisible.value = false
|
|
206
|
+
tableConfig.value.columns = []
|
|
207
|
+
clearFileData()
|
|
208
|
+
}
|
|
209
|
+
const importResponse = ref<any>(null) // 批量导入返回的数据
|
|
210
|
+
|
|
211
|
+
// 确定按钮功能执行函数
|
|
212
|
+
const handleConfirm = async () => {
|
|
213
|
+
if (parameter.value.confirmFun) {
|
|
214
|
+
const selected = getTableSelected()
|
|
215
|
+
await parameter.value.confirmFun({
|
|
216
|
+
importRes: importResponse.value,
|
|
217
|
+
selected: selected,
|
|
218
|
+
radioValue: radioInfo.value,
|
|
219
|
+
fileList: fileList.value
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
parameter.value.getTableList && parameter.value.getTableList()
|
|
223
|
+
handleClose()
|
|
224
|
+
}
|
|
225
|
+
// 获取table的选择数据 -- 多选
|
|
226
|
+
const getTableSelected = () => {
|
|
227
|
+
let selected = {
|
|
228
|
+
isSelected: null,
|
|
229
|
+
selectedList: null,
|
|
230
|
+
selectedListIds: null
|
|
231
|
+
}
|
|
232
|
+
if (jztTableRef.value) {
|
|
233
|
+
jztTableRef.value.handleButtonClick(row => {
|
|
234
|
+
selected = row
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
return selected
|
|
238
|
+
}
|
|
239
|
+
const radioInfo = ref({
|
|
240
|
+
rowKey: null,
|
|
241
|
+
row: null
|
|
242
|
+
})
|
|
243
|
+
// 获取table的选择数据 -- 单选
|
|
244
|
+
const radioChange = (rowKey, row) => {
|
|
245
|
+
radioInfo.value.rowKey = rowKey
|
|
246
|
+
radioInfo.value.row = row
|
|
247
|
+
}
|
|
248
|
+
// const templateUrl = 'http://10.4.4.133:30993'
|
|
249
|
+
const templateUrl = localGetServer()?.fileWebUrl || ''
|
|
250
|
+
// import.meta.env.VITE_TEMPLATE_BASE_URL as string;
|
|
251
|
+
// Excel 导入模板下载
|
|
252
|
+
const downloadTemp = () => {
|
|
253
|
+
if (!parameter.value.tempApi) return
|
|
254
|
+
if (typeof parameter.value.tempApi === 'string') {
|
|
255
|
+
const a = document.createElement('a')
|
|
256
|
+
if (isFullUrl(parameter.value.tempApi)) {
|
|
257
|
+
// 完整地址
|
|
258
|
+
a.href = templateUrl
|
|
259
|
+
} else {
|
|
260
|
+
let searchParams = new URLSearchParams(parameter.value.tempParams || {})
|
|
261
|
+
let url = parameter.value.tempApi + '?' + searchParams.toString()
|
|
262
|
+
if (url.startsWith('/')) {
|
|
263
|
+
url = url.substring(1, url.length)
|
|
264
|
+
}
|
|
265
|
+
a.href = templateUrl + url
|
|
266
|
+
}
|
|
267
|
+
a.download = `${parameter.value.title}模板`
|
|
268
|
+
document.body.appendChild(a)
|
|
269
|
+
a.click()
|
|
270
|
+
document.body.removeChild(a)
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
useDownload({
|
|
274
|
+
api: parameter.value.tempApi,
|
|
275
|
+
tempName: `${parameter.value.title}模板`,
|
|
276
|
+
params: parameter.value.tempParams,
|
|
277
|
+
isNotify: parameter.value.isNotify,
|
|
278
|
+
fileType: parameter.value.suffix
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
// 上传loading状态
|
|
282
|
+
const uploadLoading = ref(false)
|
|
283
|
+
|
|
284
|
+
// 文件上传
|
|
285
|
+
const uploadExcel = async (param: UploadRequestOptions) => {
|
|
286
|
+
try {
|
|
287
|
+
uploadLoading.value = true
|
|
288
|
+
let excelFormData = new FormData()
|
|
289
|
+
excelFormData.append(parameter.value.fileName || 'file', param.file)
|
|
290
|
+
// 合并importParams参数
|
|
291
|
+
if (parameter.value.importParams) {
|
|
292
|
+
Object.keys(parameter.value.importParams).forEach(key => {
|
|
293
|
+
excelFormData.append(key, parameter.value.importParams[key])
|
|
294
|
+
})
|
|
295
|
+
}
|
|
296
|
+
// excelFormData.append('isCover', isCover.value as unknown as Blob)
|
|
297
|
+
const response = await parameter.value.importApi!(excelFormData)
|
|
298
|
+
// 展示导入结果
|
|
299
|
+
if (parameter.value.isShowCount && response) {
|
|
300
|
+
const { failCount, errorMsg, successCount } = response
|
|
301
|
+
importData.value = {
|
|
302
|
+
failCount,
|
|
303
|
+
errorMsg,
|
|
304
|
+
successCount
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
console.log(excelFormData, response)
|
|
308
|
+
// 存储导入成功的接口数据
|
|
309
|
+
importResponse.value = response
|
|
310
|
+
return response
|
|
311
|
+
} catch (error: any) {
|
|
312
|
+
importData.value = {
|
|
313
|
+
successCount: 0,
|
|
314
|
+
failCount: 0,
|
|
315
|
+
errorMsg: error.message || '导入失败'
|
|
316
|
+
}
|
|
317
|
+
console.log(error)
|
|
318
|
+
} finally {
|
|
319
|
+
uploadLoading.value = false
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @description 文件上传之前判断
|
|
325
|
+
* @param file 上传的文件
|
|
326
|
+
* */
|
|
327
|
+
const beforeExcelUpload = (file: UploadRawFile) => {
|
|
328
|
+
const isExcel = parameter.value.fileType!.includes(file.type as File.ExcelMimeType)
|
|
329
|
+
const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!
|
|
330
|
+
if (!isExcel)
|
|
331
|
+
ElNotification({
|
|
332
|
+
title: '温馨提示',
|
|
333
|
+
message: '上传文件只能是 xls / xlsx 格式!',
|
|
334
|
+
type: 'warning'
|
|
335
|
+
})
|
|
336
|
+
if (!fileSize)
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
ElNotification({
|
|
339
|
+
title: '温馨提示',
|
|
340
|
+
message: `上传文件大小不能超过 ${parameter.value.fileSize}MB!`,
|
|
341
|
+
type: 'warning'
|
|
342
|
+
})
|
|
343
|
+
}, 0)
|
|
344
|
+
return isExcel && fileSize
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// 文件数超出提示
|
|
348
|
+
const handleExceed = files => {
|
|
349
|
+
// 判断是否可以替换
|
|
350
|
+
if (parameter.value.isReplace && excelLimit.value == 1) {
|
|
351
|
+
// 清空并替换操作
|
|
352
|
+
clearFileData()
|
|
353
|
+
uploadRef.value!.clearFiles()
|
|
354
|
+
const file = files[0] as UploadRawFile
|
|
355
|
+
file.uid = genFileId()
|
|
356
|
+
uploadRef.value!.handleStart(file)
|
|
357
|
+
console.log('handleExceed', files)
|
|
358
|
+
//执行上传
|
|
359
|
+
uploadRef.value!.submit()
|
|
360
|
+
} else {
|
|
361
|
+
// 温馨提示
|
|
362
|
+
ElNotification({
|
|
363
|
+
title: '温馨提示',
|
|
364
|
+
message: '最多只能上传一个文件!',
|
|
365
|
+
type: 'warning'
|
|
366
|
+
})
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 上传错误提示
|
|
371
|
+
const excelUploadError = (error: any) => {
|
|
372
|
+
// ElNotification({
|
|
373
|
+
// title: '温馨提示',
|
|
374
|
+
// message: error.message || '上传失败',
|
|
375
|
+
// type: 'error'
|
|
376
|
+
// })
|
|
377
|
+
}
|
|
378
|
+
// 文件列表移除文件时的钩子
|
|
379
|
+
const excelUploadRemove = val => {
|
|
380
|
+
nextTick(() => {
|
|
381
|
+
if (fileList.value && !fileList.value.length) {
|
|
382
|
+
clearFileData()
|
|
383
|
+
}
|
|
384
|
+
const params = { val: val, fileList: fileList.value }
|
|
385
|
+
parameter.value.onRemove && parameter.value.onRemove(params)
|
|
386
|
+
})
|
|
387
|
+
}
|
|
388
|
+
// 清空数据
|
|
389
|
+
const clearFileData = () => {
|
|
390
|
+
tableData.value = [] // 清空表格
|
|
391
|
+
fileList.value = [] // 清空文件列表
|
|
392
|
+
importResponse.value = null
|
|
393
|
+
radioInfo.value.rowKey = null
|
|
394
|
+
radioInfo.value.row = null
|
|
395
|
+
disabled.value = true
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// 导入成功的函数,response是 uploadExcel() return的值
|
|
399
|
+
const excelUploadSuccess = response => {
|
|
400
|
+
console.log(response, 'response')
|
|
401
|
+
if (!response) return
|
|
402
|
+
// 必须文件上传成功之后才能点击确定按钮
|
|
403
|
+
disabled.value = false
|
|
404
|
+
if (parameter.value.columns && parameter.value.columns.length && response.result) {
|
|
405
|
+
// 如果接口返回的表格数据不是response.result或者格式不对,就需要自己在importApi中处理数据与结构
|
|
406
|
+
tableData.value = response.result || []
|
|
407
|
+
}
|
|
408
|
+
if (parameter.value.autoClose) {
|
|
409
|
+
// 如果需要自动关闭弹框,则关闭与刷新
|
|
410
|
+
handleClose()
|
|
411
|
+
parameter.value.getTableList && parameter.value.getTableList(response)
|
|
412
|
+
}
|
|
413
|
+
// 如果需要自定义成功回调,则执行自定义回调
|
|
414
|
+
parameter.value.importCallBack && parameter.value.importCallBack(response)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
//下载错误数据
|
|
418
|
+
const downErrorFile = () => {
|
|
419
|
+
parameter.value?.downErrorFile && parameter.value?.downErrorFile(importData.value)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
defineExpose({
|
|
423
|
+
acceptParams,
|
|
424
|
+
handleClose,
|
|
425
|
+
jztTableRef
|
|
426
|
+
})
|
|
427
|
+
</script>
|
|
428
|
+
<style lang="scss" scoped>
|
|
429
|
+
@use './index';
|
|
430
|
+
</style>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ExcelParameterProps {
|
|
2
|
+
title?: string; // 标题
|
|
3
|
+
tips: string; // 说明
|
|
4
|
+
fileSize?: number; // 上传文件的大小
|
|
5
|
+
fileType?: File.ExcelMimeType[]; // 上传文件的类型
|
|
6
|
+
tempApi?: (params: any) => Promise<any>; // 下载模板的Api,
|
|
7
|
+
tempParams?: any; // 下载模板的入参
|
|
8
|
+
importApi?: (params: any) => Promise<any>; // 批量导入的Api
|
|
9
|
+
importParams?: any; // 批量导入的入参
|
|
10
|
+
getTableList?: (response?: Record<string, any>) => void; // 获取表格数据的Api
|
|
11
|
+
columns?: any[];
|
|
12
|
+
tableData?: any[];
|
|
13
|
+
isDrag?: boolean; // 是否可拖拽上传
|
|
14
|
+
isIcon?: boolean; // 是否展示提示图标
|
|
15
|
+
width?: string; // 弹框宽度
|
|
16
|
+
fileName: string; // 文件流的名称 excelFormData
|
|
17
|
+
confirmText?: string | boolean; // 确定按钮的文字
|
|
18
|
+
confirmFun?: (params: any) => void; // 确定按钮的执行函数
|
|
19
|
+
autoClose?: boolean; // 导入成功后是否自动关闭弹框
|
|
20
|
+
importCallBack?: (params: any) => void; // 导入成功的回调函数
|
|
21
|
+
onRemove?: (params: { val: any; fileList: any[] }) => void; // 文件移除时的函数
|
|
22
|
+
isReplace?: boolean; // 是否可以在选中时自动替换上一个文件,必须设置limit=1
|
|
23
|
+
suffix?: string; // 后缀
|
|
24
|
+
isNotify?: boolean;
|
|
25
|
+
}
|