发布 v1.10 #5
@@ -51,6 +51,8 @@
|
||||
"submitPublishApproval": "Submit Publish Approval",
|
||||
"viewSegmentation": "View Segments",
|
||||
"continueProcess": "Continue",
|
||||
"backToTop": "Back to top",
|
||||
"goToBottom": "Go to bottom",
|
||||
"startIndex": "Start Indexing",
|
||||
"retryParse": "Retry Parse",
|
||||
"retryIndex": "Retry Index"
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
"submitPublishApproval": "提交发布审批",
|
||||
"viewSegmentation": "查看分段",
|
||||
"continueProcess": "继续处理",
|
||||
"backToTop": "回到页顶",
|
||||
"goToBottom": "到达页末",
|
||||
"startIndex": "开始向量化",
|
||||
"retryParse": "重试解析",
|
||||
"retryIndex": "重试向量化"
|
||||
|
||||
@@ -432,19 +432,7 @@ watch(
|
||||
</div>
|
||||
</ElForm>
|
||||
|
||||
<section class="workbench__content">
|
||||
<SplitterDocPreview
|
||||
:loading="previewLoading"
|
||||
:preview-items="previewItems"
|
||||
@preview-session-change="handlePreviewSessionChange"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<div v-if="previewError" class="workbench__error">
|
||||
{{ previewError }}
|
||||
</div>
|
||||
|
||||
<div class="workbench__footer">
|
||||
<div class="workbench__actions">
|
||||
<ElButton :disabled="startLoading" @click="handleCancel">
|
||||
{{ $t('button.cancel') }}
|
||||
</ElButton>
|
||||
@@ -457,6 +445,18 @@ watch(
|
||||
{{ $t('button.startIndex') }}
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<section class="workbench__content">
|
||||
<SplitterDocPreview
|
||||
:loading="previewLoading"
|
||||
:preview-items="previewItems"
|
||||
@preview-session-change="handlePreviewSessionChange"
|
||||
/>
|
||||
</section>
|
||||
|
||||
<div v-if="previewError" class="workbench__error">
|
||||
{{ previewError }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -484,6 +484,15 @@ watch(
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.workbench__actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.workbench__content {
|
||||
min-height: 620px;
|
||||
}
|
||||
@@ -500,22 +509,6 @@ watch(
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.workbench__footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
padding: 16px 0 8px;
|
||||
margin-top: auto;
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgb(246 248 251 / 0%) 0%,
|
||||
rgb(246 248 251 / 82%) 30%,
|
||||
rgb(246 248 251 / 100%) 100%
|
||||
);
|
||||
}
|
||||
|
||||
:deep(.el-form-item__label) {
|
||||
padding-bottom: 6px;
|
||||
font-size: 13px;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export {
|
||||
ArrowDown,
|
||||
ArrowDownToLine,
|
||||
ArrowLeft,
|
||||
ArrowLeftToLine,
|
||||
ArrowRightLeft,
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { BacktopProps } from './backtop';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { ArrowUpToLine } from '@easyflow-core/icons';
|
||||
import { ArrowDownToLine, ArrowUpToLine } from '@easyflow-core/icons';
|
||||
|
||||
import { EasyFlowButton } from '../button';
|
||||
import { useBackTop } from './use-backtop';
|
||||
@@ -14,9 +14,11 @@ defineOptions({ name: 'BackTop' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
bottom: 20,
|
||||
bottomLabel: 'Scroll to bottom',
|
||||
isGroup: false,
|
||||
right: 24,
|
||||
target: '',
|
||||
topLabel: 'Back to top',
|
||||
visibilityHeight: 200,
|
||||
});
|
||||
|
||||
@@ -25,19 +27,45 @@ const backTopStyle = computed(() => ({
|
||||
right: `${props.right}px`,
|
||||
}));
|
||||
|
||||
const { handleClick, visible } = useBackTop(props);
|
||||
const {
|
||||
canScrollDown,
|
||||
canScrollUp,
|
||||
handleBottomClick,
|
||||
handleClick,
|
||||
scrollable,
|
||||
visible,
|
||||
} = useBackTop(props);
|
||||
</script>
|
||||
<template>
|
||||
<transition name="fade-down">
|
||||
<EasyFlowButton
|
||||
v-if="visible"
|
||||
<div
|
||||
v-if="isGroup ? scrollable : visible"
|
||||
:style="backTopStyle"
|
||||
class="dark:bg-accent dark:hover:bg-heavy bg-background hover:bg-heavy data shadow-float z-popup fixed bottom-10 size-10 rounded-full duration-500"
|
||||
size="icon"
|
||||
variant="icon"
|
||||
@click="handleClick"
|
||||
class="z-popup fixed flex flex-col gap-2"
|
||||
>
|
||||
<ArrowUpToLine class="size-4" />
|
||||
</EasyFlowButton>
|
||||
<EasyFlowButton
|
||||
:aria-label="topLabel"
|
||||
:disabled="isGroup && !canScrollUp"
|
||||
:title="topLabel"
|
||||
class="dark:bg-accent dark:hover:bg-heavy bg-background hover:bg-heavy shadow-float size-10 rounded-full duration-150"
|
||||
size="icon"
|
||||
variant="icon"
|
||||
@click="handleClick"
|
||||
>
|
||||
<ArrowUpToLine class="size-4" />
|
||||
</EasyFlowButton>
|
||||
<EasyFlowButton
|
||||
v-if="isGroup"
|
||||
:aria-label="bottomLabel"
|
||||
:disabled="!canScrollDown"
|
||||
:title="bottomLabel"
|
||||
class="dark:bg-accent dark:hover:bg-heavy bg-background hover:bg-heavy shadow-float size-10 rounded-full duration-150"
|
||||
size="icon"
|
||||
variant="icon"
|
||||
@click="handleBottomClick"
|
||||
>
|
||||
<ArrowDownToLine class="size-4" />
|
||||
</EasyFlowButton>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
@@ -6,6 +6,20 @@ export const backtopProps = {
|
||||
default: 40,
|
||||
type: Number,
|
||||
},
|
||||
/**
|
||||
* @zh_CN scroll-to-bottom button label.
|
||||
*/
|
||||
bottomLabel: {
|
||||
default: 'Scroll to bottom',
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* @zh_CN whether to show top and bottom controls as a group.
|
||||
*/
|
||||
isGroup: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
/**
|
||||
* @zh_CN right distance.
|
||||
*/
|
||||
@@ -20,6 +34,13 @@ export const backtopProps = {
|
||||
default: '',
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* @zh_CN back-to-top button label.
|
||||
*/
|
||||
topLabel: {
|
||||
default: 'Back to top',
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* @zh_CN the button will not show until the scroll height reaches this value.
|
||||
*/
|
||||
@@ -31,8 +52,10 @@ export const backtopProps = {
|
||||
|
||||
export interface BacktopProps {
|
||||
bottom?: number;
|
||||
bottomLabel?: string;
|
||||
isGroup?: boolean;
|
||||
right?: number;
|
||||
target?: string;
|
||||
topLabel?: string;
|
||||
visibilityHeight?: number;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
import type { BacktopProps } from './backtop';
|
||||
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
import { nextTick, onMounted, ref, shallowRef } from 'vue';
|
||||
|
||||
import { useEventListener, useThrottleFn } from '@vueuse/core';
|
||||
import {
|
||||
useEventListener,
|
||||
useResizeObserver,
|
||||
useThrottleFn,
|
||||
} from '@vueuse/core';
|
||||
|
||||
export const useBackTop = (props: BacktopProps) => {
|
||||
const el = shallowRef<HTMLElement>();
|
||||
const container = shallowRef<Document | HTMLElement>();
|
||||
const resizeTarget = shallowRef<HTMLElement>();
|
||||
const canScrollDown = ref(false);
|
||||
const canScrollUp = ref(false);
|
||||
const scrollable = ref(false);
|
||||
const visible = ref(false);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (el.value) {
|
||||
const maxScrollTop = Math.max(
|
||||
el.value.scrollHeight - el.value.clientHeight,
|
||||
0,
|
||||
);
|
||||
scrollable.value = maxScrollTop > 1;
|
||||
canScrollUp.value = el.value.scrollTop > 1;
|
||||
canScrollDown.value = el.value.scrollTop < maxScrollTop - 1;
|
||||
visible.value = el.value.scrollTop >= (props?.visibilityHeight ?? 0);
|
||||
}
|
||||
};
|
||||
@@ -19,12 +34,24 @@ export const useBackTop = (props: BacktopProps) => {
|
||||
el.value?.scrollTo({ behavior: 'smooth', top: 0 });
|
||||
};
|
||||
|
||||
const handleBottomClick = () => {
|
||||
if (!el.value) {
|
||||
return;
|
||||
}
|
||||
el.value.scrollTo({
|
||||
behavior: 'smooth',
|
||||
top: Math.max(el.value.scrollHeight - el.value.clientHeight, 0),
|
||||
});
|
||||
};
|
||||
|
||||
const handleScrollThrottled = useThrottleFn(handleScroll, 300, true);
|
||||
|
||||
useEventListener(container, 'scroll', handleScrollThrottled);
|
||||
useResizeObserver(resizeTarget, handleScrollThrottled);
|
||||
onMounted(() => {
|
||||
container.value = document;
|
||||
el.value = document.documentElement;
|
||||
resizeTarget.value = document.body;
|
||||
|
||||
if (props.target) {
|
||||
el.value = document.querySelector<HTMLElement>(props.target) ?? undefined;
|
||||
@@ -33,13 +60,17 @@ export const useBackTop = (props: BacktopProps) => {
|
||||
throw new Error(`target does not exist: ${props.target}`);
|
||||
}
|
||||
container.value = el.value;
|
||||
resizeTarget.value = el.value;
|
||||
}
|
||||
// Give visible an initial value, fix #13066
|
||||
handleScroll();
|
||||
void nextTick(handleScroll);
|
||||
});
|
||||
|
||||
return {
|
||||
canScrollDown,
|
||||
canScrollUp,
|
||||
handleBottomClick,
|
||||
handleClick,
|
||||
scrollable,
|
||||
visible,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -425,7 +425,11 @@ const headerSlots = computed(() => {
|
||||
@clear-preferences-and-logout="clearPreferencesAndLogout"
|
||||
/>
|
||||
</template>
|
||||
<EasyFlowBackTop />
|
||||
<EasyFlowBackTop
|
||||
:bottom-label="$t('button.goToBottom')"
|
||||
is-group
|
||||
:top-label="$t('button.backToTop')"
|
||||
/>
|
||||
</template>
|
||||
</EasyFlowAdminLayout>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user