@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.
Files changed (145) hide show
  1. package/package.json +68 -0
  2. package/src/JztBackTop/index.vue +255 -0
  3. package/src/JztButtonList/index.vue +88 -0
  4. package/src/JztChart/index.vue +95 -0
  5. package/src/JztCharts/index.vue +317 -0
  6. package/src/JztClassTabs/index.vue +156 -0
  7. package/src/JztDateSelect/dateSelect.vue +186 -0
  8. package/src/JztDateSelect/dateType.vue +54 -0
  9. package/src/JztDateSelect/index.ts +135 -0
  10. package/src/JztDateSelect/interface/index.ts +13 -0
  11. package/src/JztDialog/index.vue +249 -0
  12. package/src/JztEllipsisTooltip/index.vue +61 -0
  13. package/src/JztEmpty/index.vue +45 -0
  14. package/src/JztErrorPage/403.vue +30 -0
  15. package/src/JztErrorPage/404.vue +19 -0
  16. package/src/JztErrorPage/500.vue +18 -0
  17. package/src/JztErrorPage/assets/401.png +0 -0
  18. package/src/JztErrorPage/assets/403.png +0 -0
  19. package/src/JztErrorPage/assets/404.png +0 -0
  20. package/src/JztErrorPage/assets/500.png +0 -0
  21. package/src/JztErrorPage/index.scss +35 -0
  22. package/src/JztErrorPage/index.vue +35 -0
  23. package/src/JztFilePreview/components/pdfViewer.vue +221 -0
  24. package/src/JztFilePreview/hooks/useImageMethod.ts +256 -0
  25. package/src/JztFilePreview/index.scss +171 -0
  26. package/src/JztFilePreview/index.vue +68 -0
  27. package/src/JztFilePreview/interface/index.ts +18 -0
  28. package/src/JztFilePreview/previewFile.vue +371 -0
  29. package/src/JztFormGrid/README.md +520 -0
  30. package/src/JztFormGrid/components/formItem.vue +209 -0
  31. package/src/JztFormGrid/components/formItemValue.vue +384 -0
  32. package/src/JztFormGrid/components/showDetailForm.vue +172 -0
  33. package/src/JztFormGrid/index.scss +60 -0
  34. package/src/JztFormGrid/index.vue +513 -0
  35. package/src/JztFormGrid/interface/index.ts +106 -0
  36. package/src/JztGrid/components/GridItem.vue +68 -0
  37. package/src/JztGrid/index.vue +179 -0
  38. package/src/JztGrid/interface/index.ts +6 -0
  39. package/src/JztImportExcel/assets/delete.png +0 -0
  40. package/src/JztImportExcel/index.scss +46 -0
  41. package/src/JztImportExcel/index.vue +430 -0
  42. package/src/JztImportExcel/interface/index.ts +25 -0
  43. package/src/JztLabelTitle/index.vue +65 -0
  44. package/src/JztLeftRightMode/components/CollapseButton.vue +80 -0
  45. package/src/JztLeftRightMode/components/LeftCard.vue +203 -0
  46. package/src/JztLeftRightMode/components/LeftLayout.vue +173 -0
  47. package/src/JztLeftRightMode/components/RightHeader.vue +186 -0
  48. package/src/JztLeftRightMode/components/RightLayout.vue +235 -0
  49. package/src/JztLeftRightMode/components/RightTableHeader.vue +43 -0
  50. package/src/JztLeftRightMode/hooks/useCollapse.ts +17 -0
  51. package/src/JztLeftRightMode/hooks/useDefaultProps.ts +19 -0
  52. package/src/JztLeftRightMode/hooks/useLeftLayout.ts +201 -0
  53. package/src/JztLeftRightMode/hooks/useMode.ts +20 -0
  54. package/src/JztLeftRightMode/hooks/usePrevNext.ts +60 -0
  55. package/src/JztLeftRightMode/hooks/useRightLayout.ts +215 -0
  56. package/src/JztLeftRightMode/hooks/useSlots.ts +15 -0
  57. package/src/JztLeftRightMode/index.ts +3 -0
  58. package/src/JztLeftRightMode/index.vue +494 -0
  59. package/src/JztLeftRightMode/types/index.ts +457 -0
  60. package/src/JztLoading/fullScreen.ts +45 -0
  61. package/src/JztLoading/index.scss +67 -0
  62. package/src/JztLoading/index.vue +18 -0
  63. package/src/JztLogin/components/LoginFooter.vue +17 -0
  64. package/src/JztLogin/components/LoginForm.vue +99 -0
  65. package/src/JztLogin/hooks/useLogin.ts +186 -0
  66. package/src/JztLogin/index.scss +142 -0
  67. package/src/JztLogin/index.vue +31 -0
  68. package/src/JztLogin/interface/index.ts +47 -0
  69. package/src/JztNumericalRange/index.vue +81 -0
  70. package/src/JztPageCard/comm/datePicker.vue +151 -0
  71. package/src/JztPageCard/comm/details.vue +60 -0
  72. package/src/JztPageCard/comm/export.vue +24 -0
  73. package/src/JztPageCard/comm/tabs.vue +94 -0
  74. package/src/JztPageCard/comm/tooltip.vue +31 -0
  75. package/src/JztPageCard/index.vue +287 -0
  76. package/src/JztPagination/index.vue +70 -0
  77. package/src/JztProductInfo/components/imagePreview.vue +275 -0
  78. package/src/JztProductInfo/components/qxUnique.vue +101 -0
  79. package/src/JztProductInfo/components/records.vue +265 -0
  80. package/src/JztProductInfo/hooks/useParams.ts +143 -0
  81. package/src/JztProductInfo/hooks/useQxUnique.tsx +466 -0
  82. package/src/JztProductInfo/images/defaultProduct.png +0 -0
  83. package/src/JztProductInfo/index.ts +116 -0
  84. package/src/JztProductInfo/index.vue +108 -0
  85. package/src/JztProductInfo/interface/index.ts +15 -0
  86. package/src/JztQueryDetailTable/index.scss +100 -0
  87. package/src/JztQueryDetailTable/index.vue +400 -0
  88. package/src/JztQueryDetailTable/interface/index.ts +10 -0
  89. package/src/JztQueryTable/QueryTable /345/212/237/350/203/275.md" +1580 -0
  90. package/src/JztQueryTable/README.md +567 -0
  91. package/src/JztQueryTable/components/ColSetting.vue +67 -0
  92. package/src/JztQueryTable/components/ColumnsSetting.vue +404 -0
  93. package/src/JztQueryTable/components/ColumnsSetting1.vue +220 -0
  94. package/src/JztQueryTable/components/DeployToAccountLevelSetting.vue +351 -0
  95. package/src/JztQueryTable/components/Pagination.vue +54 -0
  96. package/src/JztQueryTable/components/TableColumn.vue +109 -0
  97. package/src/JztQueryTable/const.ts +1 -0
  98. package/src/JztQueryTable/hooks/useQueryTable.ts +194 -0
  99. package/src/JztQueryTable/hooks/useSelection.ts +47 -0
  100. package/src/JztQueryTable/hooks/useTableSetting.ts +197 -0
  101. package/src/JztQueryTable/hooks/useTemplate.ts +127 -0
  102. package/src/JztQueryTable/index.scss +91 -0
  103. package/src/JztQueryTable/index.vue +1445 -0
  104. package/src/JztQueryTable/interface/index.ts +185 -0
  105. package/src/JztRegionSelect/index.vue +134 -0
  106. package/src/JztSearchForm/components/SearchFormItem.vue +473 -0
  107. package/src/JztSearchForm/index.vue +530 -0
  108. package/src/JztSearchForm/interface/index.ts +100 -0
  109. package/src/JztSelectFilter/index.scss +63 -0
  110. package/src/JztSelectFilter/index.vue +110 -0
  111. package/src/JztSelectTable/index.vue +257 -0
  112. package/src/JztTable/index.scss +72 -0
  113. package/src/JztTable/index.vue +353 -0
  114. package/src/JztTable/interface/index.ts +1 -0
  115. package/src/JztTime/comm/agencySelect.vue +112 -0
  116. package/src/JztTime/comm/collapseRow.vue +132 -0
  117. package/src/JztTime/comm/dateSelect.vue +292 -0
  118. package/src/JztTime/comm/deptSelect.vue +193 -0
  119. package/src/JztTime/comm/typeSelect.vue +97 -0
  120. package/src/JztTime/index.ts +216 -0
  121. package/src/JztTime/index.vue +303 -0
  122. package/src/JztTime/interface/index.ts +23 -0
  123. package/src/JztTreeFilter/index.scss +44 -0
  124. package/src/JztTreeFilter/index.vue +177 -0
  125. package/src/JztUploadFile/interface/index.ts +21 -0
  126. package/src/JztUploadFile/multiple.scss +215 -0
  127. package/src/JztUploadFile/multiple.vue +318 -0
  128. package/src/JztUploadFile/single.scss +226 -0
  129. package/src/JztUploadFile/single.vue +274 -0
  130. package/src/JztUploadImg/Img.vue +294 -0
  131. package/src/JztUploadImg/Imgs.vue +411 -0
  132. package/src/JztUploadImg/index.scss +138 -0
  133. package/src/JztUploadImg/interface/index.ts +22 -0
  134. package/src/SelectIcon/index.scss +39 -0
  135. package/src/SelectIcon/index.vue +106 -0
  136. package/src/SvgIcon/index.vue +22 -0
  137. package/src/hooks/useAuthButtons.ts +58 -0
  138. package/src/hooks/useFormByUserType.ts +90 -0
  139. package/src/hooks/useTableEvents.ts +30 -0
  140. package/src/hooks/useUploadFileHook.ts +262 -0
  141. package/src/index.ts +91 -0
  142. package/src/typings/global.d.ts +101 -0
  143. package/src/utils/index.ts +107 -0
  144. package/src/utils/tree.ts +57 -0
  145. 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>