chore: initialize qiming workspace repository
This commit is contained in:
@@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<!-- 内容区域 -->
|
||||
<scroll-view
|
||||
class="flex-1 h-full"
|
||||
scroll-y="true"
|
||||
@scrolltolower="handleLoadMore"
|
||||
:lower-threshold="50"
|
||||
:refresher-enabled="true"
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="handleRefresh"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<template v-if="recentAgentList?.length">
|
||||
<recent-used-agent-item
|
||||
v-for="info in recentAgentList"
|
||||
:key="info.id"
|
||||
:icon="info.icon"
|
||||
:name="info.name"
|
||||
:description="info.description"
|
||||
:modifiedTime="formatTimeAgo(info.modified)"
|
||||
:showModifiedTime="isLoggedIn"
|
||||
:paymentRequired="info.paymentRequired && enableSubscription === 1"
|
||||
:subscribed="info.subscribed"
|
||||
@click="handleAgentClick(info)"
|
||||
@longpress="isLoggedIn ? handleDeleteUserUsedAgent(info) : () => {}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- loading状态或刷新状态时隐藏空状态 -->
|
||||
<template v-else-if="!loading && !refreshing">
|
||||
<view class="empty-state h-full">
|
||||
<image
|
||||
:src="noData"
|
||||
class="empty-image"
|
||||
:alt="t('Mobile.Common.noDataImageAlt')"
|
||||
/>
|
||||
<text class="empty-text">{{ t("Mobile.Common.noData") }}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 刷新状态时隐藏加载状态 -->
|
||||
<template v-else-if="!refreshing">
|
||||
<view class="loading-state">
|
||||
<text class="loading-text">{{ t("Mobile.Page.loadingMore") }}</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>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { SUCCESS_CODE } from "@/constants/codes.constants.uts";
|
||||
import type { AgentInfo } from "@/types/interfaces/agent";
|
||||
import {
|
||||
apiUserUsedAgentList,
|
||||
apiUserUsedAgentDelete,
|
||||
} from "@/servers/agentDev";
|
||||
import { apiPublishedAgentList } from "@/servers/square";
|
||||
import type {
|
||||
SquarePublishedListParams,
|
||||
SquarePublishedItemInfo,
|
||||
} from "@/types/interfaces/square";
|
||||
import type { Page, RequestResponse } from "@/types/interfaces/request";
|
||||
import RecentUsedAgentItem from "@/components/recent-used-agent-item/recent-used-agent-item.uvue";
|
||||
import noData from "@/static/assets/no_data.png";
|
||||
import { formatTimeAgo } from "@/utils/common";
|
||||
import { USER_NO_LOGIN, REDIRECT_LOGIN } from "@/constants/codes.constants";
|
||||
import { AgentComponentTypeEnum } from "@/types/enums/agent";
|
||||
import { useI18n } from "@/utils/i18n";
|
||||
import { apiTenantConfig } from "@/servers/account";
|
||||
import { TENANT_CONFIG_INFO } from "@/constants/home.constants";
|
||||
import type { TenantConfigInfo } from "@/types/interfaces/login";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
// Props 定义
|
||||
interface Props {
|
||||
isLoggedIn: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// Emits 定义 - 只保留需要父组件处理的事件
|
||||
const emit = defineEmits<{
|
||||
(e: "agent-click", info: AgentInfo): void;
|
||||
}>();
|
||||
|
||||
// 最近使用智能体列表
|
||||
const recentAgentList = ref<AgentInfo[]>([]);
|
||||
// 加载状态
|
||||
const loading = ref<boolean>(false);
|
||||
// 刷新状态
|
||||
const refreshing = ref<boolean>(false);
|
||||
// 是否还有更多
|
||||
const hasMore = ref<boolean>(false);
|
||||
// 当前页码
|
||||
const currentPage = ref<number>(1);
|
||||
|
||||
// 租户信息
|
||||
const tenantConfigInfo = ref<TenantConfigInfo | null>(null);
|
||||
|
||||
// 租户订阅限制强受控标志
|
||||
const enableSubscription = computed((): number => {
|
||||
return tenantConfigInfo.value?.enableSubscription ?? 0;
|
||||
});
|
||||
|
||||
// 是否显示没有更多数据
|
||||
const showNoMore = computed(() => {
|
||||
return (
|
||||
!loading.value && !hasMore.value && recentAgentList.value?.length > 8
|
||||
);
|
||||
});
|
||||
|
||||
// 查询用户最近使用过的智能体列表
|
||||
const fetchUserUsedAgentList = async (
|
||||
page: number = 1,
|
||||
pageSize: number = 20,
|
||||
) => {
|
||||
try {
|
||||
const res = await apiUserUsedAgentList({
|
||||
pageIndex: page,
|
||||
size: pageSize,
|
||||
});
|
||||
loading.value = false;
|
||||
const { code, data } = res || {};
|
||||
if (code === SUCCESS_CODE) {
|
||||
const newList = data || [];
|
||||
if (page > 1) {
|
||||
// 追加数据
|
||||
const existingList = recentAgentList.value || [];
|
||||
recentAgentList.value = [...existingList, ...newList];
|
||||
} else {
|
||||
// 替换数据
|
||||
recentAgentList.value = newList;
|
||||
}
|
||||
|
||||
// 是否有更多数据, 如果数据长度大于0,则有更多数据
|
||||
hasMore.value = newList?.length > 0;
|
||||
currentPage.value = page;
|
||||
return data;
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取公开的智能体列表(未登录时使用)
|
||||
const fetchPublishedAgentList = async () => {
|
||||
try {
|
||||
const params: SquarePublishedListParams = {
|
||||
targetType: AgentComponentTypeEnum.Agent,
|
||||
targetSubType: "ChatBot",
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
category: "",
|
||||
kw: "",
|
||||
};
|
||||
const res = await apiPublishedAgentList(params);
|
||||
loading.value = false;
|
||||
const { code, data } = res as unknown as RequestResponse<
|
||||
Page<SquarePublishedItemInfo>
|
||||
>;
|
||||
if (code === SUCCESS_CODE) {
|
||||
const newList = data.records || [];
|
||||
// 转换为 AgentInfo 格式
|
||||
const agentList: AgentInfo[] = newList.map(
|
||||
(item: SquarePublishedItemInfo) =>
|
||||
({
|
||||
id: item.id,
|
||||
agentId: item.targetId,
|
||||
name: item.name,
|
||||
icon: item.icon,
|
||||
description: item.description,
|
||||
agentType: item.agentType,
|
||||
paymentRequired: item.paymentRequired,
|
||||
subscribed: item.subscribed,
|
||||
}) as AgentInfo,
|
||||
);
|
||||
recentAgentList.value = agentList;
|
||||
// 只显示第一页,所以没有更多数据
|
||||
hasMore.value = false;
|
||||
currentPage.value = 1;
|
||||
return agentList;
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// 删除最近使用的智能体
|
||||
const handleDeleteUserUsedAgent = (info: AgentInfo) => {
|
||||
uni.showModal({
|
||||
title: t("Mobile.AgentList.confirmDeleteAgent"),
|
||||
content: info?.name,
|
||||
success: (res) => {
|
||||
// 确认删除
|
||||
if (res.confirm) {
|
||||
deleteUserUsedAgent(info?.agentId);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 删除用户最近智能体使用记录
|
||||
const deleteUserUsedAgent = async (agentId: number) => {
|
||||
const res = await apiUserUsedAgentDelete(agentId);
|
||||
const { code, message } = res || {};
|
||||
if (code === SUCCESS_CODE) {
|
||||
recentAgentList.value = recentAgentList.value.filter(
|
||||
(item) => item.agentId !== agentId,
|
||||
);
|
||||
} else if (code !== USER_NO_LOGIN && code !== REDIRECT_LOGIN) {
|
||||
uni.showToast({
|
||||
title: message || t("Mobile.AgentList.deleteFailed"),
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 下拉刷新
|
||||
const handleRefresh = async () => {
|
||||
if (refreshing.value) {
|
||||
return;
|
||||
}
|
||||
refreshing.value = true;
|
||||
currentPage.value = 1;
|
||||
hasMore.value = true;
|
||||
// 根据是否登录调用不同的 API
|
||||
const fetchPromise = props.isLoggedIn
|
||||
? fetchUserUsedAgentList()
|
||||
: fetchPublishedAgentList();
|
||||
fetchPromise.finally(() => {
|
||||
// 添加延迟,让loading效果更明显
|
||||
setTimeout(() => {
|
||||
refreshing.value = false;
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
// 加载更多
|
||||
const handleLoadMore = () => {
|
||||
if (hasMore.value && !loading.value) {
|
||||
// 下一页页码
|
||||
const nextPage = currentPage.value + 1;
|
||||
fetchUserUsedAgentList(nextPage);
|
||||
}
|
||||
};
|
||||
|
||||
// 点击智能体 - 发射事件给父组件处理
|
||||
const handleAgentClick = (info: AgentInfo) => {
|
||||
emit("agent-click", info);
|
||||
};
|
||||
|
||||
// 获取租户配置
|
||||
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();
|
||||
};
|
||||
|
||||
// 初始化加载数据
|
||||
const loadData = async () => {
|
||||
loading.value = true;
|
||||
// 强同步更新最新的租户配置,防止进入首页列表时计费状态渲染不正确
|
||||
await initTenantConfig();
|
||||
const list = props.isLoggedIn
|
||||
? await fetchUserUsedAgentList(1, 20 * currentPage.value)
|
||||
: await fetchPublishedAgentList();
|
||||
};
|
||||
|
||||
onMounted(()=>{
|
||||
loadData();
|
||||
})
|
||||
|
||||
onPageShow(()=>{
|
||||
loadData();
|
||||
})
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
loadData,
|
||||
recentAgentList,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 空状态 */
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.empty-image {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-state {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
&::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 没有更多数据状态 */
|
||||
.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