@neatui/vue 2.9.1 → 2.11.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatui/vue",
3
- "version": "2.9.1",
3
+ "version": "2.11.0",
4
4
  "description": "basic component ui",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -119,7 +119,7 @@
119
119
  const props: any = withDefaults(defineProps<Props>(), {
120
120
  modelValue: '',
121
121
  options: () => [],
122
- tools: true,
122
+ tools: false,
123
123
  spare: '当前没有可选项',
124
124
  search: false,
125
125
  pagination: false,
@@ -143,7 +143,7 @@
143
143
  verify: false,
144
144
  prefix: () => [],
145
145
  suffix: () => [],
146
- interacted: false
146
+ interacted: false,
147
147
  });
148
148
 
149
149
  const change = ({ value = '' }: any = {}) => {
@@ -165,7 +165,7 @@
165
165
  } else {
166
166
  return props.options;
167
167
  }
168
- })
168
+ }),
169
169
  });
170
170
  // 选择
171
171
  const select = (item: any = {}) => {
@@ -195,7 +195,7 @@
195
195
  select,
196
196
  remove,
197
197
  change,
198
- cancel
198
+ cancel,
199
199
  });
200
200
  </script>
201
201
  . .
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <label ui-form="@a type:switch" class="lh-ls">
3
3
  <input type="checkbox" name="form2" :checked="value ? true : false" />
4
- <slot>&nbsp;</slot>
4
+ <slot>
5
+ <span>{{ text }}</span>
6
+ </slot>
5
7
  </label>
6
8
  </template>
7
9
 
@@ -16,106 +18,70 @@
16
18
  // 值
17
19
  modelValue: {
18
20
  type: [Number, Boolean],
19
- default: false
21
+ default: false,
20
22
  },
21
23
  // 类型
22
24
  type: {
23
25
  type: String,
24
- default: 'text'
26
+ default: 'text',
27
+ },
28
+ text: {
29
+ type: String,
30
+ default: '',
25
31
  },
26
32
  // 提示文案
27
33
  tips: {
28
34
  type: String,
29
- default: ''
35
+ default: '',
30
36
  },
31
37
  // 内容占位
32
38
  placeholder: {
33
39
  type: String,
34
- default: ''
40
+ default: '',
35
41
  },
36
42
  // 最大长度
37
43
  maxlength: {
38
44
  type: Number,
39
- default: null
45
+ default: null,
40
46
  },
41
47
  // 最小长度
42
48
  minlength: {
43
49
  type: Number,
44
- default: null
50
+ default: null,
45
51
  },
46
52
  // 是否禁用
47
53
  disabled: {
48
54
  type: Boolean,
49
- default: false
55
+ default: false,
50
56
  },
51
57
  // 是否中读
52
58
  readonly: {
53
59
  type: Boolean,
54
- default: false
60
+ default: false,
55
61
  },
56
62
  // 是否显示清除按钮
57
63
  clearable: {
58
64
  type: Boolean,
59
- default: true
65
+ default: true,
60
66
  },
61
67
  // 校验规则
62
68
  rules: {
63
69
  type: Array,
64
- default: () => []
70
+ default: () => [],
65
71
  },
66
72
  // 是否是示校验图标
67
73
  verify: {
68
74
  type: Boolean,
69
- default: false
75
+ default: false,
70
76
  },
71
77
  prefix: {
72
78
  type: Array as PropType<any[]>,
73
- default: () => []
79
+ default: () => [],
74
80
  },
75
81
  suffix: {
76
82
  type: Array as PropType<any[]>,
77
- default: () => []
78
- }
79
- });
80
-
81
- const ready: any = ref(0);
82
- const state: any = computed(() => {
83
- if (ready.value) {
84
- // 提示文案
85
- let tips = '';
86
- // 错误类型
87
- let type = '';
88
- // 验证结果
89
- let verify = '';
90
- if (props.modelValue) {
91
- // 有值
92
- const res: any = props.rules?.some((rule: any) => {
93
- const yes = rule.validator && !rule.validator(props.modelValue);
94
- tips = rule.msg;
95
- type = rule.type;
96
- return yes;
97
- });
98
- verify = res ? 'no' : 'ok';
99
- } else {
100
- // 无值
101
- const res: any = props.rules?.some((rule: any) => {
102
- tips = rule.msg;
103
- type = rule.type;
104
- return rule.required;
105
- });
106
- verify = res ? 'no' : '';
107
- }
108
- if (verify !== 'no' && props.tips) {
109
- tips = props.tips;
110
- }
111
- // 是否显示提示
112
- const ontips = verify === 'no' && tips ? 'show' : '';
113
- // 状态颜色
114
- const co = verify === 'no' ? 'co:' + type : props.co ? 'co:' + props.co : '';
115
- return { tips, ontips, verify, co };
116
- } else {
117
- return {};
118
- }
83
+ default: () => [],
84
+ },
119
85
  });
120
86
 
121
87
  const value = ref(props.modelValue);
@@ -124,12 +90,6 @@
124
90
  () => props.modelValue,
125
91
  (newValue) => {
126
92
  value.value = newValue;
127
- }
93
+ },
128
94
  );
129
-
130
- onMounted(() => {
131
- setTimeout(() => {
132
- ready.value = 1;
133
- }, 300);
134
- });
135
95
  </script>
@@ -75,13 +75,13 @@
75
75
  <script setup lang="ts">
76
76
  import { nextTick, reactive, ref, watch, onMounted } from 'vue';
77
77
  import LayerLoader from './LayerLoader.vue';
78
- import { useBaseStore, useWorksStore, storeToRefs } from '@/store';
78
+ import { useBasicStore, useWorksStore, storeToRefs } from '@/store';
79
79
  import { deepcopy } from '@fekit/utils';
80
80
  import scrollto from '@fekit/scrollto';
81
81
 
82
82
  // 事件定义
83
83
  const store: any = useWorksStore();
84
- const base: any = useBaseStore();
84
+ const base: any = useBasicStore();
85
85
  const { word } = base;
86
86
 
87
87
  // 类型定义
@@ -87,7 +87,7 @@
87
87
  :col-fixed-side="col.fixed || 'l'"
88
88
  :data-field="col.field"
89
89
  >
90
- <slot :name="col.field" :col="col" :item="item" :idx="idx"><TableRender :col="col" :data="item" /></slot>
90
+ <slot :name="col.field" :col="col" :item="item" :idx="idx"><TableRender :node="col" :data="item" /></slot>
91
91
  </td>
92
92
  </template>
93
93
  <td v-if="slots.operate" col-fixed="r" class="ar"><slot name="operate" :item="item" :idx="idx"></slot></td>
@@ -127,7 +127,7 @@
127
127
  <tr v-if="!col.__close">
128
128
  <td class="nowrap vt n-ss">{{ col.label }}:</td>
129
129
  <td class="n-ss">
130
- <TableRender :col="col" :data="item" />
130
+ <TableRender :node="col" :data="item" />
131
131
  </td>
132
132
  </tr>
133
133
  </template>
@@ -167,14 +167,14 @@
167
167
  field: string | (() => string);
168
168
  width?: string | (() => string);
169
169
  fixed?: string | (() => string);
170
- thead?: object | (() => object);
171
- tbody?: object | (() => object);
172
170
  paths?: string | (() => string);
173
- model?: string | Array<any> | (() => string | Array<any>);
174
- enums?: Array<any> | (() => string | Array<any>);
175
- rules?: Array<any> | (() => string | Array<any>);
176
- event?: Array<any> | (() => string | Array<any>);
177
- attrs?: string | Record<string, any> | Record<string, any>[] | (() => string | Record<string, any> | Record<string, any>[]);
171
+ thead?: Record<string, any> | ((data: any) => Record<string, any>);
172
+ tbody?: Record<string, any> | ((data: any) => Record<string, any>);
173
+ model?: string | any[] | ((data: any) => string | any[]);
174
+ enums?: any[] | ((data: any) => any[]);
175
+ rules?: any[] | ((data: any) => any[]);
176
+ event?: Record<string, any> | ((data: any) => Record<string, any>);
177
+ attrs?: Record<string, any> | ((data: any) => Record<string, any>);
178
178
  value?: any | (() => any);
179
179
  clear?: boolean | (() => boolean);
180
180
  order?: 0 | 1 | 2 | number | (() => 0 | 1 | 2 | number);
@@ -268,6 +268,11 @@
268
268
  state.__last = 0;
269
269
  state.table = deepcopy(props.table || [])
270
270
  ?.map((col: TableColumn, idx: number) => {
271
+ // 标题处理
272
+ if (isFunction(col.label)) {
273
+ col.label = col.label(props.lists);
274
+ }
275
+
271
276
  // 表头属性处理
272
277
  col.thead ??= {};
273
278
  col.thead.class = (col.thead.class || '').split(/\s+/);
@@ -1,61 +1,43 @@
1
1
  <template>
2
- <template v-if="isArray(json.child)">
3
- <div :class="json?.attrs?.class?.includes('wrap') ? 'wrap' : 'nowrap'" v-bind="json.attrs" v-on="event(json, data)">
4
- <TableRender v-for="(sub, idx) in col.child" :key="idx" :col="sub" :data="data" />
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
5
  </div>
6
6
  </template>
7
- <template v-else-if="json.popup">
8
- <IFollowView>
9
- <TableRender :col="{ ...col, popup: null }" :data="data" />
7
+ <template v-else-if="node.popup">
8
+ <IFollowView v-memo="[node.popup, data]">
9
+ <TableRender :node="{ ...node, popup: null }" :data="data" />
10
10
  <template #tips>
11
- <TableRender :col="isFunction(col.popup) ? col.popup(data) : col.popup" :data="data" />
11
+ <TableRender :node="isFunction(node.popup) ? node.popup(data) : node.popup" :data="data" />
12
12
  </template>
13
13
  </IFollowView>
14
14
  </template>
15
15
  <template v-else>
16
- <div v-if="json.model === 'Image'" :class="json?.attrs?.class?.includes('wrap') ? 'wrap' : 'nowrap'" ui-img="@a read :round" v-bind="json.attrs" v-on="event(json, data)">
17
- <img :src="json.value || data[json.field] || DefaultImage" alt="" />
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-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>
18
25
  </div>
19
- <div v-else :class="json?.attrs?.class?.includes('wrap') ? 'wrap' : 'nowrap'" v-bind="json.attrs" v-on="event(json, data)" v-html="value(json, data)"></div>
26
+ <div v-else v-bind="attrs" v-on="event" v-html="value"></div>
20
27
  </template>
21
28
  </template>
22
29
  <script lang="ts" setup>
23
- import { computed } from 'vue';
24
- import { isFunction, isArray, isObject, deepcopy } from '@fekit/utils';
30
+ import { computed, onMounted, reactive, ref } from 'vue';
31
+ import { isFunction, isArray, isObject, isString, findEnumName } from '@fekit/utils';
25
32
  import { useRoute, useRouter } from 'vue-router';
26
33
  import TableRender from './TableRender.vue';
27
34
  import { IFollowView } from '../../basic';
28
35
 
29
- const DefaultImage =
30
- 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKYAAACmBAMAAACipulEAAAAHlBMVEXMzMz////z8/Pf39/Z2dnS0tLq6urk5OTu7u74+PjCLuPVAAABxUlEQVRo3u2YsUsDMRSHX9Wzupn22qObVxAd7SCuV1BwvGrVtQcqjlYQHFVEVzv575qklReE9urlFTL8vqUvQz+Se3m5yyMAAAAAAAAAAAAAsFIu7rvz+DiqprxSi7itotxQi8kqOHslzqbHNAUnul3q/PJYutzi01Jn47/KSBm+jxep8yopSojqgklaV5qRDor5zsMKTru4gazTJmEtfKcyQU3Waf/UE3bGXPb7p5fDRwmnuhkWyvJEhmt/JzOiKZtyzph+6Us5O1zY0UTI2TKy5+4nl5W/M9PpT00NmEDG2dbDgiMR5w7R1ix84brycz7oyfGU6yLO3HmIGUUSzoY9m3jxqYAzsRuIt9VYwBm7Z1OTB57Owt1XfQFn649zEOg8YzctiYyz6Wpizxzxcmtunb5K1JGtR65TkdrMiCazsMOHnZ9zlx9ozHXq5XTfyBnr/ZwdPTy30Z6OJr5OfhOf6d934tPZ15mQJjrJSVN4O3mi/NEg5GzkNCVKBZy8estYeTuZdmbuDYWScDIHd29KSTmZEJ26GsUvxpEqJw/gDrvMXTuInsASvYsgeizlvaBAelZlvTUAAAAAAAAAAAAAsEp+AEDOpCBEwOONAAAAAElFTkSuQmCC';
31
-
32
- // 通过枚举值查找枚举名
33
- const findEnumName = (enums: any, val: any, join: boolean | string = ' Ι ') => {
34
- // 转换核心
35
- const _core = (v: any) => {
36
- const { label = v }: any = enums.find(({ value = '' }: any) => value == v) || {};
37
- return label;
38
- };
39
- // 判断枚举字典是否可用
40
- if (enums && enums.find) {
41
- // 兼容多种入参类型
42
- if (isArray(val)) {
43
- // 数组
44
- const arr = val.map((i: any) => _core(i));
45
- return join ? arr.join(join) : arr;
46
- } else {
47
- return _core(val);
48
- }
49
- } else {
50
- return val;
51
- }
52
- };
53
-
54
36
  const route = useRoute();
55
37
  const router = useRouter();
56
38
 
57
39
  const props: any = defineProps({
58
- col: {
40
+ node: {
59
41
  type: Object,
60
42
  default: () => ({}),
61
43
  },
@@ -65,47 +47,95 @@
65
47
  },
66
48
  });
67
49
 
68
- // 运行函数计算
69
- const runfun = (obj: any): any => {
70
- if (isArray(obj)) {
71
- obj = obj.map((i: any) => runfun(i));
72
- } else if (isObject(obj)) {
73
- for (const k in obj) {
74
- if (k !== 'event') {
75
- obj[k] = runfun(obj[k]);
76
- }
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');
77
75
  }
78
- } else if (isFunction(obj)) {
79
- obj = obj(deepcopy(props.data));
76
+ } else {
77
+ attrs.class = ['nowrap'];
80
78
  }
81
- return obj;
82
- };
83
79
 
84
- const json = computed(() => runfun(deepcopy(props.col)));
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;
85
89
 
86
- const value = (json: any, data: any) => {
87
- return json.value !== null && json.value !== undefined
88
- ? isFunction(json.value)
89
- ? json.value(json)
90
- : json.value
91
- : findEnumName(json.enums, data[json.field]) !== null && findEnumName(json.enums, data[json.field]) !== undefined
92
- ? findEnumName(json.enums, data[json.field])
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)
93
119
  : '‐‐';
94
- };
120
+ });
95
121
 
96
- // 事件处理
97
- const event: any = (col: any, data: any): object => {
98
- const _event: any = {};
99
- if (isObject(col.event)) {
100
- for (const key in col.event) {
101
- const fun = col.event[key];
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];
102
132
  if (isFunction(fun)) {
103
- _event[key] = (...args: any) => {
104
- return fun({ data, route, router }, ...args);
133
+ events[key] = (...args: any) => {
134
+ return fun({ data: props.data, route, router }, ...args);
105
135
  };
106
136
  }
107
137
  }
108
138
  }
109
- return { ..._event };
110
- };
139
+ return events;
140
+ });
111
141
  </script>
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';
@@ -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: any) => {}) => {
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
+ };