Commit 157868ea authored by tyyin lan's avatar tyyin lan

feat: 插件中心

parent 1e125a04
......@@ -8,7 +8,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_j7wtxbx0oer.css" />
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4711453_qvvmczfd1pl.css" />
<title>Model Link</title>
</head>
......
import { request } from '@/utils/request'
export function fetchPluginCategoryKeyList<T>() {
return request.post<T>('/bizAgentApplicationPluginRest/classificationList.json')
}
export function fetchPluginCategoryDetailInfoList<T>(payload: object) {
return request.post<T>('/bizAgentApplicationPluginRest/getList.json', null, { params: payload })
}
......@@ -45,6 +45,11 @@ const menuOptions = computed<MenuOption[]>(() => {
key: 'Statistic',
icon: () => h('i', { class: 'iconfont icon-statistics' }),
},
{
label: () => h('div', {}, t('router_title_module.plugin_center')),
key: 'PluginCenter',
icon: () => h('i', { class: 'iconfont icon-plugin' }),
},
],
},
]
......
......@@ -135,6 +135,7 @@ common_module:
export_failed: 'Export failed'
copy_link: 'Copy link'
or: 'or'
search_keyword_empty_tip: 'The search content cannot be empty'
dialogue_module:
continue_question_message: 'You can keep asking questions'
......@@ -186,6 +187,7 @@ router_title_module:
personal_settings: 'Personal settings'
order_manage: 'Order Management'
data_statistic: 'Data statistic'
plugin_center: 'Plugin center'
login_module:
app_welcome_words: 'Hi, welcome to Model Link'
......@@ -634,3 +636,8 @@ statistic_module:
owner: 'Owner'
usage_count: 'Usage count'
last_usage_time: 'Last usage time'
plugin_center_module:
plugin_center: 'Plugin center'
select_application: 'Select application'
create_new_application: 'Create new application'
......@@ -134,6 +134,7 @@ common_module:
export_failed: '导出失败'
copy_link: '复制链接'
or: '或'
search_keyword_empty_tip: '搜索内容不能为空'
dialogue_module:
continue_question_message: '你可以继续提问'
......@@ -185,6 +186,7 @@ router_title_module:
personal_settings: '个人设置'
order_manage: '订单管理'
data_statistic: '数据统计'
plugin_center: '插件中心'
login_module:
app_welcome_words: 'Hi, 欢迎使用Model Link'
......@@ -633,4 +635,7 @@ statistic_module:
usage_count: '使用次数'
last_usage_time: '最后一次使用时间'
plugin_center_module:
plugin_center: '插件中心'
select_application: '选择应用'
create_new_application: '创建新应用'
......@@ -134,6 +134,7 @@ common_module:
export_failed: '導出失敗'
copy_link: '複製鏈接'
or: '或'
search_keyword_empty_tip: '搜索內容不能為空'
dialogue_module:
continue_question_message: '你可以繼續提問'
......@@ -185,6 +186,7 @@ router_title_module:
personal_settings: '個人設置'
order_manage: '訂單管理'
data_statistic: '數據統計'
plugin_center: '插件中心'
login_module:
app_welcome_words: 'Hi, 歡迎使用Model Link'
......@@ -632,3 +634,8 @@ statistic_module:
owner: '開發者'
usage_count: '使用次數'
last_usage_time: '最後一次使用時間'
plugin_center_module:
plugin_center: '插件中心'
select_application: '選擇應用'
create_new_application: '創建新應用'
import { type RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'
export default [
{
path: '/',
name: 'Root',
meta: {
rank: 1001,
title: '',
},
component: Layout,
children: [
{
path: '/plugin/center',
name: 'PluginCenter',
meta: {
rank: 1001,
title: 'router_title_module.plugin_center',
belong: 'PluginCenter',
},
component: () => import('@/views/plugin-center/plugin-center.vue'),
},
],
},
] as RouteRecordRaw[]
<script setup lang="ts">
import { ref, watch } from 'vue'
import { Search } from '@icon-park/vue-next'
import { fetchGetApplicationList } from '@/apis/agent-application'
import { useI18n } from 'vue-i18n'
interface ApplicationInfoInterface {
agentId: string
agentTitle: string
agentAvatar: string
agentDesc: string
}
interface ApplicationItemInfoInterface {
baseInfo: ApplicationInfoInterface
}
const showModal = defineModel<boolean>('showModal', { default: false })
const { t } = useI18n()
const searchKeyword = ref('')
const applicationList = ref<ApplicationInfoInterface[]>([])
const getApplicationListLoading = ref(true)
watch(showModal, (newVal) => {
newVal && getApplicationList()
})
/* created */
;(function () {
getApplicationList()
})()
function getApplicationList() {
getApplicationListLoading.value = true
fetchGetApplicationList<ApplicationItemInfoInterface[]>({ query: searchKeyword.value.trim(), publishStatus: '' })
.then((res) => {
if (res.code !== 0) return ''
if (res.data) {
const applicationListDraft: ApplicationInfoInterface[] = res.data.map((itemInfo) => {
return {
agentId: itemInfo.baseInfo.agentId,
agentTitle: itemInfo.baseInfo.agentTitle,
agentAvatar: itemInfo.baseInfo.agentAvatar,
agentDesc: itemInfo.baseInfo.agentDesc,
}
})
applicationList.value = applicationListDraft
}
})
.finally(() => {
getApplicationListLoading.value = false
})
}
function handleSearchApplication() {
if (!searchKeyword.value.trim()) {
window.$message.warning(t('common_module.search_keyword_empty_tip'))
return
}
getApplicationList()
}
function handleSearchAppKeywordClear() {
searchKeyword.value = ''
getApplicationList()
}
function handleAppAddPlugin(agentId: string) {
console.log('🐽🐽🐽🐽🐽🐽插件使用🐽🐽🐽🐽🐽🐽')
console.log(agentId)
}
function handleCreateNewApplication() {
console.log('💥💥💥💥💥💥创建新应用💥💥💥💥💥💥')
}
</script>
<template>
<n-modal
v-model:show="showModal"
preset="card"
:title="t('plugin_center_module.select_application')"
:bordered="false"
class="!w-[800px]"
content-style="padding-right: 0;"
>
<div class="pr-[10px]">
<div class="flex items-center justify-end pr-[14px]">
<n-input
v-model:value="searchKeyword"
type="text"
:placeholder="t('common_module.search')"
class="!w-[300px]"
clearable
:on-clear="handleSearchAppKeywordClear"
@keyup.enter="handleSearchApplication"
>
<template #prefix>
<Search theme="outline" size="16" fill="#999" :stroke-width="3" />
</template>
</n-input>
<n-button type="primary" class="!ml-[19px]" @click="handleCreateNewApplication">
{{ t('plugin_center_module.create_new_application') }}
</n-button>
</div>
<div v-show="!getApplicationListLoading" class="mt-[22px]">
<n-virtual-list :item-size="106" :items="applicationList" class="!h-[500px] !pr-[14px]">
<template #default="{ item }">
<div
:key="item.agentId"
class="mb-[18px] box-border flex h-[88px] items-center rounded-[10px] border-[1px] border-solid border-[#ccc] px-[21px]"
>
<img class="h-[46px] w-[46px] rounded-[6px] object-cover" :src="item.agentAvatar" alt="app-cover" />
<div class="flex h-[46px] flex-1 flex-col justify-between overflow-hidden pl-[10px] pr-[20px]">
<div class="text-[14px]">
<n-ellipsis :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ item.agentTitle || '-' }}
</n-ellipsis>
</div>
<div class="text-[12px]">
<n-ellipsis :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ item.agentDesc || '-' }}
</n-ellipsis>
</div>
</div>
<n-button type="primary" @click="handleAppAddPlugin(item.agentId)">{{ t('common_module.add') }}</n-button>
</div>
</template>
</n-virtual-list>
</div>
<div v-show="getApplicationListLoading" class="flex-center mt-[22px] flex h-[500px]">
<n-spin size="large" />
</div>
</div>
</n-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import ApplicationSelectionModal from './application-selection-modal.vue'
import { useI18n } from 'vue-i18n'
import { fetchPluginCategoryKeyList, fetchPluginCategoryDetailInfoList } from '@/apis/plugin-center'
interface PluginCategoryKeyItemInterface {
classificationName: string
classification: string
}
interface PluginCategoryDetailInfo {
pluginInfos: {
pluginId: string
title: string
description: string
icon: string
points: number
}[]
classification: string
classificationName: string
}
const { t } = useI18n()
const defaultIconUrl = 'https://gsst-poe-sit.gz.bcebos.com/data/20250114/1736844964077.png'
const currentActiveCategory = ref('all')
const isShowApplicationSelectionModal = ref(false)
const pluginCategoryKeyList = ref<PluginCategoryKeyItemInterface[]>([])
const pluginCategoryKeyListLoading = ref(true)
const pluginCategoryDetailInfoList = ref<PluginCategoryDetailInfo[]>([])
const pluginCategoryDetailInfoListLoading = ref(true)
const currentSelectPluginId = ref('')
/* created */
;(function () {
getPluginCategoryKeyList()
getPluginCategoryDetailInfoList()
})()
function getPluginCategoryKeyList() {
pluginCategoryKeyListLoading.value = true
fetchPluginCategoryKeyList<PluginCategoryKeyItemInterface[]>()
.then((res) => {
if (res.code !== 0) return ''
pluginCategoryKeyList.value = [{ classificationName: t('common_module.all'), classification: 'all' }, ...res.data]
})
.finally(() => {
pluginCategoryKeyListLoading.value = false
})
}
function getPluginCategoryDetailInfoList() {
const payload = {
classification: currentActiveCategory.value === 'all' ? '' : currentActiveCategory.value,
pluginIds: '',
}
pluginCategoryDetailInfoListLoading.value = true
fetchPluginCategoryDetailInfoList<PluginCategoryDetailInfo[]>(payload)
.then((res) => {
if (res.code !== 0) return ''
pluginCategoryDetailInfoList.value = res.data
})
.finally(() => {
pluginCategoryDetailInfoListLoading.value = false
})
}
function handleCurrentPluginCategorySwitch(key: string) {
currentActiveCategory.value = key
getPluginCategoryDetailInfoList()
}
function handlePluginUse(pluginId: string) {
currentSelectPluginId.value = pluginId
isShowApplicationSelectionModal.value = true
}
</script>
<template>
<div class="flex h-full flex-col">
<section>
<h2 class="flex items-center">
<i class="iconfont icon-plugin text-theme-color text-[20px]"></i>
<span class="font-600 ml-[4px] text-[18px]">{{ t('plugin_center_module.plugin_center') }}</span>
</h2>
<nav class="mt-[26px]">
<n-scrollbar x-scrollable>
<div v-show="!pluginCategoryKeyListLoading" class="h-[33px] whitespace-nowrap pb-[14px]">
<button
v-for="categoryItemInfo in pluginCategoryKeyList"
:key="categoryItemInfo.classification"
class="font-600 mr-[10px] rounded-[5px] px-[22px] py-[6px] text-[14px]"
:class="
currentActiveCategory === categoryItemInfo.classification ? 'bg-theme-color text-white' : 'text-[#999]'
"
@click="handleCurrentPluginCategorySwitch(categoryItemInfo.classification)"
>
{{ categoryItemInfo.classificationName }}
</button>
</div>
<div v-show="pluginCategoryKeyListLoading" class="h-[33px] pb-[14px]">
<n-skeleton width="80%" :sharp="false" size="medium" />
</div>
</n-scrollbar>
</nav>
</section>
<section v-show="!pluginCategoryDetailInfoListLoading" class="flex-1 overflow-hidden pt-[20px]">
<n-scrollbar>
<div
v-for="categoryDetailInfoItem in pluginCategoryDetailInfoList"
:key="categoryDetailInfoItem.classification"
class="mb-[30px]"
>
<h2 class="flex items-center">
<i class="bg-theme-color inline-block h-[14px] w-[4px] rounded-[2px]"></i>
<span class="font-600 ml-[6px] text-[16px]">{{ categoryDetailInfoItem.classificationName }}</span>
</h2>
<ul
class="mt-[24px] grid grid-flow-row-dense gap-[20px]"
style="grid-template-columns: repeat(auto-fill, minmax(310px, 1fr))"
>
<li
v-for="pluginInfoItem in categoryDetailInfoItem.pluginInfos"
:key="pluginInfoItem.pluginId"
class="rounded-[10px] bg-white px-[23px] py-[20px]"
>
<div class="flex">
<img
class="h-[50px] w-[50px] rounded-[6px] object-cover"
:src="pluginInfoItem.icon || defaultIconUrl"
alt="plugin-cover"
/>
<div class="ml-[14px] flex flex-col justify-between overflow-hidden">
<div class="text-[16px]">
<n-ellipsis :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ pluginInfoItem.title }}
</n-ellipsis>
</div>
<div class="text-[14px] text-[#999]">
<n-ellipsis :tooltip="{ 'content-style': 'max-width: 600px;' }">
{{ pluginInfoItem.description }}
</n-ellipsis>
</div>
</div>
</div>
<div class="mt-[24px] text-end">
<button
class="hover:bg-theme-color rounded-[5px] bg-[#eeefff] px-[15px] py-[9px] text-[14px] text-[#000DFF] transition hover:text-white"
@click="handlePluginUse(pluginInfoItem.pluginId)"
>
{{ t('applications_square_module.immediate_use_btn_text') }}
</button>
</div>
</li>
</ul>
</div>
</n-scrollbar>
</section>
<section v-show="pluginCategoryDetailInfoListLoading" class="flex-center flex flex-1 overflow-hidden pt-[20px]">
<n-spin size="large" />
</section>
<ApplicationSelectionModal v-model:show-modal="isShowApplicationSelectionModal" />
</div>
</template>
......@@ -134,6 +134,7 @@ declare namespace I18n {
export_failed: string
copy_link: string
or: string
search_keyword_empty_tip: string
dialogue_module: {
continue_question_message: string
......@@ -192,6 +193,7 @@ declare namespace I18n {
personal_settings: string
order_manage: string
data_statistic: string
plugin_center: string
}
login_module: {
......@@ -652,5 +654,11 @@ declare namespace I18n {
usage_count: string
last_usage_time: string
}
plugin_center_module: {
plugin_center: string
select_application: string
create_new_application: 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