fix: 模型库页面移动端适配
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ModelApiTypeOption, ModelLibraryDto, ModelTypeOption } from '@/api/model/types';
|
import type { ModelApiTypeOption, ModelLibraryDto, ModelTypeOption } from '@/api/model/types';
|
||||||
import { Box, CopyDocument, HomeFilled, OfficeBuilding, Search } from '@element-plus/icons-vue';
|
import { Box, Close, CopyDocument, HomeFilled, OfficeBuilding, Search } from '@element-plus/icons-vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { onMounted, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { getApiTypeOptions, getModelLibraryList, getModelTypeOptions, getProviderList } from '@/api/model';
|
import { getApiTypeOptions, getModelLibraryList, getModelTypeOptions, getProviderList } from '@/api/model';
|
||||||
|
import { useScreenStore } from '@/hooks/useScreen';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { isMobile } = useScreenStore();
|
||||||
|
|
||||||
|
const showMobileFilter = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const modelList = ref<ModelLibraryDto[]>([]);
|
const modelList = ref<ModelLibraryDto[]>([]);
|
||||||
const totalCount = ref(0);
|
const totalCount = ref(0);
|
||||||
@@ -201,6 +204,14 @@ watch([selectedProviders, selectedModelTypes, selectedApiTypes, isPremiumOnly],
|
|||||||
fetchModelList();
|
fetchModelList();
|
||||||
}, { deep: true });
|
}, { deep: true });
|
||||||
|
|
||||||
|
function openMobileFilter() {
|
||||||
|
showMobileFilter.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeMobileFilter() {
|
||||||
|
showMobileFilter.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchProviderList();
|
fetchProviderList();
|
||||||
fetchModelTypeOptions();
|
fetchModelTypeOptions();
|
||||||
@@ -270,16 +281,24 @@ onMounted(() => {
|
|||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<!-- 左侧筛选栏 -->
|
<!-- 左侧筛选栏 -->
|
||||||
<aside class="filter-sidebar">
|
<aside class="filter-sidebar" :class="{ 'mobile-active': showMobileFilter }">
|
||||||
|
<!-- 移动端遮罩 -->
|
||||||
|
<div v-if="isMobile && showMobileFilter" class="mobile-overlay" @click="closeMobileFilter" />
|
||||||
|
|
||||||
<div class="filter-section">
|
<div class="filter-section">
|
||||||
<div class="filter-header">
|
<div class="filter-header">
|
||||||
<h3 class="filter-title">
|
<h3 class="filter-title">
|
||||||
<el-icon><i-ep-filter /></el-icon>
|
<el-icon><i-ep-filter /></el-icon>
|
||||||
筛选条件
|
筛选条件
|
||||||
</h3>
|
</h3>
|
||||||
<el-button link type="primary" size="small" @click="resetFilters">
|
<div class="filter-header-actions">
|
||||||
重置
|
<el-button link type="primary" size="small" @click="resetFilters">
|
||||||
</el-button>
|
重置
|
||||||
|
</el-button>
|
||||||
|
<el-icon v-if="isMobile" class="close-btn" @click="closeMobileFilter">
|
||||||
|
<Close />
|
||||||
|
</el-icon>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 搜索框 -->
|
<!-- 搜索框 -->
|
||||||
@@ -399,6 +418,13 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 右侧模型列表 -->
|
<!-- 右侧模型列表 -->
|
||||||
<main class="model-list-section">
|
<main class="model-list-section">
|
||||||
|
<!-- 移动端筛选按钮 -->
|
||||||
|
<div v-if="isMobile" class="mobile-filter-bar">
|
||||||
|
<el-button type="primary" :icon="Search" plain class="w-full" @click="openMobileFilter">
|
||||||
|
筛选与搜索
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态 -->
|
||||||
<div v-if="loading" class="loading-wrapper">
|
<div v-if="loading" class="loading-wrapper">
|
||||||
<el-skeleton :rows="8" animated />
|
<el-skeleton :rows="8" animated />
|
||||||
@@ -494,7 +520,7 @@ onMounted(() => {
|
|||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
:page-sizes="[12, 24, 48, 96]"
|
:page-sizes="[12, 24, 48, 96]"
|
||||||
:total="totalCount"
|
:total="totalCount"
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
:layout="isMobile ? 'prev, pager, next' : 'total, sizes, prev, pager, next, jumper'"
|
||||||
prev-text="上一页"
|
prev-text="上一页"
|
||||||
next-text="下一页"
|
next-text="下一页"
|
||||||
background
|
background
|
||||||
@@ -883,6 +909,7 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
.model-icon {
|
.model-icon {
|
||||||
width: 56px;
|
width: 56px;
|
||||||
@@ -914,15 +941,19 @@ onMounted(() => {
|
|||||||
.model-info {
|
.model-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
.model-name {
|
.model-name {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
margin: 0 0 6px 0;
|
margin: 0 0 4px 0;
|
||||||
//overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 24px; // 防止文字遮挡复制按钮
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-provider {
|
.model-provider {
|
||||||
@@ -948,7 +979,6 @@ onMounted(() => {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: #909399;
|
color: #909399;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
//text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1102,4 +1132,251 @@ onMounted(() => {
|
|||||||
background-position: 0% 50%;
|
background-position: 0% 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.model-library-container {
|
||||||
|
.banner-section {
|
||||||
|
padding: 24px 16px;
|
||||||
|
|
||||||
|
.banner-content {
|
||||||
|
.banner-header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
.banner-left {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 24px;
|
||||||
|
|
||||||
|
.stats-cards {
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 16px 12px;
|
||||||
|
|
||||||
|
.content-wrapper {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.filter-sidebar {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.mobile-active {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
.mobile-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1999;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-section {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 80%;
|
||||||
|
max-width: 320px;
|
||||||
|
z-index: 2000;
|
||||||
|
border-radius: 0 16px 16px 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
animation: slideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-section {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.filter-header {
|
||||||
|
.filter-header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
font-size: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #909399;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-list-section {
|
||||||
|
.mobile-filter-bar {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background: rgba(245, 247, 250, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
padding: 12px 4px;
|
||||||
|
margin: -16px -4px 16px -4px;
|
||||||
|
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
:deep(.el-pagination) {
|
||||||
|
.el-pagination__jump {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-grid {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.model-card {
|
||||||
|
padding: 20px; // 增加内边距
|
||||||
|
margin-bottom: 16px; // 增加卡片间距
|
||||||
|
border-radius: 16px;
|
||||||
|
|
||||||
|
.copy-btn-corner {
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-card-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.model-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
.icon-placeholder {
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-img {
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-info {
|
||||||
|
.model-name {
|
||||||
|
font-size: 17px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-right: 36px; // 移动端预留更多空间给复制按钮
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-provider {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-id {
|
||||||
|
padding: 6px 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.model-id-label {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-id-value {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-description {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
min-height: auto;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
max-height: 3em;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
/* 移动端取消悬停展开,或者改为点击展开(这里简单处理为取消悬停效果以免遮挡) */
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
max-height: 3em;
|
||||||
|
overflow: hidden;
|
||||||
|
position: static;
|
||||||
|
box-shadow: none;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-footer {
|
||||||
|
padding-top: 12px;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.model-tags {
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
:deep(.el-tag) {
|
||||||
|
height: 20px;
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.model-pricing {
|
||||||
|
padding: 4px 8px;
|
||||||
|
|
||||||
|
.pricing-label {
|
||||||
|
display: none; // 移动端隐藏"计费倍率"文字,只显示数字
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing-value {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
1
Yi.Ai.Vue3/types/import_meta.d.ts
vendored
@@ -7,6 +7,7 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_WEB_BASE_API: string;
|
readonly VITE_WEB_BASE_API: string;
|
||||||
readonly VITE_API_URL: string;
|
readonly VITE_API_URL: string;
|
||||||
readonly VITE_FILE_UPLOAD_API: string;
|
readonly VITE_FILE_UPLOAD_API: string;
|
||||||
|
readonly VITE_BUILD_COMPRESS: string;
|
||||||
readonly VITE_SSO_SEVER_URL: string;
|
readonly VITE_SSO_SEVER_URL: string;
|
||||||
readonly VITE_APP_VERSION: string;
|
readonly VITE_APP_VERSION: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user