@neatui/vue 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 (100) hide show
  1. package/README.md +139 -0
  2. package/package.json +26 -0
  3. package/src/components/basic/IDraggable.vue +84 -0
  4. package/src/components/basic/IDraggable@b.vue +80 -0
  5. package/src/components/basic/IFollowView.vue +172 -0
  6. package/src/components/basic/IFollowView@v2.vue +188 -0
  7. package/src/components/basic/IPickerView.vue +351 -0
  8. package/src/components/basic/IRouterView@v2.vue.backup +119 -0
  9. package/src/components/basic/IRouterView@v3.vue.backup +252 -0
  10. package/src/components/basic/IRouterView@v4.vue +362 -0
  11. package/src/components/basic/IScrollView.vue +127 -0
  12. package/src/components/basic/Icon.vue +54 -0
  13. package/src/components/basic/LayerView/Layer.vue +333 -0
  14. package/src/components/basic/LayerView/index.ts +5 -0
  15. package/src/components/basic/follow.ts +119 -0
  16. package/src/components/basic/index.ts +7 -0
  17. package/src/components/display/Avatar.vue +1 -0
  18. package/src/components/display/Badge.vue +1 -0
  19. package/src/components/display/Calendar.vue +245 -0
  20. package/src/components/display/CalendarReg.vue +245 -0
  21. package/src/components/display/Card.vue +1 -0
  22. package/src/components/display/Carousel.vue +1 -0
  23. package/src/components/display/Collapse.vue +1 -0
  24. package/src/components/display/Desriptions.vue +1 -0
  25. package/src/components/display/Empty.vue +1 -0
  26. package/src/components/display/Image.vue +112 -0
  27. package/src/components/display/List.vue +1 -0
  28. package/src/components/display/PhotoEditor.vue +181 -0
  29. package/src/components/display/PhotoViewer.vue +50 -0
  30. package/src/components/display/Popover.vue +1 -0
  31. package/src/components/display/QRCode.vue +1 -0
  32. package/src/components/display/Segmented.vue +1 -0
  33. package/src/components/display/Statistic.vue +1 -0
  34. package/src/components/display/Table.vue +1 -0
  35. package/src/components/display/Tabs.vue +1 -0
  36. package/src/components/display/Tag.vue +1 -0
  37. package/src/components/display/Timeline.vue +1 -0
  38. package/src/components/display/Tooltip.vue +1 -0
  39. package/src/components/display/Tour.vue +1 -0
  40. package/src/components/display/Tree.vue +431 -0
  41. package/src/components/display/TreeView.vue +222 -0
  42. package/src/components/display/index.ts +7 -0
  43. package/src/components/form/Cascader.vue +435 -0
  44. package/src/components/form/DatePicker.vue +124 -0
  45. package/src/components/form/DateRangePicker@v2.vue.backup +224 -0
  46. package/src/components/form/DateRangePicker@v3.vue +116 -0
  47. package/src/components/form/DateRangeView@v3.vue +386 -0
  48. package/src/components/form/DateView.vue +231 -0
  49. package/src/components/form/DateView@v2.vue +386 -0
  50. package/src/components/form/DateView@v3.vue +471 -0
  51. package/src/components/form/EditUpload.vue +4 -0
  52. package/src/components/form/ImgUpload.vue +174 -0
  53. package/src/components/form/Input.vue.backup +230 -0
  54. package/src/components/form/Input@v3.vue +267 -0
  55. package/src/components/form/InputNumber.vue +200 -0
  56. package/src/components/form/InputRange.vue +235 -0
  57. package/src/components/form/MoreSelect.vue.backup +144 -0
  58. package/src/components/form/MoreSelect@v3.vue +190 -0
  59. package/src/components/form/MoreSelectList.vue +120 -0
  60. package/src/components/form/MoreSelectPanel@v3.vue +190 -0
  61. package/src/components/form/MoreSelectPicker.vue +119 -0
  62. package/src/components/form/MoreSelectTags.vue +119 -0
  63. package/src/components/form/PageMoreSelect.vue +187 -0
  64. package/src/components/form/PageSelect.vue +189 -0
  65. package/src/components/form/SearchMoreSelect.vue +168 -0
  66. package/src/components/form/SearchSelect.vue.backup +194 -0
  67. package/src/components/form/SearchSelect@v3.vue +202 -0
  68. package/src/components/form/Select@v3.vue +201 -0
  69. package/src/components/form/SelectList.vue +58 -0
  70. package/src/components/form/SelectPicker.vue +97 -0
  71. package/src/components/form/SelectTags.vue +52 -0
  72. package/src/components/form/SelectTree/SelectTree@v1.vue +227 -0
  73. package/src/components/form/Switch.vue +135 -0
  74. package/src/components/form/TextArea.vue +171 -0
  75. package/src/components/form/TimePicker.vue +11 -0
  76. package/src/components/form/TimeView.vue +244 -0
  77. package/src/components/form/Upload.vue +349 -0
  78. package/src/components/form/index.ts +82 -0
  79. package/src/components/loader/FormLoader/FormLoader@v2.vue +422 -0
  80. package/src/components/loader/FormLoader/FormLoader@v3.vue.backup +318 -0
  81. package/src/components/loader/FormLoader/index.ts +2 -0
  82. package/src/components/loader/LimitLoader/LimitLoader.vue.backup +131 -0
  83. package/src/components/loader/LimitLoader/LimitLoader@v2.vue.backup +174 -0
  84. package/src/components/loader/LimitLoader/LimitLoader@v3.vue +183 -0
  85. package/src/components/loader/LimitLoader/index.ts +2 -0
  86. package/src/components/loader/TableLoader/TableColView.vue +115 -0
  87. package/src/components/loader/TableLoader/TableLoader.vue +356 -0
  88. package/src/components/loader/TableLoader/index.ts +2 -0
  89. package/src/components/loader/ViewLoader/ViewLoader@v1.vue +256 -0
  90. package/src/components/loader/ViewLoader/index.ts +2 -0
  91. package/src/components/loader/index.ts +5 -0
  92. package/src/components/tools/FormDraftsView.vue +308 -0
  93. package/src/components/tools/FormVerifyView.vue +206 -0
  94. package/src/components/tools/MoreTools.vue +74 -0
  95. package/src/components/tools/MoreTools@v2.vue +74 -0
  96. package/src/components/tools/Pagination@a.vue +222 -0
  97. package/src/components/tools/Pagination@b.vue +221 -0
  98. package/src/components/tools/index.ts +5 -0
  99. package/src/index.ts +5 -0
  100. package/tsconfig.json +24 -0
@@ -0,0 +1,183 @@
1
+ <template>
2
+ <div class="full-x" ui-flex="row xt">
3
+ <div class="mr-mm-sub my-sm-sub" ui-flex="row lm :wrap">
4
+ <template v-for="(item, idx) in state.limit" :key="idx">
5
+ <div v-if="!item._close" ui-flex="row lm" v-bind="{ ...(item.attrs || {}) }">
6
+ <span class="nr-sm flex-fixed o-ls">{{ item.label }}{{ item.label ? ':' : '' }}</span>
7
+ <Input v-if="item.model[0] === 'Input'" v-model="state.param[item.field]" :block="false" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
8
+ <Select v-else-if="item.model[0] === 'Select'" v-model="state.param[item.field]" :options="item.enums" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
9
+ <SelectTree v-else-if="item.model[0] === 'SelectTree'" v-model="state.param[item.field]" :options="item.enums" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
10
+ <Cascader v-else-if="item.model[0] === 'Cascader'" v-model="state.param[item.field]" :options="item.enums" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
11
+ <MoreSelect v-else-if="item.model[0] === 'MoreSelect'" v-model="state.param[item.field]" :options="item.enums" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
12
+ <SearchMoreSelect v-else-if="item.model[0] === 'SearchMoreSelect'" v-model="state.param[item.field]" :options="item.enums" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
13
+ <DatePicker
14
+ v-else-if="item.model[0] === 'DatePicker'"
15
+ v-model="state.param[item.field]"
16
+ sz="s"
17
+ v-bind="{ ...(item.model[1] || {}) }"
18
+ @change="(val: any) => (state.param[item.field] = val)"
19
+ />
20
+ <DateRangePicker v-else-if="item.model[0] === 'DateRangePicker'" v-model="state.param[item.field]" sz="s" v-bind="{ ...(item.model[1] || {}) }" />
21
+ <slot v-else :name="item.field" :form="item" :data="param"></slot>
22
+ </div>
23
+ </template>
24
+ </div>
25
+ <div class="mr-sl-sub my-sm-sub nowrap" ui-hide="<pad">
26
+ <IFollowView tipBoxClass="nr-no">
27
+ <button ui-btn="@a s case :border"><i class="icon icon-add2 fs-xs"></i>添加条件</button>
28
+ <template v-if="state.limit.length" #tips>
29
+ <div class="w-mm h-ls nr-sl" ui-scroll="x:hidden :y">
30
+ <div v-for="(item, idx) in state.limit" :key="idx" class="item ux-hover nowrap r-ss">
31
+ <label class="ny-xs nx-sm" ui-form="@a type:checkbox">
32
+ <input :checked="!item._close" type="checkbox" @change="item._close = item._close ? 0 : 1" />
33
+ <span>{{ item.label }}</span>
34
+ </label>
35
+ </div>
36
+ </div>
37
+ </template>
38
+ </IFollowView>
39
+ <button ui-btn="@a s main" @click="fResetLimit">{{ words['common.reset'] || '重置' }}</button>
40
+ </div>
41
+ </div>
42
+ <div class="mr-sl-sub my-sm-sub nowrap" ui-hide=">mob">
43
+ <IFollowView tipBoxClass="nr-no">
44
+ <button ui-btn="@a s case :border"><i class="icon icon-add2 fs-xs"></i>添加条件</button>
45
+ <template v-if="state.limit.length" #tips>
46
+ <div class="w-mm h-ls nr-sl" ui-scroll="x:hidden :y">
47
+ <div v-for="(item, idx) in state.limit" :key="idx" class="item ux-hover nowrap r-ss">
48
+ <label class="ny-xs nx-sm" ui-form="@a type:checkbox">
49
+ <input :checked="!item._close" type="checkbox" @change="item._close = item._close ? 0 : 1" />
50
+ <span>{{ item.label }}</span>
51
+ </label>
52
+ </div>
53
+ </div>
54
+ </template>
55
+ </IFollowView>
56
+ <button ui-btn="@a s main" @click="fResetLimit">{{ words['common.reset'] || '重置' }}</button>
57
+ </div>
58
+ </template>
59
+ <script lang="ts" setup>
60
+ import { computed, reactive, watch } from 'vue';
61
+ import { useRoute } from 'vue-router';
62
+ import { deepcopy, isObject, isString } from '@fekit/utils';
63
+ import { IFollowView } from '../../basic';
64
+ import { Input, Select, SelectTree, MoreSelect, SearchMoreSelect, DatePicker, DateRangePicker, Cascader } from '../../form';
65
+
66
+ const route = useRoute();
67
+
68
+ /**
69
+ * 入参说明
70
+ *
71
+ * words 语言包
72
+ * limit 结构配置
73
+ * param 数据
74
+ */
75
+
76
+ interface LimitProps {
77
+ label?: string | (() => string);
78
+ field: string | (() => string);
79
+ model?: string | Array<any> | (() => string | Array<any>);
80
+ enums?: Array<any> | (() => string | Array<any>);
81
+ rules?: Array<any> | (() => string | Array<any>);
82
+ event?: Array<any> | (() => string | Array<any>);
83
+ attrs?: Array<any> | (() => string | Array<any>);
84
+ value?: any | (() => any);
85
+ clear?: boolean | (() => boolean);
86
+ close?: boolean;
87
+ }
88
+
89
+ // 类型
90
+ interface Props {
91
+ words?: { [key: string | number]: any };
92
+ limit: Array<LimitProps>;
93
+ param: any;
94
+ }
95
+
96
+ const props: any = withDefaults(defineProps<Props>(), {
97
+ words: () => ({}),
98
+ limit: () => [],
99
+ param: () => ({})
100
+ });
101
+
102
+ // 内部数据
103
+ const state: any = reactive({
104
+ name: computed(() => 'confs' + route.path?.replace(/\//g, '_')),
105
+ limit: [],
106
+ param: {}
107
+ });
108
+ // 监听外部配置
109
+ watch(
110
+ () => props.limit,
111
+ () => {
112
+ const _cache = JSON.parse(localStorage.getItem(`${state.name}_limit`) || '{}');
113
+ state.limit = deepcopy(props.limit)?.map((item: any) => {
114
+ if (_cache[item.field] && Object.prototype.hasOwnProperty.call(_cache[item.field], '_close')) {
115
+ item._close = Number(_cache[item.field]?._close);
116
+ } else {
117
+ if (item.close === true || item.close === 1) {
118
+ item._close = 1;
119
+ }
120
+ }
121
+
122
+ // 模型处理
123
+ if (item.model) {
124
+ if (isString(item.model)) {
125
+ item.model = [item.model, { verify: false }];
126
+ }
127
+ } else {
128
+ item.model = ['', { verify: false }];
129
+ }
130
+
131
+ if (!item.model[1] || !isObject(item.model[1])) {
132
+ item.model.push({ verify: false });
133
+ }
134
+
135
+ // 属性处理
136
+ if (item.model[1].class) {
137
+ if (!/\bw-[sml]+/.test(item.model[1].class || '')) {
138
+ item.model[1].class += ' w-ms+';
139
+ }
140
+ } else {
141
+ item.model[1].class = 'w-ms+';
142
+ }
143
+
144
+ return item;
145
+ });
146
+ },
147
+ { deep: true, immediate: true }
148
+ );
149
+ // 缓存交互配置
150
+ watch(
151
+ [() => state.limit],
152
+ () => {
153
+ const _limit: any = {};
154
+ state.limit.forEach((item: any, idx: number) => {
155
+ if (Object.prototype.hasOwnProperty.call(item, '_close')) {
156
+ _limit[item.field] = { _close: item._close ? 1 : 0 };
157
+ if (item._close) {
158
+ delete state.param[item.field];
159
+ }
160
+ }
161
+ });
162
+ localStorage.setItem(`${state.name}_limit`, JSON.stringify(_limit));
163
+ },
164
+ { deep: true }
165
+ );
166
+
167
+ watch(
168
+ () => props.param,
169
+ () => {
170
+ state.param = props.param;
171
+ },
172
+ { deep: true, immediate: true }
173
+ );
174
+
175
+ // 重置筛选
176
+ const fResetLimit = () => {
177
+ for (const key in state.param) {
178
+ if (key !== 'page' && key !== 'pageSize') {
179
+ delete state.param[key];
180
+ }
181
+ }
182
+ };
183
+ </script>
@@ -0,0 +1,2 @@
1
+ import FormLoader from './LimitLoader@v3.vue';
2
+ export default FormLoader;
@@ -0,0 +1,115 @@
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
+ <TableColView v-for="(sub, idx) in col.child" :key="idx" :col="sub" :data="data" />
5
+ </div>
6
+ </template>
7
+ <template v-else-if="json.popup">
8
+ <IFollowView>
9
+ <TableColView :col="{ ...col, popup: null }" :data="data" />
10
+ <template #tips>
11
+ <TableColView :col="isFunction(col.popup) ? col.popup(data) : col.popup" :data="data" />
12
+ </template>
13
+ </IFollowView>
14
+ </template>
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="" />
18
+ </div>
19
+ <div
20
+ v-else
21
+ :class="json?.attrs?.class?.includes('wrap') ? 'wrap' : 'nowrap'"
22
+ v-bind="json.attrs"
23
+ v-on="event(json, data)"
24
+ v-html="
25
+ json.value !== null && json.value !== undefined
26
+ ? isFunction(json.value)
27
+ ? json.value(json)
28
+ : json.value
29
+ : findEnumName(json.enums, data[json.field]) !== null && findEnumName(json.enums, data[json.field]) !== undefined
30
+ ? findEnumName(json.enums, data[json.field])
31
+ : '‐‐'
32
+ "
33
+ ></div>
34
+ </template>
35
+ </template>
36
+ <script lang="ts" setup>
37
+ import { computed } from 'vue';
38
+ import { isFunction, isArray, isObject, deepcopy } from '@fekit/utils';
39
+ import { useRoute, useRouter } from 'vue-router';
40
+ import TableColView from './TableColView.vue';
41
+ import { IFollowView } from '../../basic';
42
+
43
+ const DefaultImage =
44
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKYAAACmBAMAAACipulEAAAAHlBMVEXMzMz////z8/Pf39/Z2dnS0tLq6urk5OTu7u74+PjCLuPVAAABxUlEQVRo3u2YsUsDMRSHX9Wzupn22qObVxAd7SCuV1BwvGrVtQcqjlYQHFVEVzv575qklReE9urlFTL8vqUvQz+Se3m5yyMAAAAAAAAAAAAAsFIu7rvz+DiqprxSi7itotxQi8kqOHslzqbHNAUnul3q/PJYutzi01Jn47/KSBm+jxep8yopSojqgklaV5qRDor5zsMKTru4gazTJmEtfKcyQU3Waf/UE3bGXPb7p5fDRwmnuhkWyvJEhmt/JzOiKZtyzph+6Us5O1zY0UTI2TKy5+4nl5W/M9PpT00NmEDG2dbDgiMR5w7R1ix84brycz7oyfGU6yLO3HmIGUUSzoY9m3jxqYAzsRuIt9VYwBm7Z1OTB57Owt1XfQFn649zEOg8YzctiYyz6Wpizxzxcmtunb5K1JGtR65TkdrMiCazsMOHnZ9zlx9ozHXq5XTfyBnr/ZwdPTy30Z6OJr5OfhOf6d934tPZ15mQJjrJSVN4O3mi/NEg5GzkNCVKBZy8estYeTuZdmbuDYWScDIHd29KSTmZEJ26GsUvxpEqJw/gDrvMXTuInsASvYsgeizlvaBAelZlvTUAAAAAAAAAAAAAsEp+AEDOpCBEwOONAAAAAElFTkSuQmCC';
45
+
46
+ // 通过枚举值查找枚举名
47
+ const findEnumName = (enums: any, val: any, join: boolean | string = ' Ι ') => {
48
+ // 转换核心
49
+ const _core = (v: any) => {
50
+ const { label = v }: any = enums.find(({ value = '' }: any) => value == v) || {};
51
+ return label;
52
+ };
53
+ // 判断枚举字典是否可用
54
+ if (enums && enums.find) {
55
+ // 兼容多种入参类型
56
+ if (isArray(val)) {
57
+ // 数组
58
+ const arr = val.map((i: any) => _core(i));
59
+ return join ? arr.join(join) : arr;
60
+ } else {
61
+ return _core(val);
62
+ }
63
+ } else {
64
+ return val;
65
+ }
66
+ };
67
+
68
+ const route = useRoute();
69
+ const router = useRouter();
70
+
71
+ const props: any = defineProps({
72
+ col: {
73
+ type: Object,
74
+ default: () => ({})
75
+ },
76
+ data: {
77
+ type: [Object],
78
+ default: () => ({})
79
+ }
80
+ });
81
+
82
+ // 运行函数计算
83
+ const runfun = (obj: any): any => {
84
+ if (isArray(obj)) {
85
+ obj = obj.map((i: any) => runfun(i));
86
+ } else if (isObject(obj)) {
87
+ for (const k in obj) {
88
+ if (k !== 'event') {
89
+ obj[k] = runfun(obj[k]);
90
+ }
91
+ }
92
+ } else if (isFunction(obj)) {
93
+ obj = obj(deepcopy(props.data));
94
+ }
95
+ return obj;
96
+ };
97
+
98
+ const json = computed(() => runfun(deepcopy(props.col)));
99
+
100
+ // 事件处理
101
+ const event: any = (col: any, data: any): object => {
102
+ const _event: any = {};
103
+ if (isObject(col.event)) {
104
+ for (const key in col.event) {
105
+ const fun = col.event[key];
106
+ if (isFunction(fun)) {
107
+ _event[key] = (...args: any) => {
108
+ return fun({ data, route, router }, ...args);
109
+ };
110
+ }
111
+ }
112
+ }
113
+ return { ..._event };
114
+ };
115
+ </script>
@@ -0,0 +1,356 @@
1
+ <template>
2
+ <!-- 桌面端 -->
3
+ <table ui-hide="<pad" class="nx-ms" ui-table="@b sz:l" ref="tableRef">
4
+ <thead class="table-head-fixed">
5
+ <tr>
6
+ <th v-if="showSelectAll" col-fixed="1" min-width>
7
+ <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" :checked="state.isAllSelect" @change="fSelectAll" /><span></span></label>
8
+ </th>
9
+ <template v-for="(col, idx) in state.table" :key="col.field + idx">
10
+ <th v-if="!col._close" class="fs-xs nowrap" :min-width="col.fixed" :col-fixed="col.fixed" :col-fixed-side="col.fixed || 'l'" :data-field="col.field">
11
+ <div :ui-flex="`row ${!slots.operate && idx === state._last ? 'rm' : 'lm'}`">
12
+ <span class="nl-xs" v-if="col.sort" :ui-sort="0" @click="emits('onsort', col)">{{ col.label }}</span>
13
+ <span class="nl-xs" v-else>{{ col.label }}</span>
14
+ <IFollowView v-if="!slots.operate && idx === state._last" tipBoxClass="n-no">
15
+ <button :ui-btn="`@a none s :square`">
16
+ <Icon name="tableloader-setting" class="co-read"></Icon>
17
+ </button>
18
+ <template #tips>
19
+ <div class="w-mm h-ls n-sl" ui-scroll="x:hidden :y">
20
+ <draggable
21
+ v-model="state.table"
22
+ :force-fallback="false"
23
+ item-key="name"
24
+ chosen-class="chosen"
25
+ animation="200"
26
+ filter=".forbid"
27
+ :move="(e: any) => !e.relatedContext.element.fixed"
28
+ >
29
+ <template #item="{ element }">
30
+ <div :class="`item ux-hover nowrap r-ss ${element.fixed ? 'forbid' : 'element'}`" :key="element.field">
31
+ <label ui-form="@a type:checkbox" class="ny-xs nx-sl">
32
+ <input type="checkbox" :checked="!element._close" @change="element._close = element._close ? 0 : 1" />
33
+ <span>
34
+ {{ element.label }}
35
+ <i v-if="element.fixed" class="icon icon-fixed fs-xs"></i>
36
+ </span>
37
+ </label>
38
+ </div>
39
+ </template>
40
+ </draggable>
41
+ </div>
42
+ </template>
43
+ </IFollowView>
44
+ </div>
45
+ </th>
46
+ </template>
47
+ <th v-if="slots.operate" col-fixed="r" class="fs-xs nowrap ar">
48
+ <div ui-flex="row rm">
49
+ <span class="mr-sm">{{ words['operate'] || '操作' }}</span>
50
+ <IFollowView tipBoxClass="n-no">
51
+ <button :ui-btn="`@a none s :square`">
52
+ <Icon name="tableloader-setting" class="co-read"></Icon>
53
+ </button>
54
+ <template #tips>
55
+ <div class="w-mm h-ls n-sl" ui-scroll="x:hidden :y">
56
+ <draggable
57
+ v-model="state.table"
58
+ :force-fallback="false"
59
+ item-key="name"
60
+ chosen-class="chosen"
61
+ animation="200"
62
+ filter=".forbid"
63
+ :move="(e: any) => !e.relatedContext.element.fixed"
64
+ >
65
+ <template #item="{ element }">
66
+ <div :class="`item ux-hover nowrap r-ss ${element.fixed ? 'forbid' : 'element'}`" :key="element.field">
67
+ <label ui-form="@a type:checkbox" class="ny-xs nx-sl">
68
+ <input type="checkbox" :checked="!element._close" @change="element._close = element._close ? 0 : 1" />
69
+ <span>
70
+ {{ element.label }}
71
+ <i v-if="element.fixed" class="icon icon-fixed fs-xs"></i>
72
+ </span>
73
+ </label>
74
+ </div>
75
+ </template>
76
+ </draggable>
77
+ </div>
78
+ </template>
79
+ </IFollowView>
80
+ </div>
81
+ </th>
82
+ </tr>
83
+ </thead>
84
+ <tbody :ui-tbody-load="state.load">
85
+ <template v-if="lists.length">
86
+ <tr v-for="(item, idx) in lists" :key="idx" v-bind="rows.attrs" v-on="event(rows.event, item)">
87
+ <td v-if="showSelectAll" col-fixed="1" min-width>
88
+ <label class="mb-ss" ui-form="@a type:checkbox"><input type="checkbox" v-model="item.__selected" /><span></span></label>
89
+ </td>
90
+ <template v-for="(col, idx) in state.table" :key="col.field + idx">
91
+ <td
92
+ v-if="!col._close"
93
+ :class="`${!slots.operate && idx === state._last ? 'ar' : ''}`"
94
+ :min-width="col.fixed"
95
+ :col-fixed="col.fixed"
96
+ :col-fixed-side="col.fixed || 'l'"
97
+ :data-field="col.field"
98
+ >
99
+ <TableColView :col="col" :data="item" />
100
+ </td>
101
+ </template>
102
+ <td v-if="slots.operate" col-fixed="r" class="ar"><slot name="operate" :item="item" :idx="idx"></slot></td>
103
+ </tr>
104
+ </template>
105
+ </tbody>
106
+ </table>
107
+ <!-- 移动端 -->
108
+ <div ui-hide=">mob">
109
+ <table class="n-sl" ui-table="@a">
110
+ <thead class="table-head-fixed">
111
+ <tr>
112
+ <th class="fs-xs ar ps nr-no">
113
+ <div ui-flex="row xm">
114
+ <span class="fs-xs bold">信息</span>
115
+ <IFollowView>
116
+ <button :ui-btn="`@a none s :square`"><Icon name="tableloader-setting" class="co-read"></Icon></button>
117
+ <template #tips>
118
+ <draggable :list="state.table" :force-fallback="false" item-key="name" chosen-class="chosen" animation="200">
119
+ <template #item="{ element }">
120
+ <div class="item ux-hover nowrap r-ss" :key="element.field">
121
+ <label ui-form="@a type:checkbox" class="ny-xs nx-sl">
122
+ <input type="checkbox" :checked="!element._close" @change="element._close = element._close ? 0 : 1" />
123
+ <span>{{ element.label }}</span>
124
+ </label>
125
+ </div>
126
+ </template>
127
+ </draggable>
128
+ </template>
129
+ </IFollowView>
130
+ </div>
131
+ </th>
132
+ </tr>
133
+ </thead>
134
+ <tbody :ui-tbody-load="state.load">
135
+ <template v-if="lists.length">
136
+ <tr v-for="(item, idx) in lists" :key="idx" v-bind="rows.attrs" v-on="event(rows.event, item)">
137
+ <td ui-card="@a bk:line" class="mb-sl">
138
+ <div ui-card-body="" class="n-ss">
139
+ <ul>
140
+ <template v-for="(col, idx) in state.table" :key="col.field + idx">
141
+ <li v-if="!col._close" :class="`${col.fixed ? 'ps z-sm' : ''}`" ui-flex="row lm">
142
+ <div class="w-sl">{{ col.label }}:</div>
143
+ <TableColView :col="col" :data="item" />
144
+ </li>
145
+ </template>
146
+ <li v-if="slots.operate"><slot name="operate" :item="item" :idx="idx"></slot></li>
147
+ </ul>
148
+ </div>
149
+ </td>
150
+ </tr>
151
+ </template>
152
+ </tbody>
153
+ </table>
154
+ </div>
155
+ <div v-if="!lists.length" class="ny-ls o-mm" ui-flex="col cm">
156
+ <div class="ac">
157
+ <p><i class="icon icon-nodata fs-lm lh-ss"></i></p>
158
+ <p>{{ words['common.nodata'] || '暂无记录' }}</p>
159
+ </div>
160
+ </div>
161
+ </template>
162
+ <script setup lang="ts">
163
+ import { computed, onMounted, reactive, watch, ref, nextTick, onUnmounted, useSlots } from 'vue';
164
+ import { useRoute, useRouter } from 'vue-router';
165
+ import draggable from 'vuedraggable';
166
+ import { IFollowView, Icon } from '../../basic';
167
+ import { itable } from '@fekit/itable';
168
+ import TableColView from './TableColView.vue';
169
+ import { isFunction, isObject, deepcopy } from '@fekit/utils';
170
+ const route = useRoute();
171
+ const router = useRouter();
172
+ // 外部插槽
173
+ const slots: any = useSlots();
174
+ // 外部方法
175
+ const emits = defineEmits(['update:table', 'onsort', 'refresh']);
176
+
177
+ // 类型定义
178
+ interface JSONRules {
179
+ label: string | (() => string);
180
+ field: string | (() => string);
181
+ paths: string | (() => string);
182
+ model?: string | Array<any> | (() => string | Array<any>);
183
+ enums?: Array<any> | (() => string | Array<any>);
184
+ rules?: Array<any> | (() => string | Array<any>);
185
+ event?: Array<any> | (() => string | Array<any>);
186
+ attrs?: Array<any> | (() => string | Array<any>);
187
+ value?: any | (() => any);
188
+ clear?: boolean | (() => boolean);
189
+ }
190
+ interface Props {
191
+ words?: { [key: string]: any };
192
+ table: Array<JSONRules>;
193
+ lists: Array<any>;
194
+ showSelectAll?: boolean;
195
+ rows?: {
196
+ attrs?: { [key: string]: any };
197
+ event?: { [key: string]: any };
198
+ };
199
+ }
200
+ // 外部入参
201
+ const props: any = withDefaults(defineProps<Props>(), {
202
+ words: () => ({}),
203
+ table: () => [],
204
+ lists: () => [],
205
+ showSelectAll: true,
206
+ rows: () => ({ attrs: {}, event: {} })
207
+ });
208
+
209
+ // 内部数据
210
+ const state: any = reactive({
211
+ name: computed(() => 'confs' + route.path?.replace(/\//g, '_')),
212
+ load: 0,
213
+ tool: 1,
214
+ mode: 0,
215
+ id: 0,
216
+ limit: computed(() => {
217
+ const _cache = JSON.parse(localStorage.getItem(`${state.name}_limit`) || '{}');
218
+ return (props.limit || [])?.map((item: any) => {
219
+ if (_cache[item.field] && Object.prototype.hasOwnProperty.call(_cache[item.field], '_close')) {
220
+ item._close = _cache[item.field]?._close;
221
+ } else {
222
+ if (item.close === true || item.close === 1 || item.close === 'always') {
223
+ item._close = 1;
224
+ }
225
+ }
226
+ return item;
227
+ });
228
+ }),
229
+ table: [],
230
+ param: {},
231
+ // 列表是否全选
232
+ isAllSelect: computed(() => {
233
+ return (
234
+ props.lists.length &&
235
+ props.lists.every((item: any) => {
236
+ return item.__selected;
237
+ })
238
+ );
239
+ }),
240
+ // 列表是否有在选行
241
+ isHasSelect: computed(() => {
242
+ return props.lists.filter((item: any) => {
243
+ return item.__selected;
244
+ });
245
+ }),
246
+ bulk: computed(() => {
247
+ return props.isHasSelect.map((item: any) => {
248
+ return item.customerId;
249
+ });
250
+ }),
251
+ // 最后一列可见列
252
+ last: 0
253
+ });
254
+
255
+ watch(
256
+ () => props.table,
257
+ () => {
258
+ const _cache = JSON.parse(localStorage.getItem(`${state.name}_table`) || '{}');
259
+ state._last = 0;
260
+ state.table = deepcopy(props.table || [])
261
+ ?.map((item: any, idx: number) => {
262
+ // 缓存的显示信息
263
+ if (_cache[item.field] && Object.prototype.hasOwnProperty.call(_cache[item.field], '_close')) {
264
+ item._close = _cache[item.field]?._close;
265
+ } else {
266
+ if (item.close === true || item.close === 1 || item.close === 'always') {
267
+ item._close = 1;
268
+ }
269
+ }
270
+ // 缓存的排序信息
271
+ if (_cache[item.field] && Object.prototype.hasOwnProperty.call(_cache[item.field], '_sort')) {
272
+ item._sort = _cache[item.field]?._sort;
273
+ }
274
+ if (!item._close) {
275
+ state._last = idx;
276
+ }
277
+ return item;
278
+ })
279
+ .sort((a: any, b: any) => a._sort - b._sort)
280
+ .sort((a: any, b: any) => {
281
+ const fixedOrder: any = { 1: -1, l: -1, r: 1 };
282
+ const aFixed = a.fixed || '';
283
+ const bFixed = b.fixed || '';
284
+ return (fixedOrder[aFixed] || 0) - (fixedOrder[bFixed] || 0);
285
+ });
286
+ },
287
+ {
288
+ deep: true,
289
+ immediate: true
290
+ }
291
+ );
292
+ watch(
293
+ [() => state.table],
294
+ () => {
295
+ const _table: any = {};
296
+ state._last = 0;
297
+ state.table.forEach((item: any, idx: number) => {
298
+ _table[item.field] = { _sort: idx };
299
+ if (Object.prototype.hasOwnProperty.call(item, '_close')) {
300
+ _table[item.field]._close = item._close ? 1 : 0;
301
+ }
302
+ if (!item._close) {
303
+ state._last = idx;
304
+ }
305
+ });
306
+ console.log(338);
307
+ localStorage.setItem(`${state.name}_table`, JSON.stringify(_table));
308
+ },
309
+ { deep: true }
310
+ );
311
+
312
+ // 全选
313
+ const fSelectAll = ({ target = {} }: any = {}) => {
314
+ props.lists.map((item: any = {}) => {
315
+ item.__selected = target.checked;
316
+ });
317
+ };
318
+ const tableRef: any = ref(null);
319
+ onMounted(() => {
320
+ nextTick(() => {
321
+ if (tableRef.value) {
322
+ itable.listen(tableRef.value);
323
+ }
324
+ });
325
+ });
326
+ watch(
327
+ () => state.table,
328
+ () => {
329
+ nextTick(() => {
330
+ itable.listen(tableRef.value);
331
+ });
332
+ },
333
+ {
334
+ deep: true
335
+ }
336
+ );
337
+ onUnmounted(() => {
338
+ itable.remove();
339
+ });
340
+
341
+ // 事件处理
342
+ const event: any = (event: any, data: any): object => {
343
+ const _event: any = {};
344
+ if (isObject(event)) {
345
+ for (const key in event) {
346
+ const fun = event[key];
347
+ if (isFunction(fun)) {
348
+ _event[key] = (...args: any) => {
349
+ return fun({ data, route, router }, ...args);
350
+ };
351
+ }
352
+ }
353
+ }
354
+ return { ..._event };
355
+ };
356
+ </script>
@@ -0,0 +1,2 @@
1
+ import TableLoader from './TableLoader.vue';
2
+ export default TableLoader;