@ithinkdt/ui 4.0.0-22 → 4.0.0-30

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/locale.d.ts CHANGED
@@ -62,6 +62,9 @@ export interface UILocale {
62
62
  resetText: string
63
63
  cancelText: string
64
64
  selectFileText: string
65
+ validate: {
66
+ fileErrorMessage: string
67
+ }
65
68
  }
66
69
  filter: {
67
70
  submitText: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ithinkdt/ui",
3
- "version": "4.0.0-22",
3
+ "version": "4.0.0-30",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "iThinkDT UI",
@@ -63,7 +63,7 @@
63
63
  "sortablejs": "^1.15.6",
64
64
  "@types/sortablejs": "^1.15.8",
65
65
  "nanoid": "^5.1.6",
66
- "@ithinkdt/common": "^4.0.0-12"
66
+ "@ithinkdt/common": "^4.0.0-30"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@ithinkdt/page": ">=4.0",
@@ -82,13 +82,13 @@
82
82
  },
83
83
  "devDependencies": {
84
84
  "@vitejs/plugin-vue-jsx": "^5.1.1",
85
- "ithinkdt-ui": "^1.7.3",
85
+ "ithinkdt-ui": "^1.8.0",
86
86
  "typescript": "~5.9.3",
87
87
  "unocss": ">=66.5.4",
88
- "vite": "npm:rolldown-vite@^7.1.17",
88
+ "vite": "npm:rolldown-vite@^7.1.19",
89
89
  "vue": "^3.5.22",
90
- "vue-router": "^4.6.0",
91
- "@ithinkdt/page": "^4.0.0-14"
90
+ "vue-router": "^4.6.3",
91
+ "@ithinkdt/page": "^4.0.0-30"
92
92
  },
93
93
  "scripts": {
94
94
  "release": "pnpm publish --no-git-checks"
@@ -3,7 +3,7 @@ import { computed, defineComponent, ref, watch } from 'vue'
3
3
 
4
4
  import { useI18n } from '../use-i18n.js'
5
5
 
6
- export const NCheckboxes = defineComponent({
6
+ export const NCheckboxes = /* @__PURE__ */ defineComponent({
7
7
  name: 'Checkboxes',
8
8
  inheritAttrs: false,
9
9
  props: {
@@ -125,10 +125,7 @@ export const DataTable = /* @__PURE__ */ defineComponent({
125
125
  type: Array,
126
126
  },
127
127
  },
128
- emits: {
129
- sort: () => true,
130
- select: () => true,
131
- },
128
+ emits: ['sort', 'select', 'custom'],
132
129
  setup(props, { slots, emit, expose }) {
133
130
  const { keyField } = inject(PAGE_INJECTION)
134
131
 
@@ -207,6 +204,13 @@ export const DataTable = /* @__PURE__ */ defineComponent({
207
204
  emit('select', keys)
208
205
  }
209
206
 
207
+ const onColumnResize = (resizedWidth, limitedWidth, column) => {
208
+ emit('custom', {
209
+ key: column.key,
210
+ width: resizedWidth,
211
+ })
212
+ }
213
+
210
214
  return () => (
211
215
  <NDataTable
212
216
  class={cls}
@@ -220,6 +224,7 @@ export const DataTable = /* @__PURE__ */ defineComponent({
220
224
  checkedRowKeys={props.selectedKeys}
221
225
  onUpdateSorter={onUpdateSorter}
222
226
  onUpdateCheckedRowKeys={onUpdateCheckedRowKeys}
227
+ onUnstableColumnResize={onColumnResize}
223
228
  >
224
229
  {slots}
225
230
  </NDataTable>
@@ -157,6 +157,7 @@ export type DataTableProps<Data extends {}, KeyField extends keyof Data>
157
157
  export type DataTableEmits<Data extends {}, KeyField extends keyof Data> = {
158
158
  (e: 'sort', param: SortParams<Data>): void
159
159
  (e: 'select', param: Data[KeyField][]): void
160
+ (e: 'custom', params: { key: string, width: number }): void
160
161
  }
161
162
 
162
163
  export type DataTableInst<Data extends {}, KeyField extends keyof Data> = NDataTableInst & {
@@ -182,7 +183,7 @@ export declare function useDataTableDrag<T>(
182
183
  { data, onSort, ...options }: {
183
184
  data: MaybeRef<T[]>
184
185
  onSort: (info: { from: number, to: number }) => void
185
- } & Omit<Sortable.Options, 'onSort'>
186
+ } & Omit<Sortable.Options, 'onSort'>,
186
187
  ): void
187
188
 
188
189
  export type DataPaginationProps = (PageParams | { page: PageParams }) & {
@@ -302,7 +303,7 @@ export declare function DataLocaleInput(
302
303
  export declare function useLocaleEdit(
303
304
  title: VNodeChild | (() => VNodeChild),
304
305
  onSubmit: (data: Record<string, string | undefined>) => void,
305
- defaultRows?: MaybeRef<number | undefined>
306
+ defaultRows?: MaybeRef<number | undefined>,
306
307
  ): {
307
308
  open(this: void, data: MaybePromise<Record<string, string | undefined>>, readonly?: boolean): PromiseLike<Record<string, string | undefined>>
308
309
  setSupports(this: void, supports: { label: string, value: string, required?: boolean }[]): void
package/src/page.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import {
2
- CheckboxProps, DatePickerProps, DatePickerSlots,
2
+ CheckboxProps,
3
+ DatePickerProps, DatePickerSlots,
3
4
  DrawerContentProps, DrawerProps,
4
5
  InputNumberProps, InputNumberSlots, InputProps, InputSlots,
5
6
  ModalOptions,
6
7
  SelectGroupOption, SelectOption, SelectProps, SelectSlots,
7
- UploadFileInfo,
8
+ UploadFileInfo, UploadProps,
8
9
  } from 'ithinkdt-ui'
9
10
  import { MaybeRef, VNode, VNodeProps } from 'vue'
10
11
 
@@ -75,6 +76,20 @@ declare module '@ithinkdt/page' {
75
76
  max?: number | undefined
76
77
  accept?: string | undefined
77
78
  maxSize?: number | undefined
79
+ onBeforeUpload?: UploadProps['onBeforeUpload']
80
+ } & VNodeProps>
81
+ slots?: {
82
+ default?: (() => VNode[]) | undefined
83
+ }
84
+ }
85
+ upload: {
86
+ props?: DeepMaybeRef<{
87
+ type?: 'file' | 'image' | undefined
88
+ multiple?: boolean | undefined
89
+ max?: number | undefined
90
+ accept?: string | undefined
91
+ maxSize?: number | undefined
92
+ onBeforeUpload?: UploadProps['onBeforeUpload']
78
93
  } & VNodeProps>
79
94
  slots?: {
80
95
  default?: (() => VNode[]) | undefined
@@ -142,6 +157,7 @@ export declare function createPageFormHelper(options?: {
142
157
  uploadFile?: (file: File, options?: {
143
158
  onProgress?: ((percent: number) => void) | undefined
144
159
  }) => Promise<string>
160
+ getFileInfos?: (files: string[]) => Promise<UploadFileInfo[]>
145
161
  }): PageOptions['getFormItemRenderer']
146
162
  export declare function createPageTableHelper(options?: {
147
163
  getUsersByUsername?: (usernames: string[]) => Promise<{ username: string, nickname: string }[]>
package/src/page.jsx CHANGED
@@ -1,10 +1,10 @@
1
+ import { until } from '@vueuse/core'
1
2
  import { format } from 'date-fns'
2
3
  import {
3
4
  NButton, NCheckbox, NColorPicker, NDatePicker, NDrawer, NDrawerContent, NFlex, NInput,
4
- NInputNumber, NModal, NScrollbar, NSelect, NText, NUpload,
5
- useMessage,
5
+ NInputNumber, NModal, NScrollbar, NSelect, NText, NUpload, useMessage,
6
6
  } from 'ithinkdt-ui'
7
- import { defineComponent, h, mergeProps, unref } from 'vue'
7
+ import { computed, defineComponent, h, mergeProps, shallowRef, unref } from 'vue'
8
8
 
9
9
  import { useDict, useDictMap } from '@ithinkdt/common/dict'
10
10
 
@@ -14,64 +14,68 @@ import { useI18n } from './use-i18n.js'
14
14
  const mapProps = (props) => {
15
15
  return Object.fromEntries(Object.entries(props || {}).map(([prop, value]) => [prop, unref(value)]))
16
16
  }
17
- export function createPageFormHelper({
18
- getUserGroups, getUsersByGroup, getUsersByDept, getUsersByUsername, uploadFile,
19
- }) {
20
- const SimpleUpload = defineComponent({
21
- name: 'SimpleUpload',
22
- props: {
23
- type: { type: String, default: 'file' }, // file | image
24
- multiple: { type: Boolean, default: false },
25
- max: { type: Number, default: undefined },
26
- accept: { type: String, default: undefined },
27
- maxSize: { type: Number, default: undefined }, // MB
28
- disabled: { type: Boolean, default: undefined },
29
- fileList: { type: Array, default: () => [] },
30
- onUpdateFileList: { type: [Array, Function] },
31
- },
32
- setup(props, { slots }) {
33
- const { t } = useI18n()
34
17
 
35
- const message = useMessage()
36
- const customRequest = computed(() => props.customRequest || (
37
- async ({ file, onProgress, onFinish, onError }) => {
38
- uploadFile(file, percent => onProgress({ percent }))
39
- .then((id) => {
40
- file.file.fileId = id
41
- onFinish()
42
- }).catch((error) => {
43
- message.success(error.message)
44
- onError(error)
45
- })
46
- }
47
- ))
48
- return () => {
49
- const { type, onUpdateFileList, ...props0 } = props
50
- return (
51
- <NUpload
52
- {...props0}
53
- onFinish={({ file, event }) => {
54
- props0.onFinish?.({ file, event })
55
- return {
56
- ...file,
57
- id: file.file.fileId,
58
- }
59
- }}
60
- customRequest={customRequest.value}
61
- listType={type === 'image' ? 'image-card' : 'text'}
62
- accept={props0.accept ?? type === 'image' ? 'image/*' : undefined}
63
- onUpdate:fileList={onUpdateFileList}
64
- >
65
- {{
66
- default: () => <NButton disabled={props.disabled}>{t('common.page.form.selectFileText')}</NButton>,
67
- ...slots,
68
- }}
69
- </NUpload>
70
- )
18
+ const SimpleUpload = /* @__PURE__ */ defineComponent({
19
+ name: 'SimpleUpload',
20
+ props: {
21
+ type: { type: String, default: 'file' }, // file | image
22
+ multiple: { type: Boolean, default: false },
23
+ max: { type: Number, default: undefined },
24
+ accept: { type: String, default: undefined },
25
+ maxSize: { type: Number, default: undefined }, // MB
26
+ disabled: { type: Boolean, default: undefined },
27
+ fileList: { type: Array, default: () => [] },
28
+ onUpdateFileList: { type: [Array, Function] },
29
+ uploadFile: { type: Function },
30
+ },
31
+ setup(props, { slots, expose }) {
32
+ const { t } = useI18n()
33
+ SimpleUpload.t = t
34
+
35
+ const message = useMessage()
36
+ const customRequest = computed(() => props.customRequest || (
37
+ ({ file, onProgress, onFinish, onError }) => {
38
+ props.uploadFile(file.file, percent => onProgress({ percent }))
39
+ .then((id) => {
40
+ file.file.fileId = id
41
+ onFinish()
42
+ }).catch((error) => {
43
+ message.error(error.message)
44
+ onError(error)
45
+ })
71
46
  }
72
- },
73
- })
47
+ ))
74
48
 
49
+ const inst = ref()
50
+ expose({
51
+ submit() {
52
+ return until(inst).toBeTruthy().then(inst => inst.submit())
53
+ },
54
+ })
55
+ return () => {
56
+ const { type, onUpdateFileList, ...props0 } = props
57
+ return (
58
+ <NUpload
59
+ {...props0}
60
+ ref={inst}
61
+ customRequest={customRequest.value}
62
+ listType={type === 'image' ? 'image-card' : 'text'}
63
+ accept={props0.accept ?? type === 'image' ? 'image/*' : undefined}
64
+ onUpdate:fileList={onUpdateFileList}
65
+ >
66
+ {{
67
+ default: () => <NButton disabled={props.disabled}>{t('common.page.form.selectFileText')}</NButton>,
68
+ ...slots,
69
+ }}
70
+ </NUpload>
71
+ )
72
+ }
73
+ },
74
+ })
75
+
76
+ export function createPageFormHelper({
77
+ getUserGroups, getUsersByGroup, getUsersByDept, getUsersByUsername, uploadFile, getFileInfos,
78
+ }) {
75
79
  return {
76
80
  input: () => (
77
81
  { slots, props },
@@ -298,9 +302,10 @@ export function createPageFormHelper({
298
302
  ) => {
299
303
  props = mapProps(props)
300
304
  if (readonly) {
301
- if (modelValue.length === 0) return
305
+ console.warn(`[file] 原则上此组建不应该显示在详情中`)
306
+ if (fileList.value.length === 0) return
302
307
  return (
303
- <NFlex gap="8" wrap>
308
+ <NFlex gap="8" vertical>
304
309
  {modelValue.map(it => (
305
310
  <a
306
311
  key={it.id}
@@ -318,11 +323,97 @@ export function createPageFormHelper({
318
323
  return h(SimpleUpload, {
319
324
  ...props,
320
325
  ...params,
326
+ defaultUpload: false,
327
+ uploadFile,
321
328
  fileList: modelValue,
322
329
  onUpdateFileList: onUpdateValue,
323
330
  }, slots)
324
331
  }
325
332
  },
333
+ upload: () => {
334
+ let lastModelValue = null
335
+ const idMap = new Map()
336
+ const fileMap = new Map()
337
+ const fileList = shallowRef([])
338
+ let key = 0
339
+
340
+ const update = (ids) => {
341
+ const key0 = ++key
342
+ fileList.value = ids.map(id => fileMap.get(id)).filter(Boolean)
343
+ const ids0 = ids.filter(id => !fileMap.has(id))
344
+ Promise.resolve(ids0.length > 0 ? getFileInfos(ids0) : [])
345
+ .then((res) => {
346
+ if (key0 !== key) return
347
+ fileList.value = ids.map(id => fileMap.get(id) || res.find(it => it.id === id))
348
+ fileMap.clear()
349
+ for (const it of fileList.value) {
350
+ fileMap.set(it.id, it)
351
+ }
352
+ })
353
+ }
354
+
355
+ let inst
356
+ const renderer = (
357
+ { slots, props },
358
+ { modelValue, 'onUpdate:modelValue': onUpdateValue, required, readonly, ...params },
359
+ ) => {
360
+ const props0 = mapProps(props)
361
+ inst = (props0.ref ??= shallowRef())
362
+ if (lastModelValue !== modelValue) {
363
+ lastModelValue = modelValue
364
+ update(modelValue?.split(',') ?? [])
365
+ }
366
+ if (readonly) {
367
+ if (fileList.value.length === 0) return
368
+ return (
369
+ <NFlex gap="8" vertical>
370
+ {fileList.value.map(it => (
371
+ <a
372
+ key={it.id}
373
+ href={it.url}
374
+ target="_blank"
375
+ rel="noreferrer"
376
+ title={it.name}
377
+ style="max-width: 100px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; display: inline-block"
378
+ >
379
+ {it.name}
380
+ </a>
381
+ ))}
382
+ </NFlex>
383
+ )
384
+ }
385
+ return h(SimpleUpload, {
386
+ ...props0,
387
+ ...params,
388
+ uploadFile,
389
+ fileList: fileList.value,
390
+ onUpdateFileList: (fileList0 = []) => {
391
+ fileMap.clear()
392
+ for (const f of fileList0) {
393
+ if (f.file.fileId) {
394
+ idMap.set(f.id, f.file.fileId)
395
+ }
396
+
397
+ if (f.file.fileId) fileMap.set(f.file.fileId, f)
398
+ else fileMap.set(f.id, f)
399
+ }
400
+ fileList.value = fileList0
401
+ const ids = fileList.value.map(f => idMap.get(f.id) || f.id).join(',') || null
402
+ onUpdateValue(ids)
403
+ },
404
+ }, slots)
405
+ }
406
+ return {
407
+ renderer,
408
+ beforeSubmit: async () => {
409
+ await inst?.submit()
410
+ await until(fileList).toMatch(list => list.every(file => !['pending', 'uploading'].includes(file.status)))
411
+ if (fileList.value.some(file => file.status === 'error')) {
412
+ return SimpleUpload.t('common.page.form.validate.fileErrorMessage')
413
+ }
414
+ },
415
+ }
416
+ },
326
417
  user: () => {
327
418
  let users, groups, depts
328
419
  return (
@@ -555,7 +646,7 @@ export function createModalHelper() {
555
646
  const footer0 = footer
556
647
  ? () => footer
557
648
  : footer === null
558
- ? () => null
649
+ ? null
559
650
  : () => (
560
651
  <NFlex justify="end" gap="16">
561
652
  {cancelText === null ? undefined : <NButton onClick={onCancel} disabled={cancelDisabled} loading={cancelLoading} style="min-width: 60px">{cancelText}</NButton>}
@@ -598,6 +689,7 @@ export function createModalHelper() {
598
689
  )
599
690
  }
600
691
 
692
+ const { bodyContentClass, bodyContentStyle, ...options2 } = options
601
693
  return (
602
694
  <NDrawer
603
695
  show={visible}
@@ -613,9 +705,14 @@ export function createModalHelper() {
613
705
  onAfterEnter={onAfterOpen}
614
706
  onAfterLeave={onAfterClose}
615
707
  style={style}
616
- {...options}
708
+ {...options2}
617
709
  >
618
- <NDrawerContent closable={closable} nativeScrollbar={nativeScrollbar ?? false}>
710
+ <NDrawerContent
711
+ closable={closable}
712
+ nativeScrollbar={nativeScrollbar ?? false}
713
+ bodyContentClass={bodyContentClass}
714
+ bodyContentStyle={bodyContentStyle}
715
+ >
619
716
  {{
620
717
  default: () => content,
621
718
  header: () => title,