@neatui/nuxt 0.1.0 → 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 (84) hide show
  1. package/BUILD.md +128 -0
  2. package/README.md +98 -1
  3. package/SSR_COMPATIBILITY.md +201 -0
  4. package/USAGE.md +260 -0
  5. package/nuxt.config.example.ts +37 -0
  6. package/package.json +29 -11
  7. package/src/components/basic/IDraggable.vue +87 -65
  8. package/src/components/basic/IFollowView.vue +32 -23
  9. package/src/components/basic/IRouterView.vue +38 -23
  10. package/src/components/basic/IScrollView.vue +11 -7
  11. package/src/components/basic/Icon.vue +17 -17
  12. package/src/components/basic/LayerView/Layer.vue +33 -106
  13. package/src/components/basic/follow.ts +133 -0
  14. package/src/components/display/Calendar.vue +14 -14
  15. package/src/components/display/CalendarReg.vue +14 -14
  16. package/src/components/display/Image.vue +8 -8
  17. package/src/components/display/PhotoEditor.vue +36 -36
  18. package/src/components/display/PhotoViewer.vue +8 -8
  19. package/src/components/display/Tree.vue +6 -6
  20. package/src/components/display/TreeView.vue +4 -4
  21. package/src/components/display/index.ts +2 -2
  22. package/src/components/form/Cascader.vue +19 -19
  23. package/src/components/form/Checkbox.vue +64 -0
  24. package/src/components/form/DatePicker.vue +6 -7
  25. package/src/components/form/DateRangePicker@v3.vue +4 -4
  26. package/src/components/form/DateRangeView@v3.vue +18 -19
  27. package/src/components/form/DateView.vue +14 -14
  28. package/src/components/form/DateView@v2.vue +14 -14
  29. package/src/components/form/DateView@v3.vue +331 -318
  30. package/src/components/form/ImgUpload.vue +7 -7
  31. package/src/components/form/Input@v3.vue +11 -11
  32. package/src/components/form/MoreSelect@v3.vue +87 -17
  33. package/src/components/form/MoreSelectList.vue +8 -8
  34. package/src/components/form/MoreSelectPanel@v3.vue +3 -3
  35. package/src/components/form/MoreSelectPicker.vue +7 -7
  36. package/src/components/form/MoreSelectTags.vue +8 -8
  37. package/src/components/form/PageMoreSelect.vue +14 -14
  38. package/src/components/form/PageSelect.vue +16 -16
  39. package/src/components/form/SearchMoreSelect.vue +12 -12
  40. package/src/components/form/SearchSelect@v3.vue +3 -3
  41. package/src/components/form/Select@v3.vue +229 -23
  42. package/src/components/form/SelectList.vue +8 -8
  43. package/src/components/form/SelectPicker.vue +6 -6
  44. package/src/components/form/SelectTags.vue +7 -7
  45. package/src/components/form/SelectTree/SelectTree@v1.vue +5 -5
  46. package/src/components/form/Switch.vue +38 -103
  47. package/src/components/form/TextArea.vue +18 -18
  48. package/src/components/form/Textarea@v2.vue +275 -0
  49. package/src/components/form/TimeView.vue +14 -14
  50. package/src/components/form/Upload.vue +806 -297
  51. package/src/components/form/date.ts +321 -0
  52. package/src/components/form/index.ts +7 -5
  53. package/src/components/form/number.ts +3 -0
  54. package/src/components/form/type.ts +224 -0
  55. package/src/components/icon/OrderIcon.vue +113 -0
  56. package/src/components/loader/FormLoader/FormLoader@v2.vue +193 -195
  57. package/src/components/loader/FormLoader/FormLoader@v3.vue.backup +372 -291
  58. package/src/components/loader/FormLoader/FormRender@v3.vue.backup +4 -0
  59. package/src/components/loader/FormLoader/NodeLoader.vue +85 -0
  60. package/src/components/loader/FormLoader@v1/FormLoader.vue +1 -1
  61. package/src/components/loader/FormLoader@v1/FormRender.vue +49 -24
  62. package/src/components/loader/LayerLoader/LayerLoader.vue +318 -0
  63. package/src/components/loader/LayerLoader/index.ts +2 -0
  64. package/src/components/loader/LayerLoader/style.scss +77 -0
  65. package/src/components/loader/LimitLoader/LimitLoader@v3.vue +39 -28
  66. package/src/components/loader/MoveLoader/MoveLoader.vue +628 -0
  67. package/src/components/loader/MoveLoader/index.ts +2 -0
  68. package/src/components/loader/MoveLoader/style.scss +77 -0
  69. package/src/components/loader/TableLoader/TableLoader.vue +227 -195
  70. package/src/components/loader/TableLoader/TableRender.vue +141 -0
  71. package/src/components/loader/TableLoader/index.ts +47 -0
  72. package/src/components/loader/index.ts +3 -2
  73. package/src/components/tools/Pagination@a.vue +17 -18
  74. package/src/components/tools/Pagination@b.vue +16 -16
  75. package/src/index.ts +2 -1
  76. package/src/module.ts +169 -0
  77. package/src/runtime/types.d.ts +36 -0
  78. package/src/store/{myui.ts → frame.ts} +4 -4
  79. package/src/utils/theme.ts +14 -0
  80. package/tsconfig.json +1 -1
  81. package/src/components/loader/FormLoader/index.ts +0 -2
  82. package/src/components/loader/LimitLoader/LimitLoader.vue.backup +0 -131
  83. package/src/components/loader/LimitLoader/LimitLoader@v2.vue.backup +0 -174
  84. package/src/components/loader/TableLoader/TableColView.vue +0 -115
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <template v-if="isArray(node.child)">
3
+ <div v-bind="attrs" v-on="event" v-memo="[node.child, data]">
4
+ <TableRender v-for="(sub, idx) in node.child" :key="idx" :node="sub" :data="data" />
5
+ </div>
6
+ </template>
7
+ <template v-else-if="node.popup">
8
+ <IFollowView v-memo="[node.popup, data]">
9
+ <TableRender :node="{ ...node, popup: null }" :data="data" />
10
+ <template #tips>
11
+ <TableRender :node="isFunction(node.popup) ? node.popup(data) : node.popup" :data="data" />
12
+ </template>
13
+ </IFollowView>
14
+ </template>
15
+ <template v-else>
16
+ <div v-if="model.value" v-bind="attrs" v-on="event">
17
+ <div v-if="model.value === 'image'" ui-cover="1:1" v-bind="model.attrs">
18
+ <div class="pa full bg-case-o-ls r-sm ot-no ol-no" :class="state.isloaded ? 'o-no' : 'o-ms'" ui-flex="col cm"><i ui-load="@d"></i></div>
19
+ <img ref="img" class="r-sm" :src="value" @load="state.isloaded = 1" />
20
+ </div>
21
+ <label v-else-if="model.value === 'switch'" ui-form="@a type:switch" @click.prevent>
22
+ <input type="checkbox" :checked="value" />
23
+ <span v-if="model.attrs?.label">{{ model.attrs?.label }}</span>
24
+ </label>
25
+ </div>
26
+ <div v-else v-bind="attrs" v-on="event" v-html="value"></div>
27
+ </template>
28
+ </template>
29
+ <script lang="ts" setup>
30
+ import { computed, onMounted, reactive, ref } from 'vue';
31
+ import { isFunction, isArray, isObject, isString, findEnumName } from '@fekit/utils';
32
+ import { useRoute, useRouter } from 'vue-router';
33
+ import TableRender from './TableRender.vue';
34
+ import { IFollowView } from '../../basic';
35
+
36
+ const route = useRoute();
37
+ const router = useRouter();
38
+
39
+ const props: any = defineProps({
40
+ node: {
41
+ type: Object,
42
+ default: () => ({}),
43
+ },
44
+ data: {
45
+ type: [Object],
46
+ default: () => ({}),
47
+ },
48
+ });
49
+
50
+ const img = ref<HTMLImageElement | null>(null);
51
+ onMounted(() => {
52
+ if (img.value) {
53
+ state.isloaded = img.value.complete && img.value.naturalWidth > 0 ? 1 : 0;
54
+ }
55
+ });
56
+
57
+ const state: any = reactive({
58
+ isloaded: 0,
59
+ });
60
+
61
+ // 属性处理
62
+ const attrs = computed(() => {
63
+ const a = props.node.attrs;
64
+ let attrs = isFunction(a) ? a?.(props.data) : a;
65
+
66
+ // 确保 attrs 存在
67
+ if (!attrs) attrs = {};
68
+ else attrs = { ...attrs };
69
+
70
+ // 处理 class 属性
71
+ if (attrs.class) {
72
+ attrs.class = attrs.class.split(/\s+/);
73
+ if (!attrs.class.some((c: string) => c === 'wrap' || c === 'nowrap')) {
74
+ attrs.class.push('nowrap');
75
+ }
76
+ } else {
77
+ attrs.class = ['nowrap'];
78
+ }
79
+
80
+ return attrs;
81
+ });
82
+
83
+ // 模型处理
84
+ const model: any = computed(() => {
85
+ const m = props.node.model;
86
+ if (!m) return { value: '', attrs: {} };
87
+
88
+ const model = isFunction(m) ? m(props.data) : m;
89
+
90
+ let value = '';
91
+ let attrs = {};
92
+ if (isString(model)) {
93
+ value = model;
94
+ } else if (isArray(model)) {
95
+ value = model[0] ?? '';
96
+ attrs = isObject(model[1]) ? model[1] : {};
97
+ }
98
+ value = value.toString().toLowerCase();
99
+
100
+ return { value, attrs };
101
+ });
102
+
103
+ // 枚举处理
104
+ const enums = computed(() => {
105
+ const e = props.node.enums;
106
+ return isFunction(e) ? e(props.data) : e;
107
+ });
108
+
109
+ // 数值处理
110
+ const value = computed(() => {
111
+ const n = props?.node;
112
+ const v = props?.data?.[n.field];
113
+ return n?.value !== null && n?.value !== undefined
114
+ ? isFunction(n?.value)
115
+ ? n?.value(props?.data)
116
+ : n?.value
117
+ : findEnumName(enums.value, v) !== null && findEnumName(enums.value, v) !== undefined
118
+ ? findEnumName(enums.value, v)
119
+ : '‐‐';
120
+ });
121
+
122
+ // 事件处理 (直接使用 computed)
123
+ const event = computed(() => {
124
+ let e = props.node.event;
125
+ if (isFunction(e)) {
126
+ e = e(props.data);
127
+ }
128
+ const events: any = {};
129
+ if (isObject(e)) {
130
+ for (const key in e) {
131
+ const fun = e[key];
132
+ if (isFunction(fun)) {
133
+ events[key] = (...args: any) => {
134
+ return fun({ data: props.data, route, router }, ...args);
135
+ };
136
+ }
137
+ }
138
+ }
139
+ return events;
140
+ });
141
+ </script>
@@ -1,2 +1,49 @@
1
+ import { isFunction } from '@fekit/utils';
1
2
  import TableLoader from './TableLoader.vue';
3
+
4
+ // 类型定义
5
+ interface ListItem {
6
+ __selected?: boolean;
7
+ [key: string]: any;
8
+ }
9
+
10
+ type KeyExtractor<T = ListItem> = string | ((item: T) => any);
11
+
2
12
  export default TableLoader;
13
+
14
+ // 是否有选中记录
15
+ export const hasSelected = (lists: ListItem[] = []): boolean => {
16
+ return lists.some((item) => item.__selected === true);
17
+ };
18
+
19
+ // 获取选中记录的数量
20
+ export const getSelectedCount = (lists: ListItem[] = []): number => {
21
+ return lists.filter((item) => item.__selected === true).length;
22
+ };
23
+
24
+ // 获取选中的记录
25
+ export const getSelected = <T extends Record<string, any> = ListItem>(lists: T[] = [], key: KeyExtractor<T> = ''): any[] => {
26
+ return (
27
+ lists
28
+ ?.filter((item: T) => {
29
+ return item.__selected === true;
30
+ })
31
+ ?.map((item: T) => {
32
+ return isFunction(key) ? (key as (item: T) => any)(item) : key ? item[key as string] : item;
33
+ }) || []
34
+ );
35
+ };
36
+
37
+ // 全选计算属性
38
+ export function allSelected(lists: ListItem[] = []) {
39
+ return {
40
+ get(): boolean {
41
+ return lists.length > 0 && lists.every((item) => item.__selected === true);
42
+ },
43
+ set(value: boolean): void {
44
+ lists.forEach((item) => {
45
+ item.__selected = value;
46
+ });
47
+ },
48
+ };
49
+ }
@@ -1,5 +1,6 @@
1
- import FormLoader from './FormLoader';
1
+ import FormLoader from './FormLoader/FormLoader@v2.vue';
2
2
  import LimitLoader from './LimitLoader';
3
- import TableLoader from './TableLoader';
3
+ export * from './TableLoader/index';
4
+ import TableLoader from './TableLoader/TableLoader.vue';
4
5
  import ViewLoader from './ViewLoader';
5
6
  export { FormLoader, LimitLoader, TableLoader, ViewLoader };
@@ -1,9 +1,9 @@
1
1
  <template>
2
- <div ui-pagination="@a" class="full-x pading" ui-flex="row rm">
2
+ <div ui-pagination="@a" class="w-full pading" ui-flex="row rm">
3
3
  <div class="ml-sl ar mob-ac" ui-flex="row rm">
4
4
  <div class="mx-ss-sub" ui-flex="row">
5
5
  <div v-if="state.showTotal" ui-flex="row cm" ui-hide="<pad">共 {{ data[defined.total || 'total'] || 0 }} 条</div>
6
- <button :ui-btn="`@a s read :border :square ${data[defined.current || 'current'] > 1 ? '' : ':disabled'}`" @click="prev">
6
+ <button :ui-btn="`@a s read :border :square ${data[defined.page || 'page'] > 1 ? '' : ':disabled'}`" @click="prev">
7
7
  <svg
8
8
  style="width: 1em; height: 1em"
9
9
  t="1701228540398"
@@ -23,7 +23,7 @@
23
23
  </button>
24
24
  <div ui-tips="@a co:well ux:hover">
25
25
  <div ui-btn="@a s read :border">
26
- <code>{{ data[defined.current || 'current'] || 0 }}/{{ data[defined.totalPages || 'totalPages'] || 0 }}</code>
26
+ <code>{{ data[defined.page || 'page'] || 0 }}/{{ data[defined.totalPages || 'totalPages'] || 0 }}</code>
27
27
  <svg
28
28
  style="width: 1em; height: 1em"
29
29
  t="1701229420486"
@@ -45,7 +45,7 @@
45
45
  <div class="w-mm max-h-ls" ui-scroll=":y s">
46
46
  <ul class="n-sl n-ss-sub dib-sub">
47
47
  <li v-for="item in state.lists" :key="item">
48
- <button :ui-btn="`@a s ${item === data[defined.current || 'current'] ? 'main' : 'read :border'}`" @click="page(item)">
48
+ <button :ui-btn="`@a s ${item === data[defined.page || 'page'] ? 'main' : 'read :border'}`" @click="page(item)">
49
49
  <code>{{ item }}</code>
50
50
  </button>
51
51
  </li>
@@ -53,7 +53,7 @@
53
53
  </div>
54
54
  </div>
55
55
  </div>
56
- <button :ui-btn="`@a s read :border :square ${data[defined.current || 'current'] < data[defined.totalPages || 'totalPages'] ? '' : ':disabled'}`" @click="next">
56
+ <button :ui-btn="`@a s read :border :square ${data[defined.page || 'page'] < data[defined.totalPages || 'totalPages'] ? '' : ':disabled'}`" @click="next">
57
57
  <svg style="width: 1em; height: 1em" class="icon co-read" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2426" width="64" height="64">
58
58
  <path
59
59
  d="M341.63640988 781.41564784c20.63742578 21.22981847 54.12571728 21.22981847 74.81456101 0l265.95100164-273.75421824c20.62728703-21.26747667 20.62728703-55.75008988 0-76.97556317l-265.95100164-273.74407946c-20.68449855-21.26747667-54.17423844-21.26747667-74.81456101 0-20.67291141 21.23706043-20.67291142 55.71170748 0 76.98497771l228.55639458 235.24869384-228.55567038 235.22117436c-20.67218722 21.26747667-20.67146302 55.74357212 0 77.02263594l0 0z"
@@ -111,23 +111,22 @@
111
111
  <script setup lang="ts">
112
112
  import { reactive, computed, watch } from 'vue';
113
113
  import { useRoute } from 'vue-router';
114
- import { isObject } from '@fekit/utils';
115
114
 
116
115
  interface ModelValueProps {
117
116
  pageSize?: number;
118
- current?: number;
117
+ page?: number;
119
118
  [key: string]: any;
120
119
  }
121
120
 
122
121
  interface DataProps {
123
122
  total?: number;
124
123
  totalPages?: number;
125
- current?: number;
124
+ page?: number;
126
125
  [key: string]: any;
127
126
  }
128
127
  interface DefinedProps {
129
128
  // 当前页
130
- current?: string;
129
+ page?: string;
131
130
  // 总页数
132
131
  totalPages?: string;
133
132
  // 总条数
@@ -149,7 +148,7 @@
149
148
  id: '',
150
149
  modelValue: () => ({}),
151
150
  data: () => ({}),
152
- defined: () => ({})
151
+ defined: () => ({}),
153
152
  });
154
153
 
155
154
  const emits = defineEmits(['update:modelValue', 'page', 'size']);
@@ -157,7 +156,7 @@
157
156
  id: computed(() => (props.id || 'PAGINATION' + route.path?.replace(/\//g, '_')).toUpperCase()),
158
157
  showTotal: true,
159
158
  list: computed(() => {
160
- const _page = Number(props.data[props.defined.current || 'current'] || 1);
159
+ const _page = Number(props.data[props.defined.page || 'page'] || 1);
161
160
  const list = [_page];
162
161
  if (_page > 1) {
163
162
  list.unshift(_page - 1);
@@ -185,26 +184,26 @@
185
184
  _lists.push(i);
186
185
  }
187
186
  return _lists;
188
- })
187
+ }),
189
188
  });
190
189
 
191
190
  const prev = () => {
192
- const _page = Number(props.data[props.defined.current || 'current']) - 1 < 1 ? 1 : Number(props.data[props.defined.current || 'current']) - 1;
193
- emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.current || 'current']: _page } });
191
+ const _page = Number(props.data[props.defined.page || 'page']) - 1 < 1 ? 1 : Number(props.data[props.defined.page || 'page']) - 1;
192
+ emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.page || 'page']: _page } });
194
193
  emits('page', _page);
195
194
  };
196
195
 
197
196
  const next = () => {
198
197
  const _page =
199
- Number(props.data[props.defined.current || 'current']) + 1 > props.data[props.defined.totalPages || 'totalPages']
198
+ Number(props.data[props.defined.page || 'page']) + 1 > props.data[props.defined.totalPages || 'totalPages']
200
199
  ? props.data[props.defined.totalPages || 'totalPages']
201
- : Number(props.data[props.defined.current || 'current']) + 1;
202
- emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.current || 'current']: _page } });
200
+ : Number(props.data[props.defined.page || 'page']) + 1;
201
+ emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.page || 'page']: _page } });
203
202
  emits('page', _page);
204
203
  };
205
204
 
206
205
  const page = (idx: any) => {
207
- emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.current || 'current']: idx } });
206
+ emits('update:modelValue', { ...props.modelValue, ...{ [props.defined.page || 'page']: idx } });
208
207
  emits('page', idx);
209
208
  };
210
209
 
@@ -13,7 +13,7 @@
13
13
  </div>
14
14
  <div class="ml-sl ar mob-ac">
15
15
  <div class="fr mob-fn m-ss-sub" ui-flex="row rm">
16
- <button class="flex-fixed" :ui-btn="`@a s read :border :square ${data.current > 1 ? '' : ':disabled'}`" @click="prev">
16
+ <button class="flex-fixed" :ui-btn="`@a s read :border :square ${data.page > 1 ? '' : ':disabled'}`" @click="prev">
17
17
  <svg
18
18
  style="width: 1em; height: 1em"
19
19
  t="1701228540398"
@@ -32,15 +32,15 @@
32
32
  </svg>
33
33
  </button>
34
34
 
35
- <button class="flex-fixed" v-for="idx in state.list" :key="idx" :ui-btn="`@a s ${idx === data.current ? 'main' : 'read :border'}`" ui-hide="<pad" @click="page(idx)">
35
+ <button class="flex-fixed" v-for="idx in state.list" :key="idx" :ui-btn="`@a s ${idx === data.page ? 'main' : 'read :border'}`" ui-hide="<pad" @click="page(idx)">
36
36
  <code>{{ idx }}</code>
37
37
  </button>
38
38
  <button class="flex-fixed" ui-btn="@a s read :border" ui-hide=">mob">
39
39
  <span>当前第</span>
40
- <code class="nx-ss">{{ data.current || 1 }}</code>
40
+ <code class="nx-ss">{{ data.page || 1 }}</code>
41
41
  <span>页</span>
42
42
  </button>
43
- <button class="flex-fixed" :ui-btn="`@a s read :border :square ${data.current < data.totalPages ? '' : ':disabled'}`" @click="next">
43
+ <button class="flex-fixed" :ui-btn="`@a s read :border :square ${data.page < data.totalPages ? '' : ':disabled'}`" @click="next">
44
44
  <svg style="width: 1em; height: 1em" class="icon co-read" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2426" width="64" height="64">
45
45
  <path
46
46
  d="M341.63640988 781.41564784c20.63742578 21.22981847 54.12571728 21.22981847 74.81456101 0l265.95100164-273.75421824c20.62728703-21.26747667 20.62728703-55.75008988 0-76.97556317l-265.95100164-273.74407946c-20.68449855-21.26747667-54.17423844-21.26747667-74.81456101 0-20.67291141 21.23706043-20.67291142 55.71170748 0 76.98497771l228.55639458 235.24869384-228.55567038 235.22117436c-20.67218722 21.26747667-20.67146302 55.74357212 0 77.02263594l0 0z"
@@ -133,13 +133,13 @@
133
133
 
134
134
  interface ModelValueProps {
135
135
  pageSize: number;
136
- current: number;
136
+ page: number;
137
137
  }
138
138
 
139
139
  interface DataProps {
140
140
  total: number;
141
141
  totalPages: number;
142
- current: number;
142
+ page: number;
143
143
  }
144
144
  // 类型
145
145
  interface Props {
@@ -155,15 +155,15 @@
155
155
  id: '',
156
156
  modelValue: () => ({
157
157
  pageSize: 0,
158
- current: 0
158
+ page: 0,
159
159
  }),
160
160
  data: () => {
161
161
  return {
162
162
  total: 0,
163
163
  totalPages: 0,
164
- current: 0
164
+ page: 0,
165
165
  };
166
- }
166
+ },
167
167
  });
168
168
 
169
169
  const emits = defineEmits(['update:modelValue', 'page', 'size']);
@@ -172,7 +172,7 @@
172
172
  showTotal: true,
173
173
  id: computed(() => (props.id || 'PAGINATION' + route.path?.replace(/\//g, '_')).toUpperCase()),
174
174
  list: computed(() => {
175
- const _page = Number(props.data.current || 1);
175
+ const _page = Number(props.data.page || 1);
176
176
  const list = [_page];
177
177
  if (_page > 1) {
178
178
  list.unshift(_page - 1);
@@ -193,23 +193,23 @@
193
193
  }
194
194
  }
195
195
  return list;
196
- })
196
+ }),
197
197
  });
198
198
 
199
199
  const prev = () => {
200
- const _page = Number(props.data.current) - 1 < 1 ? 1 : Number(props.data.current) - 1;
201
- emits('update:modelValue', { ...props.modelValue, ...{ current: _page } });
200
+ const _page = Number(props.data.page) - 1 < 1 ? 1 : Number(props.data.page) - 1;
201
+ emits('update:modelValue', { ...props.modelValue, ...{ page: _page } });
202
202
  emits('page', _page);
203
203
  };
204
204
 
205
205
  const next = () => {
206
- const _page = Number(props.data.current) + 1 > props.data.totalPages ? props.data.totalPages : Number(props.data.current) + 1;
207
- emits('update:modelValue', { ...props.modelValue, ...{ current: _page } });
206
+ const _page = Number(props.data.page) + 1 > props.data.totalPages ? props.data.totalPages : Number(props.data.page) + 1;
207
+ emits('update:modelValue', { ...props.modelValue, ...{ page: _page } });
208
208
  emits('page', _page);
209
209
  };
210
210
 
211
211
  const page = (idx: any) => {
212
- emits('update:modelValue', { ...props.modelValue, ...{ current: idx } });
212
+ emits('update:modelValue', { ...props.modelValue, ...{ page: idx } });
213
213
  emits('page', idx);
214
214
  };
215
215
 
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // 数据
2
- export * from './store/myui';
2
+ export * from './store/frame';
3
+ export * from './utils/theme';
3
4
 
4
5
  // 组件
5
6
  export * from './components/basic';
package/src/module.ts ADDED
@@ -0,0 +1,169 @@
1
+ import { defineNuxtModule, addComponent, createResolver } from '@nuxt/kit'
2
+ import { readdirSync, statSync } from 'fs'
3
+ import { join, extname, basename } from 'path'
4
+
5
+ export interface ModuleOptions {
6
+ /**
7
+ * 组件前缀
8
+ * @default 'Neat'
9
+ */
10
+ prefix?: string
11
+
12
+ /**
13
+ * 主题配置
14
+ * @default 'default'
15
+ */
16
+ theme?: 'default' | 'dark' | string
17
+
18
+ /**
19
+ * 是否启用自动导入
20
+ * @default true
21
+ */
22
+ autoImport?: boolean
23
+
24
+ /**
25
+ * 要排除的组件列表
26
+ * @default []
27
+ */
28
+ exclude?: string[]
29
+
30
+ /**
31
+ * CSS配置
32
+ */
33
+ css?: {
34
+ variables?: Record<string, string>
35
+ }
36
+ }
37
+
38
+ export default defineNuxtModule<ModuleOptions>({
39
+ meta: {
40
+ name: '@neatui/nuxt',
41
+ configKey: 'neatui',
42
+ compatibility: {
43
+ nuxt: '^3.0.0'
44
+ }
45
+ },
46
+ defaults: {
47
+ prefix: 'Neat',
48
+ theme: 'default',
49
+ autoImport: true,
50
+ exclude: [],
51
+ css: {}
52
+ },
53
+ setup(options: ModuleOptions, nuxt: any) {
54
+ const { resolve } = createResolver(import.meta.url)
55
+
56
+ // 注册组件
57
+ if (options.autoImport) {
58
+ registerComponents(resolve, options)
59
+ }
60
+
61
+ // 添加CSS
62
+ // nuxt.options.css.push(resolve('./runtime/css/index.css'))
63
+
64
+ // 添加全局样式变量
65
+ if (options.css?.variables) {
66
+ const cssVars = Object.entries(options.css.variables)
67
+ .map(([key, value]) => `--neat-${key}: ${value};`)
68
+ .join('\n')
69
+
70
+ nuxt.options.css.push({
71
+ src: `data:text/css,:root { ${cssVars} }`,
72
+ lang: 'css'
73
+ } as any)
74
+ }
75
+
76
+ // 添加类型定义
77
+ nuxt.hook('prepare:types', ({ references }: any) => {
78
+ references.push({ path: resolve('./runtime/types.d.ts') })
79
+ })
80
+ }
81
+ })
82
+
83
+ /**
84
+ * 自动注册组件
85
+ */
86
+ function registerComponents(resolve: (path: string) => string, options: ModuleOptions) {
87
+ const componentsDir = resolve('./components')
88
+
89
+ try {
90
+ // 扫描所有组件目录
91
+ const componentDirs = ['basic', 'display', 'form', 'icon', 'loader', 'tools']
92
+
93
+ for (const dir of componentDirs) {
94
+ const dirPath = join(componentsDir, dir)
95
+
96
+ try {
97
+ const files = readdirSync(dirPath)
98
+
99
+ for (const file of files) {
100
+ const filePath = join(dirPath, file)
101
+ const stat = statSync(filePath)
102
+
103
+ if (stat.isFile() && extname(file) === '.vue') {
104
+ const componentName = basename(file, '.vue')
105
+
106
+ // 跳过版本化组件 (包含@的)
107
+ if (componentName.includes('@')) continue
108
+
109
+ // 跳过排除的组件
110
+ if (options.exclude?.includes(componentName)) continue
111
+
112
+ // 注册组件
113
+ addComponent({
114
+ name: `${options.prefix}${componentName}`,
115
+ filePath: filePath,
116
+ global: true
117
+ })
118
+ } else if (stat.isDirectory()) {
119
+ // 处理嵌套目录 (如 SelectTree)
120
+ try {
121
+ const nestedFiles = readdirSync(filePath)
122
+ for (const nestedFile of nestedFiles) {
123
+ if (extname(nestedFile) === '.vue') {
124
+ const nestedComponentName = basename(nestedFile, '.vue')
125
+
126
+ if (nestedComponentName.includes('@')) continue
127
+ if (options.exclude?.includes(nestedComponentName)) continue
128
+
129
+ addComponent({
130
+ name: `${options.prefix}${nestedComponentName}`,
131
+ filePath: join(filePath, nestedFile),
132
+ global: true
133
+ })
134
+ }
135
+ }
136
+ } catch {
137
+ // 忽略嵌套目录错误
138
+ }
139
+ }
140
+ }
141
+ } catch {
142
+ // 目录不存在时忽略
143
+ console.warn(`@neatui/nuxt: Directory ${dir} not found, skipping...`)
144
+ }
145
+ }
146
+
147
+ // 特殊处理 loader 组件 (从 index.ts 导出)
148
+ try {
149
+ addComponent({
150
+ name: `${options.prefix}FormLoader`,
151
+ filePath: resolve('./components/loader/FormLoader/FormLoader@v2.vue'),
152
+ global: true
153
+ })
154
+
155
+ addComponent({
156
+ name: `${options.prefix}TableLoader`,
157
+ filePath: resolve('./components/loader/TableLoader/TableLoader.vue'),
158
+ global: true
159
+ })
160
+ } catch {
161
+ console.warn('@neatui/nuxt: Some loader components not found')
162
+ }
163
+
164
+ } catch (error) {
165
+ console.error('@neatui/nuxt: Error registering components:', error)
166
+ }
167
+ }
168
+
169
+ // 导出类型 (已在上面定义过,这里不需要重复导出)
@@ -0,0 +1,36 @@
1
+ declare module '@nuxt/schema' {
2
+ interface NuxtConfig {
3
+ neatui?: {
4
+ /**
5
+ * 组件前缀
6
+ * @default 'Neat'
7
+ */
8
+ prefix?: string
9
+
10
+ /**
11
+ * 主题配置
12
+ * @default 'default'
13
+ */
14
+ theme?: 'default' | 'dark' | string
15
+
16
+ /**
17
+ * 是否启用自动导入
18
+ * @default true
19
+ */
20
+ autoImport?: boolean
21
+
22
+ /**
23
+ * 要排除的组件列表
24
+ * @default []
25
+ */
26
+ exclude?: string[]
27
+
28
+ /**
29
+ * CSS配置
30
+ */
31
+ css?: {
32
+ variables?: Record<string, string>
33
+ }
34
+ }
35
+ }
36
+ }
@@ -1,7 +1,7 @@
1
1
  import { defineStore } from 'pinia';
2
2
 
3
3
  // 基础数据
4
- export const useMyuiStore = defineStore('myui', {
4
+ export const useFrameStore = defineStore('frame', {
5
5
  state: () => {
6
6
  return {
7
7
  full: localStorage.getItem('neatui_full') || 0,
@@ -10,7 +10,7 @@ export const useMyuiStore = defineStore('myui', {
10
10
  // 主题
11
11
  theme: localStorage.getItem('neatui_theme') || document?.documentElement?.getAttribute('theme') || 'auto',
12
12
  // 侧边
13
- side: 1
13
+ side: 1,
14
14
  };
15
15
  },
16
16
  getters: {},
@@ -45,6 +45,6 @@ export const useMyuiStore = defineStore('myui', {
45
45
  document.documentElement.setAttribute('size', this.size);
46
46
  then(this.size);
47
47
  }
48
- }
49
- }
48
+ },
49
+ },
50
50
  });
@@ -0,0 +1,14 @@
1
+ // 主题
2
+ export const theme = (then: (theme: string) => void = () => {}) => {
3
+ // 自动设置主题
4
+ const auto = (scheme: any) => {
5
+ const _theme = window.localStorage.getItem('theme') || (scheme.matches ? 'dark' : 'light');
6
+ document.documentElement.setAttribute('theme', _theme);
7
+ then(_theme);
8
+ };
9
+ // 监听系统主题
10
+ const scheme: any = window.matchMedia('(prefers-color-scheme: dark)');
11
+ auto(scheme);
12
+ // 监控主题事件
13
+ scheme.addListener(auto);
14
+ };