Commit 5262f496 authored by shirlyn.guo's avatar shirlyn.guo 👌🏻

feat: 应用广场

parent 1a3479b8
import { request } from '@/utils/request'
export function fetchGetAgentApplicationList<T>(payload: object) {
return request.post<T>('/bizAgentApplicationMallRest/getList.json', payload)
}
export function fetchCollectOrCancelAgentApplication<T>(id: number | undefined) {
return request.post<T>(`/bizAgentApplicationMallRest/collectOrCancelAgentInMall.json?id=${id}`)
}
export function fetchGetMallCategoryList<T>() {
return request.post<T>('/bizAgentApplicationMallRest/getMallCategoryList.json')
}
......@@ -2,7 +2,7 @@
import { h, readonly, ref, shallowReadonly, watchEffect } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Plus } from '@icon-park/vue-next'
import { Plus, Commodity } from '@icon-park/vue-next'
import type { MenuOption } from 'naive-ui'
import CustomIcon from '@/components/custom-icon/custom-icon.vue'
import { useUserStore } from '@/store/modules/user'
......@@ -29,6 +29,18 @@ const menuOptions = shallowReadonly<MenuOption[]>([
key: 'PersonalSpace',
icon: () => h('i', { class: 'iconfont icon-personal' }),
},
{
type: 'group',
label: () => h('div', {}, t('router_title_module.explore')),
key: 'Dialogue',
children: [
{
label: () => h('div', {}, t('router_title_module.application_square')),
key: 'ApplicationsSquare',
icon: () => h(Commodity, { theme: 'outline', size: '18', fill: '#333' }),
},
],
},
])
const avatarOptions = readonly([
......
......@@ -67,7 +67,6 @@ common_module:
removal_failed: '下架失败'
collect_successfully: '收藏成功'
collect_unsubscribed: '已取消收藏'
cancel: '取消'
preservation: '保存'
open: '开'
close: '关'
......@@ -148,6 +147,8 @@ router_title_module:
knowledge_document_list: '知识库文档列表'
knowledge_document_detail: '知识库文档详情'
multi_model_dialogue: '多模型调试'
explore: '探索'
application_square: '应用广场'
personal_space_module:
title: '个人空间'
......@@ -165,7 +166,6 @@ personal_space_module:
channel_popover_text: '查看发布详情'
agent_copy: '的副本'
empty_agent_list: '暂无应用'
search_empty_agent_list: '没有搜索到相关内容'
delete_agent_dialog_title: '确定要删除选中的应用吗?'
delete_agent_dialog_content: '删除后,如需再次使用,请重新创建'
remove_applications_dialog_title: '确定要下架应用吗?'
......@@ -400,3 +400,8 @@ multi_model_dialogue_module:
please_select_model_first: '请先选择模型'
replace_configuration_tip: '是否将该模型的配置覆盖到原来的配置项上'
open_new_conversation: '已开启新会话'
applications_square_module:
create_application_btn_text: '创建应用'
all_application_btn_text: '所有应用'
immediate_use_btn_text: '立即使用'
......@@ -67,7 +67,6 @@ common_module:
removal_failed: '下架失敗'
collect_successfully: '收藏成功'
collect_unsubscribed: '已取消收藏'
cancel: '取消'
preservation: '保存'
open: '開'
close: '關'
......@@ -148,6 +147,8 @@ router_title_module:
knowledge_document_list: '知識庫文檔列表'
knowledge_document_detail: '知識庫文檔詳情'
multi_model_dialogue: '多模型調試'
explore: '探索'
applications-square: '應用廣場'
personal_space_module:
title: '個人空間'
......@@ -165,7 +166,6 @@ personal_space_module:
channel_popover_text: '查看發佈詳情'
agent_copy: '的副本'
empty_agent_list: '暫無應用'
search_empty_agent_list: '沒有蒐索到相關內容'
delete_agent_dialog_title: '確定要刪除選中的應用嗎?'
delete_agent_dialog_content: '刪除後,如需再次使用,請重新創建'
remove_applications_dialog_title: '確定要下架應用嗎?'
......@@ -400,3 +400,9 @@ multi_model_dialogue_module:
please_select_model_first: '請先選擇模型'
replace_configuration_tip: '是否將該模型的配置覆蓋到原來的配置項上'
open_new_conversation: '已開啓新會話'
applications_square_module:
title: '應用廣場'
create_application_btn_text: '創建應用'
all_application_btn_text: '所有應用'
immediate_use_btn_text: '立即使用'
import { type RouteRecordRaw } from 'vue-router'
export default [
{
path: '/explore',
name: 'Explore',
meta: {
rank: 1001,
title: 'router_title_module.explore',
icon: 'mingcute:user-2-line',
belong: 'applications-square',
},
component: () => import('@/layout/index.vue'),
redirect: '/personalSpaceLayout',
children: [
{
path: '/applications-square',
name: 'ApplicationsSquare',
meta: {
rank: 1001,
title: 'router_title_module.applications-square',
belong: 'ApplicationsSquare',
},
component: () => import('@/views/applications-square/applications-square.vue'),
},
],
},
] as RouteRecordRaw[]
......@@ -4,6 +4,7 @@ export interface VariableStructureItem {
}
export interface PersonalAppConfigState {
agentType?: string
baseInfo: {
agentId: string //应用ID
agentTitle: string //应用标题
......@@ -11,7 +12,10 @@ export interface PersonalAppConfigState {
agentDesc: string //应用描述
agentSystem: string //角色指令
agentPublishStatus: 'draft' | 'publish' //发布状态 draft-草稿 publish-发布
memberId: number
}
clickNumber?: number
collectNumber?: number
commConfig: {
preamble: string //开场白
featuredQuestions: string[] //推荐问
......@@ -30,8 +34,11 @@ export interface PersonalAppConfigState {
communicationTurn: number //参考对话轮次 0-100
temperature: number //多样性 0-1.00
}
popularity?: number
modifiedTime: Date
id?: number
isCollect: string
isSale: string
isCopy?: string
agentPublishId: number
}
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
import { Star, PreviewOpen } from '@icon-park/vue-next'
import { computed, ref, useTemplateRef, watch } from 'vue'
import {
fetchCollectOrCancelAgentApplication,
fetchGetAgentApplicationList,
fetchGetMallCategoryList,
} from '@/apis/application-square'
import type { PaginationInfo } from '@/components/custom-pagination/custom-pagination.vue'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { useScroll } from '@vueuse/core'
import { router } from '@/router'
import searchEmptyImage from '@/assets/images/search-empty.png'
import applicationEmptyImage from '@/assets/images/application-empty.png'
const { t } = useI18n()
const searchQuery = ref('')
const checkedClassifyValue = ref(t('applications_square_module.all_application_btn_text'))
const agentApplicationList = ref<PersonalAppConfigState[]>([])
const pagingInfo = ref<PaginationInfo>({
pageNo: 1,
pageSize: 12,
totalPages: 0,
totalRows: 0,
})
const mallCategoryList = ref<string[]>([])
const cardContentWrapRef = useTemplateRef<HTMLDivElement>('cardContentWrapRef')
const isShowCarousel = ref(true)
const smooth = ref(false)
const agentApplicationBottomIsLoading = ref(false)
const agentApplicationClassifyIsLoading = ref(false)
const emptyTableText = ref(t('personal_space_module.agent_module.agent_list_module.empty_agent_list'))
const emptyTableImage = ref(applicationEmptyImage)
const behavior = computed(() => (smooth.value ? 'smooth' : 'auto'))
const { arrivedState } = useScroll(cardContentWrapRef, { behavior })
;(function () {
handleGetAgentApplicationList()
handleGetMallCategoryList()
})()
watch(checkedClassifyValue, () => {
agentApplicationClassifyIsLoading.value = true
if (searchQuery.value.length === 0) {
handleGetAgentApplicationList(true)
} else {
handleGetAgentApplicationList(true, true)
}
})
watch(
() => arrivedState.bottom,
() => {
if (arrivedState.bottom) {
if (pagingInfo.value.pageNo < pagingInfo.value.totalPages) {
agentApplicationBottomIsLoading.value = true
pagingInfo.value.pageNo += 1
if (searchQuery.value.length === 0) {
handleGetAgentApplicationList()
} else {
handleGetAgentApplicationList(false, true)
}
}
}
},
)
function handleGetAgentApplicationList(update = false, search = false) {
const agentType = ref('')
if (agentApplicationBottomIsLoading.value && agentApplicationClassifyIsLoading.value) return
if (search && searchQuery.value.length !== 0) {
isShowCarousel.value = false
} else {
isShowCarousel.value = true
}
if (update) pagingInfo.value.pageNo = 1
checkedClassifyValue.value === t('applications_square_module.all_application_btn_text')
? (agentType.value = '')
: (agentType.value = checkedClassifyValue.value)
const payload = {
search: searchQuery.value,
pagingInfo: pagingInfo.value,
agentType: agentType.value,
}
fetchGetAgentApplicationList<PersonalAppConfigState[]>(payload).then((res) => {
agentApplicationList.value = update ? res.data : [...agentApplicationList.value, ...res.data]
pagingInfo.value = res.pagingInfo as PaginationInfo
emptyTableText.value = searchQuery.value
? t('common_module.search_empty_data')
: t('personal_space_module.agent_module.agent_list_module.empty_agent_list')
emptyTableImage.value =
searchQuery.value && agentApplicationList.value.length === 0 ? searchEmptyImage : applicationEmptyImage
agentApplicationClassifyIsLoading.value = false
agentApplicationBottomIsLoading.value = false
})
}
function handleCollectOrCancelAgentApplication(id: number | undefined) {
fetchCollectOrCancelAgentApplication(id).then(() => {
handleGetAgentApplicationList(true)
})
}
function handleGetMallCategoryList() {
mallCategoryList.value[0] = t('applications_square_module.all_application_btn_text')
fetchGetMallCategoryList<string[]>().then((res) => {
if (res.code !== 0) return
mallCategoryList.value = [...mallCategoryList.value, ...res.data]
})
}
function handleToUseAgentApplication(agentId: string) {
router.replace({ name: 'ShareWebApplication', params: { agentId: agentId } })
}
</script>
<template>
<div ref="cardContentWrapRef" class="flex h-full flex-col overflow-y-auto py-6" style="scrollbar-width: none">
<div class="mb-[19px] flex items-center justify-between">
<div class="flex flex-col">
<div class="flex items-center">
<img src="@/assets/images/applications-square-icon.png" />
<p class="ml-[5px] text-lg">{{ t('router_title_module.application_square') }}</p>
</div>
</div>
<div class="w-5/9">
<n-input
v-model:value="searchQuery"
type="text"
placeholder="搜索"
class="search-input rounded-[26px]! text-[16px]! leading-[32px]! border-[#9ea3ff]! border-[1px] py-[6px] shadow-[0_4px_10px_0px_rgba(103,103,103,.1)]"
clearable
@keyup.enter="handleGetAgentApplicationList(true, true)"
>
<template #prefix>
<div @click="handleGetAgentApplicationList(true)">
<img src="@/assets/images/search.png" width="14" height="14" class="mr-[5px] cursor-pointer" />
</div>
</template>
</n-input>
</div>
<div>
<n-button type="primary" :bordered="false" :focusable="false" class="w-[86px]!">
<span class="text-sm text-[#fff]">{{ t('applications_square_module.create_application_btn_text') }}</span>
</n-button>
</div>
</div>
<div>
<n-collapse-transition :show="isShowCarousel">
<div>
<n-carousel autoplay class="h-[280px] w-full rounded-[10px] object-cover 2xl:object-fill">
<img class="h-[280px] w-full cursor-pointer" src="@/assets/images/application-square-carousel.png" />
<img class="h-[280px] w-full cursor-pointer" src="@/assets/images/application-square-carousel.png" />
</n-carousel>
</div>
<div class="h-[20px] bg-[#f3f6f9]"></div>
</n-collapse-transition>
<div>
<div class="sticky top-[-30px] z-10">
<div class="flex items-center rounded-[5px] bg-white py-[9px] pl-[9px]">
<button
v-for="classify in mallCategoryList"
:key="classify"
:focusable="false"
:class="['classify-radio-button', { active: checkedClassifyValue === classify }]"
class="mr-[4px] w-[86px] cursor-pointer rounded-[5px] border-[1px] border-[#fff] bg-transparent py-[5px] transition-colors duration-300 hover:bg-[#eeefff]"
@click="checkedClassifyValue = classify"
>
{{ classify }}
</button>
</div>
</div>
<div class="mt-[14px] min-h-[800px]">
<div class="flex justify-center">
<n-spin v-show="agentApplicationClassifyIsLoading" size="large" />
</div>
<n-grid
v-show="!agentApplicationClassifyIsLoading && agentApplicationList.length !== 0"
cols="l:3 xl:4"
responsive="screen"
>
<n-grid-item
v-for="agentApplicationItem in agentApplicationList"
:key="agentApplicationItem.id"
class="mb-[20px] mr-[15px]"
>
<div
class="rounded-[10px]! bg-[#fff] px-[24px] pb-[20px] pt-[20px] shadow-[0_4px_10px_0px_rgba(103,103,103,.1)]"
>
<div class="flex cursor-pointer justify-between">
<div class="popover-trigger mr-[22px] text-[14px]">
<div class="agent-desc h-[23px] w-full max-w-[160px] font-semibold">
<n-ellipsis style="max-width: 180px" :line-clamp="1">
{{ agentApplicationItem.baseInfo.agentTitle }}
<template #tooltip>
<div style="max-width: 230px">
{{ agentApplicationItem.baseInfo.agentTitle }}
</div>
</template>
</n-ellipsis>
</div>
<div class="agent-desc mb-[15px] mt-[18px] h-[44px] w-full max-w-[228px] text-[#999999]">
<n-ellipsis style="max-width: 180px" :line-clamp="2">
{{ agentApplicationItem.baseInfo.agentDesc }}
<template #tooltip>
<div style="max-width: 230px">
{{ agentApplicationItem.baseInfo.agentDesc }}
</div>
</template>
</n-ellipsis>
</div>
</div>
<div class="h-[84px] w-[84px]">
<img :src="agentApplicationItem.baseInfo.agentAvatar" class="h-[84px] w-[84px] rounded-[10px]" />
</div>
</div>
<n-divider class="mt-0! mb-[14px]!" dashed />
<div>
<div class="flex justify-between">
<div class="flex">
<div class="flex">
<Star
theme="two-tone"
size="18"
:fill="agentApplicationItem.isCollect === 'Y' ? ['#ffc06d', '#ffc06d'] : ['#333', '#fff']"
:stroke-width="2"
class="cursor-pointer transition-all delay-150 duration-300 ease-in-out"
@click="handleCollectOrCancelAgentApplication(agentApplicationItem.id)"
/>
<span class="ml-[6px] text-[12px] text-[#333]">{{ agentApplicationItem.collectNumber }}</span>
</div>
<div class="flex">
<PreviewOpen
theme="outline"
size="18"
fill="#333"
:stroke-width="2"
class="ml-[12px] cursor-pointer"
/>
<span class="ml-[6px] text-[12px] text-[#333]">{{ agentApplicationItem.clickNumber }}</span>
</div>
</div>
<button
class="hover:bg-theme-color text-theme-color border-[#eeefff]! rounded-[5px] border-[1px] bg-[#eeefff] bg-transparent px-[16px] py-[5px] text-[14px] transition-colors duration-300 hover:text-[#fff]"
@click="handleToUseAgentApplication(agentApplicationItem.baseInfo.agentId)"
>
{{ t('applications_square_module.immediate_use_btn_text') }}
</button>
</div>
</div>
</div>
</n-grid-item>
<n-grid-item v-for="item in 4" :key="item" class="mr-[15px]">
<div v-show="agentApplicationBottomIsLoading && pagingInfo.pageNo !== pagingInfo.totalPages">
<n-skeleton text :repeat="6" /> <n-skeleton text style="width: 60%" />
</div>
</n-grid-item>
</n-grid>
<div v-show="agentApplicationList.length === 0">
<div class="flex h-[500px] w-full items-center justify-center">
<div class="flex flex-col items-center justify-center">
<img :src="emptyTableImage" class="mb-[20px] h-[68px] w-[68px]" />
<p class="mb-[14px] text-[14px] text-[#999999]">{{ emptyTableText }}</p>
</div>
</div>
</div>
<div
v-show="agentApplicationList.length > 9 && pagingInfo.pageNo === pagingInfo.totalPages"
class="mb-[50px] mt-[30px] flex justify-center text-center text-[14px] text-[#a9b4cc]"
>
<div class="relative top-[10px] h-[1px] w-[14px] bg-[#a9b4cc]"></div>
<div class="mb-[8px] w-[80px]">
{{ t('personal_space_module.agent_module.agent_list_module.already_bottom') }}
</div>
<div class="relative top-[10px] h-[1px] w-[14px] bg-[#a9b4cc]"></div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.classify-radio-button.active {
color: #000dff;
background-color: #eeefff;
border: 1px #000dff solid;
}
@media (width >= 1536px) and (width <= 1670px) {
.popover-trigger,
.agent-desc {
width: 100%;
max-width: 120px;
}
}
@media (width >= 1371px) and (width <= 1535px) {
.popover-trigger,
.agent-desc {
width: 100%;
max-width: 160px;
}
}
@media (width <= 1370px) {
.popover-trigger,
.agent-desc {
width: 100%;
max-width: 120px;
}
}
.search-input:focus {
.carousel-content {
display: none;
}
}
</style>
......@@ -105,7 +105,7 @@ function getApplicationList(isLoadMore = false) {
agentAppList.value = isLoadMore ? [...agentAppList.value, ...res.data] : res.data
pagingInfo.value = res.pagingInfo as PaginationInfo
emptyTableText.value = agentSearchInputValue.value
? t('personal_space_module.agent_module.agent_list_module.search_empty_agent_list')
? t('common_module.search_empty_data')
: t('personal_space_module.agent_module.agent_list_module.application_empty')
emptyTableImage.value =
agentSearchInputValue.value && agentAppList.value.length === 0 ? searchEmptyImage : applicationEmptyImage
......
<script setup lang="ts">
import { fetchSalePublishApplication } from '@/apis/agent-application'
import { fetchGetMallCategoryList } from '@/apis/application-square'
import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { Close, Help, Notes, CheckSmall } from '@icon-park/vue-next'
import { ref, watch } from 'vue'
......@@ -23,64 +24,11 @@ const saleApplicationsId = ref(props.data.agentPublishId)
const isClassifyError = ref(false)
const applicationsClassify = [
{
value: 'mediaEntertainment',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.media_entertainment',
),
},
{
value: 'educationTraining',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.education_training',
),
},
{
value: 'businessServices',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.business_services',
),
},
{
value: 'medicalHealth',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.medical_health',
),
},
{
value: 'efficiencyTools',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.efficiency_tools',
),
},
{
value: 'officePersonnel',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.office_personnel',
),
},
{
value: 'marketingCommerce',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.marketing_commerce',
),
},
{
value: 'finance',
label: t('personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.finance'),
},
{
value: 'law',
label: t('personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.law'),
},
{
value: 'cultureTourism',
label: t(
'personal_space_module.agent_module.agent_setting_module.agent_sale_module.application_classify.culture_tourism',
),
},
]
const applicationsClassify = ref<string[]>([])
;(function () {
handleGetMallCategoryList()
})()
watch(
() => checkedClassifyValue.value,
......@@ -127,6 +75,13 @@ function handleApplicationReleaseBtn() {
function handleIsCopySwitchUpdateValue(value: string) {
isCopy.value = value
}
function handleGetMallCategoryList() {
fetchGetMallCategoryList().then((res) => {
if (res.code !== 0) return
applicationsClassify.value = res.data as string[]
})
}
</script>
<template>
......@@ -163,22 +118,22 @@ function handleIsCopySwitchUpdateValue(value: string) {
</div>
<div class="text-[#f33e3e]">*</div>
</div>
<div class="flex flex-wrap justify-between">
<div class="flex flex-wrap">
<button
v-for="classify in applicationsClassify"
:key="classify.value"
:class="['classify-radio-button', { active: checkedClassifyValue === classify.value }]"
class="relative mb-[8px] mr-[4px] h-[26px] cursor-pointer rounded-[6px] border-[1px] border-[#edeef7] bg-[#edeef7] px-[8px] text-[12px]"
@click="checkedClassifyValue = classify.value"
:key="classify"
:class="['classify-radio-button', { active: checkedClassifyValue === classify }]"
class="relative mb-[8px] mr-[8px] h-[26px] cursor-pointer rounded-[6px] border-[1px] border-[#edeef7] bg-[#edeef7] px-[8px] text-[12px]"
@click="checkedClassifyValue = classify"
>
<div
v-show="checkedClassifyValue === classify.value"
v-show="checkedClassifyValue === classify"
class="bg-theme-color absolute left-0 top-[-1px] h-[14px] w-[14px] rounded-[3px]"
style="clip-path: polygon(0 0, 100% 0, 0 100%)"
>
<CheckSmall theme="outline" size="10" fill="#fff" />
</div>
{{ classify.label }}
{{ classify }}
</button>
</div>
<div class="mb-[10px] h-[16px] text-red-500">
......@@ -219,7 +174,7 @@ function handleIsCopySwitchUpdateValue(value: string) {
class="hover:text-theme-color hover:border-theme-color !mr-[12px] box-content !h-[38px] !w-[74px] cursor-pointer rounded-[6px] border-[1px] border-solid border-[#dde3f0] px-[10px] outline-none transition-all duration-300 hover:border-[theme-color]"
@click="handleApplicationsSaleSettingModalClose"
>
{{ t('common_module.cancel') }}
{{ t('common_module.cancel_btn_text') }}
</button>
<button
class="bg-theme-color !box-content !h-[38px] !w-[74px] cursor-pointer rounded-[6px] border-[1px] border-solid border-[#dde3f0] bg-[te] px-[10px] text-[#ffffff] transition-all duration-300 hover:bg-[#528EFF]"
......
......@@ -68,7 +68,6 @@ declare namespace I18n {
removal_failed: string
collect_successfully: string
collect_unsubscribed: string
cancel: string
preservation: string
open: string
close: string
......@@ -157,6 +156,8 @@ declare namespace I18n {
knowledge_document_list: string
knowledge_document_detail: string
multi_model_dialogue: string
explore: string
applications_square: string
}
personal_space_module: {
......@@ -177,7 +178,6 @@ declare namespace I18n {
channel_popover_text: string
agent_copy: string
empty_agent_list: string
search_empty_agent_list: string
delete_agent_dialog_title: string
delete_agent_dialog_content: string
}
......@@ -415,5 +415,12 @@ declare namespace I18n {
replace_configuration_tip: string
open_new_conversation: string
}
applications_square_module: {
title: string
create_application_btn_text: string
all_application_btn_text: string
immediate_use_btn_text: string
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment