@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,317 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="Jzt-charts" v-bind="$attrs">
|
|
3
|
+
<div :style="{ height, width }" :id="id" ref="eChartRef" />
|
|
4
|
+
<slot></slot>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts" name="JztCharts">
|
|
9
|
+
import * as echarts from 'echarts' // 引入echarts
|
|
10
|
+
import { useResizeObserver } from '@vueuse/core'
|
|
11
|
+
import { debounce } from 'lodash-es'
|
|
12
|
+
import { onMounted, onBeforeUnmount, onActivated, nextTick, watch, ref, markRaw, computed, reactive } from 'vue'
|
|
13
|
+
import { notData } from '@jzt-spd/styles/assets'
|
|
14
|
+
const props = defineProps({
|
|
15
|
+
options: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: () => ({})
|
|
18
|
+
},
|
|
19
|
+
id: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: () => Math.random().toString(36).substring(2, 8)
|
|
22
|
+
},
|
|
23
|
+
theme: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: ''
|
|
26
|
+
},
|
|
27
|
+
// 是否显示空数据
|
|
28
|
+
isEmpty: {
|
|
29
|
+
type: [Boolean, Function],
|
|
30
|
+
default: false
|
|
31
|
+
},
|
|
32
|
+
description: {
|
|
33
|
+
type: String,
|
|
34
|
+
default: '无数据'
|
|
35
|
+
},
|
|
36
|
+
className: {
|
|
37
|
+
type: String,
|
|
38
|
+
default: 'Jzt-charts-container'
|
|
39
|
+
},
|
|
40
|
+
width: {
|
|
41
|
+
type: String,
|
|
42
|
+
default: '100%'
|
|
43
|
+
},
|
|
44
|
+
height: {
|
|
45
|
+
type: String,
|
|
46
|
+
default: '100%'
|
|
47
|
+
},
|
|
48
|
+
loading: {
|
|
49
|
+
type: Boolean,
|
|
50
|
+
default: true
|
|
51
|
+
},
|
|
52
|
+
// 是否获取zr实例
|
|
53
|
+
isGetZr: {
|
|
54
|
+
type: Boolean,
|
|
55
|
+
default: false
|
|
56
|
+
},
|
|
57
|
+
// 是否是横向柱状图
|
|
58
|
+
isCrossBar: {
|
|
59
|
+
type: Boolean,
|
|
60
|
+
default: false
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
// 空数据图配置
|
|
64
|
+
const emptyImgWidth = ref(150)
|
|
65
|
+
const emptyImgSize = ref(16)
|
|
66
|
+
const emptyData = ref({
|
|
67
|
+
graphic: {
|
|
68
|
+
elements: [
|
|
69
|
+
{
|
|
70
|
+
type: 'group',
|
|
71
|
+
left: 'center',
|
|
72
|
+
top: 'center',
|
|
73
|
+
children: [
|
|
74
|
+
{
|
|
75
|
+
type: 'image',
|
|
76
|
+
left: -emptyImgWidth.value / 2,
|
|
77
|
+
top: -emptyImgWidth.value / 2,
|
|
78
|
+
style: {
|
|
79
|
+
image: notData,
|
|
80
|
+
width: emptyImgWidth.value,
|
|
81
|
+
height: emptyImgWidth.value
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: 'text',
|
|
86
|
+
left: -(emptyImgSize.value * (props.description.length / 2)), // 与图片同一中心点
|
|
87
|
+
top: emptyImgWidth.value / 2 + 24, // 与图片同一中心点
|
|
88
|
+
style: {
|
|
89
|
+
text: props.description,
|
|
90
|
+
fontSize: emptyImgSize.value,
|
|
91
|
+
fill: '#333333'
|
|
92
|
+
},
|
|
93
|
+
transform: 'translate(-50%, -50%)'
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
xAxis: { show: false },
|
|
100
|
+
yAxis: { show: false },
|
|
101
|
+
series: [],
|
|
102
|
+
tooltip: { show: false },
|
|
103
|
+
legend: { show: false },
|
|
104
|
+
grid: { top: 0, bottom: 0, left: 0, right: 0 }
|
|
105
|
+
})
|
|
106
|
+
const eChartRef = ref<HTMLDivElement>()
|
|
107
|
+
const myChart = ref()
|
|
108
|
+
const emits = defineEmits()
|
|
109
|
+
|
|
110
|
+
// 图表初始化
|
|
111
|
+
const renderChart = () => {
|
|
112
|
+
myChart.value = markRaw(echarts.init(eChartRef.value, props.theme))
|
|
113
|
+
|
|
114
|
+
setOption(props.options)
|
|
115
|
+
|
|
116
|
+
// 返回chart实例
|
|
117
|
+
emits('chart', myChart.value)
|
|
118
|
+
|
|
119
|
+
// 事件监听 图形点击
|
|
120
|
+
onClick()
|
|
121
|
+
// 空白/区域点击
|
|
122
|
+
if (props.isGetZr) {
|
|
123
|
+
onGetZr()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 监听元素变化
|
|
127
|
+
useResizeObserver(eChartRef.value, resizeChart)
|
|
128
|
+
// 如果不想用vueuse,可以使用下边的方法代替,但组件使用v-show时,不会触发resize事件
|
|
129
|
+
// window.addEventListener('resize', resizeChart)
|
|
130
|
+
}
|
|
131
|
+
// 事件监听
|
|
132
|
+
const onClick = () => {
|
|
133
|
+
myChart.value.on(
|
|
134
|
+
'click',
|
|
135
|
+
debounce(function (params: any) {
|
|
136
|
+
console.log(props.options)
|
|
137
|
+
if (props.options?.type === 'pie' || props.options?.series[0]?.type === 'pie') {
|
|
138
|
+
clickSeriesSelect(params)
|
|
139
|
+
emits('pieSelect', {
|
|
140
|
+
activeIndex: activeIndex,
|
|
141
|
+
params: params
|
|
142
|
+
})
|
|
143
|
+
}
|
|
144
|
+
if (params.seriesId) {
|
|
145
|
+
emits('onClick', params)
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
500,
|
|
149
|
+
true
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
let activeIndex = null
|
|
153
|
+
const onGetZr = () => {
|
|
154
|
+
// myChart.value.getZr().off('click') //防止触发两次点击事件
|
|
155
|
+
myChart.value.getZr().on(
|
|
156
|
+
'click',
|
|
157
|
+
debounce(function (params: any) {
|
|
158
|
+
const point = [params.offsetX, params.offsetY]
|
|
159
|
+
if (props.options?.type === 'pie' || props.options?.series[0]?.type === 'pie') {
|
|
160
|
+
// 饼图单选
|
|
161
|
+
if (!myChart.value.containPixel('series', point) && activeIndex !== null) {
|
|
162
|
+
// 点击空白触发
|
|
163
|
+
pieSelectHandle('pieUnSelect', 0, activeIndex)
|
|
164
|
+
activeIndex = null
|
|
165
|
+
emits('pieSelect', {
|
|
166
|
+
activeIndex: activeIndex,
|
|
167
|
+
params: params
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
} else if (
|
|
171
|
+
['bar', 'line'].includes(props.options?.type) ||
|
|
172
|
+
['bar', 'line'].includes(props.options?.series[0]?.type)
|
|
173
|
+
) {
|
|
174
|
+
// 柱状
|
|
175
|
+
if (myChart.value.containPixel('grid', point)) {
|
|
176
|
+
// 正常柱状图坐标
|
|
177
|
+
let xIndex = Math.abs(myChart.value.convertFromPixel({ seriesIndex: 0 }, [params.offsetX, params.offsetY])[0])
|
|
178
|
+
// 横向柱状图坐标
|
|
179
|
+
let yIndex = Math.abs(myChart.value.convertFromPixel({ seriesIndex: 0 }, [params.offsetX, params.offsetY])[1])
|
|
180
|
+
emits('onClickCol', props.isCrossBar ? yIndex : xIndex)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}),
|
|
184
|
+
500,
|
|
185
|
+
{ leading: true, trailing: false }
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
// 饼图单选
|
|
189
|
+
const clickSeriesSelect = params => {
|
|
190
|
+
if (params.componentType !== 'series') return
|
|
191
|
+
const { dataIndex, seriesIndex } = params
|
|
192
|
+
// 再点当前 → 取消
|
|
193
|
+
if (activeIndex === dataIndex) {
|
|
194
|
+
pieSelectHandle('unSelect', seriesIndex, dataIndex)
|
|
195
|
+
activeIndex = null
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
// 取消上一个
|
|
199
|
+
if (activeIndex !== null) {
|
|
200
|
+
pieSelectHandle('unSelect', seriesIndex, activeIndex)
|
|
201
|
+
}
|
|
202
|
+
// 选中当前
|
|
203
|
+
pieSelectHandle('pieSelect', seriesIndex, dataIndex)
|
|
204
|
+
activeIndex = dataIndex
|
|
205
|
+
}
|
|
206
|
+
const pieSelectHandle = (type: 'pieSelect' | 'unSelect' | 'pieUnSelect', seriesIndex, dataIndex) => {
|
|
207
|
+
const chart = myChart.value
|
|
208
|
+
chart.dispatchAction({
|
|
209
|
+
type: type,
|
|
210
|
+
seriesIndex: seriesIndex,
|
|
211
|
+
dataIndex: dataIndex
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 重绘图表函数
|
|
216
|
+
const resizeChart = debounce(() => {
|
|
217
|
+
myChart.value?.resize()
|
|
218
|
+
}, 300)
|
|
219
|
+
|
|
220
|
+
// 设置图表函数
|
|
221
|
+
const setOption = debounce(async data => {
|
|
222
|
+
if (!myChart.value) return
|
|
223
|
+
myChart.value.setOption(formatEmpty.value ? emptyData.value : data, true, true)
|
|
224
|
+
await nextTick()
|
|
225
|
+
resizeChart()
|
|
226
|
+
}, 300)
|
|
227
|
+
const setLoading = (isLOading: boolean) => {
|
|
228
|
+
if (isLOading) {
|
|
229
|
+
myChart.value.showLoading({
|
|
230
|
+
text: '加载中...', //提示文字
|
|
231
|
+
showSpinner: true,
|
|
232
|
+
fontSize: 14,
|
|
233
|
+
color: '#409eff',
|
|
234
|
+
textColor: '#409eff',
|
|
235
|
+
zlevel: 5,
|
|
236
|
+
// 旋转动画(spinner)的半径。
|
|
237
|
+
spinnerRadius: 8,
|
|
238
|
+
// 旋转动画(spinner)的线宽。
|
|
239
|
+
lineWidth: 3
|
|
240
|
+
})
|
|
241
|
+
} else {
|
|
242
|
+
myChart.value.hideLoading()
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// 为空时
|
|
246
|
+
const formatEmpty = computed(() => {
|
|
247
|
+
if (typeof props.isEmpty === 'function') {
|
|
248
|
+
return props.isEmpty(props.options)
|
|
249
|
+
}
|
|
250
|
+
return props.isEmpty
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
watch(
|
|
254
|
+
() => props.options,
|
|
255
|
+
async nw => {
|
|
256
|
+
await nextTick()
|
|
257
|
+
setOption(nw)
|
|
258
|
+
},
|
|
259
|
+
{ deep: true }
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
watch(
|
|
263
|
+
() => props.theme,
|
|
264
|
+
async () => {
|
|
265
|
+
await nextTick()
|
|
266
|
+
myChart.value.dispose()
|
|
267
|
+
renderChart()
|
|
268
|
+
}
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
const refreshChart = () => {
|
|
272
|
+
myChart.value.clear()
|
|
273
|
+
myChart.value.setOption(props.options)
|
|
274
|
+
}
|
|
275
|
+
onMounted(() => {
|
|
276
|
+
nextTick(() => {
|
|
277
|
+
renderChart()
|
|
278
|
+
})
|
|
279
|
+
})
|
|
280
|
+
onBeforeUnmount(() => {
|
|
281
|
+
// 取消监听
|
|
282
|
+
// window.removeEventListener('resize', resizeChart)
|
|
283
|
+
// 销毁echarts实例
|
|
284
|
+
myChart.value.dispose()
|
|
285
|
+
myChart.value = null
|
|
286
|
+
})
|
|
287
|
+
onActivated(() => {
|
|
288
|
+
// 激活时重新渲染图表
|
|
289
|
+
myChart.value?.resize()
|
|
290
|
+
})
|
|
291
|
+
defineExpose({
|
|
292
|
+
refreshChart,
|
|
293
|
+
setLoading,
|
|
294
|
+
myChart
|
|
295
|
+
})
|
|
296
|
+
</script>
|
|
297
|
+
|
|
298
|
+
<style lang="scss" scoped>
|
|
299
|
+
.Jzt-charts {
|
|
300
|
+
position: relative;
|
|
301
|
+
width: 100%;
|
|
302
|
+
height: 100%;
|
|
303
|
+
overflow: hidden;
|
|
304
|
+
|
|
305
|
+
&-container {
|
|
306
|
+
width: 100%;
|
|
307
|
+
height: 100%;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
:deep(.empty) {
|
|
311
|
+
width: 100%;
|
|
312
|
+
height: 100%;
|
|
313
|
+
}
|
|
314
|
+
.isShow {
|
|
315
|
+
display: none;
|
|
316
|
+
}
|
|
317
|
+
</style>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tabs ml8 mt8">
|
|
3
|
+
<span
|
|
4
|
+
v-for="option in typeOptions"
|
|
5
|
+
:key="option.productClassId"
|
|
6
|
+
@click="handleTypeChange(option)"
|
|
7
|
+
:class="[
|
|
8
|
+
'btn flx-center px-4 py-1 text-sm font-medium rounded-md transition-all duration-200',
|
|
9
|
+
tabsType === option?.productClassId ? 'btn-active' : 'btn-hover'
|
|
10
|
+
]"
|
|
11
|
+
>
|
|
12
|
+
{{ option?.productClassName || '' }}
|
|
13
|
+
</span>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup lang="ts" name="JztTime">
|
|
18
|
+
import { ref, onMounted, computed } from 'vue'
|
|
19
|
+
import { isEmpty } from '@jzt-spd/utils'
|
|
20
|
+
import { getProductClassByUser } from '@jzt-spd/utils/commonApi'
|
|
21
|
+
import { ProductCodeKeys } from '@jzt-spd/utils/consts'
|
|
22
|
+
|
|
23
|
+
interface ClassType {
|
|
24
|
+
productClassId: string
|
|
25
|
+
productClassName: string
|
|
26
|
+
}
|
|
27
|
+
interface PropsType {
|
|
28
|
+
hasAll?: boolean // 是否包含全部选项
|
|
29
|
+
classList?: ClassType[] // 固定大类类型集合
|
|
30
|
+
authList?: ProductCodeKeys[] // 固定大类中,根据权限判断
|
|
31
|
+
productClassId?: string // 大类Id
|
|
32
|
+
productClassCode?: string //大类Code 'G' | 'D' | 'J' | 'M' | 'K' | 'H' | 'Y'
|
|
33
|
+
isInit?: boolean // 是否初始化调用一次
|
|
34
|
+
}
|
|
35
|
+
const props = withDefaults(defineProps<PropsType>(), {
|
|
36
|
+
hasAll: true,
|
|
37
|
+
productClassId: '',
|
|
38
|
+
authList: () => [],
|
|
39
|
+
classList: () => [],
|
|
40
|
+
isInit: true
|
|
41
|
+
})
|
|
42
|
+
const emit = defineEmits(['onChange', 'onPermission', 'update:productClassId'])
|
|
43
|
+
// 全部选项
|
|
44
|
+
const all = [
|
|
45
|
+
{
|
|
46
|
+
productClassId: '',
|
|
47
|
+
productClassName: '全院',
|
|
48
|
+
isAll: true
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
// 全部选项包含的id集合
|
|
52
|
+
const allClassIds = ref<string[]>([])
|
|
53
|
+
const tabsType = computed({
|
|
54
|
+
get() {
|
|
55
|
+
return props.productClassId
|
|
56
|
+
},
|
|
57
|
+
set(val) {
|
|
58
|
+
emit('update:productClassId', val)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// 选项
|
|
63
|
+
const typeOptions = ref<ClassType[]>(all)
|
|
64
|
+
|
|
65
|
+
const getClassType = async () => {
|
|
66
|
+
try {
|
|
67
|
+
const res = await getProductClassByUser()
|
|
68
|
+
if (!isEmpty(res.result)) {
|
|
69
|
+
let result = res.result || []
|
|
70
|
+
// 过滤指定的大类
|
|
71
|
+
if (!isEmpty(props.authList) && Array.isArray(props.authList)) {
|
|
72
|
+
result = result.filter(item => props.authList?.includes(item.productClassCode))
|
|
73
|
+
}
|
|
74
|
+
// 把全部的id 赋值:全部选项
|
|
75
|
+
allClassIds.value = result.map((item: any) => item.productClassId) // 过滤后的大类ID集合
|
|
76
|
+
const authLen = result.length // 过滤后大类长度
|
|
77
|
+
if (authLen == 0) {
|
|
78
|
+
typeOptions.value = []
|
|
79
|
+
} else if (authLen == 1) {
|
|
80
|
+
typeOptions.value = [...result]
|
|
81
|
+
tabsType.value = result[0]?.productClassId
|
|
82
|
+
} else {
|
|
83
|
+
initSetValue(result)
|
|
84
|
+
}
|
|
85
|
+
// 是否有权限
|
|
86
|
+
emit('onPermission', authLen != 0)
|
|
87
|
+
props.isInit && handleTypeChange(typeOptions.value[0])
|
|
88
|
+
}
|
|
89
|
+
} catch (error) {
|
|
90
|
+
allClassIds.value = []
|
|
91
|
+
emit('onPermission', true)
|
|
92
|
+
handleTypeChange({})
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const handleTypeChange = (option: any) => {
|
|
96
|
+
tabsType.value = option?.productClassId
|
|
97
|
+
// if (option?.isAll) {
|
|
98
|
+
// emit('onChange', {
|
|
99
|
+
// ...option,
|
|
100
|
+
// productClassId: allClassIds.value
|
|
101
|
+
// })
|
|
102
|
+
// } else {
|
|
103
|
+
emit('onChange', {
|
|
104
|
+
...option,
|
|
105
|
+
productClassIdList: allClassIds.value
|
|
106
|
+
})
|
|
107
|
+
// }
|
|
108
|
+
}
|
|
109
|
+
// 初始化赋值
|
|
110
|
+
const initSetValue = classList => {
|
|
111
|
+
typeOptions.value = props.hasAll ? [...all, ...classList] : [...classList]
|
|
112
|
+
if (!props.hasAll && isEmpty(props.productClassId)) {
|
|
113
|
+
tabsType.value = classList?.[0]?.productClassId
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
onMounted(() => {
|
|
117
|
+
if (!isEmpty(props.classList)) {
|
|
118
|
+
initSetValue(props.classList)
|
|
119
|
+
} else {
|
|
120
|
+
getClassType()
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<style lang="scss" scoped>
|
|
126
|
+
.tabs {
|
|
127
|
+
background-color: #ffffff;
|
|
128
|
+
box-shadow: 0 0 0 1px var(--el-border-color) inset;
|
|
129
|
+
padding: 4px;
|
|
130
|
+
border-radius: 4px;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
display: inline-block;
|
|
133
|
+
}
|
|
134
|
+
.btn {
|
|
135
|
+
display: inline-block;
|
|
136
|
+
height: 26px;
|
|
137
|
+
line-height: 25px;
|
|
138
|
+
text-align: center;
|
|
139
|
+
padding: 0 16px;
|
|
140
|
+
color: #00000099;
|
|
141
|
+
border-radius: 4px;
|
|
142
|
+
margin-right: 8px;
|
|
143
|
+
transition: all 0.2s ease;
|
|
144
|
+
}
|
|
145
|
+
.btn:last-child {
|
|
146
|
+
margin-right: 0;
|
|
147
|
+
}
|
|
148
|
+
.btn-active {
|
|
149
|
+
background: #3475fa;
|
|
150
|
+
color: #ffffff;
|
|
151
|
+
}
|
|
152
|
+
.btn-hover:hover {
|
|
153
|
+
background: #e5e7eb;
|
|
154
|
+
color: #111827;
|
|
155
|
+
}
|
|
156
|
+
</style>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flx-left-center">
|
|
3
|
+
<el-date-picker
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
v-model="timeDate"
|
|
6
|
+
:type="pickerType"
|
|
7
|
+
suffix-icon="Calendar"
|
|
8
|
+
:style="{ flex: 1.5, 'min-width': '80px', 'flex-shrink': 0 }"
|
|
9
|
+
:value-format="timeDataConfig"
|
|
10
|
+
:format="timeDataConfig"
|
|
11
|
+
:disabled-date="disableFutureDate"
|
|
12
|
+
range-separator="至"
|
|
13
|
+
:clearable="clearable"
|
|
14
|
+
:editable="false"
|
|
15
|
+
@change="changeTime"
|
|
16
|
+
@calendar-change="handleCalendarChange"
|
|
17
|
+
/>
|
|
18
|
+
<el-select
|
|
19
|
+
v-if="timeType == 'halfYear'"
|
|
20
|
+
v-model="halfYear"
|
|
21
|
+
:placeholder="''"
|
|
22
|
+
:style="{ flex: 1, 'flex-shrink': 0, marginLeft: '4px' }"
|
|
23
|
+
@change="changeDateType"
|
|
24
|
+
>
|
|
25
|
+
<el-option v-for="item in allHalfYear" :key="'half' + item.value" :label="item.label" :value="item.value" />
|
|
26
|
+
</el-select>
|
|
27
|
+
<el-select
|
|
28
|
+
v-if="timeType == 'quarter'"
|
|
29
|
+
v-model="quarterType"
|
|
30
|
+
:placeholder="''"
|
|
31
|
+
:style="{ flex: 1, 'flex-shrink': 0, marginLeft: '4px' }"
|
|
32
|
+
@change="changeDateType"
|
|
33
|
+
>
|
|
34
|
+
<el-option v-for="item in allQuarters" :key="'quarter' + item.value" :label="item.label" :value="item.value" />
|
|
35
|
+
</el-select>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<script setup lang="ts" name="JztDateSelect">
|
|
40
|
+
/**
|
|
41
|
+
* @component 类型日期选择组件
|
|
42
|
+
*/
|
|
43
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
44
|
+
import { dateOptions, allQuarters, allHalfYear, getChangeDateType, getDataRangeByType } from './index'
|
|
45
|
+
import { DateTypeProps, DateType } from './interface/index'
|
|
46
|
+
import { QueryItemsProps, SearchProps } from '../JztSearchForm/interface/index'
|
|
47
|
+
import dayjs from 'dayjs'
|
|
48
|
+
export interface DateSelectProps {
|
|
49
|
+
timeMode?: string // 日期模式
|
|
50
|
+
disabledDate?: Function //日期禁止函数
|
|
51
|
+
clearable?: boolean // 是否可清空
|
|
52
|
+
disabled?: boolean // 是否禁用
|
|
53
|
+
searchProp?: SearchProps
|
|
54
|
+
queryItem: QueryItemsProps<Record<string, any>>
|
|
55
|
+
searchParam: Record<string, any>
|
|
56
|
+
}
|
|
57
|
+
// 默认值
|
|
58
|
+
const props = withDefaults(defineProps<DateSelectProps>(), {
|
|
59
|
+
timeMode: 'timeMode',
|
|
60
|
+
clearable: false,
|
|
61
|
+
searchParam: () => ({}),
|
|
62
|
+
disabled: false
|
|
63
|
+
})
|
|
64
|
+
const timeDate = ref(props.searchProp?.defaultValue || '')
|
|
65
|
+
const startField = ref(props.searchProp?.startField)
|
|
66
|
+
const endField = ref(props.searchProp?.endField)
|
|
67
|
+
const getCurrentQuarterType = (): 1 | 2 | 3 | 4 => (Math.floor(dayjs().month() / 3) + 1) as 1 | 2 | 3 | 4
|
|
68
|
+
const getCurrentHalfYearType = (): 1 | 2 => (dayjs().month() < 6 ? 1 : 2)
|
|
69
|
+
const quarterType = ref<1 | 2 | 3 | 4>(getCurrentQuarterType())
|
|
70
|
+
const halfYear = ref<1 | 2>(getCurrentHalfYearType())
|
|
71
|
+
|
|
72
|
+
// 日期类型
|
|
73
|
+
const timeType = ref<DateType>('month')
|
|
74
|
+
// 日期选择器类型
|
|
75
|
+
const pickerType = ref<DateTypeProps>('month')
|
|
76
|
+
|
|
77
|
+
// 日期类型选中项信息
|
|
78
|
+
const typeSelectedItem = computed(() => {
|
|
79
|
+
return dateOptions.filter(item => item.value == timeType.value)[0]
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// 日期选择器配置信息
|
|
83
|
+
const timeDataConfig = computed(() => {
|
|
84
|
+
// return getDateConfig(pickerType.value)
|
|
85
|
+
const dateType = pickerType.value
|
|
86
|
+
if (dateType === 'year') {
|
|
87
|
+
return 'YYYY'
|
|
88
|
+
} else if (['month'].includes(dateType)) {
|
|
89
|
+
return 'YYYY-MM'
|
|
90
|
+
} else {
|
|
91
|
+
return 'YYYY-MM-DD'
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
const syncCurrentPeriodType = (type: DateType) => {
|
|
96
|
+
if (type === 'quarter') {
|
|
97
|
+
quarterType.value = getCurrentQuarterType()
|
|
98
|
+
} else if (type === 'halfYear') {
|
|
99
|
+
halfYear.value = getCurrentHalfYearType()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// 日期类型选择框改变
|
|
103
|
+
const dateSelectChangeType = (val: DateType) => {
|
|
104
|
+
timeType.value = val
|
|
105
|
+
pickerType.value = typeSelectedItem.value?.type as DateTypeProps
|
|
106
|
+
syncCurrentPeriodType(val)
|
|
107
|
+
const newDefault = getChangeDateType(val)
|
|
108
|
+
timeDate.value = newDefault as string
|
|
109
|
+
changeDateType()
|
|
110
|
+
}
|
|
111
|
+
// 日期选择框改变
|
|
112
|
+
const changeTime = val => {
|
|
113
|
+
timeDate.value = val
|
|
114
|
+
changeDateType()
|
|
115
|
+
}
|
|
116
|
+
// 季度 // 半年
|
|
117
|
+
const changeDateType = () => {
|
|
118
|
+
const timeDefault = timeDate.value || getChangeDateType(timeType.value)
|
|
119
|
+
// console.log(timeDefault)
|
|
120
|
+
const str = getDataRangeByType({
|
|
121
|
+
timeMode: timeType.value,
|
|
122
|
+
typeNum: timeType.value === 'halfYear' ? halfYear.value : quarterType.value,
|
|
123
|
+
value: timeDefault
|
|
124
|
+
})
|
|
125
|
+
const { startTime, endTime, timeValue } = str
|
|
126
|
+
timeDate.value = timeValue
|
|
127
|
+
if (startField.value) {
|
|
128
|
+
props.searchParam[startField.value] = startTime
|
|
129
|
+
}
|
|
130
|
+
if (endField.value) {
|
|
131
|
+
props.searchParam[endField.value] = endTime
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 日期禁用函数
|
|
136
|
+
const rangeStartDate = ref<Date | null>(null)
|
|
137
|
+
|
|
138
|
+
const disableFutureDate = (time: Date) => {
|
|
139
|
+
// daterange 模式下限制最多选两年
|
|
140
|
+
if (pickerType.value === 'daterange' && rangeStartDate.value) {
|
|
141
|
+
const minDate = dayjs(rangeStartDate.value).subtract(2, 'year').add(1, 'day')
|
|
142
|
+
const maxDate = dayjs(rangeStartDate.value).add(2, 'year').subtract(1, 'day')
|
|
143
|
+
if (dayjs(time).isBefore(minDate) || dayjs(time).isAfter(maxDate)) {
|
|
144
|
+
return true
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 自定义函数
|
|
148
|
+
if (props.disabledDate) {
|
|
149
|
+
return props.disabledDate(time)
|
|
150
|
+
}
|
|
151
|
+
return false
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// daterange 选中第一个日期时记录,选完第二个后清除
|
|
155
|
+
const handleCalendarChange = (val: [Date, Date] | null) => {
|
|
156
|
+
if (pickerType.value !== 'daterange') return
|
|
157
|
+
if (val && val[0] && !val[1]) {
|
|
158
|
+
rangeStartDate.value = val[0]
|
|
159
|
+
} else {
|
|
160
|
+
rangeStartDate.value = null
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 监听 timeMode 变化
|
|
165
|
+
watch(
|
|
166
|
+
() => props.searchParam?.[props.timeMode],
|
|
167
|
+
val => {
|
|
168
|
+
if (val) {
|
|
169
|
+
dateSelectChangeType(val)
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
immediate: true
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
watch(
|
|
177
|
+
() => props.searchParam?.[props.queryItem.prop] || halfYear.value || quarterType.value,
|
|
178
|
+
val => {
|
|
179
|
+
if (val && val.length) {
|
|
180
|
+
timeDate.value = val
|
|
181
|
+
changeDateType()
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{ deep: true, immediate: true }
|
|
185
|
+
)
|
|
186
|
+
</script>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flx-left-center">
|
|
3
|
+
<el-select
|
|
4
|
+
class="select"
|
|
5
|
+
v-model="searchParam[queryItem.prop]"
|
|
6
|
+
:placeholder="''"
|
|
7
|
+
:clearable="false"
|
|
8
|
+
:disabled="disabled"
|
|
9
|
+
@change="dateSelectChangeType"
|
|
10
|
+
>
|
|
11
|
+
<el-option v-for="item in getDateOptions" :key="item.name" :label="item.name" :value="item.value" />
|
|
12
|
+
</el-select>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts" name="JztDateType">
|
|
17
|
+
/**
|
|
18
|
+
* @component 类型日期选择组件
|
|
19
|
+
*/
|
|
20
|
+
import { computed } from 'vue'
|
|
21
|
+
import { dateOptions } from './index'
|
|
22
|
+
import { DateType } from './interface/index'
|
|
23
|
+
import { QueryItemsProps } from '../JztSearchForm/interface/index'
|
|
24
|
+
|
|
25
|
+
export interface DateSelectProps {
|
|
26
|
+
clearable?: boolean // 是否可清空
|
|
27
|
+
disabled?: boolean // 是否禁用
|
|
28
|
+
showTimeType?: DateType[] // 允许的日期类型
|
|
29
|
+
queryItem: QueryItemsProps<Record<string, any>> // 配置项
|
|
30
|
+
searchParam: Record<string, any> // 表单参数
|
|
31
|
+
}
|
|
32
|
+
// 默认值
|
|
33
|
+
const props = withDefaults(defineProps<DateSelectProps>(), {
|
|
34
|
+
clearable: false, // 是否可清空 放在search.props 中
|
|
35
|
+
showTimeType: () => [], // 允许的日期类型 放在search.props 中
|
|
36
|
+
searchParam: () => ({}),// 查询表单
|
|
37
|
+
disabled: false // 是否禁用 放在search.props 中
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
// 日期类型选中项信息
|
|
41
|
+
const getDateOptions = computed(() => {
|
|
42
|
+
if (props.showTimeType && props.showTimeType.length) {
|
|
43
|
+
return dateOptions.filter(item => props.showTimeType.includes(item.value))
|
|
44
|
+
} else {
|
|
45
|
+
return dateOptions
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
// 日期类型选择框改变
|
|
49
|
+
const dateSelectChangeType = (val: DateType) => {
|
|
50
|
+
props.searchParam[props.queryItem.prop] = val
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<style lang="scss" scoped></style>
|