初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,221 @@
<script setup>
import { computed, ref } from 'vue';
import { ArrowDown } from '@element-plus/icons-vue';
import {
ElButton,
ElDropdown,
ElDropdownItem,
ElDropdownMenu,
ElIcon,
ElInput,
} from 'element-plus';
import { hasPermission } from '#/api/common/hasPermission.ts';
import { $t } from '#/locales';
// 定义组件属性
const props = defineProps({
// 按钮配置数组
buttons: {
type: Array,
default: () => [],
validator: (value) => {
return value.every((button) => {
return (
typeof button.text === 'string' &&
(button.key || typeof button.key === 'string')
);
});
},
},
// 最大显示按钮数量(不包括下拉菜单)
maxVisibleButtons: {
type: Number,
default: 3,
},
// 搜索框占位符
searchPlaceholder: {
type: String,
default: $t('common.searchPlaceholder'),
},
});
const emit = defineEmits(['search', 'button-click', 'buttonClick']);
// 搜索值
const searchValue = ref('');
// 计算显示的按钮
const visibleButtons = computed(() => {
return props.buttons.slice(0, props.maxVisibleButtons);
});
// 计算下拉菜单中的按钮
const dropdownButtons = computed(() => {
const dropdownButtonsTemp = props.buttons.slice(props.maxVisibleButtons);
if (dropdownButtonsTemp.length === 0) {
return [];
}
return dropdownButtonsTemp.value.filter((action) => {
return hasPermission([action.permission]);
});
});
// 处理搜索
const handleSearch = () => {
emit('search', searchValue.value);
};
const handleReset = () => {
searchValue.value = '';
emit('search', '');
};
// 处理按钮点击
const handleButtonClick = (button) => {
emit('buttonClick', {
type: 'button',
key: button.key,
button,
data: button.data,
});
};
// 处理下拉菜单点击
const handleDropdownClick = (button) => {
emit('buttonClick', {
type: 'dropdown',
key: button.key,
button,
data: button.data,
});
};
</script>
<template>
<div class="custom-header">
<!-- 左侧搜索区域 -->
<div class="header-left">
<div class="search-container">
<div>
<ElInput
v-model="searchValue"
:placeholder="$t('common.searchPlaceholder')"
class="search-input"
@keyup.enter="handleSearch"
clearable
/>
</div>
<div>
<ElButton type="primary" auto-insert-space @click="handleSearch">
{{ $t('button.query') }}
</ElButton>
</div>
<div>
<ElButton auto-insert-space @click="handleReset">
{{ $t('button.reset') }}
</ElButton>
</div>
</div>
</div>
<!-- 右侧按钮区域 -->
<div class="header-right">
<!-- 显示的按钮最多3个 -->
<template
v-for="(button, index) in visibleButtons"
:key="button.key || index"
>
<ElButton
:type="button.type || 'default'"
:icon="button.icon"
:disabled="button.disabled"
v-access:code="button.permission"
@click="handleButtonClick(button)"
>
{{ button.text }}
</ElButton>
</template>
<!-- 下拉菜单隐藏的按钮 -->
<ElDropdown
v-if="dropdownButtons.length > 0"
@command="handleDropdownClick"
>
<ElButton>
{{ $t('button.more')
}}<ElIcon class="el-icon--right"><ArrowDown /></ElIcon>
</ElButton>
<template #dropdown>
<ElDropdownMenu>
<ElDropdownItem
v-for="button in dropdownButtons"
:key="button.key"
:command="button"
:disabled="button.disabled"
>
<ElIcon v-if="button.icon">
<component :is="button.icon" />
</ElIcon>
<span style="margin-left: 8px">{{ button.text }}</span>
</ElDropdownItem>
</ElDropdownMenu>
</template>
</ElDropdown>
</div>
</div>
</template>
<style scoped>
/* 响应式设计 */
@media (max-width: 768px) {
.custom-header {
flex-direction: column;
gap: 16px;
padding: 12px 16px;
}
.header-left,
.header-right {
justify-content: center;
width: 100%;
}
.search-container {
width: 100%;
}
.header-right {
flex-wrap: wrap;
}
}
.custom-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.header-left {
display: flex;
align-items: center;
}
.search-container {
display: flex;
gap: 12px;
}
.search-input {
width: 300px;
border-radius: 4px;
}
.header-right {
display: flex;
gap: 12px;
align-items: center;
}
</style>