@xmszm/core 0.0.1

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 (45) hide show
  1. package/dist/index.cjs +2 -0
  2. package/dist/index.mjs +2145 -0
  3. package/dist/style.css +1 -0
  4. package/docs/components/dataform.md +61 -0
  5. package/docs/components/datatable.md +77 -0
  6. package/docs/components/dialog.md +78 -0
  7. package/docs/components/options.md +55 -0
  8. package/docs/components/query.md +49 -0
  9. package/docs/components/utils.md +56 -0
  10. package/docs/guide/demo.md +213 -0
  11. package/docs/guide/quickstart.md +77 -0
  12. package/docs/index.md +25 -0
  13. package/docs/usage.md +61 -0
  14. package/examples/demo.vue +224 -0
  15. package/package.json +64 -0
  16. package/src/dialog/commonDialog.jsx +230 -0
  17. package/src/dialog/style/commonDialog.less +40 -0
  18. package/src/dialog/utils/dialog.js +82 -0
  19. package/src/enum/options.js +3 -0
  20. package/src/enum/sort.jsx +31 -0
  21. package/src/form/DataForm.vue +125 -0
  22. package/src/image/ImagesUpload.vue +268 -0
  23. package/src/image/SvgIcon.vue +30 -0
  24. package/src/index.js +46 -0
  25. package/src/list/useList.jsx +99 -0
  26. package/src/options/Options.jsx +338 -0
  27. package/src/options/defaultOptions.jsx +580 -0
  28. package/src/options/options.md +77 -0
  29. package/src/plugin/vite/initRouteMeta.js +54 -0
  30. package/src/query/CommonQuery.vue +272 -0
  31. package/src/store/utils/index.js +6 -0
  32. package/src/table/DataTable.vue +315 -0
  33. package/src/table/FilterDialog.vue +157 -0
  34. package/src/table/opr/DataColumnCollet.jsx +127 -0
  35. package/src/table/opr/useDataColumn.jsx +196 -0
  36. package/src/table/opr/useDataColumnButton.jsx +56 -0
  37. package/src/table/opr/useDataColumnPop.jsx +57 -0
  38. package/src/table/test.md +248 -0
  39. package/src/table/utils/ellipsis.js +22 -0
  40. package/src/utils/array.js +26 -0
  41. package/src/utils/auth.js +118 -0
  42. package/src/utils/object.js +32 -0
  43. package/src/utils/time.js +7 -0
  44. package/src/utils/upload.js +46 -0
  45. package/types/index.d.ts +67 -0
@@ -0,0 +1,272 @@
1
+ <script setup lang="jsx">
2
+ import { RefreshOutline, SearchOutline } from '@vicons/ionicons5'
3
+ import { DataForm } from 'core'
4
+ import { getOptions } from 'core/options/defaultOptions.jsx'
5
+ import { ObjectToArray } from 'core/utils/object.js'
6
+ import { computed, onMounted, onUnmounted, ref } from 'vue'
7
+
8
+ // 防抖函数
9
+ function debounce(func, delay) {
10
+ let timeoutId
11
+ return function (...args) {
12
+ clearTimeout(timeoutId)
13
+ timeoutId = setTimeout(() => {
14
+ func.apply(this, args)
15
+ }, delay)
16
+ }
17
+ }
18
+ const emit = defineEmits(['update:query', 'submit', 'reset'])
19
+
20
+ const props = defineProps({
21
+ inlineText: {
22
+ type: Boolean,
23
+ default: () => true,
24
+ },
25
+ options: {
26
+ type: Array,
27
+ default: () => [],
28
+ },
29
+ query: {
30
+ type: Object,
31
+ default: () => ({}),
32
+ },
33
+ selectCount: {
34
+ type: Number,
35
+ default: () => 1,
36
+ },
37
+ type: {
38
+ type: String,
39
+ default: () => 'primary',
40
+ },
41
+ noButton: {
42
+ type: Boolean,
43
+ default: () => false,
44
+ },
45
+ isRead: {
46
+ type: Boolean,
47
+ default: () => false,
48
+ },
49
+ btn: {
50
+ type: Array,
51
+ default: () => ['reset', 'search'],
52
+ },
53
+ size: {
54
+ type: String,
55
+ default: () => 'medium',
56
+ },
57
+
58
+ })
59
+
60
+ function onBeforeOptions(arr){
61
+ return arr.map(v=>({
62
+ ...v,
63
+ props:{
64
+ ...v.props,
65
+ ...((!v.way ||v.way==='input')? {
66
+ onUpdateValue: (...v) => {
67
+ v.props?.onUpdateValue?.(...v)
68
+ debouncedSubmit()
69
+ }
70
+ }: {}),
71
+ ...(v?.way === 'select' ? {
72
+ onUpdateValue: (...v) => {
73
+ v.props?.onUpdateValue?.(...v)
74
+ debouncedSubmit()
75
+ }
76
+ }: {})
77
+ }
78
+
79
+ }))
80
+ }
81
+
82
+ // 创建防抖的提交函数(500ms延迟)
83
+ const debouncedSubmit = debounce(() => {
84
+ emit('submit')
85
+ }, 500)
86
+
87
+ function onSubmit() {
88
+ // loading.value = true
89
+ debouncedSubmit()
90
+ // loading.value = false
91
+ // return false
92
+ }
93
+ const loading = ref(false)
94
+ const _query = defineModel('query', {
95
+ type: Object,
96
+ default: () => ({}),
97
+ })
98
+
99
+ const defaultFormItemProps = {
100
+ style: {
101
+ width: '33%',
102
+ },
103
+ }
104
+ const _queryOptionsKey = computed(() =>
105
+ props.options?.map(v => v?.way || 'input'),
106
+ )
107
+ const defaultOptions = getOptions(_queryOptionsKey.value)
108
+
109
+ const _queryOptions = computed(() => {
110
+ try {
111
+ const arr = []
112
+ for (let i = 0; i < props.options.length; i++) {
113
+ const v = props.options[i]
114
+ if (v?.enum && !v?.options)
115
+ v.options = ObjectToArray(v.enum)
116
+ if (!v?.props) {
117
+ v.props = {
118
+ size: props.size,
119
+ }
120
+ }
121
+
122
+ if (!v?.formItemProps) {
123
+ v.formItemProps = { ...defaultFormItemProps }
124
+ }
125
+ else {
126
+ v.formItemProps = {
127
+ ...defaultFormItemProps,
128
+ ...v.formItemProps,
129
+ style: { ...defaultFormItemProps.style, ...v.formItemProps.style },
130
+ }
131
+ }
132
+
133
+ const key = v?.key || v?.value
134
+ if (!key)
135
+ throw new Error('key no set')
136
+ arr.push({
137
+ ...v,
138
+
139
+ key,
140
+ })
141
+ }
142
+ return onBeforeOptions(arr)
143
+ }
144
+ catch (e) {
145
+ console.warn('error', e)
146
+ return []
147
+ }
148
+ })
149
+
150
+ const classComponent = {
151
+ input: '',
152
+ select: '',
153
+ dateRange: 'input-range',
154
+ dateRangeTime: 'input-range-time',
155
+ }
156
+ const defaultBtnProps = {
157
+ style: {
158
+ borderRadius: '3px',
159
+ },
160
+ }
161
+ const defaultBtn = {
162
+ search: () => (
163
+ <NButton
164
+ type={props.type}
165
+ loading={loading.value}
166
+ default-props={{ attrType: 'submit' }}
167
+ onClick={() => onSubmit()}
168
+ {...defaultBtnProps}
169
+ >
170
+ {{
171
+ default: () => '搜索',
172
+ icon: () => <SearchOutline />,
173
+ }}
174
+ </NButton>
175
+ ),
176
+ reset: () => (
177
+ <NButton
178
+ type="default"
179
+ onClick={() => {
180
+ clearQuery()
181
+ }}
182
+ {...defaultBtnProps}
183
+ >
184
+ {{
185
+ default: () => '重置',
186
+ icon: () => <RefreshOutline />,
187
+ }}
188
+ </NButton>
189
+ ),
190
+ }
191
+
192
+ function clearQuery() {
193
+ props.options.forEach((v) => {
194
+ const key = v?.key || v?.value
195
+ if (key) {
196
+ if (v?.queryType)
197
+ _query.value[v.queryType][key] = null
198
+ else _query.value[key] = null
199
+ }
200
+ })
201
+ emit('reset')
202
+ }
203
+
204
+ // 全局监听 Enter 键的方法
205
+ function handleGlobalEnter(event) {
206
+ // 检查是否按下了 Enter 键
207
+ if (event.key === 'Enter') {
208
+ // 检查是否在输入框、选择框等表单元素中
209
+ // const target = event.target
210
+ // const isFormElement = target.tagName === 'INPUT' ||
211
+ // target.tagName === 'SELECT' ||
212
+ // target.tagName === 'TEXTAREA' ||
213
+ // target.contentEditable === 'true'
214
+
215
+ // if (isFormElement) {
216
+ // // 阻止默认行为(如换行)
217
+ // event.preventDefault()
218
+ // // 触发搜索
219
+
220
+ // }
221
+ onSubmit()
222
+ }
223
+ }
224
+
225
+ // 组件挂载时添加全局监听
226
+ onMounted(() => {
227
+ document.addEventListener('keydown', handleGlobalEnter)
228
+ })
229
+
230
+ // 组件卸载时移除全局监听
231
+ onUnmounted(() => {
232
+ document.removeEventListener('keydown', handleGlobalEnter)
233
+ })
234
+ </script>
235
+
236
+ <template>
237
+ <n-space
238
+ :wrap-item="false"
239
+ justify="space-between"
240
+ align="center"
241
+ :wrap="false"
242
+ >
243
+ <div class="flex-1">
244
+ <DataForm
245
+ v-model:value="_query"
246
+ :options="_queryOptions"
247
+ :form-props="{ showFeedback: false }"
248
+ />
249
+ </div>
250
+ <n-space v-if="!props.noButton" align="center" :wrap="false">
251
+ <slot name="left-btn" />
252
+ <template v-for="(itm, idx) in props.btn" :key="idx">
253
+ <component :is="defaultBtn?.[itm]?.()" />
254
+ </template>
255
+ <slot name="right-btn" />
256
+ </n-space>
257
+ </n-space>
258
+ </template>
259
+
260
+ <style scoped lang="less">
261
+ .select-text {
262
+ min-width: 100px;
263
+ max-width: 240px;
264
+ text-align: center;
265
+ }
266
+
267
+ .select-line-text {
268
+ text-align: center;
269
+ white-space: nowrap;
270
+ display: inline;
271
+ }
272
+ </style>
@@ -0,0 +1,6 @@
1
+ import { computed, ref, unref } from 'vue'
2
+
3
+ export function useStoreUtils(e) {
4
+ const value = ref(e)
5
+ return [value, computed(() => unref(value)), v => (value.value = v)]
6
+ }
@@ -0,0 +1,315 @@
1
+ <script setup lang="jsx">
2
+ import { ChevronDown, ChevronUp, Code, Funnel } from '@vicons/ionicons5'
3
+ import { commonDialogMethod, toArray } from 'core'
4
+ import { ellipsis } from 'core/table/utils/ellipsis'
5
+ import dayjs from 'dayjs'
6
+ import { uniqueId } from 'lodash-es'
7
+ import { NButton, NTooltip } from 'naive-ui'
8
+ import { computed, onMounted, ref, unref, watch ,isProxy} from 'vue'
9
+ import { useRoute } from 'vue-router'
10
+ import FilterDialog from './FilterDialog.vue'
11
+ import { orderEnum } from 'core'
12
+
13
+ const props = defineProps({
14
+ data: {
15
+ type: Array,
16
+ default: () => [],
17
+ },
18
+ pagination: {
19
+ type: [Object, null],
20
+ default: () => undefined,
21
+ },
22
+ columns: {
23
+ type: Array,
24
+ default: () => [
25
+ {
26
+ title: '测试案例',
27
+ },
28
+ ],
29
+ },
30
+ oprColumns: {
31
+ type: [Object, null],
32
+ default: () => null,
33
+ },
34
+ selectColumns: {
35
+ type: [Object, null],
36
+ default: () => null,
37
+ },
38
+ defaultColumns: {
39
+ type: Array,
40
+ default: () => [],
41
+ },
42
+ summaryColumns: {
43
+ type: null,
44
+ default: () => null,
45
+ },
46
+ emptyText: {
47
+ type: String,
48
+ default: () => '没有数据',
49
+ },
50
+ emptyIcon: {
51
+ type: String,
52
+ default: () => '',
53
+ },
54
+ isFilter: {
55
+ type: Boolean,
56
+ default: () => false,
57
+ },
58
+ isEllipsis: {
59
+ type: Boolean,
60
+ default: () => true,
61
+ },
62
+ virtual: {
63
+ type: null,
64
+ default: () => {},
65
+ },
66
+ singleColumn: {
67
+ type: Boolean,
68
+ default: () => false,
69
+ },
70
+ })
71
+ const route = useRoute()
72
+ const FilterKey = 'filter_key'
73
+ const emit = defineEmits(['sorted'])
74
+ const _data = computed(() => {
75
+ console.log('table -data', props.data)
76
+
77
+ return props.data
78
+ })
79
+ function setHeadFilter(val) {
80
+ window.localStorage.setItem(FilterKey, JSON.stringify(val))
81
+ }
82
+
83
+ function getHeadFilter() {
84
+ return JSON.parse(window.localStorage.getItem(FilterKey)) || {}
85
+ }
86
+
87
+ const getFilterAll = ref(getHeadFilter())
88
+ const headDefault = ref([])
89
+
90
+ const scrollX = ref(0)
91
+
92
+ function _summary(pageData) {
93
+ if (!props.summaryColumns)
94
+ return
95
+ return [
96
+ props.selectColumns,
97
+ ...unref(props.columns),
98
+ props.oprColumns,
99
+ ]?.reduce((o, n) => {
100
+ if (n?.key)
101
+ o[n.key] = props.summaryColumns?.(pageData)?.[n.key] || { value: null }
102
+ else o[uniqueId('table')] = { value: null }
103
+ return o
104
+ }, {})
105
+ }
106
+
107
+ const _columns = ref([])
108
+
109
+ watch(
110
+ () => unref(props.columns),
111
+ () => {
112
+ init()
113
+ },
114
+ {
115
+ immediate: true,
116
+ },
117
+ )
118
+
119
+ watch(
120
+ () => props.oprColumns,
121
+ (v) => {
122
+ console.log('oprColumns', v)
123
+ },
124
+ )
125
+
126
+ function init() {
127
+ const columns = unref(props.columns)
128
+ headDefault.value
129
+ = getFilterAll.value?.[route.fullPath]
130
+ || columns?.map((v, i) => v?.key || dayjs().valueOf() + i)
131
+
132
+ const arr = props.isFilter
133
+ ? columns.filter(item => headDefault.value?.includes(item.key))
134
+ : [...columns]
135
+ if (props.selectColumns)
136
+ arr.unshift({ key: 'selectKey', ...props.selectColumns })
137
+ if (props.oprColumns)
138
+ arr.push(props.oprColumns)
139
+ let scrollNum = 0
140
+ _columns.value = arr.reduce((o, obj) => {
141
+ if (obj?.display)
142
+ console.log('display', obj?.display)
143
+ if (!(obj?.display ?? true))
144
+ return o
145
+ const v = {
146
+ 'align': 'center',
147
+ 'width': 120,
148
+ ...obj,
149
+ 'key': obj?.key || uniqueId('table'),
150
+ 'ellipsis':
151
+ obj?.ellipsis || props.isEllipsis
152
+ ? obj?.ellipsisProp
153
+ ? obj?.ellipsisProp(ellipsis)
154
+ : ellipsis
155
+ : false,
156
+ 'ellipsis-component': 'ellipsis' || 'performant-ellipsis',
157
+ 'title': () => {
158
+ const title = obj?.label || obj?.title || ''
159
+ return (
160
+ <div style={{ width: '100%', whiteSpace: 'pre-wrap' }}>
161
+ {typeof title === 'string' ? title : title?.()}
162
+ </div>
163
+ )
164
+ },
165
+ }
166
+
167
+ if (obj?.sorter) {
168
+ v.renderSorterIcon = ({ order }) => {
169
+ const { Icon, title } = orderEnum[order]
170
+ return (
171
+ <NTooltip>
172
+ {{
173
+ trigger: () => Icon,
174
+ default: () => title,
175
+ }}
176
+ </NTooltip>
177
+ )
178
+ }
179
+ v.sorter = {
180
+ multiple: 1,
181
+ fn:obj.sorter
182
+ }
183
+ }
184
+
185
+
186
+ scrollNum += v?.width
187
+ ? Number.parseInt(v?.width)
188
+ : null || v?.title?.length * v.length + 30 || 0
189
+
190
+ o.push(v)
191
+ return o
192
+ }, [])
193
+ scrollX.value = scrollNum
194
+ console.log('计算')
195
+ }
196
+
197
+ function filterButton() {
198
+ return (
199
+ <NButton type="info" onClick={() => filterHandle()}>
200
+ {{
201
+ default: () => '筛选字段',
202
+ icon: () => <Funnel />,
203
+ }}
204
+ </NButton>
205
+ )
206
+ }
207
+
208
+ function filterHandle() {
209
+ const { cancel } = commonDialogMethod(
210
+ {
211
+ title: '筛选字段',
212
+ read: true,
213
+ options: [
214
+ {
215
+ render: () => (
216
+ <FilterDialog
217
+ style={{
218
+ width: '100%',
219
+ margin: '0',
220
+ padding: 0,
221
+ }}
222
+ filterItem={unref(props.columns)}
223
+ selectItem={headDefault.value}
224
+ defaultItem={props.defaultColumns}
225
+ onSubmit={(v) => {
226
+ getFilterAll.value[route.fullPath] = v
227
+ setHeadFilter(getFilterAll.value)
228
+ init()
229
+ cancel()
230
+ }}
231
+ />
232
+ ),
233
+ },
234
+ ],
235
+ },
236
+ {
237
+ closable: false,
238
+ style: {
239
+ width: '500px',
240
+ },
241
+ },
242
+ )
243
+ }
244
+
245
+ function onSorter(e) {
246
+ console.log('onSorter', e)
247
+ if (!e)
248
+ return
249
+ const sortArr = toArray(e)
250
+
251
+ sortArr.forEach(v => {
252
+ console.log('v', v)
253
+
254
+ if (v?.sorter) {
255
+ v?.sorter?.fn
256
+ ((listQuery, pageState, key) => {
257
+ orderEnum[v.order]?.fn(listQuery, key)
258
+ pageState.fetchData()
259
+ },{
260
+ field: v?.columnKey,
261
+ value: orderEnum[v?.order]?.value,
262
+ isClick : !isProxy(v)
263
+ })
264
+ }
265
+ })
266
+
267
+ emit('sorted')
268
+ }
269
+
270
+ defineExpose({
271
+ filterHandle,
272
+ filterButton,
273
+ initColumns: init,
274
+ })
275
+
276
+ onMounted(() => {})
277
+ </script>
278
+
279
+ <template>
280
+ <n-data-table
281
+ :data="_data"
282
+ :columns="_columns"
283
+ :scroll-x="scrollX"
284
+ :single-column="props.singleColumn"
285
+ :summary="props.summaryColumns ? _summary : undefined"
286
+ summary-placement="bottom"
287
+ :pagination="props.pagination"
288
+ :row-props="() => ({ style: { height: '60px' } })"
289
+ flex-height
290
+ remote
291
+ :virtual-scroll="props.virtual ?? props.data.length > 1000"
292
+ style="flex: 1"
293
+ @update:sorter="onSorter"
294
+ >
295
+ <!-- props.data.length > 30 -->
296
+ <template #empty>
297
+ <slot name="empty">
298
+ <n-empty>{{ emptyText }}</n-empty>
299
+ </slot>
300
+ </template>
301
+ </n-data-table>
302
+ </template>
303
+
304
+ <style scoped lang="less">
305
+ :deep(.n-data-table-tr--summary) {
306
+ position: sticky;
307
+ bottom: 0;
308
+ left: 0;
309
+ right: 0;
310
+ z-index: 2;
311
+ .n-data-table-td--summary {
312
+ border-top: 1px solid var(--n-merged-border-color);
313
+ }
314
+ }
315
+ </style>