v-nuxt-ui 0.2.31 → 0.2.32
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/dist/module.json +1 -1
- package/dist/runtime/components/AsyncSelect.vue +1 -1
- package/dist/runtime/components/Select.vue +1 -1
- package/dist/runtime/components/date-picker/Input.vue +0 -1
- package/dist/runtime/components/layout/default.vue +2 -1
- package/dist/runtime/components/sys/user/Table.vue +9 -1
- package/dist/runtime/components/table/Page.vue +33 -10
- package/dist/runtime/components/table/header/index.vue +3 -0
- package/dist/runtime/components/table/index.vue +2 -0
- package/dist/runtime/components/table/query/where/Newer.vue +28 -3
- package/dist/runtime/components/table/query/where/index.vue +231 -61
- package/dist/runtime/components/table/query/where/simple/item/ColumnPicker.vue +34 -10
- package/dist/runtime/components/table/query/where/simple/item/index.d.vue.ts +1 -0
- package/dist/runtime/components/table/query/where/simple/item/index.vue +11 -2
- package/dist/runtime/components/table/query/where/simple/item/index.vue.d.ts +1 -0
- package/dist/runtime/components/table/query/where/simple/item/opr/DatePicker.vue +15 -8
- package/dist/runtime/components/table/query/where/simple/item/opr/Input.vue +3 -0
- package/dist/runtime/composables/table/useTable.js +4 -0
- package/dist/runtime/composables/table/useTableQuery.js +4 -2
- package/dist/runtime/composables/table/useTableView.d.ts +1 -0
- package/dist/runtime/composables/table/useTableView.js +1 -0
- package/dist/runtime/constants/options.js +2 -2
- package/dist/runtime/types/components/table/column.d.ts +1 -0
- package/dist/runtime/types/components/table/header.d.ts +2 -0
- package/dist/runtime/types/components/table/index.d.ts +2 -0
- package/dist/runtime/types/components/table/query/where.d.ts +1 -0
- package/dist/runtime/types/storage.d.ts +1 -0
- package/package.json +2 -2
package/dist/module.json
CHANGED
|
@@ -119,7 +119,7 @@ const onSelect = (values) => {
|
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
121
|
const ui = computed(() => ({
|
|
122
|
-
root: [
|
|
122
|
+
root: [props.roundedNone && "rounded-none"].filter(Boolean).join(" "),
|
|
123
123
|
base: "peer",
|
|
124
124
|
content: "min-w-fit",
|
|
125
125
|
tagsItem: "max-w-48 inline-flex min-w-0",
|
|
@@ -30,7 +30,7 @@ const filteredItems = computed(() => {
|
|
|
30
30
|
return props.items.filter((item) => getItemLabel(item).toLowerCase().includes(searchTerm.value.toLowerCase()));
|
|
31
31
|
});
|
|
32
32
|
const ui = computed(() => ({
|
|
33
|
-
root: [
|
|
33
|
+
root: [props.roundedNone && "rounded-none"].filter(Boolean).join(" "),
|
|
34
34
|
base: "peer",
|
|
35
35
|
content: "min-w-fit",
|
|
36
36
|
tagsInput: "min-w-4 w-0"
|
|
@@ -54,6 +54,7 @@ const columns = [
|
|
|
54
54
|
header: "\u76F4\u5C5E\u4E0A\u7EA7",
|
|
55
55
|
sortOption: true,
|
|
56
56
|
cell: ({ row }) => row.original.supervisor?.nickname,
|
|
57
|
+
preferred: false,
|
|
57
58
|
filterOption: {
|
|
58
59
|
type: "async-select",
|
|
59
60
|
listApi: useUserApi().list,
|
|
@@ -66,7 +67,8 @@ const columns = [
|
|
|
66
67
|
},
|
|
67
68
|
{
|
|
68
69
|
accessorKey: "roles",
|
|
69
|
-
header: "\
|
|
70
|
+
header: "\u6743\u9650\u89D2\u8272",
|
|
71
|
+
preferred: false,
|
|
70
72
|
cell: ({ row }) => row.original.roles?.map((role) => role.name).join(", "),
|
|
71
73
|
filterOption: {
|
|
72
74
|
type: "async-select",
|
|
@@ -97,6 +99,7 @@ const columns = [
|
|
|
97
99
|
type: "select",
|
|
98
100
|
items: loginTypeOptions
|
|
99
101
|
},
|
|
102
|
+
preferred: false,
|
|
100
103
|
meta: {
|
|
101
104
|
class: {
|
|
102
105
|
td: "min-w-24"
|
|
@@ -113,9 +116,11 @@ const columns = [
|
|
|
113
116
|
{
|
|
114
117
|
accessorKey: "needFillWh",
|
|
115
118
|
header: "\u662F\u5426\u9700\u8981\u586B\u5199\u5DE5\u65F6",
|
|
119
|
+
preferred: false,
|
|
116
120
|
filterOption: {
|
|
117
121
|
type: "select",
|
|
118
122
|
items: booleanOptions,
|
|
123
|
+
multiple: true,
|
|
119
124
|
empty: {
|
|
120
125
|
label: "\u5426",
|
|
121
126
|
variant: "outline",
|
|
@@ -146,6 +151,7 @@ const columns = [
|
|
|
146
151
|
accessorKey: "entryDate",
|
|
147
152
|
header: "\u5165\u804C\u65F6\u95F4",
|
|
148
153
|
sortOption: true,
|
|
154
|
+
preferred: false,
|
|
149
155
|
filterOption: {
|
|
150
156
|
type: "date-picker"
|
|
151
157
|
},
|
|
@@ -154,6 +160,7 @@ const columns = [
|
|
|
154
160
|
{
|
|
155
161
|
accessorKey: "resignDate",
|
|
156
162
|
header: "\u79BB\u804C\u65F6\u95F4",
|
|
163
|
+
preferred: false,
|
|
157
164
|
filterOption: {
|
|
158
165
|
type: "date-picker"
|
|
159
166
|
},
|
|
@@ -176,6 +183,7 @@ const columns = [
|
|
|
176
183
|
accessorKey: "isAdmin",
|
|
177
184
|
header: "\u662F\u5426\u662F\u7CFB\u7EDF\u7BA1\u7406\u5458",
|
|
178
185
|
sortOption: true,
|
|
186
|
+
preferred: false,
|
|
179
187
|
filterOption: {
|
|
180
188
|
type: "select",
|
|
181
189
|
items: booleanOptions
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { useId } from "vue";
|
|
2
3
|
import { useProTableView } from "#v/composables/table/useTableView";
|
|
4
|
+
import USlideover from "@nuxt/ui/components/Slideover.vue";
|
|
3
5
|
import TableHeader from "#v/components/table/header/index.vue";
|
|
4
6
|
import TableQueryWhere from "#v/components/table/query/where/index.vue";
|
|
5
7
|
import TablePagination from "#v/components/table/Pagination.vue";
|
|
@@ -25,6 +27,8 @@ const props = defineProps({
|
|
|
25
27
|
extraButtons: { type: Array, required: false },
|
|
26
28
|
onNew: { type: Function, required: false },
|
|
27
29
|
disableWhereQuery: { type: Boolean, required: false },
|
|
30
|
+
whereQueryMode: { type: String, required: false },
|
|
31
|
+
whereQueryPopoverWidth: { type: [String, Number], required: false },
|
|
28
32
|
whereQueryOpen: { type: Boolean, required: false },
|
|
29
33
|
onUpdateWhereQueryOpen: { type: Function, required: false },
|
|
30
34
|
extraWhereQueryOptions: { type: Array, required: false },
|
|
@@ -48,6 +52,7 @@ const props = defineProps({
|
|
|
48
52
|
customRowCopyFn: { type: Function, required: false },
|
|
49
53
|
displayFnInDeleteModal: { type: Function, required: false }
|
|
50
54
|
});
|
|
55
|
+
const tablePortalId = `v-table-portal-${useId().replace(/[^A-Za-z0-9_-]/g, "-")}`;
|
|
51
56
|
const {
|
|
52
57
|
// data
|
|
53
58
|
data,
|
|
@@ -77,9 +82,9 @@ defineExpose({ createRow, updateRow, deleteRow, refresh: fetchList, stats, data
|
|
|
77
82
|
</script>
|
|
78
83
|
|
|
79
84
|
<template>
|
|
80
|
-
<div class="flex-1 flex flex-col overflow-hidden">
|
|
85
|
+
<div class="flex-1 flex flex-col overflow-hidden divide-y divide-default">
|
|
81
86
|
<!-- header -->
|
|
82
|
-
<div class="h-(--ui-header-height) flex items-center pl-2.5 pr-2.5
|
|
87
|
+
<div class="h-(--ui-header-height) flex items-center pl-2.5 pr-2.5 z-3 bg-default">
|
|
83
88
|
<div class="flex items-center gap-1">
|
|
84
89
|
<LayoutButtonCollapse />
|
|
85
90
|
<span class="font-bold">{{ cnName }}</span>
|
|
@@ -87,15 +92,8 @@ defineExpose({ createRow, updateRow, deleteRow, refresh: fetchList, stats, data
|
|
|
87
92
|
<TableHeader v-bind="tblHeaderProps" class="ml-auto" />
|
|
88
93
|
</div>
|
|
89
94
|
|
|
90
|
-
<!-- query where -->
|
|
91
|
-
<UCollapsible :open="tblHeaderProps.whereQueryProps.whereQueryOpen">
|
|
92
|
-
<template #content>
|
|
93
|
-
<TableQueryWhere ref="proTableQueryWhere" v-bind="tblWhereQueryProps" class="bg-muted border-b border-default" />
|
|
94
|
-
</template>
|
|
95
|
-
</UCollapsible>
|
|
96
|
-
|
|
97
95
|
<!-- table -->
|
|
98
|
-
<div ref="table" class="flex-1 flex flex-col h-full overflow-hidden
|
|
96
|
+
<div :id="tablePortalId" ref="table" class="relative flex-1 flex flex-col h-full overflow-hidden">
|
|
99
97
|
<!-- table -->
|
|
100
98
|
<UContextMenu :items="tblContextMenuItems">
|
|
101
99
|
<ScrollArea class="flex-1">
|
|
@@ -126,5 +124,30 @@ defineExpose({ createRow, updateRow, deleteRow, refresh: fetchList, stats, data
|
|
|
126
124
|
|
|
127
125
|
<!-- pagination -->
|
|
128
126
|
<TablePagination v-bind="tblPaginationProps" />
|
|
127
|
+
|
|
128
|
+
<!-- query where (slideover from top, portalled into table) -->
|
|
129
|
+
<USlideover
|
|
130
|
+
:open="tblHeaderProps.whereQueryProps.whereQueryOpen"
|
|
131
|
+
side="top"
|
|
132
|
+
:close="false"
|
|
133
|
+
:portal="`#${tablePortalId}`"
|
|
134
|
+
:ui="{
|
|
135
|
+
content: '!absolute z-2',
|
|
136
|
+
body: 'p-0 sm:p-0',
|
|
137
|
+
overlay: 'z-1'
|
|
138
|
+
}"
|
|
139
|
+
@update:open="tblHeaderProps.whereQueryProps.onUpdateWhereQueryOpen"
|
|
140
|
+
>
|
|
141
|
+
<template #body>
|
|
142
|
+
<TableQueryWhere
|
|
143
|
+
ref="proTableQueryWhere"
|
|
144
|
+
v-bind="tblWhereQueryProps"
|
|
145
|
+
:trigger-fetching="async (fromStart) => {
|
|
146
|
+
await tblWhereQueryProps.triggerFetching(fromStart);
|
|
147
|
+
tblHeaderProps.whereQueryProps.onUpdateWhereQueryOpen?.(false);
|
|
148
|
+
}"
|
|
149
|
+
/>
|
|
150
|
+
</template>
|
|
151
|
+
</USlideover>
|
|
129
152
|
</div>
|
|
130
153
|
</template>
|
|
@@ -25,6 +25,8 @@ const props = defineProps({
|
|
|
25
25
|
displayFnInDeleteModal: { type: Function, required: false },
|
|
26
26
|
disableCreation: { type: Boolean, required: false },
|
|
27
27
|
disableWhereQuery: { type: Boolean, required: false },
|
|
28
|
+
whereQueryMode: { type: String, required: false },
|
|
29
|
+
whereQueryPopoverWidth: { type: [String, Number], required: false },
|
|
28
30
|
whereQueryProps: { type: Object, required: true },
|
|
29
31
|
disableOrderQuery: { type: Boolean, required: false },
|
|
30
32
|
orderQueryProps: { type: Object, required: true },
|
|
@@ -139,6 +141,7 @@ async function onRightExtraButtonClick(btn, e) {
|
|
|
139
141
|
刷新
|
|
140
142
|
</UButton>
|
|
141
143
|
|
|
144
|
+
<!-- whereQuery: toggle panel -->
|
|
142
145
|
<UChip
|
|
143
146
|
v-if="opr === 'whereQuery' && !disableWhereQuery"
|
|
144
147
|
:show="!whereQueryProps.isWhereQueryValueEmpty"
|
|
@@ -24,6 +24,8 @@ const props = defineProps({
|
|
|
24
24
|
extraButtons: { type: Array, required: false },
|
|
25
25
|
onNew: { type: Function, required: false },
|
|
26
26
|
disableWhereQuery: { type: Boolean, required: false },
|
|
27
|
+
whereQueryMode: { type: String, required: false },
|
|
28
|
+
whereQueryPopoverWidth: { type: [String, Number], required: false },
|
|
27
29
|
whereQueryOpen: { type: Boolean, required: false },
|
|
28
30
|
onUpdateWhereQueryOpen: { type: Function, required: false },
|
|
29
31
|
extraWhereQueryOptions: { type: Array, required: false },
|
|
@@ -10,7 +10,7 @@ const props = defineProps({
|
|
|
10
10
|
});
|
|
11
11
|
const emit = defineEmits(["new"]);
|
|
12
12
|
const popoverOpen = ref(false);
|
|
13
|
-
|
|
13
|
+
function buildFieldItem(field) {
|
|
14
14
|
const option = props.options.find((option2) => option2.field === field);
|
|
15
15
|
const column = props.bizColumns.find((column2) => column2["accessorKey"] === field);
|
|
16
16
|
return {
|
|
@@ -21,12 +21,37 @@ const unselectedOptions = computed(() => props.unselectedFields.map((field) => {
|
|
|
21
21
|
popoverOpen.value = false;
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
|
+
const unselectedOptions = computed(() => {
|
|
26
|
+
const commonFields = [];
|
|
27
|
+
const otherFields = [];
|
|
28
|
+
props.unselectedFields.forEach((field) => {
|
|
29
|
+
const option = props.options.find((opt) => opt.field === field);
|
|
30
|
+
if (option?.preferred !== false) {
|
|
31
|
+
commonFields.push(field);
|
|
32
|
+
} else {
|
|
33
|
+
otherFields.push(field);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const items = [];
|
|
37
|
+
if (commonFields.length > 0) {
|
|
38
|
+
items.push({ type: "label", label: "\u5E38\u7528\u6761\u4EF6" });
|
|
39
|
+
commonFields.forEach((field) => items.push(buildFieldItem(field)));
|
|
40
|
+
}
|
|
41
|
+
if (otherFields.length > 0) {
|
|
42
|
+
if (items.length > 0) {
|
|
43
|
+
items.push({ type: "separator" });
|
|
44
|
+
}
|
|
45
|
+
items.push({ type: "label", label: "\u5176\u4ED6\u6761\u4EF6" });
|
|
46
|
+
otherFields.forEach((field) => items.push(buildFieldItem(field)));
|
|
47
|
+
}
|
|
48
|
+
return items;
|
|
49
|
+
});
|
|
25
50
|
</script>
|
|
26
51
|
|
|
27
52
|
<template>
|
|
28
53
|
<!-- NOTE: 自己实现DropdownMenu, 原生DropdownMenu的Focus有问题,会让查询字段打开的Popover关闭 -->
|
|
29
|
-
<ButtonDropdown :items="unselectedOptions">
|
|
54
|
+
<ButtonDropdown :items="unselectedOptions" class="w-fit h-fit">
|
|
30
55
|
<UButton
|
|
31
56
|
label="新增"
|
|
32
57
|
:size="size"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, ref, nextTick } from "vue";
|
|
2
|
+
import { computed, ref, reactive, watch, nextTick } from "vue";
|
|
3
3
|
import { useTableOpr } from "#v/composables/table/useTableOpr";
|
|
4
4
|
import { useToast } from "@nuxt/ui/composables";
|
|
5
|
+
import UFieldGroup from "@nuxt/ui/components/FieldGroup.vue";
|
|
6
|
+
import UDropdownMenu from "@nuxt/ui/components/DropdownMenu.vue";
|
|
7
|
+
import Dnd from "#v/components/Dnd.client.vue";
|
|
5
8
|
import TableQueryWhereSimpleItem from "#v/components/table/query/where/simple/item/index.vue";
|
|
6
9
|
import TableQueryWhereNewer from "#v/components/table/query/where/Newer.vue";
|
|
7
10
|
const props = defineProps({
|
|
@@ -24,6 +27,16 @@ const selectedWhereFields = computed(() => {
|
|
|
24
27
|
const unselectedWhereFields = computed(() => {
|
|
25
28
|
return props.whereOptions.map((option) => option.field).filter((field) => !selectedWhereFields.value.includes(field));
|
|
26
29
|
});
|
|
30
|
+
const unselectedPreferredFields = computed(
|
|
31
|
+
() => unselectedWhereFields.value.filter(
|
|
32
|
+
(field) => props.whereOptions.find((opt) => opt.field === field)?.preferred !== false
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
const unselectedOtherFields = computed(
|
|
36
|
+
() => unselectedWhereFields.value.filter(
|
|
37
|
+
(field) => props.whereOptions.find((opt) => opt.field === field)?.preferred === false
|
|
38
|
+
)
|
|
39
|
+
);
|
|
27
40
|
const itemRefMap = ref(/* @__PURE__ */ new Map());
|
|
28
41
|
function setItemRef(field, el) {
|
|
29
42
|
if (el && "focus" in el && typeof el.focus === "function") {
|
|
@@ -63,18 +76,133 @@ const onNewField = (field) => {
|
|
|
63
76
|
}
|
|
64
77
|
});
|
|
65
78
|
};
|
|
66
|
-
const
|
|
67
|
-
return !props.whereQuery || Object.keys(props.whereQuery ?? {}).length === 0 || // 仅检查有对应option的item的值是否为空
|
|
68
|
-
(props.whereQuery?.items?.filter((query) => props.whereOptions.find((option) => option.field === query.field)).length ?? 0) === 0 && (props.whereQuery?.groups?.length ?? 0) === 0;
|
|
69
|
-
});
|
|
79
|
+
const getDefaultKeys = () => props.extraWhereQueryInitValues?.items?.map((query) => query.field) ?? [];
|
|
70
80
|
const whereQueryWithoutInitValues = computed(() => {
|
|
71
81
|
if (!props.whereQuery) return [];
|
|
72
|
-
const defaultKeys =
|
|
82
|
+
const defaultKeys = getDefaultKeys();
|
|
73
83
|
return props.whereQuery.items?.filter((query) => {
|
|
74
84
|
const field = query.field;
|
|
75
85
|
return !defaultKeys.includes(field);
|
|
76
86
|
}) ?? [];
|
|
77
87
|
});
|
|
88
|
+
const whereQuerySectionExtraDataKey = "__whereQuerySection";
|
|
89
|
+
function getBaseItemSection(item) {
|
|
90
|
+
return props.whereOptions.find((opt) => opt.field === item.field)?.preferred === false ? "other" : "preferred";
|
|
91
|
+
}
|
|
92
|
+
function getItemSectionOverride(item) {
|
|
93
|
+
if (!item.extraData || typeof item.extraData !== "object" || Array.isArray(item.extraData)) return void 0;
|
|
94
|
+
const section = item.extraData[whereQuerySectionExtraDataKey];
|
|
95
|
+
return section === "preferred" || section === "other" ? section : void 0;
|
|
96
|
+
}
|
|
97
|
+
function getItemSection(item) {
|
|
98
|
+
return getItemSectionOverride(item) ?? getBaseItemSection(item);
|
|
99
|
+
}
|
|
100
|
+
function isPreferredItem(item) {
|
|
101
|
+
return getItemSection(item) === "preferred";
|
|
102
|
+
}
|
|
103
|
+
function setItemSection(item, section) {
|
|
104
|
+
const baseSection = getBaseItemSection(item);
|
|
105
|
+
if (item.extraData && (typeof item.extraData !== "object" || Array.isArray(item.extraData))) {
|
|
106
|
+
return item;
|
|
107
|
+
}
|
|
108
|
+
const extraData = item.extraData ? { ...item.extraData } : {};
|
|
109
|
+
if (section === baseSection) {
|
|
110
|
+
const { [whereQuerySectionExtraDataKey]: _section, ...restExtraData } = extraData;
|
|
111
|
+
return {
|
|
112
|
+
...item,
|
|
113
|
+
extraData: Object.keys(restExtraData).length > 0 ? restExtraData : void 0
|
|
114
|
+
};
|
|
115
|
+
} else {
|
|
116
|
+
;
|
|
117
|
+
extraData[whereQuerySectionExtraDataKey] = section;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
...item,
|
|
121
|
+
extraData: Object.keys(extraData).length > 0 ? extraData : void 0
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const preferredItems = computed(
|
|
125
|
+
() => whereQueryWithoutInitValues.value.filter(isPreferredItem)
|
|
126
|
+
);
|
|
127
|
+
const otherItems = computed(
|
|
128
|
+
() => whereQueryWithoutInitValues.value.filter((item) => !isPreferredItem(item))
|
|
129
|
+
);
|
|
130
|
+
const preferredDndItems = ref([]);
|
|
131
|
+
const otherDndItems = ref([]);
|
|
132
|
+
watch([preferredItems, otherItems], () => {
|
|
133
|
+
preferredDndItems.value = [...preferredItems.value];
|
|
134
|
+
otherDndItems.value = [...otherItems.value];
|
|
135
|
+
}, { immediate: true });
|
|
136
|
+
function onDndEnd() {
|
|
137
|
+
const defaultKeys = getDefaultKeys();
|
|
138
|
+
const initItems = props.whereQuery?.items?.filter((q) => defaultKeys.includes(q.field)) ?? [];
|
|
139
|
+
props.onUpdateWhereQuery({
|
|
140
|
+
...props.whereQuery,
|
|
141
|
+
items: [
|
|
142
|
+
...initItems,
|
|
143
|
+
...preferredDndItems.value.map((item) => setItemSection(item, "preferred")),
|
|
144
|
+
...otherDndItems.value.map((item) => setItemSection(item, "other"))
|
|
145
|
+
]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function onUpdateWhereQueryItem(field, newWhereQueryItem) {
|
|
149
|
+
const items = props.whereQuery?.items ?? [];
|
|
150
|
+
const realIdx = items.findIndex((query) => query.field === field);
|
|
151
|
+
if (realIdx === -1) return;
|
|
152
|
+
const currentItem = items[realIdx];
|
|
153
|
+
if (!currentItem) return;
|
|
154
|
+
const currentSection = getItemSection(currentItem);
|
|
155
|
+
const updatedItems = [...items];
|
|
156
|
+
updatedItems[realIdx] = setItemSection(newWhereQueryItem, currentSection);
|
|
157
|
+
props.onUpdateWhereQuery({
|
|
158
|
+
...props.whereQuery,
|
|
159
|
+
items: updatedItems
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const rangeOprList = ["range_gt_lt", "range_gt_lte", "range_gte_lt", "range_gte_lte"];
|
|
163
|
+
const isDateRangeQueryItem = (item) => {
|
|
164
|
+
const option = props.whereOptions.find((option2) => option2.field === item.field);
|
|
165
|
+
return option?.type === "date-picker" && rangeOprList.includes(item.opr);
|
|
166
|
+
};
|
|
167
|
+
const onClearValues = () => {
|
|
168
|
+
if (!props.whereQuery?.items) return;
|
|
169
|
+
props.onUpdateWhereQuery({
|
|
170
|
+
...props.whereQuery,
|
|
171
|
+
items: props.whereQuery.items.map((item) => ({ ...item, value: null }))
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
const onResetAll = () => {
|
|
175
|
+
props.onUpdateWhereQuery(props.defaultWhereQuery);
|
|
176
|
+
};
|
|
177
|
+
const onFillMissingFields = () => {
|
|
178
|
+
const currentFields = new Set((props.whereQuery?.items ?? []).map((item) => item.field));
|
|
179
|
+
const missingOptions = props.whereOptions.filter((opt) => !currentFields.has(opt.field));
|
|
180
|
+
if (missingOptions.length === 0) return;
|
|
181
|
+
const preferred = missingOptions.filter((opt) => opt.preferred !== false);
|
|
182
|
+
const other = missingOptions.filter((opt) => opt.preferred === false);
|
|
183
|
+
const newItems = [...preferred, ...other].map((opt) => ({
|
|
184
|
+
field: opt.field,
|
|
185
|
+
opr: opt.defaultOpr ?? useTableOpr().getDefaultOprByType(opt.type),
|
|
186
|
+
value: null,
|
|
187
|
+
custom: opt.custom
|
|
188
|
+
}));
|
|
189
|
+
props.onUpdateWhereQuery({
|
|
190
|
+
...props.whereQuery,
|
|
191
|
+
items: [...props.whereQuery?.items ?? [], ...newItems]
|
|
192
|
+
});
|
|
193
|
+
};
|
|
194
|
+
const onRemoveAllFields = () => {
|
|
195
|
+
const defaultKeys = props.extraWhereQueryInitValues?.items?.map((q) => q.field) ?? [];
|
|
196
|
+
const initItems = props.whereQuery?.items?.filter((q) => defaultKeys.includes(q.field)) ?? [];
|
|
197
|
+
props.onUpdateWhereQuery({
|
|
198
|
+
...props.whereQuery,
|
|
199
|
+
items: initItems.map((item) => ({ ...item, value: null }))
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
const moreActions = computed(() => [
|
|
203
|
+
{ label: "\u8865\u5168\u5B57\u6BB5", icon: "i-lucide-list-plus", onSelect: onFillMissingFields },
|
|
204
|
+
{ label: "\u5220\u9664\u6240\u6709\u5B57\u6BB5", icon: "i-lucide-trash-2", onSelect: onRemoveAllFields }
|
|
205
|
+
]);
|
|
78
206
|
const focusField = (field) => {
|
|
79
207
|
const item = itemRefMap.value.get(field);
|
|
80
208
|
if (item) {
|
|
@@ -83,67 +211,109 @@ const focusField = (field) => {
|
|
|
83
211
|
}
|
|
84
212
|
return false;
|
|
85
213
|
};
|
|
214
|
+
const conditionListClass = "grid grid-cols-24 gap-3";
|
|
215
|
+
const sections = reactive([
|
|
216
|
+
{ key: "preferred", label: "\u5E38\u7528\u6761\u4EF6", dndItems: preferredDndItems, unselectedFields: unselectedPreferredFields },
|
|
217
|
+
{ key: "other", label: "\u5176\u4ED6\u6761\u4EF6", dndItems: otherDndItems, unselectedFields: unselectedOtherFields }
|
|
218
|
+
]);
|
|
86
219
|
defineExpose({ focusField });
|
|
87
220
|
</script>
|
|
88
221
|
|
|
89
222
|
<template>
|
|
90
|
-
<div class="
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
223
|
+
<div class="divide-y divide-default">
|
|
224
|
+
<div class="@container p-2.5 space-y-4">
|
|
225
|
+
<div
|
|
226
|
+
v-for="section in sections"
|
|
227
|
+
:key="section.key"
|
|
228
|
+
>
|
|
229
|
+
<div class="font-bold text-xs text-dimmed mb-1.5">
|
|
230
|
+
{{ section.label }}
|
|
231
|
+
</div>
|
|
232
|
+
<Dnd
|
|
233
|
+
v-model="section.dndItems"
|
|
234
|
+
group="where-query"
|
|
235
|
+
handle=".where-query-handle"
|
|
236
|
+
:on-end="onDndEnd"
|
|
237
|
+
:class="conditionListClass"
|
|
238
|
+
>
|
|
239
|
+
<div
|
|
240
|
+
v-for="item in section.dndItems"
|
|
241
|
+
:key="item.field"
|
|
242
|
+
class="flex items-center gap-1 col-span-24 @3xl:col-span-12 @5xl:col-span-8 @7xl:col-span-6"
|
|
243
|
+
:class="isDateRangeQueryItem(item) ? '@3xl:col-span-24 @5xl:col-span-12 @7xl:col-span-8' : void 0"
|
|
244
|
+
>
|
|
245
|
+
<TableQueryWhereSimpleItem
|
|
246
|
+
:ref="(el) => setItemRef(item.field, el)"
|
|
247
|
+
:where-query-item="item"
|
|
248
|
+
:options="whereOptions"
|
|
249
|
+
:fetching="fetching"
|
|
250
|
+
:trigger-fetching="() => triggerFetching(true)"
|
|
251
|
+
handle-class-name="where-query-handle"
|
|
252
|
+
@remove="onRemoveFilter"
|
|
253
|
+
@update:where-query-item="(newWhereQueryItem) => onUpdateWhereQueryItem(item.field, newWhereQueryItem)"
|
|
254
|
+
/>
|
|
255
|
+
</div>
|
|
256
|
+
<div class="col-span-24 @3xl:col-span-12 @5xl:col-span-8 @7xl:col-span-6">
|
|
257
|
+
<TableQueryWhereNewer
|
|
258
|
+
v-if="unselectedWhereFields.length > 0"
|
|
259
|
+
:options="whereOptions"
|
|
260
|
+
:unselected-fields="unselectedWhereFields"
|
|
261
|
+
:biz-columns="bizColumns ?? []"
|
|
262
|
+
size="sm"
|
|
263
|
+
@new="onNewField"
|
|
264
|
+
/>
|
|
265
|
+
</div>
|
|
266
|
+
</Dnd>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
<!-- action bar -->
|
|
270
|
+
<div class="flex items-center gap-2.5 p-2.5">
|
|
271
|
+
<div class="flex-1" />
|
|
272
|
+
<div class="flex items-center gap-2.5">
|
|
273
|
+
<UButton
|
|
274
|
+
v-if="!hideQueryButton"
|
|
275
|
+
label="查询"
|
|
276
|
+
color="neutral"
|
|
277
|
+
variant="subtle"
|
|
278
|
+
size="sm"
|
|
279
|
+
:loading="fetching"
|
|
280
|
+
icon="i-lucide-search"
|
|
281
|
+
@click="async () => {
|
|
134
282
|
await triggerFetching(true);
|
|
135
283
|
}"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
284
|
+
/>
|
|
285
|
+
<UButton
|
|
286
|
+
color="neutral"
|
|
287
|
+
variant="subtle"
|
|
288
|
+
size="sm"
|
|
289
|
+
icon="i-lucide-eraser"
|
|
290
|
+
:disabled="fetching"
|
|
291
|
+
@click="onClearValues"
|
|
292
|
+
>
|
|
293
|
+
清空
|
|
294
|
+
</UButton>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="flex-1 flex justify-end items-center">
|
|
297
|
+
<UFieldGroup size="sm">
|
|
298
|
+
<UButton
|
|
299
|
+
color="neutral"
|
|
300
|
+
variant="subtle"
|
|
301
|
+
icon="i-lucide-timer-reset"
|
|
302
|
+
:disabled="fetching"
|
|
303
|
+
@click="onResetAll"
|
|
304
|
+
>
|
|
305
|
+
还原默认
|
|
306
|
+
</UButton>
|
|
307
|
+
<UDropdownMenu :items="moreActions">
|
|
308
|
+
<UButton
|
|
309
|
+
color="neutral"
|
|
310
|
+
variant="subtle"
|
|
311
|
+
icon="i-lucide-ellipsis"
|
|
312
|
+
:disabled="fetching"
|
|
313
|
+
/>
|
|
314
|
+
</UDropdownMenu>
|
|
315
|
+
</UFieldGroup>
|
|
316
|
+
</div>
|
|
147
317
|
</div>
|
|
148
318
|
</div>
|
|
149
319
|
</template>
|
|
@@ -9,17 +9,41 @@ const props = defineProps({
|
|
|
9
9
|
focus: { type: Function, required: false }
|
|
10
10
|
});
|
|
11
11
|
const whereQueryItem = defineModel("whereQueryItem", { type: Object, ...{ required: true } });
|
|
12
|
-
const items = computed(() =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const items = computed(() => {
|
|
13
|
+
const commonItems = [];
|
|
14
|
+
const otherItems = [];
|
|
15
|
+
props.options.forEach((option) => {
|
|
16
|
+
const item = {
|
|
17
|
+
label: option.label,
|
|
18
|
+
value: option.field,
|
|
19
|
+
icon: tableWhereQueryItemIconMap.get(option.type) || "field",
|
|
20
|
+
onSelect: () => {
|
|
21
|
+
modelValue.value = option.field;
|
|
22
|
+
nextTick(() => {
|
|
23
|
+
props.focus?.();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
if (option.preferred === true) {
|
|
28
|
+
commonItems.push(item);
|
|
29
|
+
} else {
|
|
30
|
+
otherItems.push(item);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
const result = [];
|
|
34
|
+
if (commonItems.length > 0) {
|
|
35
|
+
result.push({ type: "label", label: "\u5E38\u7528\u6761\u4EF6" });
|
|
36
|
+
result.push(...commonItems);
|
|
21
37
|
}
|
|
22
|
-
|
|
38
|
+
if (otherItems.length > 0) {
|
|
39
|
+
if (result.length > 0) {
|
|
40
|
+
result.push({ type: "separator" });
|
|
41
|
+
}
|
|
42
|
+
result.push({ type: "label", label: "\u5176\u4ED6\u6761\u4EF6" });
|
|
43
|
+
result.push(...otherItems);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
});
|
|
23
47
|
const modelValue = computed({
|
|
24
48
|
get() {
|
|
25
49
|
return whereQueryItem.value.field;
|
|
@@ -7,7 +7,8 @@ const props = defineProps({
|
|
|
7
7
|
options: { type: Array, required: true },
|
|
8
8
|
fetching: { type: Boolean, required: false },
|
|
9
9
|
triggerFetching: { type: Function, required: false },
|
|
10
|
-
onRemove: { type: Function, required: true }
|
|
10
|
+
onRemove: { type: Function, required: true },
|
|
11
|
+
handleClassName: { type: String, required: false }
|
|
11
12
|
});
|
|
12
13
|
const whereQueryItem = defineModel("whereQueryItem", { type: Object, ...{ required: true } });
|
|
13
14
|
const option = computed(() => props.options.find((option2) => option2.field === whereQueryItem.value.field));
|
|
@@ -26,11 +27,18 @@ defineExpose({
|
|
|
26
27
|
</script>
|
|
27
28
|
|
|
28
29
|
<template>
|
|
29
|
-
<UFieldGroup size="sm">
|
|
30
|
+
<UFieldGroup size="sm" class="w-full">
|
|
31
|
+
<UButton
|
|
32
|
+
variant="outline"
|
|
33
|
+
icon="i-lucide-grip-vertical"
|
|
34
|
+
color="neutral"
|
|
35
|
+
:class="handleClassName"
|
|
36
|
+
/>
|
|
30
37
|
<TableQueryWhereSimpleItemColumnPicker
|
|
31
38
|
v-model:where-query-item="whereQueryItem"
|
|
32
39
|
:options="options"
|
|
33
40
|
:fetching="fetching"
|
|
41
|
+
disabled
|
|
34
42
|
:focus="() => oprRef?.focus()"
|
|
35
43
|
/>
|
|
36
44
|
<TableQueryWhereSimpleItemOprPicker
|
|
@@ -46,6 +54,7 @@ defineExpose({
|
|
|
46
54
|
:options="options"
|
|
47
55
|
:fetching="fetching"
|
|
48
56
|
:trigger-fetching="triggerFetching"
|
|
57
|
+
class="w-full"
|
|
49
58
|
/>
|
|
50
59
|
<UButton
|
|
51
60
|
color="neutral"
|
|
@@ -153,14 +153,16 @@ const dateRangeShortcuts = [
|
|
|
153
153
|
<!-- 输入框直接暴露 -->
|
|
154
154
|
<template v-if="isNoCalendarOpr" />
|
|
155
155
|
|
|
156
|
-
<
|
|
157
|
-
|
|
156
|
+
<UFieldGroup
|
|
157
|
+
v-else-if="isRangeOpr"
|
|
158
|
+
class="w-full"
|
|
159
|
+
>
|
|
158
160
|
<div />
|
|
159
161
|
<DatePickerInput
|
|
160
162
|
ref="startDateStrValueInput"
|
|
161
163
|
v-model:value="startDateStrValue"
|
|
162
164
|
icon=""
|
|
163
|
-
input-class="w-
|
|
165
|
+
input-class="min-w-28 flex-1"
|
|
164
166
|
placeholder="YYYY-MM-DD"
|
|
165
167
|
@focus="onOpenCalendar"
|
|
166
168
|
@blur="onCloseCalendar"
|
|
@@ -172,25 +174,30 @@ const dateRangeShortcuts = [
|
|
|
172
174
|
ref="endDateStrValueInput"
|
|
173
175
|
v-model:value="endDateStrValue"
|
|
174
176
|
icon=""
|
|
175
|
-
input-class="w-
|
|
177
|
+
input-class="min-w-28 flex-1"
|
|
176
178
|
placeholder="YYYY-MM-DD"
|
|
177
179
|
@focus="onOpenCalendar"
|
|
178
180
|
@blur="onCloseCalendar"
|
|
179
181
|
/>
|
|
180
|
-
|
|
182
|
+
<div />
|
|
183
|
+
</UFieldGroup>
|
|
181
184
|
|
|
182
|
-
<
|
|
185
|
+
<div
|
|
186
|
+
v-else
|
|
187
|
+
class="w-full"
|
|
188
|
+
>
|
|
183
189
|
<div />
|
|
184
190
|
<DatePickerInput
|
|
185
191
|
ref="singleDateStrValueInput"
|
|
186
192
|
v-model:value="singleDateStrValue"
|
|
187
193
|
icon=""
|
|
188
|
-
input-class="w-32"
|
|
194
|
+
input-class="w-full min-w-32"
|
|
189
195
|
placeholder="YYYY-MM-DD"
|
|
190
196
|
@focus="onOpenCalendar"
|
|
191
197
|
@blur="onCloseCalendar"
|
|
192
198
|
/>
|
|
193
|
-
|
|
199
|
+
<div />
|
|
200
|
+
</div>
|
|
194
201
|
|
|
195
202
|
<template #content>
|
|
196
203
|
<div
|
|
@@ -25,6 +25,8 @@ export function useTable(props) {
|
|
|
25
25
|
onNew,
|
|
26
26
|
// where query
|
|
27
27
|
disableWhereQuery,
|
|
28
|
+
whereQueryMode,
|
|
29
|
+
whereQueryPopoverWidth,
|
|
28
30
|
extraWhereQueryOptions,
|
|
29
31
|
extraWhereQueryInitValues,
|
|
30
32
|
// order query
|
|
@@ -316,6 +318,8 @@ export function useTable(props) {
|
|
|
316
318
|
displayFnInDeleteModal,
|
|
317
319
|
disableCreation,
|
|
318
320
|
disableWhereQuery,
|
|
321
|
+
whereQueryMode,
|
|
322
|
+
whereQueryPopoverWidth,
|
|
319
323
|
whereQueryProps: tblWhereQueryProps.value,
|
|
320
324
|
disableOrderQuery,
|
|
321
325
|
orderQueryProps: tblOrderQueryProps.value,
|
|
@@ -10,6 +10,7 @@ export function useTableQuery(props) {
|
|
|
10
10
|
const options = bizColumns.filter((col) => col.filterOption).map((col) => ({
|
|
11
11
|
field: col.accessorKey,
|
|
12
12
|
label: col.header,
|
|
13
|
+
preferred: col.preferred,
|
|
13
14
|
...col.filterOption
|
|
14
15
|
}));
|
|
15
16
|
extraWhereQueryOptions?.forEach((option) => {
|
|
@@ -19,7 +20,7 @@ export function useTableQuery(props) {
|
|
|
19
20
|
});
|
|
20
21
|
const whereQueryInitValues = computed(() => {
|
|
21
22
|
const initValues = {
|
|
22
|
-
items: whereQueryOptions.value.
|
|
23
|
+
items: whereQueryOptions.value.map((option) => ({
|
|
23
24
|
field: option.field,
|
|
24
25
|
opr: option.defaultOpr ?? useTableOpr().getDefaultOprByType(option.type),
|
|
25
26
|
value: option.initValues ?? null,
|
|
@@ -46,7 +47,8 @@ export function useTableQuery(props) {
|
|
|
46
47
|
() => bizColumns.map((col) => ({
|
|
47
48
|
accessorKey: col["accessorKey"],
|
|
48
49
|
fixed: "unfixed",
|
|
49
|
-
checked: !col.initHide
|
|
50
|
+
checked: !col.initHide,
|
|
51
|
+
preferred: col.preferred
|
|
50
52
|
}))
|
|
51
53
|
);
|
|
52
54
|
const localStgSettings = useLocalStorage(`${name}-table-settings`, {
|
|
@@ -16,6 +16,7 @@ export interface UseProTableViewReturn<T> {
|
|
|
16
16
|
tblPaginationProps: ComputedRef<TablePaginationProps<T>>;
|
|
17
17
|
tblContextMenuItems: Ref<ContextMenuItem[]>;
|
|
18
18
|
tableWidth: Ref<number>;
|
|
19
|
+
tableDiv: Ref<HTMLDivElement | null>;
|
|
19
20
|
updateTableWidth: () => void;
|
|
20
21
|
tblClasses: ComputedRef<(string | boolean | (string | boolean)[])[]>;
|
|
21
22
|
tblUi: ComputedRef<{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const booleanOptions = [
|
|
2
|
-
{ label: "
|
|
3
|
-
{ label: "
|
|
2
|
+
{ label: "\u662F", value: true },
|
|
3
|
+
{ label: "\u5426", value: false }
|
|
4
4
|
];
|
|
5
5
|
export var Gender = /* @__PURE__ */ ((Gender2) => {
|
|
6
6
|
Gender2[Gender2["MALE"] = 1] = "MALE";
|
|
@@ -17,6 +17,8 @@ export type TableHeaderProps<T> = {
|
|
|
17
17
|
displayFnInDeleteModal?: (model: T) => string | undefined;
|
|
18
18
|
disableCreation?: boolean;
|
|
19
19
|
disableWhereQuery?: boolean;
|
|
20
|
+
whereQueryMode?: 'inline' | 'popover';
|
|
21
|
+
whereQueryPopoverWidth?: string | number;
|
|
20
22
|
whereQueryProps: WhereQueryProps<T>;
|
|
21
23
|
disableOrderQuery?: boolean;
|
|
22
24
|
orderQueryProps: OrderQueryProps<T>;
|
|
@@ -82,6 +82,8 @@ export type VTableProps<T> = {
|
|
|
82
82
|
extraButtons?: TableHeaderProps<T>['extraButtons'];
|
|
83
83
|
onNew?: TableHeaderProps<T>['onNew'];
|
|
84
84
|
disableWhereQuery?: TableHeaderProps<T>['disableWhereQuery'];
|
|
85
|
+
whereQueryMode?: 'inline' | 'popover';
|
|
86
|
+
whereQueryPopoverWidth?: string | number;
|
|
85
87
|
whereQueryOpen?: boolean;
|
|
86
88
|
onUpdateWhereQueryOpen?: (open: boolean) => void;
|
|
87
89
|
extraWhereQueryOptions?: WhereQueryOption<T>[];
|
|
@@ -2,6 +2,7 @@ import type { VColumn, WhereQuery, WhereQueryColumnOption, WhereQueryItemGroup }
|
|
|
2
2
|
export type WhereQueryOption<T> = {
|
|
3
3
|
field: string & keyof T | string;
|
|
4
4
|
label: string;
|
|
5
|
+
preferred?: boolean;
|
|
5
6
|
} & WhereQueryColumnOption<T>;
|
|
6
7
|
export type WhereQueryProps<T> = {
|
|
7
8
|
whereOptions: WhereQueryOption<T>[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "v-nuxt-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.32",
|
|
4
4
|
"description": "Veken UI Component Library - Reusable Nuxt UI components, composables, and utilities for enterprise applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"style": "./dist/runtime/index.css",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@nuxt/eslint-config": "^1.15.2",
|
|
73
73
|
"@nuxt/fonts": "^0.12.1",
|
|
74
74
|
"@nuxt/module-builder": "^1.0.2",
|
|
75
|
-
"@nuxt/ui": "^4.
|
|
75
|
+
"@nuxt/ui": "^4.8.1",
|
|
76
76
|
"@nuxtjs/i18n": "^10.3.0",
|
|
77
77
|
"@nuxtjs/mdc": "^0.20.2",
|
|
78
78
|
"@tanstack/table-core": "^8.21.3",
|