refactor(frontend): 统一管理页面工具条布局和操作列样式
## 修复内容 ### 1. 统一操作列按钮样式 - 所有操作列按钮统一为"图标+文字"垂直排列样式 - UsersView: 编辑和更多按钮添加文字标签 - 与 AccountsView、GroupsView 等页面保持一致 ### 2. 统一顶部工具条布局(6个管理页面) - 使用 flex + justify-between 布局 - 左侧:模糊搜索框、筛选器(可多行排列) - 右侧:刷新、创建等操作按钮(靠右对齐) - 响应式:宽度不够时右侧按钮自动换行到上一行 ### 3. 修复的页面 - AccountsView: 合并 actions/filters 到单行工具条 - UsersView: 标准左右分栏,操作列添加文字 - GroupsView: 新增搜索框,左右分栏布局 - ProxiesView: 左右分栏,响应式布局 - SubscriptionsView: 新增用户模糊搜索,左右分栏 - UsageView: 补齐所有筛选项,左右分栏 ### 4. 新增功能 - GroupsView: 新增分组名称/描述模糊搜索 - SubscriptionsView: 新增用户模糊搜索功能 - UsageView: 补齐 API Key 搜索筛选 ### 5. 国际化 - 新增相关搜索框的 placeholder 文案(中英文) ## 技术细节 - 使用 flex-wrap-reverse 实现响应式换行 - 左侧筛选区使用 flex-wrap 支持多行 - 右侧按钮区使用 ml-auto + justify-end 保持右对齐 - 移动端使用 w-full sm:w-* 响应式宽度 ## 验证结果 - ✅ TypeScript 类型检查通过 - ✅ 所有页面布局统一 - ✅ 响应式布局正常工作
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<div class="flex justify-end gap-3">
|
||||
<button @click="$emit('refresh')" :disabled="loading" class="btn btn-secondary"><svg :class="['h-5 w-5', loading ? 'animate-spin' : '']" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></svg></button>
|
||||
<button @click="$emit('sync')" class="btn btn-secondary">{{ t('admin.accounts.syncFromCrs') }}</button>
|
||||
<button @click="$emit('create')" class="btn btn-primary">{{ t('admin.accounts.createAccount') }}</button>
|
||||
<div class="flex max-w-full flex-wrap justify-end gap-3">
|
||||
<button @click="$emit('refresh')" :disabled="loading" class="btn btn-secondary flex-shrink-0"><svg :class="['h-5 w-5', loading ? 'animate-spin' : '']" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /></svg></button>
|
||||
<button @click="$emit('sync')" class="btn btn-secondary flex-shrink-0">{{ t('admin.accounts.syncFromCrs') }}</button>
|
||||
<button @click="$emit('create')" class="btn btn-primary flex-shrink-0">{{ t('admin.accounts.createAccount') }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'; defineProps(['loading']); defineEmits(['refresh', 'sync', 'create']); const { t } = useI18n()
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div class="relative max-w-md flex-1">
|
||||
<SearchInput
|
||||
:model-value="searchQuery"
|
||||
:placeholder="t('admin.accounts.searchAccounts')"
|
||||
<div class="flex flex-wrap items-start gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<SearchInput
|
||||
:model-value="searchQuery"
|
||||
:placeholder="t('admin.accounts.searchAccounts')"
|
||||
@update:model-value="$emit('update:searchQuery', $event)"
|
||||
@search="$emit('change')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<Select v-model="filters.platform" :options="pOpts" @change="$emit('change')" />
|
||||
<Select v-model="filters.status" :options="sOpts" @change="$emit('change')" />
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<Select v-model="filters.platform" class="w-40 flex-shrink-0" :options="pOpts" @change="$emit('change')" />
|
||||
<Select v-model="filters.status" class="w-40 flex-shrink-0" :options="sOpts" @change="$emit('change')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user