v-nuxt-ui 0.2.25 → 0.2.27
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/README.md +6 -0
- package/dist/module.json +1 -1
- package/dist/runtime/components/AsyncSelect.vue +16 -1
- package/dist/runtime/components/AsyncTreeSelect.vue +15 -9
- package/dist/runtime/components/DeleteModal.d.vue.ts +25 -22
- package/dist/runtime/components/DeleteModal.vue +39 -4
- package/dist/runtime/components/DeleteModal.vue.d.ts +25 -22
- package/dist/runtime/components/ScrollArea.vue +20 -9
- package/dist/runtime/components/form/save-modal-template/ConfirmUpdateModal.d.vue.ts +19 -0
- package/dist/runtime/components/form/save-modal-template/ConfirmUpdateModal.vue +111 -0
- package/dist/runtime/components/form/save-modal-template/ConfirmUpdateModal.vue.d.ts +19 -0
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.d.vue.ts +2 -2
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.vue +4 -3
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.vue.d.ts +2 -2
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.d.vue.ts +2 -2
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.vue +54 -7
- package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.vue.d.ts +2 -2
- package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.d.vue.ts +19 -0
- package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.vue +111 -0
- package/dist/runtime/components/form/save-model-template/ConfirmUpdateModal.vue.d.ts +19 -0
- package/dist/runtime/components/form/save-model-template/WithApi.d.vue.ts +19 -0
- package/dist/runtime/components/form/save-model-template/WithApi.vue +37 -0
- package/dist/runtime/components/form/save-model-template/WithApi.vue.d.ts +19 -0
- package/dist/runtime/components/form/save-model-template/index.d.vue.ts +21 -0
- package/dist/runtime/components/form/save-model-template/index.vue +123 -0
- package/dist/runtime/components/form/save-model-template/index.vue.d.ts +21 -0
- package/dist/runtime/components/layout/button/UserMenu.vue +1 -2
- package/dist/runtime/components/sys/company/{CreateModal.vue → SaveModal.vue} +2 -2
- package/dist/runtime/components/sys/company/Table.vue +5 -4
- package/dist/runtime/components/sys/department/{CreateModal.vue → SaveModal.vue} +3 -2
- package/dist/runtime/components/sys/department/Table.vue +6 -5
- package/dist/runtime/components/sys/flow/EditNodeModal.vue +4 -3
- package/dist/runtime/components/sys/flow/{CreateModal.vue → SaveModal.vue} +3 -2
- package/dist/runtime/components/sys/flow/Table.vue +5 -4
- package/dist/runtime/components/sys/issue-record/{CreateModal.vue → SaveModal.vue} +2 -2
- package/dist/runtime/components/sys/issue-record/Table.vue +5 -4
- package/dist/runtime/components/sys/job-title/{CreateModal.vue → SaveModal.vue} +2 -2
- package/dist/runtime/components/sys/job-title/Table.vue +5 -4
- package/dist/runtime/components/sys/menu/{CreateModal.vue → SaveModal.vue} +3 -2
- package/dist/runtime/components/sys/menu/Table.vue +6 -5
- package/dist/runtime/components/sys/role/{CreateModal.vue → SaveModal.vue} +8 -5
- package/dist/runtime/components/sys/role/Table.vue +5 -4
- package/dist/runtime/components/sys/table/{CreateModal.vue → SaveModal.vue} +4 -3
- package/dist/runtime/components/sys/table/Table.vue +4 -4
- package/dist/runtime/components/sys/table/TableColumnModal.vue +4 -3
- package/dist/runtime/components/sys/user/{CreateModal.vue → SaveModal.vue} +4 -3
- package/dist/runtime/components/sys/user/Table.vue +7 -6
- package/dist/runtime/components/table/Page.vue +2 -1
- package/dist/runtime/components/table/UpdateDiffModal.d.vue.ts +14 -0
- package/dist/runtime/components/table/UpdateDiffModal.vue +156 -0
- package/dist/runtime/components/table/UpdateDiffModal.vue.d.ts +14 -0
- package/dist/runtime/components/table/header/index.vue +181 -183
- package/dist/runtime/components/table/index.vue +2 -1
- package/dist/runtime/composables/form/index.d.ts +2 -0
- package/dist/runtime/composables/form/index.js +2 -0
- package/dist/runtime/composables/form/useForm.d.ts +21 -0
- package/dist/runtime/composables/{useForm.js → form/useForm.js} +23 -1
- package/dist/runtime/composables/form/useFormUpdate.d.ts +6 -0
- package/dist/runtime/composables/form/useFormUpdate.js +101 -0
- package/dist/runtime/composables/index.d.ts +1 -1
- package/dist/runtime/composables/index.js +1 -1
- package/dist/runtime/composables/table/useTable.js +10 -1
- package/dist/runtime/composables/table/useTableRowActions.d.ts +3 -0
- package/dist/runtime/composables/table/useTableRowActions.js +24 -0
- package/dist/runtime/composables/useDate.js +2 -0
- package/dist/runtime/types/components/form/field.d.ts +2 -0
- package/dist/runtime/types/components/form/index.d.ts +4 -3
- package/dist/runtime/types/components/table/header.d.ts +2 -0
- package/dist/runtime/types/components/table/index.d.ts +1 -0
- package/dist/runtime/types/time.d.ts +1 -1
- package/dist/runtime/utils/cron.d.ts +1 -0
- package/dist/runtime/utils/cron.js +182 -0
- package/dist/runtime/utils/index.d.ts +1 -0
- package/dist/runtime/utils/index.js +1 -0
- package/dist/runtime/utils/vueuse.d.ts +1 -1
- package/dist/runtime/utils/vueuse.js +1 -0
- package/package.json +16 -15
- package/dist/runtime/composables/useForm.d.ts +0 -9
- /package/dist/runtime/components/sys/company/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/company/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/department/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/department/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/flow/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/flow/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/issue-record/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/issue-record/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/job-title/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/job-title/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/menu/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/menu/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/role/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/role/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/table/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/table/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
- /package/dist/runtime/components/sys/user/{CreateModal.d.vue.ts → SaveModal.d.vue.ts} +0 -0
- /package/dist/runtime/components/sys/user/{CreateModal.vue.d.ts → SaveModal.vue.d.ts} +0 -0
package/README.md
CHANGED
package/dist/module.json
CHANGED
|
@@ -122,6 +122,8 @@ const ui = computed(() => ({
|
|
|
122
122
|
root: ["min-w-32", props.roundedNone && "rounded-none"].filter(Boolean).join(" "),
|
|
123
123
|
base: "peer",
|
|
124
124
|
content: "min-w-fit",
|
|
125
|
+
tagsItem: "max-w-48 inline-flex min-w-0",
|
|
126
|
+
tagsItemText: "truncate",
|
|
125
127
|
tagsInput: "min-w-4 w-0"
|
|
126
128
|
}));
|
|
127
129
|
const dropdownOpen = ref(false);
|
|
@@ -138,6 +140,13 @@ defineExpose({
|
|
|
138
140
|
inputMenuRef.value?.inputRef.focus();
|
|
139
141
|
}
|
|
140
142
|
});
|
|
143
|
+
function findLabel(tagItem) {
|
|
144
|
+
if (tagItem && typeof tagItem === "object" && "label" in tagItem) {
|
|
145
|
+
return String(tagItem.label ?? "");
|
|
146
|
+
}
|
|
147
|
+
const match = items.value.find((i) => i != null && String(i.value) === String(tagItem));
|
|
148
|
+
return match?.label ?? String(tagItem ?? "");
|
|
149
|
+
}
|
|
141
150
|
</script>
|
|
142
151
|
|
|
143
152
|
<template>
|
|
@@ -176,5 +185,11 @@ defineExpose({
|
|
|
176
185
|
}"
|
|
177
186
|
@update:model-value="onSelect"
|
|
178
187
|
@create="onCreateNew"
|
|
179
|
-
|
|
188
|
+
>
|
|
189
|
+
<template v-if="multiple" #tags-item-text="{ item }">
|
|
190
|
+
<UTooltip :delay-duration="0" :text="findLabel(item)" :content="{ side: 'top' }">
|
|
191
|
+
<span class="truncate">{{ findLabel(item) }}</span>
|
|
192
|
+
</UTooltip>
|
|
193
|
+
</template>
|
|
194
|
+
</UInputMenu>
|
|
180
195
|
</template>
|
|
@@ -31,7 +31,13 @@ const props = defineProps({
|
|
|
31
31
|
roundedNone: { type: Boolean, required: false }
|
|
32
32
|
});
|
|
33
33
|
const modelValue = defineModel("modelValue", { type: Object, ...{ required: true } });
|
|
34
|
-
const
|
|
34
|
+
const treeSelectedItems = computed(() => {
|
|
35
|
+
const flat = flattenTree(items.value);
|
|
36
|
+
if (props.multiple) {
|
|
37
|
+
return flat.filter((i) => modelValue.value.values?.includes(i.value));
|
|
38
|
+
}
|
|
39
|
+
return flat.find((i) => i.value === modelValue.value.values) ?? void 0;
|
|
40
|
+
});
|
|
35
41
|
const open = ref(false);
|
|
36
42
|
const searchedModel = ref([]);
|
|
37
43
|
const currentSelectedModels = computed(() => [modelValue.value.extraModels ?? []].flat());
|
|
@@ -43,13 +49,13 @@ const allModels = computed(() => {
|
|
|
43
49
|
[props.labelField]: "\u65E0"
|
|
44
50
|
});
|
|
45
51
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
newSearchedModel.push(searchedItem);
|
|
52
|
+
currentSelectedModels.value.forEach((currentModel) => {
|
|
53
|
+
const existInSearched = searchedModel.value.some((searched) => searched[props.valueField] === currentModel[props.valueField]);
|
|
54
|
+
if (!existInSearched) {
|
|
55
|
+
newSearchedModel.unshift(currentModel);
|
|
51
56
|
}
|
|
52
57
|
});
|
|
58
|
+
newSearchedModel.push(...searchedModel.value);
|
|
53
59
|
return newSearchedModel;
|
|
54
60
|
});
|
|
55
61
|
const onSelect = (values) => {
|
|
@@ -67,6 +73,7 @@ const onSelect = (values) => {
|
|
|
67
73
|
values: newValue,
|
|
68
74
|
extraModels: newExtraModel ? [newExtraModel] : []
|
|
69
75
|
};
|
|
76
|
+
open.value = false;
|
|
70
77
|
}
|
|
71
78
|
};
|
|
72
79
|
const items = computed(() => {
|
|
@@ -139,12 +146,11 @@ const onFetchItems = async () => {
|
|
|
139
146
|
|
|
140
147
|
<template #content>
|
|
141
148
|
<UTree
|
|
142
|
-
:model-value="
|
|
149
|
+
:model-value="treeSelectedItems"
|
|
143
150
|
:items="items"
|
|
144
151
|
:disabled="fetching"
|
|
145
152
|
:expanded="expandedItemValues"
|
|
146
|
-
:
|
|
147
|
-
multiple
|
|
153
|
+
:multiple="multiple"
|
|
148
154
|
class="p-1 max-h-80 overflow-auto"
|
|
149
155
|
/>
|
|
150
156
|
</template>
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import type { RequestResult } from '#v/types';
|
|
2
2
|
import type { Ref } from 'vue';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
onClose?: ((args_0: boolean) => any) | undefined;
|
|
11
|
-
}> & (typeof globalThis extends {
|
|
12
|
-
__VLS_PROPS_FALLBACK: infer P;
|
|
13
|
-
} ? P : {});
|
|
14
|
-
expose: (exposed: {}) => void;
|
|
15
|
-
attrs: any;
|
|
16
|
-
slots: {};
|
|
17
|
-
emit: (evt: "close", args_0: boolean) => void;
|
|
18
|
-
}>) => import("vue").VNode & {
|
|
19
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
ids?: number[];
|
|
5
|
+
models?: any[];
|
|
6
|
+
displayFn?: ((model: any) => string | undefined);
|
|
7
|
+
onDelete?: ((ids: number[]) => Promise<{
|
|
8
|
+
data: Ref<RequestResult<any>>;
|
|
9
|
+
}> | undefined);
|
|
20
10
|
};
|
|
11
|
+
declare var __VLS_15: {};
|
|
12
|
+
type __VLS_Slots = {} & {
|
|
13
|
+
default?: (props: typeof __VLS_15) => any;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
|
+
close: (args_0: boolean) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
+
onClose?: ((args_0: boolean) => any) | undefined;
|
|
19
|
+
}>, {
|
|
20
|
+
ids: number[];
|
|
21
|
+
models: any[];
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
21
24
|
declare const _default: typeof __VLS_export;
|
|
22
25
|
export default _default;
|
|
23
|
-
type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
26
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
27
|
+
new (): {
|
|
28
|
+
$slots: S;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
2
3
|
import { useSubmitting } from "#v/composables/useBoolean";
|
|
3
4
|
const props = defineProps({
|
|
4
|
-
ids: { type: Array, required:
|
|
5
|
-
|
|
5
|
+
ids: { type: Array, required: false, default: () => [] },
|
|
6
|
+
models: { type: Array, required: false, default: () => [] },
|
|
7
|
+
displayFn: { type: Function, required: false },
|
|
8
|
+
onDelete: { type: Function, required: false }
|
|
6
9
|
});
|
|
7
10
|
const emit = defineEmits(["close"]);
|
|
11
|
+
const effectiveIds = computed(() => {
|
|
12
|
+
if (props.models && props.models.length > 0) {
|
|
13
|
+
return props.models.map((m) => m.id);
|
|
14
|
+
}
|
|
15
|
+
return props.ids ?? [];
|
|
16
|
+
});
|
|
17
|
+
const displayLabels = computed(() => {
|
|
18
|
+
if (props.models && props.models.length > 0) {
|
|
19
|
+
if (props.displayFn) {
|
|
20
|
+
return props.models.map((m) => props.displayFn(m) ?? String(m.id));
|
|
21
|
+
}
|
|
22
|
+
return props.models.map((m) => String(m.id));
|
|
23
|
+
}
|
|
24
|
+
return (props.ids ?? []).map((id) => String(id));
|
|
25
|
+
});
|
|
8
26
|
const { submitting, startSubmitting, endSubmitting } = useSubmitting();
|
|
9
27
|
async function onSubmit(e) {
|
|
10
28
|
if (!props.onDelete) return;
|
|
11
29
|
e.preventDefault();
|
|
12
30
|
try {
|
|
13
31
|
startSubmitting();
|
|
14
|
-
const { data } = await props.onDelete(
|
|
32
|
+
const { data } = await props.onDelete(effectiveIds.value);
|
|
15
33
|
if (!data.value.error) {
|
|
16
34
|
emit("close", true);
|
|
17
35
|
}
|
|
@@ -35,6 +53,23 @@ async function onSubmit(e) {
|
|
|
35
53
|
>
|
|
36
54
|
删除
|
|
37
55
|
</UButton>
|
|
56
|
+
<template #body>
|
|
57
|
+
<div v-if="effectiveIds.length > 0" class="mb-4">
|
|
58
|
+
<p class="text-sm text-(--ui-text-muted) mb-2">
|
|
59
|
+
以下数据将被删除:
|
|
60
|
+
</p>
|
|
61
|
+
<ul class="list-disc list-inside space-y-1">
|
|
62
|
+
<li
|
|
63
|
+
v-for="(label, index) in displayLabels"
|
|
64
|
+
:key="effectiveIds[index]"
|
|
65
|
+
class="text-sm"
|
|
66
|
+
>
|
|
67
|
+
{{ label }}
|
|
68
|
+
</li>
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
<slot />
|
|
72
|
+
</template>
|
|
38
73
|
<template #footer>
|
|
39
74
|
<div class="flex justify-end gap-2">
|
|
40
75
|
<UButton
|
|
@@ -44,7 +79,7 @@ async function onSubmit(e) {
|
|
|
44
79
|
@click="emit('close', false)"
|
|
45
80
|
/>
|
|
46
81
|
<UButton
|
|
47
|
-
:label="`\u5220\u9664 ${
|
|
82
|
+
:label="`\u5220\u9664 ${effectiveIds.length} \u6761\u6570\u636E`"
|
|
48
83
|
color="error"
|
|
49
84
|
variant="solid"
|
|
50
85
|
icon="i-lucide-trash"
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import type { RequestResult } from '#v/types';
|
|
2
2
|
import type { Ref } from 'vue';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
onClose?: ((args_0: boolean) => any) | undefined;
|
|
11
|
-
}> & (typeof globalThis extends {
|
|
12
|
-
__VLS_PROPS_FALLBACK: infer P;
|
|
13
|
-
} ? P : {});
|
|
14
|
-
expose: (exposed: {}) => void;
|
|
15
|
-
attrs: any;
|
|
16
|
-
slots: {};
|
|
17
|
-
emit: (evt: "close", args_0: boolean) => void;
|
|
18
|
-
}>) => import("vue").VNode & {
|
|
19
|
-
__ctx?: Awaited<typeof __VLS_setup>;
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
ids?: number[];
|
|
5
|
+
models?: any[];
|
|
6
|
+
displayFn?: ((model: any) => string | undefined);
|
|
7
|
+
onDelete?: ((ids: number[]) => Promise<{
|
|
8
|
+
data: Ref<RequestResult<any>>;
|
|
9
|
+
}> | undefined);
|
|
20
10
|
};
|
|
11
|
+
declare var __VLS_15: {};
|
|
12
|
+
type __VLS_Slots = {} & {
|
|
13
|
+
default?: (props: typeof __VLS_15) => any;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
16
|
+
close: (args_0: boolean) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
+
onClose?: ((args_0: boolean) => any) | undefined;
|
|
19
|
+
}>, {
|
|
20
|
+
ids: number[];
|
|
21
|
+
models: any[];
|
|
22
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
23
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
21
24
|
declare const _default: typeof __VLS_export;
|
|
22
25
|
export default _default;
|
|
23
|
-
type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
26
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
27
|
+
new (): {
|
|
28
|
+
$slots: S;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { ref, computed, useTemplateRef, onMounted, onUnmounted } from "vue";
|
|
2
|
+
import { ref, computed, useTemplateRef, onMounted, onUnmounted, onActivated, nextTick } from "vue";
|
|
3
3
|
import { useDebounceFn } from "@vueuse/core";
|
|
4
4
|
import {
|
|
5
5
|
ScrollAreaRoot,
|
|
@@ -51,12 +51,14 @@ const onDetectBoundary = () => {
|
|
|
51
51
|
isAtLeft.value = scrollLeft <= 1;
|
|
52
52
|
isAtRight.value = scrollLeft + clientWidth >= scrollWidth - 1;
|
|
53
53
|
};
|
|
54
|
-
const
|
|
54
|
+
const savedScrollTop = ref(0);
|
|
55
|
+
const savedScrollLeft = ref(0);
|
|
56
|
+
const onScrollWithSave = useDebounceFn(() => {
|
|
55
57
|
onDetectBoundary();
|
|
56
58
|
const viewport = scrollArea.value?.viewport;
|
|
57
|
-
if (!viewport)
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
if (!viewport) return;
|
|
60
|
+
savedScrollTop.value = viewport.scrollTop;
|
|
61
|
+
savedScrollLeft.value = viewport.scrollLeft;
|
|
60
62
|
props.onScrollEvent?.(viewport);
|
|
61
63
|
}, 128);
|
|
62
64
|
const scrollHorizontally = (targetX) => {
|
|
@@ -108,11 +110,20 @@ defineExpose({
|
|
|
108
110
|
scrollVerticallyBy
|
|
109
111
|
});
|
|
110
112
|
onMounted(() => {
|
|
111
|
-
|
|
112
|
-
window.addEventListener("resize",
|
|
113
|
+
onScrollWithSave();
|
|
114
|
+
window.addEventListener("resize", onScrollWithSave);
|
|
113
115
|
});
|
|
114
116
|
onUnmounted(() => {
|
|
115
|
-
window.removeEventListener("resize",
|
|
117
|
+
window.removeEventListener("resize", onScrollWithSave);
|
|
118
|
+
});
|
|
119
|
+
onActivated(() => {
|
|
120
|
+
nextTick(() => {
|
|
121
|
+
const vp = scrollArea.value?.viewport;
|
|
122
|
+
if (vp) {
|
|
123
|
+
vp.scrollTop = savedScrollTop.value;
|
|
124
|
+
vp.scrollLeft = savedScrollLeft.value;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
116
127
|
});
|
|
117
128
|
</script>
|
|
118
129
|
|
|
@@ -149,7 +160,7 @@ onUnmounted(() => {
|
|
|
149
160
|
/>
|
|
150
161
|
</Transition>
|
|
151
162
|
|
|
152
|
-
<ScrollAreaViewport class="size-full overscroll-contain" @scroll="
|
|
163
|
+
<ScrollAreaViewport class="size-full overscroll-contain" @scroll="onScrollWithSave">
|
|
153
164
|
<slot />
|
|
154
165
|
</ScrollAreaViewport>
|
|
155
166
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VFormFieldProps } from '#v/types';
|
|
2
|
+
export interface ConfirmDiffItem {
|
|
3
|
+
fieldName: string;
|
|
4
|
+
oldValue: unknown;
|
|
5
|
+
newValue: unknown;
|
|
6
|
+
}
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
fields: VFormFieldProps[];
|
|
9
|
+
diffItems: ConfirmDiffItem[];
|
|
10
|
+
oldModelValue: Record<string, unknown>;
|
|
11
|
+
newModelValue: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
close: (args_0: boolean) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onClose?: ((args_0: boolean) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { isEmptyString } from "#v/utils";
|
|
3
|
+
import { diffEligibleTypes, useConfirmDiff } from "#v/composables";
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
fields: { type: Array, required: true },
|
|
6
|
+
diffItems: { type: Array, required: true },
|
|
7
|
+
oldModelValue: { type: Object, required: true },
|
|
8
|
+
newModelValue: { type: Object, required: true }
|
|
9
|
+
});
|
|
10
|
+
const emit = defineEmits(["close"]);
|
|
11
|
+
const { diffedItems } = useConfirmDiff(
|
|
12
|
+
() => props.fields,
|
|
13
|
+
() => props.diffItems,
|
|
14
|
+
() => props.oldModelValue,
|
|
15
|
+
() => props.newModelValue
|
|
16
|
+
);
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<UModal
|
|
21
|
+
title="确认修改"
|
|
22
|
+
:close="{ onClick: () => emit('close', false) }"
|
|
23
|
+
:dismissible="false"
|
|
24
|
+
>
|
|
25
|
+
<template #description>
|
|
26
|
+
<div class="flex items-center gap-3 w-full">
|
|
27
|
+
<span>
|
|
28
|
+
请确认以下 {{ diffedItems.length }} 处修改内容
|
|
29
|
+
</span>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
<template #body>
|
|
33
|
+
<div class="overflow-y-auto max-h-96">
|
|
34
|
+
<div class="space-y-4">
|
|
35
|
+
<UFormField
|
|
36
|
+
v-for="item in diffedItems"
|
|
37
|
+
:key="item.fieldName"
|
|
38
|
+
:label="item.field.label || item.fieldName"
|
|
39
|
+
>
|
|
40
|
+
<!-- String types: word diff -->
|
|
41
|
+
<div v-if="item.field.diffable ?? diffEligibleTypes.has(item.field.type)" class="flex items-center flex-wrap gap-2">
|
|
42
|
+
<span
|
|
43
|
+
class="rounded-md bg-muted px-2 py-1 text-sm text-dimmed"
|
|
44
|
+
:title="item.oldDisplay"
|
|
45
|
+
>
|
|
46
|
+
<span v-if="!item.parts.filter((part2) => !part2.added).length">{{ "\xA0" }}</span>
|
|
47
|
+
<template v-for="(part, idx) in item.parts" :key="idx">
|
|
48
|
+
<span
|
|
49
|
+
v-if="!part.added"
|
|
50
|
+
:class="{ 'bg-red-200 text-red-800 dark:bg-red-800 dark:text-red-200 rounded-sm line-through px-0.5': part.removed }"
|
|
51
|
+
>{{ isEmptyString(part.value) ? "\xA0" : part.value }}</span>
|
|
52
|
+
</template>
|
|
53
|
+
</span>
|
|
54
|
+
<UIcon
|
|
55
|
+
name="i-lucide-arrow-right"
|
|
56
|
+
class="text-dimmed shrink-0 size-4"
|
|
57
|
+
/>
|
|
58
|
+
<span
|
|
59
|
+
class="rounded-md bg-elevated px-2 py-1 text-sm text-highlighted font-bold"
|
|
60
|
+
:title="item.newDisplay"
|
|
61
|
+
>
|
|
62
|
+
<span v-if="!item.parts.filter((part2) => !part2.removed).length">{{ "\xA0" }}</span>
|
|
63
|
+
<template v-for="(part, idx) in item.parts" :key="idx">
|
|
64
|
+
<span
|
|
65
|
+
v-if="!part.removed"
|
|
66
|
+
:class="{ 'bg-green-200 text-green-800 dark:bg-green-800 dark:text-green-200 rounded-sm px-0.5': part.added }"
|
|
67
|
+
>{{ isEmptyString(part.value) ? "\xA0" : part.value }}</span>
|
|
68
|
+
</template>
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
<!-- Other types: simple old -> new -->
|
|
72
|
+
<div v-else class="flex items-center flex-wrap gap-2">
|
|
73
|
+
<span
|
|
74
|
+
class="rounded-md bg-muted px-2 py-1 text-sm text-dimmed"
|
|
75
|
+
:title="item.oldDisplay"
|
|
76
|
+
>
|
|
77
|
+
{{ isEmptyString(item.oldDisplay) ? "\xA0" : item.oldDisplay }}
|
|
78
|
+
</span>
|
|
79
|
+
<UIcon
|
|
80
|
+
name="i-lucide-arrow-right"
|
|
81
|
+
class="text-dimmed shrink-0 size-4"
|
|
82
|
+
/>
|
|
83
|
+
<span
|
|
84
|
+
class="rounded-md bg-elevated px-2 py-1 text-sm text-highlighted font-medium"
|
|
85
|
+
:title="item.newDisplay"
|
|
86
|
+
>
|
|
87
|
+
{{ isEmptyString(item.newDisplay) ? "\xA0" : item.newDisplay }}
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
</UFormField>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
<template #footer>
|
|
95
|
+
<UButton
|
|
96
|
+
label="取消"
|
|
97
|
+
color="neutral"
|
|
98
|
+
variant="subtle"
|
|
99
|
+
icon="i-lucide-x"
|
|
100
|
+
@click="emit('close', false)"
|
|
101
|
+
/>
|
|
102
|
+
<UButton
|
|
103
|
+
:label="`\u786E\u8BA4 ${diffedItems.length} \u5904\u4FEE\u6539`"
|
|
104
|
+
color="primary"
|
|
105
|
+
variant="solid"
|
|
106
|
+
icon="i-lucide-check"
|
|
107
|
+
@click="emit('close', true)"
|
|
108
|
+
/>
|
|
109
|
+
</template>
|
|
110
|
+
</UModal>
|
|
111
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VFormFieldProps } from '#v/types';
|
|
2
|
+
export interface ConfirmDiffItem {
|
|
3
|
+
fieldName: string;
|
|
4
|
+
oldValue: unknown;
|
|
5
|
+
newValue: unknown;
|
|
6
|
+
}
|
|
7
|
+
type __VLS_Props = {
|
|
8
|
+
fields: VFormFieldProps[];
|
|
9
|
+
diffItems: ConfirmDiffItem[];
|
|
10
|
+
oldModelValue: Record<string, unknown>;
|
|
11
|
+
newModelValue: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
close: (args_0: boolean) => any;
|
|
15
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
16
|
+
onClose?: ((args_0: boolean) => any) | undefined;
|
|
17
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
18
|
+
declare const _default: typeof __VLS_export;
|
|
19
|
+
export default _default;
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.d.vue.ts
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BaseModel,
|
|
1
|
+
import type { BaseModel, SaveModalFormTemplatePropsWithApi } from '#v/types';
|
|
2
2
|
declare const __VLS_export: <T extends BaseModel>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<SaveModalFormTemplatePropsWithApi<T>> & (typeof globalThis extends {
|
|
4
4
|
__VLS_PROPS_FALLBACK: infer P;
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.vue
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { toRef } from "vue";
|
|
3
|
-
import { useFormSubmission, useFormValues } from "#v/composables/useForm";
|
|
4
|
-
import
|
|
3
|
+
import { useFormSubmission, useFormValues } from "#v/composables/form/useForm";
|
|
4
|
+
import FormSaveModalTemplate from "./index.vue";
|
|
5
5
|
const props = defineProps({
|
|
6
6
|
apiGroup: { type: Function, required: true },
|
|
7
7
|
valuePruneFn: { type: Function, required: false },
|
|
@@ -25,8 +25,9 @@ const { onSubmit } = useFormSubmission(
|
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
27
|
<template>
|
|
28
|
-
<
|
|
28
|
+
<FormSaveModalTemplate
|
|
29
29
|
v-model:model-value="newValues"
|
|
30
|
+
:old-model-value="oldValues"
|
|
30
31
|
:title="title"
|
|
31
32
|
:description="description"
|
|
32
33
|
:on-close="onClose"
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/WithApi.vue.d.ts
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BaseModel,
|
|
1
|
+
import type { BaseModel, SaveModalFormTemplatePropsWithApi } from '#v/types';
|
|
2
2
|
declare const __VLS_export: <T extends BaseModel>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<SaveModalFormTemplatePropsWithApi<T>> & (typeof globalThis extends {
|
|
4
4
|
__VLS_PROPS_FALLBACK: infer P;
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.d.vue.ts
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BaseModel,
|
|
1
|
+
import type { BaseModel, SaveModalFormTemplateProps } from '#v/types';
|
|
2
2
|
declare const __VLS_export: <T extends BaseModel>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<SaveModalFormTemplateProps<T>> & (typeof globalThis extends {
|
|
4
4
|
__VLS_PROPS_FALLBACK: infer P;
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.vue
RENAMED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { computed, useTemplateRef } from "vue";
|
|
3
|
-
import {
|
|
2
|
+
import { computed, ref, useTemplateRef } from "vue";
|
|
3
|
+
import { useOverlay } from "@nuxt/ui/composables";
|
|
4
|
+
import { useSubmitting } from "#v/composables";
|
|
4
5
|
import Form from "../index.vue";
|
|
6
|
+
import ConfirmUpdateModal from "./ConfirmUpdateModal.vue";
|
|
5
7
|
const props = defineProps({
|
|
6
8
|
title: { type: String, required: true },
|
|
7
9
|
description: { type: String, required: false },
|
|
8
10
|
onClose: { type: Function, required: true },
|
|
9
11
|
onSubmit: { type: Function, required: true },
|
|
10
12
|
fields: { type: Array, required: true },
|
|
13
|
+
oldModelValue: { type: Object, required: true },
|
|
11
14
|
modelValue: { type: Object, required: true },
|
|
12
15
|
onUpdateModelValue: { type: Function, required: false },
|
|
13
16
|
rowKey: { type: null, required: false, default: "id" },
|
|
@@ -15,10 +18,42 @@ const props = defineProps({
|
|
|
15
18
|
});
|
|
16
19
|
const form = useTemplateRef("form");
|
|
17
20
|
const action = computed(() => props.modelValue[props.rowKey] === 0 ? "\u521B\u5EFA" : "\u66F4\u65B0");
|
|
21
|
+
const isUpdate = computed(() => props.modelValue[props.rowKey] !== 0);
|
|
18
22
|
const titleWithAction = computed(() => `${action.value}${props.title}`);
|
|
19
23
|
const descWithAction = computed(() => props.description ?? `\u8BF7${action.value}${props.title}`);
|
|
20
24
|
const submitIcon = computed(() => props.modelValue[props.rowKey] === 0 ? "i-lucide-clipboard-plus" : "i-lucide-clipboard-pen-line");
|
|
25
|
+
const initialModelValue = ref(JSON.parse(JSON.stringify(props.modelValue)));
|
|
26
|
+
const overlay = useOverlay();
|
|
27
|
+
const confirmModal = overlay.create(ConfirmUpdateModal);
|
|
28
|
+
function computeDiff() {
|
|
29
|
+
const items = [];
|
|
30
|
+
for (const field of props.fields) {
|
|
31
|
+
if (!field.name || field.hidden) continue;
|
|
32
|
+
const name = field.name;
|
|
33
|
+
const oldVal = initialModelValue.value[name];
|
|
34
|
+
const newVal = props.modelValue[name];
|
|
35
|
+
if ((oldVal === null || oldVal === void 0) && (newVal === null || newVal === void 0)) continue;
|
|
36
|
+
if (oldVal === newVal) continue;
|
|
37
|
+
if (typeof oldVal === "object" && typeof newVal === "object" && oldVal !== null && newVal !== null) {
|
|
38
|
+
if (JSON.stringify(oldVal) === JSON.stringify(newVal)) continue;
|
|
39
|
+
}
|
|
40
|
+
items.push({
|
|
41
|
+
fieldName: name,
|
|
42
|
+
oldValue: oldVal,
|
|
43
|
+
newValue: newVal
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return items;
|
|
47
|
+
}
|
|
21
48
|
const { submitting, startSubmitting, endSubmitting } = useSubmitting();
|
|
49
|
+
async function doSubmit() {
|
|
50
|
+
try {
|
|
51
|
+
startSubmitting();
|
|
52
|
+
await props.onSubmit();
|
|
53
|
+
} finally {
|
|
54
|
+
endSubmitting();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
22
57
|
async function onSubmitWithValidation(e) {
|
|
23
58
|
e.preventDefault();
|
|
24
59
|
e.stopPropagation();
|
|
@@ -27,12 +62,24 @@ async function onSubmitWithValidation(e) {
|
|
|
27
62
|
} catch {
|
|
28
63
|
return;
|
|
29
64
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
65
|
+
if (isUpdate.value) {
|
|
66
|
+
const items = computeDiff();
|
|
67
|
+
if (items.length === 0) {
|
|
68
|
+
await doSubmit();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const confirmed = await confirmModal.open({
|
|
72
|
+
fields: props.fields,
|
|
73
|
+
diffItems: items,
|
|
74
|
+
oldModelValue: props.oldModelValue,
|
|
75
|
+
newModelValue: props.modelValue
|
|
76
|
+
}).result;
|
|
77
|
+
if (confirmed) {
|
|
78
|
+
await doSubmit();
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
35
81
|
}
|
|
82
|
+
await doSubmit();
|
|
36
83
|
}
|
|
37
84
|
</script>
|
|
38
85
|
|
package/dist/runtime/components/form/{create-modal-template → save-modal-template}/index.vue.d.ts
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { BaseModel,
|
|
1
|
+
import type { BaseModel, SaveModalFormTemplateProps } from '#v/types';
|
|
2
2
|
declare const __VLS_export: <T extends BaseModel>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3
|
-
props: import("vue").PublicProps & __VLS_PrettifyLocal<
|
|
3
|
+
props: import("vue").PublicProps & __VLS_PrettifyLocal<SaveModalFormTemplateProps<T>> & (typeof globalThis extends {
|
|
4
4
|
__VLS_PROPS_FALLBACK: infer P;
|
|
5
5
|
} ? P : {});
|
|
6
6
|
expose: (exposed: {}) => void;
|