@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,235 @@
1
+ <script setup lang="tsx" name="RightLayout">
2
+ import { JztButtonList, JztEmpty, JztFormGrid, JztQueryTable } from '@jzt-spd/components'
3
+ import { default as RightHeader } from './RightHeader.vue'
4
+ import { default as RightTableHeader } from './RightTableHeader.vue'
5
+
6
+ import { useElementSize, watchOnce } from '@vueuse/core'
7
+ import { useSlots } from 'vue'
8
+ import { useCollapse } from '../hooks/useCollapse'
9
+ import { useRightProps } from '../hooks/useDefaultProps'
10
+ import { useRightLayout } from '../hooks/useRightLayout'
11
+
12
+ import { type RightLayoutEmits, type RightLayoutProps } from '../types/index'
13
+
14
+ const props = withDefaults(defineProps<RightLayoutProps>(), {
15
+ ...useRightProps(),
16
+ headerProps: () => ({}),
17
+ tableProps: () => ({
18
+ tableConfig: { queryItems: [], topList: [], columns: [] },
19
+ toolButton: ['refresh', 'setting']
20
+ })
21
+ })
22
+ const emits = defineEmits<RightLayoutEmits>()
23
+
24
+ const slots = useSlots()
25
+ const { isCollapse, onCollapse } = useCollapse()
26
+
27
+ const {
28
+ tableRef,
29
+ formGridRef,
30
+ rightLayoutRef,
31
+ loading,
32
+ background,
33
+ headerData,
34
+ mergedTotalProps,
35
+ totalData,
36
+ expose,
37
+ descriptionsProps,
38
+ onSortChange,
39
+ onGetTableData
40
+ } = useRightLayout(props, emits)
41
+
42
+ const { height } = useElementSize(rightLayoutRef)
43
+
44
+ watchOnce(
45
+ () => height.value,
46
+ newVal => {
47
+ isCollapse.value = newVal > (props.overHeight ?? 400)
48
+ }
49
+ )
50
+
51
+ defineExpose(expose)
52
+
53
+ export type Expose = typeof expose
54
+ </script>
55
+
56
+ <template>
57
+ <div
58
+ ref="rightLayoutRef"
59
+ class="right-aside"
60
+ :class="{ 'is-over-height': height < overHeight, 'is-bg-color': showBgColor }"
61
+ >
62
+ <template v-if="!showEmpty">
63
+ <slot name="main">
64
+ <div class="right-aside-main">
65
+ <jzt-query-table
66
+ ref="tableRef"
67
+ v-bind="{ ...tableProps, requestApi: onGetTableData }"
68
+ :table-card="false"
69
+ :request-auto="false"
70
+ :table-loading="loading"
71
+ :page-height="height < overHeight ? `${overHeight}px` : '100%'"
72
+ @sort-change="onSortChange"
73
+ @get-end-columns="tableRef?.searchAndClear"
74
+ @reset="$emit('rightReset')"
75
+ >
76
+ <template #formTopSlot>
77
+ <slot name="main-header"></slot>
78
+ </template>
79
+ <template #tableTops>
80
+ <slot v-if="showHeader" name="header">
81
+ <right-header
82
+ v-bind="props"
83
+ v-model:background="background"
84
+ :data="headerData"
85
+ :is-collapse="isCollapse"
86
+ @collapse="onCollapse"
87
+ >
88
+ <template #header-title>
89
+ <slot name="header-title" :data="headerData"></slot>
90
+ </template>
91
+ <template #header-extra>
92
+ <slot name="header-extra" :data="headerData"></slot>
93
+ </template>
94
+ </right-header>
95
+ </slot>
96
+ <el-collapse-transition v-if="showDescriptions">
97
+ <div class="pl4 pr4" v-show="showHeader && showCollapse ? isCollapse : true">
98
+ <slot name="descriptions">
99
+ <jzt-form-grid
100
+ ref="formGridRef"
101
+ v-bind="{
102
+ ...descriptionsProps,
103
+ showOverflowTooltip: (descriptionsProps as any)?.showOverflowTooltip ?? true,
104
+ groupProps: { fontSize: '14px' }
105
+ }"
106
+ />
107
+ </slot>
108
+ </div>
109
+ </el-collapse-transition>
110
+ </template>
111
+ <template #headerTitle>
112
+ <right-table-header :title="tableProps.title">
113
+ <template #table-header-left>
114
+ <slot name="table-header-left" :data="headerData"></slot>
115
+ </template>
116
+ <template #table-header-right>
117
+ <slot name="table-header-right" :data="headerData"></slot>
118
+ </template>
119
+ </right-table-header>
120
+ </template>
121
+ <template #toolBtnRight>
122
+ <el-space v-if="mergedTotalProps?.length && !tableProps.isHideTotal">
123
+ <div v-for="config in mergedTotalProps" :key="config.type" class="flx-left-center">
124
+ <el-text class="flex-shrink-0">{{ config.label }}</el-text>
125
+ <span class="count" :style="{ color: config?.color }">
126
+ <EllipsisTooltip :content="totalData[config?.type || ''] + ''" />
127
+ </span>
128
+ <el-text v-if="config.unit">{{ `${config.unit}` }}</el-text>
129
+ </div>
130
+ </el-space>
131
+ </template>
132
+ <template #tableHeader>
133
+ <slot name="table-header"></slot>
134
+ </template>
135
+ <template v-if="slots['table-main']" #queryTableMain>
136
+ <div style="flex: 1">
137
+ <slot name="table-main"></slot>
138
+ </div>
139
+ </template>
140
+ <template #pageFooter="{ loading }">
141
+ <slot name="footer" :data="headerData" :loading="loading">
142
+ <div v-if="showFooter" class="right-aside-footer p12">
143
+ <jzt-button-list
144
+ v-if="buttonList"
145
+ :button-list="buttonList"
146
+ @onClick="$event?.(expose)"
147
+ ></jzt-button-list>
148
+ </div>
149
+ </slot>
150
+ </template>
151
+ </jzt-query-table>
152
+ </div>
153
+ </slot>
154
+ </template>
155
+ <div v-else class="position-center">
156
+ <jzt-empty></jzt-empty>
157
+ </div>
158
+ </div>
159
+ </template>
160
+
161
+ <style lang="scss" scoped>
162
+ .right-aside {
163
+ flex: 1;
164
+ display: flex;
165
+ flex-direction: column;
166
+ overflow: hidden;
167
+ position: relative;
168
+ &.is-bg-color {
169
+ background: v-bind(background);
170
+ }
171
+ &.is-over-height &-main {
172
+ overflow: auto;
173
+ }
174
+
175
+ &:not(.is-over-height) &-main {
176
+ overflow: auto;
177
+ flex: 1;
178
+ }
179
+
180
+ &-footer {
181
+ text-align: center;
182
+ margin: 0 -12px;
183
+ border-top: 1px solid #e5e5e5;
184
+ display: flex;
185
+ justify-content: center;
186
+ gap: 12px;
187
+ :deep(.el-button) {
188
+ margin-left: 0 !important;
189
+ margin-right: 0 !important;
190
+ }
191
+ }
192
+ }
193
+
194
+ :deep(.header-button-ri) {
195
+ flex: 1;
196
+ }
197
+ :deep(.check-total) {
198
+ padding-left: 12px;
199
+ }
200
+
201
+ .position-center {
202
+ position: absolute;
203
+ top: 50%;
204
+ left: 50%;
205
+ transform: translate(-50%, -50%);
206
+ }
207
+ </style>
208
+
209
+ <style lang="scss">
210
+ :not(.el-dialog) .right-aside {
211
+ .table-header {
212
+ padding: 0 12px;
213
+ align-items: center !important;
214
+ }
215
+
216
+ .el-table {
217
+ width: calc(100% - 24px);
218
+ margin: 0 12px;
219
+ }
220
+
221
+ .el-pagination {
222
+ padding: 0 12px 12px 0;
223
+ }
224
+ }
225
+
226
+ .el-dialog:has(.right-aside) {
227
+ .el-dialog__body {
228
+ padding: 0 12px !important;
229
+ }
230
+
231
+ .el-dialog__header {
232
+ border-bottom: none !important;
233
+ }
234
+ }
235
+ </style>
@@ -0,0 +1,43 @@
1
+ <script setup lang="tsx" name="RightTableHeader">
2
+ import { JztLabelTitle } from '@jzt-spd/components'
3
+
4
+ const props = defineProps<{
5
+ title?: string
6
+ }>()
7
+ </script>
8
+
9
+ <template>
10
+ <div class="right-aside-table-header">
11
+ <slot name="table-header-left"></slot>
12
+ <jzt-label-title
13
+ v-if="title"
14
+ :title="title"
15
+ font-size="14px"
16
+ style="padding: 0; padding-right: 12px; width: fit-content"
17
+ />
18
+ <slot name="table-header-right"></slot>
19
+ </div>
20
+ </template>
21
+
22
+ <style lang="scss" scoped>
23
+ .el-text:not(.el-text--primary) {
24
+ color: #333333;
25
+ }
26
+ .right-aside-table-header {
27
+ border-radius: 4px;
28
+ display: flex;
29
+ align-items: center;
30
+ height: 36px;
31
+ margin-top: 2px;
32
+ gap: 20px;
33
+ transition: all 0.2s ease-in-out;
34
+ .title {
35
+ color: #333333;
36
+ font-size: 16px;
37
+ font-weight: 500;
38
+ }
39
+ .flex-shrink-0 {
40
+ flex-shrink: 0;
41
+ }
42
+ }
43
+ </style>
@@ -0,0 +1,17 @@
1
+ import { ref } from 'vue'
2
+
3
+ export const useCollapse = () => {
4
+ /** 是否折叠侧边栏 */
5
+ const isCollapse = ref<boolean>(false)
6
+ /**
7
+ * 切换侧边栏折叠状态
8
+ */
9
+ const onCollapse = () => {
10
+ isCollapse.value = !isCollapse.value
11
+ }
12
+
13
+ return {
14
+ isCollapse,
15
+ onCollapse
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ import { type LeftLayoutProps, type RightLayoutProps } from '../types'
2
+
3
+ export const useLeftProps = () =>
4
+ ({
5
+ rowKey: 'id',
6
+ showBackTop: true,
7
+ debounceTime: 100
8
+ }) as Omit<LeftLayoutProps, 'statusEnum' | 'headerButtonList' | 'footerButtonList'>
9
+
10
+ export const useRightProps = () =>
11
+ ({
12
+ rowKey: 'id',
13
+ showHeader: true,
14
+ showBgColor: true,
15
+ showCollapse: true,
16
+ showDescriptions: true,
17
+ showScrollAnimation: true,
18
+ overHeight: 400
19
+ }) as Omit<RightLayoutProps, 'descriptionsProps' | 'buttonList'>
@@ -0,0 +1,201 @@
1
+ import { reactiveComputed, reactivePick, useDebounceFn, useVModel } from '@vueuse/core'
2
+ import { nextTick, reactive, ref, toRefs, watch } from 'vue'
3
+
4
+ import { type LeftLayoutProps } from '../types/index'
5
+
6
+ export type Row = { selected: boolean; options: any }
7
+
8
+ export const useLeftLayout = (props: LeftLayoutProps, emits: any) => {
9
+ /** 滚条实例 */
10
+ const scrollbarRef = ref<any>()
11
+ /** 列表元素实例列表 */
12
+ const itemRefs = ref<HTMLElement[]>([])
13
+ /** 加载动画实例 */
14
+ const loading = useVModel(props, 'loading', emits, { passive: true })
15
+ /** 数据行列表 */
16
+ const rows = ref<Row[]>([])
17
+ /** 当前选中行 */
18
+ const currentRow = ref<Row['options']>({})
19
+ /** 当前选中行索引 */
20
+ const currentIndex = ref<number>(0)
21
+ /** 是否全选 */
22
+ const isSelectedAll = ref<boolean>(false)
23
+
24
+ /** 分页信息 */
25
+ const page = reactive({
26
+ pageIndex: 0,
27
+ pageSize: 20,
28
+ total: 0
29
+ })
30
+ const more = reactive({
31
+ loading: false,
32
+ text: '正在加载中'
33
+ })
34
+
35
+ /** 数据列表 */
36
+ const dataList = reactiveComputed(() => rows.value.map((item: any) => item.options))
37
+
38
+ /** 选中数据列表 */
39
+ const selectedList = reactiveComputed(() =>
40
+ rows.value.filter((item: any) => item.selected).map((item: any) => item.options)
41
+ )
42
+
43
+ /** 选中数据ID列表 */
44
+ const selectedIds = reactiveComputed(() => selectedList.map((item: any) => item[props?.rowKey || 'id']))
45
+
46
+ /** 插槽绑定数据 */
47
+ const slotBind = reactiveComputed(() => ({
48
+ page,
49
+ item: currentRow.value,
50
+ index: currentIndex.value,
51
+ list: [...dataList],
52
+ selectedList: [...selectedList],
53
+ selectedIds: [...selectedIds]
54
+ }))
55
+
56
+ /** 暴露方法 */
57
+ const expose = {
58
+ ...toRefs(slotBind),
59
+ search: () => onResetList(),
60
+ scrollToIndex: (index: number) => onScrollToIndex(index),
61
+ setCurrentRow: (val: Row['options']) => (currentRow.value = val),
62
+ setCurrentIndex: (val: number) => (currentIndex.value = val)
63
+ }
64
+
65
+ /**
66
+ * 查询列表数据
67
+ */
68
+ const onQueryList = async () => {
69
+ try {
70
+ const { requestApi } = props
71
+ const { result, totalCount } = await requestApi?.(reactivePick(page, ['pageIndex', 'pageSize']))
72
+ rows.value = rows.value.concat(result.map((item: any) => ({ checked: false, options: item })))
73
+ page.total = totalCount
74
+ emits('query', slotBind)
75
+ } finally {
76
+ more.loading = false
77
+ loading.value = false
78
+ }
79
+ }
80
+
81
+ /**
82
+ * 重置列表数据
83
+ */
84
+ const onResetList = async () => {
85
+ page.pageIndex = 0
86
+ page.total = 0
87
+ more.loading = true
88
+ more.text = '正在加载中'
89
+ rows.value = []
90
+ currentRow.value = {}
91
+ isSelectedAll.value = false
92
+ loading.value = true
93
+ await onQueryList()
94
+ emits('reset', slotBind)
95
+ }
96
+
97
+ /**
98
+ * 加载更多数据
99
+ */
100
+ const onLoadList = async ({ scrollTop }) => {
101
+ const { clientHeight, scrollHeight } = scrollbarRef.value?.wrapRef
102
+ const isBottom = scrollTop + clientHeight >= scrollHeight - 250
103
+ const isLoadMore = rows.value.length < page.total
104
+ if (!more.loading && isBottom && isLoadMore) {
105
+ more.loading = true
106
+ more.text = '正在加载中'
107
+ page.pageIndex++
108
+ await onQueryList()
109
+ emits('load', slotBind)
110
+ }
111
+ }
112
+
113
+ /**
114
+ * 全选/取消全选
115
+ */
116
+ const onSelectedAllChange = (val: any) => {
117
+ rows.value.forEach((item: any) => (item.selected = val))
118
+ }
119
+
120
+ /**
121
+ * 卡片点击事件
122
+ */
123
+ const onCardClick = useDebounceFn((val: Row, index: number) => {
124
+ const { options } = val
125
+ currentRow.value = options
126
+ currentIndex.value = index
127
+ emits('cardClick', options)
128
+ }, props?.debounceTime)
129
+
130
+ /**
131
+ * 选中/取消选中
132
+ */
133
+ const onCardSelect = (val: Row) => {
134
+ val.selected = !val.selected
135
+ emits('cardSelected', val.options)
136
+ }
137
+
138
+ /**
139
+ * 批量存 ref
140
+ */
141
+ const onSetItemRef = (el: any, index: number) => {
142
+ if (el) itemRefs.value[index] = el
143
+ }
144
+
145
+ /**
146
+ * 滚动到指定 index
147
+ */
148
+ const onScrollToIndex = async (index: number, offset: number = 8) => {
149
+ await nextTick()
150
+ const el = itemRefs.value[index]
151
+ scrollbarRef.value?.scrollTo({ top: el.offsetTop - offset, behavior: 'smooth' })
152
+ }
153
+
154
+ /**
155
+ * 监听数据列表变化
156
+ */
157
+ watch(
158
+ () => dataList.length,
159
+ (newVal, oldVal) => {
160
+ if (newVal > 0 && oldVal === 0) {
161
+ currentIndex.value = 0
162
+ currentRow.value = [...dataList]?.[0]
163
+ emits('init', currentRow.value)
164
+ }
165
+ if (newVal === page.total) {
166
+ more.text = '没有更多单据了'
167
+ }
168
+ }
169
+ )
170
+
171
+ /**
172
+ * 监听数据列表和选中列表变化,更新全选状态
173
+ */
174
+ watch(
175
+ [() => dataList.length, () => selectedIds.length],
176
+ ([dataListLength, selectedKeysLength]) => (isSelectedAll.value = selectedKeysLength === dataListLength)
177
+ )
178
+
179
+ return {
180
+ scrollbarRef,
181
+ isSelectedAll,
182
+ rows,
183
+ currentRow,
184
+ currentIndex,
185
+ page,
186
+ more,
187
+ dataList,
188
+ selectedList,
189
+ selectedIds,
190
+ slotBind,
191
+ expose,
192
+ itemRefs,
193
+ onQueryList,
194
+ onResetList,
195
+ onLoadList,
196
+ onSelectedAllChange,
197
+ onCardClick,
198
+ onCardSelect,
199
+ onSetItemRef
200
+ }
201
+ }
@@ -0,0 +1,20 @@
1
+ import { useVModel } from '@vueuse/core'
2
+ import { reactive } from 'vue'
3
+
4
+ import type { Emits, Props, Mode } from '../types'
5
+
6
+ export const useMode = ({ props, emits }: { props: Props; emits: Emits }) => {
7
+ const modelMode = useVModel(props, 'mode', emits, { passive: true })
8
+ const options = reactive([
9
+ { label: '列表模式', value: 'list', icon: `list-mode` },
10
+ { label: '详情模式', value: 'detail', icon: `detail-mode` }
11
+ ])
12
+ const onChange = (value: Mode) => {
13
+ emits('modeChange', value)
14
+ }
15
+ return {
16
+ modelMode,
17
+ options,
18
+ onChange
19
+ }
20
+ }
@@ -0,0 +1,60 @@
1
+ import { reactiveComputed } from '@vueuse/core'
2
+
3
+ import { type Ref } from 'vue'
4
+ import { type Emits, type LeftLayoutInstance, type Props, type RightLayoutInstance } from '../types/index'
5
+
6
+ export const usePrevNext = ({
7
+ leftProps,
8
+ rightProps,
9
+ leftLayoutRef,
10
+ rightLayoutRef,
11
+ emits
12
+ }: {
13
+ leftProps: Props['leftProps']
14
+ rightProps?: Props['rightProps']
15
+ leftLayoutRef: Ref<LeftLayoutInstance>
16
+ rightLayoutRef: Ref<RightLayoutInstance>
17
+ emits: Emits
18
+ }) => {
19
+ const prevNextButtonList = reactiveComputed<any[]>(() => {
20
+ const { index: pageIndex, page: { total = 0 } = {} } = leftLayoutRef.value ?? {}
21
+ return [
22
+ {
23
+ text: '上一个',
24
+ type: 'primary',
25
+ disabledFn: () => pageIndex === 0,
26
+ fun: () => onQueryList(pageIndex - 1)
27
+ },
28
+ {
29
+ text: '下一个',
30
+ type: 'primary',
31
+ disabledFn: () => pageIndex === total - 1,
32
+ fun: () => onQueryList(pageIndex + 1)
33
+ }
34
+ ]
35
+ })
36
+
37
+ const onQueryList = async (pageIndex: number) => {
38
+ const { requestApi } = leftProps
39
+ const { showScrollAnimation = true } = rightProps ?? {}
40
+ const { search, setData, setLoading } = rightLayoutRef.value ?? {}
41
+ const { scrollToIndex, setCurrentRow, setCurrentIndex } = leftLayoutRef.value ?? {}
42
+ try {
43
+ setLoading?.(true)
44
+ emits?.('nextPrev-before')
45
+ const { result: [row] = [{}] } = await requestApi?.({ pageIndex, pageSize: 1 })
46
+ setData?.(row)
47
+ search?.()
48
+ setCurrentRow(row)
49
+ setCurrentIndex(pageIndex)
50
+ showScrollAnimation && scrollToIndex?.(pageIndex)
51
+ emits?.('nextPrev-after', { row, index: pageIndex })
52
+ } finally {
53
+ setLoading?.(false)
54
+ }
55
+ }
56
+
57
+ return {
58
+ prevNextButtonList
59
+ }
60
+ }