Files
Docker/WebVue/src/components/basic/Toolbar.vue
T
2026-09-19 10:24:21 +08:00

312 lines
8.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-row :wrap="false">
<a-col flex="auto">
<a-row>
<a-col flex="100px">
<a-row justify="start">
<a-col :span="8"
><a-tooltip title="返回顶层">
<a-button
type="text"
class="nav-btn"
:disabled="!canGoTop"
@click="$emit('go-top')"
>
<HomeOutlined />
</a-button> </a-tooltip
></a-col>
<a-col :span="8"
><a-tooltip title="返回上层">
<a-button
type="text"
class="nav-btn"
:disabled="!canGoBack"
@click="$emit('go-back')"
>
<ArrowLeftOutlined />
</a-button> </a-tooltip
></a-col>
<a-col :span="8"
><a-tooltip title="刷新">
<a-button
type="text"
class="nav-btn"
:loading="loading"
@click="onRefresh"
>
<ReloadOutlined />
</a-button> </a-tooltip
></a-col>
</a-row>
</a-col>
<a-col :xs="0" :sm="0" :lg="20" flex="auto">
<div
class="toolbar-center"
style="margin-top: 5px; margin-left: 10px"
>
<a-breadcrumb class="breadcrumb">
<a-breadcrumb-item
v-for="(item, index) in breadcrumbItems"
:key="index"
@click="onBreadcrumbClick(item, index)"
:class="{ clickable: index < breadcrumbItems.length - 1 }"
>
<template v-if="index === 0">
<LaptopOutlined style="margin-right: 4px" />
</template>
<template v-else>
<FolderOutlined style="margin-right: 4px" />
</template>
<span
:style="{
cursor:
index < breadcrumbItems.length - 1
? 'pointer'
: 'default',
color: index < breadcrumbItems.length - 1 ? '#1677ff' : '',
}"
>{{ item.displayName }}</span
>
</a-breadcrumb-item>
</a-breadcrumb>
</div>
</a-col>
<a-col :xs="10" :sm="10" :lg="0" flex="auto">
<div
v-if="viewMode == 'list'"
style="
margin-top: 8px;
margin-left: 10px;
display: flex;
align-items: center;
"
>
<FolderOutlined style="margin-right: 4px" />
<span class="breadcrumb-text">{{
breadcrumbItems.length > 0
? breadcrumbItems[breadcrumbItems.length - 1].displayName
: ""
}}</span>
</div>
<div
style="
max-width: 260px;
min-width: 220px;
margin-left: 20px;
margin-top: -2px;
display: flex;
justify-content: center;
gap: 4px;
"
v-else
>
<a-input-search
v-model:value="searchKeyword"
placeholder="搜索文件名称"
allow-clear
@search="onSearch"
@pressEnter="onSearch"
>
</a-input-search>
<a-button size="small" type="default" @click="viewMode = 'list'" style="height: 32px;">
<cust-icon-font size="16" type="icon-gongcheng" />
</a-button>
</div>
</a-col> </a-row
></a-col>
<a-col :xs="0" :sm="0" :lg="10" flex="none">
<div style="margin-bottom: 8px; margin-right: 8px">
<a-input-search
v-model:value="searchKeyword"
placeholder="搜索文件名称"
allow-clear
@search="onSearch"
@pressEnter="onSearch"
>
</a-input-search>
</div>
</a-col>
<a-col :xs="4" :sm="4" :lg="0" flex="none">
<div
v-if="viewMode == 'list'"
style="
margin-right: 20px;
display: flex;
justify-content: center;
gap: 4px;
"
>
<a-button size="small" type="default" @click="viewMode = 'search'">
<cust-icon-font size="14" type="icon-sousuo" />
</a-button>
<a-button
size="small"
type="default"
title="使用帮助"
@click="helpVisible = true"
>
<cust-icon-font size="14" type="icon-help" />
</a-button>
</div>
</a-col>
<a-col :xs="0" :sm="0" :lg="1" flex="none">
<div
v-if="viewMode == 'list'"
style="
margin-right: 20px;
display: flex;
justify-content: center;
gap: 4px;
"
>
<a-button
style="height: 32px"
size="small"
type="default"
title="使用帮助"
@click="helpVisible = true"
>
<cust-icon-font size="16" type="icon-help" />
</a-button>
</div>
</a-col>
<!-- 使用帮助弹窗:内容默认取「我的云盘」页面说明,父组件可用 #help 插槽覆盖 -->
<!-- 宽度不写死:跟随 DragModal 的默认宽度(视口 90%) -->
<dragModal
v-model:open="helpVisible"
title="我的云盘 · 使用帮助"
:footer="null"
:fullscreen="width < 640"
>
<slot name="help">
<DmsFileEnquiryHelp />
</slot>
</dragModal>
</a-row>
</template>
<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { useWindowSize } from "@vueuse/core";
import {
HomeOutlined,
ArrowLeftOutlined,
ReloadOutlined,
LaptopOutlined,
FolderOutlined,
SearchOutlined,
} from "@ant-design/icons-vue";
import dragModal from "@/components/basic/DragModal.vue";
import DmsFileEnquiryHelp from "@/components/basic/DmsFileEnquiryHelp.vue";
interface BreadcrumbItem {
value: string;
displayName: string;
}
const props = defineProps<{
pathStack: BreadcrumbItem[];
rootPath: string;
loading: boolean;
}>();
const emit = defineEmits([
"go-top",
"go-back",
"refresh",
"search",
"breadcrumb-click",
]);
const searchKeyword = ref("");
const viewMode = ref<"search" | "list">("list");
/** 使用帮助弹窗显示状态 */
const helpVisible = ref(false);
/** 小屏(手机)下帮助弹窗全屏显示 */
const { width } = useWindowSize();
const canGoTop = computed(() => {
return props.pathStack.length > 1;
});
const canGoBack = computed(() => {
return props.pathStack.length > 1;
});
const breadcrumbItems = computed(() => {
return props.pathStack;
});
const onSearch = () => {
emit("search", searchKeyword.value);
};
const onBreadcrumbClick = (item: BreadcrumbItem, index: number) => {
emit("breadcrumb-click", item, index);
};
/**
* 刷新:重新加载「当前所在目录」,也就是路径的最后一个节点。
* 不能取第一个节点,否则会把路径截回到根目录,变成跳转而不是刷新。
*/
const onRefresh = () => {
const lastIndex = breadcrumbItems.value.length - 1;
if (lastIndex < 0) return;
onBreadcrumbClick(breadcrumbItems.value[lastIndex], lastIndex);
};
</script>
<style lang="less">
.nav-btn {
width: 36px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.nav-btn:hover:not(:disabled) {
background: #e6f4ff;
color: #1677ff;
}
.nav-btn:disabled {
color: #bfbfbf;
}
.toolbar-center {
flex: 1;
overflow: hidden;
}
.breadcrumb {
margin: 0;
font-size: 14px;
}
.breadcrumb-item.clickable {
cursor: pointer;
}
.breadcrumb-item.clickable:hover {
color: #1677ff;
}
.toolbar-right {
flex-shrink: 0;
}
.breadcrumb-text {
display: inline-block;
max-width: 16ch; /* 6个字符宽度,ch 按字符计算 */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>