整理
This commit is contained in:
44
src/pages/UI-components/AntdV/HAPopconfirm/HAPopconfirm.vue
Normal file
44
src/pages/UI-components/AntdV/HAPopconfirm/HAPopconfirm.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import type { PopconfirmProps } from 'ant-design-vue';
|
||||
|
||||
import type { HPopconfirmProps } from './types';
|
||||
|
||||
defineOptions({ inheritAttrs: true });
|
||||
|
||||
const props = defineProps<HPopconfirmProps>();
|
||||
|
||||
const _loading = shallowRef(false);
|
||||
const onConfirm: PopconfirmProps['onConfirm'] = async (e) => {
|
||||
if (props.onConfirm) {
|
||||
try {
|
||||
_loading.value = true;
|
||||
await props.onConfirm(e);
|
||||
} finally {
|
||||
_loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<APopconfirm
|
||||
:align="{
|
||||
targetOffset: [0, 0],
|
||||
}"
|
||||
:arrow-point-at-center="!true"
|
||||
:cancel-button-props="{ disabled: _loading }"
|
||||
:description
|
||||
:disabled="_loading"
|
||||
:on-confirm
|
||||
:title
|
||||
placement="topRight"
|
||||
>
|
||||
<slot></slot>
|
||||
</APopconfirm>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
[class^='ant-'] .anticon svg {
|
||||
vertical-align: unset; /* baseline */
|
||||
}
|
||||
</style>
|
9
src/pages/UI-components/AntdV/HAPopconfirm/types.ts
Normal file
9
src/pages/UI-components/AntdV/HAPopconfirm/types.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { PopconfirmProps } from 'ant-design-vue';
|
||||
|
||||
export type HPopconfirmProps = {
|
||||
description: PopconfirmProps['description'];
|
||||
onConfirm?: (...args: PopconfirmOnConfirmParameters) => Promise<void>;
|
||||
title: PopconfirmProps['title'];
|
||||
};
|
||||
type NotUndefined<T> = T extends undefined ? never : T;
|
||||
type PopconfirmOnConfirmParameters = Parameters<NotUndefined<PopconfirmProps['onConfirm']>>;
|
37
src/pages/UI-components/AntdV/index.page.vue
Normal file
37
src/pages/UI-components/AntdV/index.page.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import HAPopconfirm from './HAPopconfirm/HAPopconfirm.vue';
|
||||
|
||||
async function handleConfirmAsync(e: MouseEvent) {
|
||||
console.debug('handleConfirmAsync', e);
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 2000);
|
||||
});
|
||||
message.success('哇!数据已被成功送入黑洞 🕳️');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ACard>
|
||||
<ACard>
|
||||
<HAPopconfirm title="你确定吗? 🤔 " description="别担心,我们只是假装很严肃 🎭" @confirm="handleConfirmAsync">
|
||||
<AButton size="small" danger type="text">HAPopconfirm</AButton>
|
||||
</HAPopconfirm>
|
||||
</ACard>
|
||||
<ACard class="mt-4">
|
||||
<AForm name="basic" :label-col="{ style: { width: '7em' } }">
|
||||
<AFormItem required label="Username">
|
||||
<AInput />
|
||||
</AFormItem>
|
||||
<AFormItem label="Password">
|
||||
<AInput />
|
||||
</AFormItem>
|
||||
<AFormItem>
|
||||
<AButton html-type="submit" type="primary">Submit</AButton>
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
</ACard>
|
||||
</ACard>
|
||||
</template>
|
||||
./HAPopconfirm.vue
|
11
src/pages/UI-components/PrimeVue/__dialog-content.vue
Normal file
11
src/pages/UI-components/PrimeVue/__dialog-content.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
const dialogRef = usePrimevueDialogRef();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mb-8>dialog-content.vue</div>
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button type="button" label="关闭" severity="secondary" @click="dialogRef?.close"></Button>
|
||||
<Button type="button" label="关闭" @click="dialogRef?.close"></Button>
|
||||
</div>
|
||||
</template>
|
74
src/pages/UI-components/PrimeVue/fns.tsx
Normal file
74
src/pages/UI-components/PrimeVue/fns.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import dialogContent from './__dialog-content.vue';
|
||||
|
||||
const dynamicComponent = defineComponent({
|
||||
props: {
|
||||
'defining-props': {
|
||||
default: () => ({}),
|
||||
type: Object as PropType<Record<string, unknown>>,
|
||||
},
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const dialogRef = usePrimevueDialogRef();
|
||||
return () => (
|
||||
<div>
|
||||
<button onClick={() => ctx.emit('close')}>emit('close')</button> <hr />
|
||||
<pre>{JSON.stringify({ 'dialogRef?.data': dialogRef!.value.data }, null, 2)}</pre> <hr />
|
||||
<div>PrimeVue DynamicDialog</div> <hr />
|
||||
<pre>{`${JSON.stringify({ 'ctx.attrs': ctx, props }, null, 2)}`}</pre> <hr />
|
||||
<pre>{JSON.stringify({ "inject('dialogRef')": dialogRef!.value }, null, 2)}</pre> <hr />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// https://primevue.org/dynamicdialog/
|
||||
export const openDialog = async () => {
|
||||
const dialog1 = DialogService.open(dynamicComponent, {
|
||||
data: {
|
||||
用inject接收: '通过 DynamicDialogOptions.data 传递的数据',
|
||||
},
|
||||
emits: {
|
||||
// https://github.com/primefaces/primevue/blob/bd7161298a472c8cd954e35e6a538a8bd1b1b386/packages/primevue/src/dynamicdialog/DynamicDialog.vue#L5
|
||||
// ↑v-bind="instance.options.emits", 所以 props 也可以通过 emits 传递给 content 的组件
|
||||
'defining-props': {
|
||||
'用-props-接收': '定义在-DynamicDialogOptions.emits-的数据',
|
||||
},
|
||||
onClose: () => dialog1.close(),
|
||||
绑定给组件的attrs: '定义在-DynamicDialogOptions.emits-的数据,',
|
||||
},
|
||||
props: {
|
||||
draggable: true,
|
||||
header: '对话框1 可以拖动',
|
||||
position: 'bottomleft',
|
||||
pt: { mask: { class: 'backdrop-blur-sm' } }, // 相当于: pt:mask:class="backdrop-blur-sm"
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
// if ($__DEV__) return;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||
DialogService.open(dialogContent, {
|
||||
props: {
|
||||
header: '对话框2',
|
||||
// draggable: false, // Header1的 draggable: true 会影响 Header2,如果指定 draggable: false,则不会受到影响。
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const openConfirm = async () => {
|
||||
ConfirmationService.require({
|
||||
accept: () => {
|
||||
ToastService.add({ detail: '您已同意操作', life: 3000, severity: 'info', summary: '已确认' });
|
||||
},
|
||||
// rejectProps: { style: { display: 'none' } },
|
||||
acceptProps: { label: '确定' },
|
||||
header: '确认',
|
||||
icon: 'pi pi-exclamation-triangle',
|
||||
message: '确定要继续吗?',
|
||||
reject: () => {
|
||||
ToastService.add({ detail: '您已取消操作', life: 3000, severity: 'error', summary: '已取消' });
|
||||
},
|
||||
rejectProps: { label: '取消', outlined: true, severity: 'secondary' },
|
||||
});
|
||||
};
|
91
src/pages/UI-components/PrimeVue/index.page.vue
Normal file
91
src/pages/UI-components/PrimeVue/index.page.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<script lang="ts"></script>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import type { ToastMessageOptions } from 'primevue/toast';
|
||||
|
||||
import { openConfirm, openDialog } from './fns';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="primevue py-4 flex items-start flex-wrap gap-6">
|
||||
<FloatLabel>
|
||||
<InputText default-value="DEFAULT_VALUE" id="username" />
|
||||
<label for="username">Username</label>
|
||||
</FloatLabel>
|
||||
|
||||
<FloatLabel>
|
||||
<DatePicker :manualInput="false" inputId="datepicker" showButtonBar dateFormat="dd/mm/yy" />
|
||||
<label for="datepicker">DatePicker</label>
|
||||
</FloatLabel>
|
||||
|
||||
<FloatLabel>
|
||||
<Select
|
||||
:options="[
|
||||
{ name: '纽约', code: 'NY' },
|
||||
{ name: '罗马', code: 'RM' },
|
||||
{ name: '伦敦', code: 'LDN' },
|
||||
{ name: '伊斯坦布尔', code: 'IST' },
|
||||
{ name: '巴黎', code: 'PRS' },
|
||||
]"
|
||||
optionLabel="name"
|
||||
class="min-w-[200px]"
|
||||
/>
|
||||
<label>SELECT</label>
|
||||
</FloatLabel>
|
||||
|
||||
<FloatLabel w-full>
|
||||
<UploadDemo />
|
||||
<label>FileUpload</label>
|
||||
</FloatLabel>
|
||||
|
||||
<FloatLabel>
|
||||
<FileUpload name="demo[]" url="/api/upload" :maxFileSize="1000000" />
|
||||
<label>FileUpload</label>
|
||||
</FloatLabel>
|
||||
|
||||
<!-- <Button @click="openToast">提示服务</Button> -->
|
||||
<Card>
|
||||
<template #title>提示服务</template>
|
||||
<template #content>
|
||||
<div flex="~ wrap" gap="4">
|
||||
<!-- oastService.add({ severity: 'info', summary: '提示', life: 3000, detail: '消息内容' }); -->
|
||||
<!-- oastService.add({ severity: 'info', summary: '提示', life: 0, detail: '消息内容' }); -->
|
||||
<Button
|
||||
outlined
|
||||
v-for="severity in [
|
||||
'success',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'secondary',
|
||||
'contrast',
|
||||
undefined,
|
||||
] satisfies ToastMessageOptions['severity'][]"
|
||||
:key="severity"
|
||||
@click="
|
||||
ToastService.add({ severity: severity, summary: `severity: ${severity}`, life: 5000, detail: '消息内容' })
|
||||
"
|
||||
>
|
||||
{{ `${severity}` }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
<Paginator
|
||||
:template="{
|
||||
'640px': 'PrevPageLink CurrentPageReport NextPageLink',
|
||||
'960px': 'FirstPageLink PrevPageLink CurrentPageReport NextPageLink LastPageLink',
|
||||
'1300px': 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink',
|
||||
default: 'FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink JumpToPageDropdown JumpToPageInput',
|
||||
}"
|
||||
:rows="10"
|
||||
:totalRecords="120"
|
||||
>
|
||||
</Paginator>
|
||||
|
||||
<ProgressSpinner class="w-6! h-6! m-0!" />
|
||||
|
||||
<Button @click="openDialog">对话框服务</Button>
|
||||
<Button @click="openConfirm">确认服务</Button>
|
||||
</div>
|
||||
</template>
|
13
src/pages/UI-components/infinite-loading/detail.page.vue
Normal file
13
src/pages/UI-components/infinite-loading/detail.page.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
const route = useRoute();
|
||||
definePage({
|
||||
meta: {
|
||||
hidden: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>Detail</h1>
|
||||
<p>id: {{ route.query.id }}</p>
|
||||
</template>
|
130
src/pages/UI-components/infinite-loading/index.page.vue
Normal file
130
src/pages/UI-components/infinite-loading/index.page.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<script lang="ts">
|
||||
const structuredClone = window.structuredClone || ((obj) => JSON.parse(JSON.stringify(obj)));
|
||||
|
||||
const K_INITIAL_STATE = deepFreeze({
|
||||
complete: false,
|
||||
error: null as unknown,
|
||||
list: [] as Record<string, never>[],
|
||||
loading: false,
|
||||
page: 0,
|
||||
});
|
||||
|
||||
let page3ShouldError = true;
|
||||
const state = shallowReactive(structuredClone(K_INITIAL_STATE));
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
beforeRouteEnter: (to, from) => {
|
||||
if (from.name !== 'UIComponentsInfiniteLoadingDetail') {
|
||||
Object.assign(state, structuredClone(K_INITIAL_STATE));
|
||||
}
|
||||
},
|
||||
beforeRouteLeave: (to, from) => {
|
||||
if (to.name !== 'UIComponentsInfiniteLoadingDetail') {
|
||||
Object.assign(state, structuredClone(K_INITIAL_STATE));
|
||||
}
|
||||
},
|
||||
});
|
||||
const refresh = () => {
|
||||
state.list.splice(0, state.list.length);
|
||||
state.page = 0;
|
||||
state.complete = false;
|
||||
loadMore();
|
||||
};
|
||||
|
||||
type CurrentUse = 'use-intersection-observer-infinite-loading' | 'van-list';
|
||||
|
||||
async function loadMore() {
|
||||
if (state.loading) return;
|
||||
|
||||
try {
|
||||
state.loading = true;
|
||||
const response = await fetch(
|
||||
`https://jsonplaceholder.typicode.com/comments?_page=${++state.page}&_limit=1×tamp=${Date.now()}`,
|
||||
).then((res) => {
|
||||
if (state.page === 3 && page3ShouldError) {
|
||||
page3ShouldError = false;
|
||||
throw new Error('test error');
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
state.list = state.list.concat(data);
|
||||
if ($__DEV__) await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
state.complete = state.list.length >= 5;
|
||||
} catch (error) {
|
||||
state.error = error;
|
||||
state.page--;
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
}
|
||||
const currentUse = ref<CurrentUse>((sessionStorage.getItem('currentUse') as CurrentUse) || 'van-list');
|
||||
watchEffect(() => {
|
||||
sessionStorage.setItem('currentUse', currentUse.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mb-4 class="flex justify-end gap-4 flex-wrap items-start">
|
||||
<Button label="刷新" @click="refresh" />
|
||||
<Button
|
||||
label="push"
|
||||
@click="state.list.push({ id: state.list.length + 1, name: 'name', body: 'body', email: 'email' } as any)"
|
||||
/>
|
||||
<Button
|
||||
:label="currentUse"
|
||||
@click="currentUse = currentUse === 'van-list' ? 'use-intersection-observer-infinite-loading' : 'van-list'"
|
||||
class="mb-4"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="currentUse === 'van-list'">
|
||||
<VanList
|
||||
@load="loadMore"
|
||||
:loading="state.loading"
|
||||
:error="!!state.error"
|
||||
:error-text="`${state.error}`"
|
||||
:onUpdate:error="() => (state.error = null)"
|
||||
:finished="state.complete"
|
||||
>
|
||||
<template v-for="item in state.list" :key="item.id">
|
||||
<div
|
||||
class="border p-4 mb-[16px]"
|
||||
@click="$router.push({ name: 'UIComponentsInfiniteLoadingDetail', query: { id: item.id } })"
|
||||
>
|
||||
<div>id:{{ item.id }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</VanList>
|
||||
</template>
|
||||
|
||||
<template v-if="currentUse === 'use-intersection-observer-infinite-loading'">
|
||||
<Card
|
||||
v-for="item in state.list"
|
||||
:key="item.id"
|
||||
class="mb-[16px]"
|
||||
@click="$router.push({ name: 'UIComponentsInfiniteLoadingDetail', query: { id: item.id } })"
|
||||
>
|
||||
<template #title>{{ item.name }}</template>
|
||||
<template #content>
|
||||
<p class="mt-[8px]">{{ item.body }}</p>
|
||||
</template>
|
||||
<template #subtitle>id:{{ item.id }} </template>
|
||||
<template #footer>{{ item.email }}</template>
|
||||
</Card>
|
||||
<UseIntersectionObserverInfiniteLoading
|
||||
@load="loadMore"
|
||||
:loading="state.loading"
|
||||
:error="!!state.error"
|
||||
:error-text="`${state.error}`"
|
||||
@clickError="state.error = null"
|
||||
:complete="state.complete"
|
||||
/>
|
||||
<div border="1 px solid red rounded-lg" class="p-4 mb-[16px]">
|
||||
{{ { 'list.length': state.list.length } }}
|
||||
</div>
|
||||
</template>
|
||||
<ScrollTop />
|
||||
</template>
|
Reference in New Issue
Block a user