chore: initialize qiming workspace repository
This commit is contained in:
741
qiming-mobile/subpackages/pages/agent-search/agent-search.uvue
Normal file
741
qiming-mobile/subpackages/pages/agent-search/agent-search.uvue
Normal file
@@ -0,0 +1,741 @@
|
||||
<template>
|
||||
<view class="search-page-container flex flex-col">
|
||||
<!-- 微信安全区 -->
|
||||
<safety-zone></safety-zone>
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-container border-b">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<text class="cancel-text" @click="jumpNavigateBack">{{
|
||||
t("Mobile.Common.cancel")
|
||||
}}</text>
|
||||
<!-- #endif -->
|
||||
<view class="search-box" :class="{ focused: isFocused }">
|
||||
<text class="iconfont icon-Search"></text>
|
||||
<input
|
||||
ref="searchInputRef"
|
||||
class="search-input"
|
||||
:placeholder="searchPlaceholder"
|
||||
placeholder-class="placeholder-class"
|
||||
type="search"
|
||||
confirm-type="search"
|
||||
:value="searchKeyword"
|
||||
@confirm="handleSearch($event.detail.value)"
|
||||
@input="handleSearch($event.detail.value)"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
:auto-focus="shouldAutoFocus"
|
||||
/>
|
||||
<template v-if="searchKeyword">
|
||||
<text
|
||||
class="iconfont icon-a-Xcircle-fill clear-icon"
|
||||
@click="handleSearch('')"
|
||||
></text>
|
||||
</template>
|
||||
</view>
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<text class="cancel-text" @click="jumpNavigateBack">{{
|
||||
t("Mobile.Common.cancel")
|
||||
}}</text>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 微信胶囊按钮功能区 -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<view
|
||||
class="right-safety"
|
||||
:style="{
|
||||
width: clientRect.width + 13 + 'px',
|
||||
height: clientRect.height + 'px',
|
||||
}"
|
||||
></view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<!-- 滚动部分 -->
|
||||
<view class="scroll-section flex-1">
|
||||
<scroll-view
|
||||
class="h-full"
|
||||
@scrolltolower="handleLoadMore"
|
||||
:refresher-enabled="searchMode === 'api'"
|
||||
:refresher-triggered="refreshing || false"
|
||||
@refresherrefresh="handleRefresh"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<template v-if="(agentList || []).length">
|
||||
<template v-for="info in agentList" :key="info.id">
|
||||
<!-- 页面应用 -->
|
||||
<page-card
|
||||
v-if="cardComponentType === 'PageApp'"
|
||||
:coverImg="info.coverImg"
|
||||
:name="info.name"
|
||||
:userName="info.publishUser?.userName"
|
||||
:created="info.created"
|
||||
@click="handleAgentClick(info)"
|
||||
/>
|
||||
<!-- 首页 -->
|
||||
<recent-used-agent-item
|
||||
v-else-if="cardComponentType === 'Home'"
|
||||
:icon="info.icon"
|
||||
:name="info.name"
|
||||
:description="info.description"
|
||||
:paymentRequired="info.paymentRequired && enableSubscription === 1"
|
||||
:subscribed="info.subscribed"
|
||||
@click="handleRecentUsedAgentClick(info)"
|
||||
/>
|
||||
<!-- 聊天机器人: 默认 cardComponentType 为 ChatBot -->
|
||||
<agent-component
|
||||
v-else
|
||||
:icon="info.icon"
|
||||
:name="info.name"
|
||||
:avatar="info.publishUser?.avatar"
|
||||
:userName="
|
||||
info.publishUser?.nickName || info.publishUser?.userName
|
||||
"
|
||||
:description="info.description"
|
||||
:userCount="info.statistics?.userCount"
|
||||
:convCount="info.statistics?.convCount"
|
||||
:collectCount="info.statistics?.collectCount"
|
||||
:paymentRequired="info.paymentRequired && enableSubscription === 1"
|
||||
:subscribed="info.subscribed"
|
||||
@click="handleAgentClick(info)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-if="loading">
|
||||
<view class="loading-state">
|
||||
<text class="loading-text">{{ t("Mobile.Page.loadingMore") }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template
|
||||
v-else-if="!loading && searchKeyword && agentList?.length === 0"
|
||||
>
|
||||
<view class="empty-state">
|
||||
<image
|
||||
:src="noData"
|
||||
class="empty-image"
|
||||
:alt="t('Mobile.Common.noDataImageAlt')"
|
||||
/>
|
||||
<text class="empty-text">{{
|
||||
t("Mobile.AgentSearch.emptyResult")
|
||||
}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-if="showNoMore">
|
||||
<view class="no-more-state">
|
||||
<text class="no-more-text">{{
|
||||
t("Mobile.Common.noMoreData")
|
||||
}}</text>
|
||||
</view>
|
||||
</template>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="uts" setup>
|
||||
import { apiPublishedAgentList } from "@/servers/square";
|
||||
import { apiUserUsedAgentList } from "@/servers/agentDev";
|
||||
import {
|
||||
SquarePublishedListParams,
|
||||
SquarePublishedItemInfo,
|
||||
} from "@/types/interfaces/square";
|
||||
import { SquareAgentTypeEnum } from "@/types/enums/square";
|
||||
import { Page, RequestResponse } from "@/types/interfaces/request";
|
||||
import { SUCCESS_CODE } from "@/constants/codes.constants";
|
||||
import { AgentComponentTypeEnum } from "@/types/enums/agent";
|
||||
import RecentUsedAgentItem from "@/components/recent-used-agent-item/recent-used-agent-item.uvue";
|
||||
import { apiTenantConfig } from "@/servers/account";
|
||||
import { TENANT_CONFIG_INFO } from "@/constants/home.constants";
|
||||
import type { TenantConfigInfo } from "@/types/interfaces/login";
|
||||
import {
|
||||
jumpToAgentDetailPage,
|
||||
getCurrentPagePath,
|
||||
} from "@/utils/commonBusiness";
|
||||
import type { AgentInfo } from "@/types/interfaces/agent";
|
||||
import noData from "@/static/assets/no_data.png";
|
||||
// import { jumpNavigateBack } from '@/utils/common'
|
||||
import type { CategoryItemInfo } from "@/types/interfaces/agentConfig";
|
||||
import { AllowCopyEnum } from "@/types/enums/agent";
|
||||
import { PluginTypeEnum } from "@/types/enums/plugin";
|
||||
import { isTabPage } from "@/utils/commonBusiness";
|
||||
import { setCurrentPageNavigationBarTitle } from "@/utils/system.uts";
|
||||
import { useI18n, translateText } from "@/utils/i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const searchKeyword = ref<string>("");
|
||||
const searchTimeoutRef = ref<number | null>(null);
|
||||
const agentList = ref<SquarePublishedItemInfo[] | AgentInfo[]>([]);
|
||||
const hasMore = ref<boolean>(true);
|
||||
const page = ref<number>(1);
|
||||
const loading = ref<boolean>(false);
|
||||
const refreshing = ref<boolean>(false);
|
||||
// 搜索输入框ref
|
||||
const searchInputRef = ref<UniInputElement | null>(null);
|
||||
const isFocused = ref<boolean>(false);
|
||||
const cardComponentType = ref<"Home" | "PageApp" | "ChatBot">("ChatBot");
|
||||
// 租户信息
|
||||
const tenantConfigInfo = ref<TenantConfigInfo | null>(null);
|
||||
|
||||
// 租户订阅限制强受控标志
|
||||
const enableSubscription = computed((): number => {
|
||||
return tenantConfigInfo.value?.enableSubscription ?? 0;
|
||||
});
|
||||
// 搜索模式:'api' 接口搜索模式,'local' 前端过滤模式
|
||||
const searchMode = ref<"api" | "local">("api");
|
||||
// 前端过滤模式下的原始列表数据
|
||||
const localAgentList = ref<CategoryItemInfo[]>([]);
|
||||
// 搜索框 placeholder key(运行时翻译,保证语言切换可生效)
|
||||
const searchPlaceholderSource = ref<string>("Mobile.AgentSearch.placeholder");
|
||||
const searchPlaceholder = computed(() => {
|
||||
// 默认占位符 Key
|
||||
let placeholderKey = "Mobile.AgentSearch.placeholder";
|
||||
|
||||
// 如果是 PageApp 类型,切换 Key
|
||||
if (cardComponentType.value === "PageApp") {
|
||||
placeholderKey = "Mobile.AgentSearch.pageAppPlaceholder";
|
||||
}
|
||||
|
||||
// 如果 searchPlaceholderSource 有值且不是默认值(通常是 local 模式下从外部传入),则优先使用它
|
||||
const finalKey =
|
||||
searchPlaceholderSource.value &&
|
||||
searchPlaceholderSource.value !== "Mobile.AgentSearch.placeholder"
|
||||
? searchPlaceholderSource.value
|
||||
: placeholderKey;
|
||||
|
||||
return translateText(finalKey);
|
||||
});
|
||||
// 小程序平台控制 auto-focus
|
||||
const shouldAutoFocus = ref<boolean>(false);
|
||||
// 返回地址
|
||||
const backUrl = ref<string>("");
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
const clientRect = uni.getMenuButtonBoundingClientRect();
|
||||
// #endif
|
||||
|
||||
const jumpNavigateBack = () => {
|
||||
const toUrl = backUrl.value ? backUrl.value : "/pages/index/index";
|
||||
console.log(toUrl);
|
||||
|
||||
if (isTabPage(toUrl)) {
|
||||
uni.switchTab({ url: toUrl });
|
||||
} else {
|
||||
uni.redirectTo({ url: toUrl });
|
||||
}
|
||||
};
|
||||
|
||||
// 获取租户配置
|
||||
const fetchTenantConfig = async () => {
|
||||
const { code, data } = await apiTenantConfig();
|
||||
if (code === SUCCESS_CODE) {
|
||||
tenantConfigInfo.value = data;
|
||||
// 缓存租户信息
|
||||
uni.setStorageSync(TENANT_CONFIG_INFO, JSON.stringify(data));
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化租户配置数据
|
||||
const initTenantConfig = async () => {
|
||||
// 1. 首先尝试从本地缓存读取
|
||||
const tenantConfigInfoString = await uni.getStorageSync(TENANT_CONFIG_INFO);
|
||||
if (tenantConfigInfoString) {
|
||||
tenantConfigInfo.value = JSON.parse(tenantConfigInfoString);
|
||||
}
|
||||
// 2. 然后必须发起网络强同步请求,以获得最新的 enableSubscription 状态
|
||||
await fetchTenantConfig();
|
||||
};
|
||||
|
||||
onLoad(
|
||||
async (options: {
|
||||
type: "Home" | "PageApp" | "ChatBot";
|
||||
searchMode?: "api" | "local";
|
||||
backUrl?: string;
|
||||
}) => {
|
||||
// 强同步获取并更新租户配置信息,保障收费 tag 状态的百分百实时受控
|
||||
await initTenantConfig();
|
||||
|
||||
const { type, searchMode: mode } = options;
|
||||
backUrl.value = options.backUrl
|
||||
? decodeURIComponent(options.backUrl)
|
||||
: "";
|
||||
cardComponentType.value = type;
|
||||
// 设置当前页面导航栏标题
|
||||
setCurrentPageNavigationBarTitle();
|
||||
// 检测 searchMode 参数,区分搜索模式
|
||||
if (mode === "local") {
|
||||
// 从 globalData 获取列表数据和文案
|
||||
const app = getApp();
|
||||
// 兜底逻辑:如果 globalData 没有相关数据,回退到 api 模式
|
||||
if (
|
||||
app.globalData &&
|
||||
app.globalData.localAgentList &&
|
||||
Array.isArray(app.globalData.localAgentList) &&
|
||||
app.globalData.localAgentList.length > 0
|
||||
) {
|
||||
// 有数据,使用前端过滤模式
|
||||
searchMode.value = "local";
|
||||
localAgentList.value = app.globalData
|
||||
.localAgentList as CategoryItemInfo[];
|
||||
// 默认不显示全部列表,只有搜索时才显示
|
||||
agentList.value = [];
|
||||
hasMore.value = false;
|
||||
|
||||
// 获取搜索文案,如果没有则使用默认值
|
||||
if (app.globalData.searchPlaceholder) {
|
||||
searchPlaceholderSource.value = app.globalData
|
||||
.searchPlaceholder as string;
|
||||
}
|
||||
} else {
|
||||
// 没有数据,回退到 api 模式
|
||||
searchMode.value = "api";
|
||||
}
|
||||
} else {
|
||||
searchMode.value = "api";
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
// 页面加载完成后自动获取输入框焦点
|
||||
// #ifndef MP-WEIXIN
|
||||
// 非小程序平台可以直接调用 focus
|
||||
nextTick(() => {
|
||||
if (
|
||||
searchInputRef.value &&
|
||||
typeof searchInputRef.value.focus === "function"
|
||||
) {
|
||||
searchInputRef.value.focus();
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
|
||||
// 小程序平台使用 onReady 确保页面渲染完成后再聚焦
|
||||
onReady(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序平台:通过设置 auto-focus 为 true 来触发聚焦
|
||||
shouldAutoFocus.value = true;
|
||||
// #endif
|
||||
});
|
||||
|
||||
// 小程序页面显示时也尝试聚焦
|
||||
onShow(() => {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 小程序平台:通过重置 auto-focus 来重新触发聚焦
|
||||
shouldAutoFocus.value = false;
|
||||
nextTick(() => {
|
||||
shouldAutoFocus.value = true;
|
||||
});
|
||||
// #endif
|
||||
});
|
||||
|
||||
// 是否显示没有更多数据(前端过滤模式下不显示)
|
||||
const showNoMore = computed(() => {
|
||||
if (searchMode.value === "local") {
|
||||
return false;
|
||||
}
|
||||
return !loading.value && !hasMore.value && agentList.value?.length > 8;
|
||||
});
|
||||
|
||||
/**
|
||||
* 将 CategoryItemInfo 转换为 SquarePublishedItemInfo 格式
|
||||
* @param items CategoryItemInfo 数组
|
||||
* @returns SquarePublishedItemInfo 数组
|
||||
*/
|
||||
const convertCategoryItemsToSquareItems = (
|
||||
items: CategoryItemInfo[],
|
||||
): SquarePublishedItemInfo[] => {
|
||||
return items.map((item) => ({
|
||||
id: item.targetId, // 使用 targetId 作为 id
|
||||
tenantId: 0,
|
||||
spaceId: 0,
|
||||
targetType: item.targetType as SquareAgentTypeEnum,
|
||||
agentType: item.agentType,
|
||||
targetId: item.targetId,
|
||||
name: item.name,
|
||||
description: item.description || null,
|
||||
icon: item.icon,
|
||||
remark: "",
|
||||
modified: "",
|
||||
created: "",
|
||||
statistics: item.statistics,
|
||||
publishUser: item.publishUser,
|
||||
category: "",
|
||||
allowCopy: AllowCopyEnum.No,
|
||||
pluginType: PluginTypeEnum.HTTP,
|
||||
collect: item.collect,
|
||||
paymentRequired: (item as any).paymentRequired,
|
||||
subscribed: (item as any).subscribed,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
isFocused.value = true;
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
isFocused.value = false;
|
||||
};
|
||||
|
||||
// 获取智能体、页面应用列表
|
||||
const fetchAgentList = async (
|
||||
pageNum: number = 1,
|
||||
keyword: string = searchKeyword.value,
|
||||
) => {
|
||||
const params: SquarePublishedListParams = {
|
||||
targetType: AgentComponentTypeEnum.Agent,
|
||||
targetSubType:
|
||||
cardComponentType.value === "PageApp" ? "PageApp" : "ChatBot",
|
||||
page: pageNum,
|
||||
pageSize: 10,
|
||||
category: "",
|
||||
kw: keyword,
|
||||
};
|
||||
// 获取智能体列表
|
||||
const response = await apiPublishedAgentList(params);
|
||||
const { code, data, message } = response as unknown as RequestResponse<
|
||||
Page<SquarePublishedItemInfo>
|
||||
>;
|
||||
// 成功
|
||||
if (code === SUCCESS_CODE) {
|
||||
const newList = data.records || [];
|
||||
if (pageNum > 1) {
|
||||
agentList.value = [...agentList.value, ...newList];
|
||||
} else {
|
||||
agentList.value = newList;
|
||||
}
|
||||
hasMore.value = pageNum < data.pages;
|
||||
page.value = pageNum;
|
||||
loading.value = false;
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: message || t("Mobile.AgentSearch.fetchAgentFailed"),
|
||||
icon: "error",
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 查询用户最近使用过的智能体列表
|
||||
const fetchUserUsedAgentList = async (
|
||||
pageNum: number = 1,
|
||||
keyword: string = searchKeyword.value,
|
||||
) => {
|
||||
const res = await apiUserUsedAgentList({
|
||||
pageIndex: pageNum,
|
||||
size: 20,
|
||||
keyword,
|
||||
});
|
||||
const { code, data } = res || {};
|
||||
if (code === SUCCESS_CODE) {
|
||||
const newList = data || [];
|
||||
if (pageNum > 1) {
|
||||
// 追加数据
|
||||
agentList.value = [...(agentList.value || []), ...newList];
|
||||
} else {
|
||||
// 替换数据
|
||||
agentList.value = newList;
|
||||
}
|
||||
|
||||
// 是否有更多数据, 如果数据长度大于0,则有更多数据
|
||||
hasMore.value = newList?.length > 0;
|
||||
page.value = pageNum;
|
||||
loading.value = false;
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: t("Mobile.AgentSearch.fetchRecentUsedFailed"),
|
||||
icon: "none",
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取智能体列表
|
||||
const fetchList = async (
|
||||
pageNum: number = 1,
|
||||
keyword: string = searchKeyword.value,
|
||||
) => {
|
||||
// 前端过滤模式下不调用接口
|
||||
if (searchMode.value === "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cardComponentType.value === "Home") {
|
||||
await fetchUserUsedAgentList(pageNum, keyword);
|
||||
} else {
|
||||
await fetchAgentList(pageNum, keyword);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 前端过滤搜索(本地过滤)
|
||||
* @param keyword 搜索关键词
|
||||
*/
|
||||
const filterLocalList = (keyword: string) => {
|
||||
// 如果没有关键词,返回空列表
|
||||
if (!keyword || !localAgentList.value.length) {
|
||||
agentList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// 对本地列表进行过滤,匹配 name 或 description(不区分大小写)
|
||||
const lowerKeyword = keyword.toLowerCase();
|
||||
const filtered = localAgentList.value.filter((item) => {
|
||||
const nameMatch =
|
||||
item.name?.toLowerCase().includes(lowerKeyword) || false;
|
||||
const descMatch =
|
||||
item.description?.toLowerCase().includes(lowerKeyword) || false;
|
||||
return nameMatch || descMatch;
|
||||
});
|
||||
|
||||
agentList.value = convertCategoryItemsToSquareItems(filtered);
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (keyword: string) => {
|
||||
searchKeyword.value = keyword;
|
||||
// 清除之前的定时器
|
||||
if (searchTimeoutRef.value) {
|
||||
clearTimeout(searchTimeoutRef.value);
|
||||
}
|
||||
|
||||
// 如果搜索关键词为空
|
||||
if (!keyword) {
|
||||
// 前端过滤模式和接口搜索模式都清空列表
|
||||
agentList.value = [];
|
||||
loading.value = false;
|
||||
// #ifdef H5 || WEB
|
||||
nextTick(() => {
|
||||
if (searchInputRef.value) {
|
||||
searchInputRef.value?.focus();
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据搜索模式选择不同的处理方式
|
||||
if (searchMode.value === "local") {
|
||||
// 前端过滤模式:本地过滤
|
||||
loading.value = true;
|
||||
// 设置新的定时器,防抖500ms
|
||||
searchTimeoutRef.value = setTimeout(() => {
|
||||
filterLocalList(keyword);
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
} else {
|
||||
// 接口搜索模式:调用接口
|
||||
loading.value = true;
|
||||
// 设置新的定时器,防抖500ms
|
||||
searchTimeoutRef.value = setTimeout(() => {
|
||||
page.value = 1;
|
||||
hasMore.value = true;
|
||||
fetchList(1, keyword);
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载更多
|
||||
const handleLoadMore = () => {
|
||||
// 前端过滤模式下禁用加载更多
|
||||
if (searchMode.value === "local") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasMore.value && !loading.value) {
|
||||
const nextPage = page.value + 1;
|
||||
fetchList(nextPage);
|
||||
}
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const handleRefresh = () => {
|
||||
// 前端过滤模式下禁用下拉刷新
|
||||
if (searchMode.value === "local") {
|
||||
refreshing.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (refreshing.value) {
|
||||
return;
|
||||
}
|
||||
refreshing.value = true;
|
||||
fetchList().finally(() => {
|
||||
refreshing.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// 跳转到智能体详情页
|
||||
const handleAgentClick = (info: SquarePublishedItemInfo) => {
|
||||
jumpToAgentDetailPage(info.targetId, null, info.agentType, info.name);
|
||||
};
|
||||
|
||||
// 跳转到智能体详情页
|
||||
const handleRecentUsedAgentClick = (info: AgentInfo) => {
|
||||
jumpToAgentDetailPage(info.agentId, null, info.agentType, info.name);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-page-container {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: #fff;
|
||||
/* #ifdef MP-WEIXIN */
|
||||
// padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* #endif */
|
||||
|
||||
/* 搜索框 */
|
||||
.search-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 10rpx 0 10rpx 32rpx;
|
||||
flex-shrink: 0;
|
||||
|
||||
.search-box {
|
||||
flex: 1;
|
||||
gap: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
height: 76rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 16rpx;
|
||||
border: 2rpx solid transparent;
|
||||
background: rgba(12, 20, 102, 0.04);
|
||||
transition: border-color 0.3s ease;
|
||||
|
||||
&.focused {
|
||||
border-color: #5147ff;
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-size: 32rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
font-size: 28rpx;
|
||||
color: #15171f;
|
||||
outline: none;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.placeholder-class {
|
||||
color: rgba(21, 23, 31, 0.5);
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.clear-icon {
|
||||
font-size: 32rpx;
|
||||
color: #999 !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 76rpx;
|
||||
font-weight: 400;
|
||||
padding: 0 32rpx;
|
||||
color: rgba(21, 23, 31, 0.5);
|
||||
font-size: 32rpx;
|
||||
transition: background-color 0.3s ease;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&:active {
|
||||
background-color: rgba(12, 20, 102, 0.04);
|
||||
}
|
||||
/* #ifdef H5 */
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
/* #endif */
|
||||
}
|
||||
}
|
||||
|
||||
/* 滚动部分 */
|
||||
.scroll-section {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.empty-image {
|
||||
width: 172rpx;
|
||||
height: 172rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 32rpx;
|
||||
color: #15171f;
|
||||
font-weight: 400;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-state {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-text::before {
|
||||
content: "";
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
border: 4rpx solid #e8e8e8;
|
||||
border-top: 4rpx solid #8b5cf6;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 没有更多数据状态 */
|
||||
.no-more-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user