This commit is contained in:
hailin 2025-07-29 13:23:43 +08:00
parent 0693ecdece
commit 67ce5e5259
2 changed files with 482 additions and 479 deletions

View File

@ -0,0 +1,481 @@
<template>
<div class="w-full h-full">
<ElScrollbar>
<div class="w-[720px] mx-auto py-[60px]">
<div>
<div class="flex">
<div
class="border border-token-primary rounded-full px-3 flex items-center justify-center gap-x-[6px] h-[28px]">
<Icon name="local-icon-beautify"></Icon>
<span>热门工具</span>
</div>
</div>
<div class="font-bold text-[32px] mt-[10px]">打造智能化新体验</div>
<div class="mt-[10px] text-xs text-[rgba(0,0,0,0.5)]">
智能AI工具结合先进算法与智能学习覆盖内容生成客户服务图像设计语音处理等多元场景帮助企业降本增效提升生产力让复杂问题变得简单可控
</div>
</div>
<div class="mt-6">
<div class="search-bar border-token-primary" :class="{ 'is-active': isSearchActive }">
<div class="bg-black text-[rgba(255,255,255,0.8)] px-[25px] py-[8px] rounded-md">全部</div>
<div class="mx-4 text-[rgba(0,0,0,0.5)]">精选推荐</div>
<ElDivider direction="vertical" class="!mx-0" />
<div class="flex-1" v-click-outside="onClickOutside">
<ElInput
ref="searchInputRef"
class="w-full search-input"
v-model="searchValue"
placeholder="您需要什么帮助"
@input="handleSearch"
@focus="
isSearchActive = true;
showSearchPop = true;
"
@blur="
isSearchActive = false;
showSearchPop = false;
" />
</div>
<ElPopover
:visible="showSearchPop"
trigger="click"
ref="searchPopRef"
virtual-triggering
popper-class="!w-[720px] !border-none !rounded-xl !p-0"
:trigger-keys="[]"
:show-arrow="false"
:popper-options="{
modifiers: [
{
name: 'offset',
options: {
offset: [-80, 20],
},
},
],
}"
:virtual-ref="searchInputRef">
<div class="p-2">
<div
class="max-h-[500px] overflow-y-auto overflow-x-hidden dynamic-scroller"
v-if="getSearchApp.length">
<app-more
:list="getSearchApp"
:count="searchCount"
:page-size="4"
:page="searchPage"
@handle="toDetail"
@load-more="loadSearchMore" />
</div>
<ElEmpty v-else description="暂无搜索结果" :image-size="100" />
</div>
</ElPopover>
</div>
<div class="mt-3 flex items-center gap-2">
<div
class="tag-style"
v-for="item in tagList"
:key="item.key"
@click="handleTagClick(item.key)">
{{ item.name }}·{{ item.name_en }}
</div>
</div>
</div>
<div class="mt-8 flex flex-col">
<div>
<div class="text-xl font-bold">精选推荐</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">让智能为你赋能驱动无限可能</div>
<div class="mt-6 grid gap-3">
<template v-for="item in recommendApp" :key="item.key">
<div
class="bg-black rounded-2xl p-6 bg-cover bg-center cursor-pointer"
:style="{
backgroundImage: `url(${appImageData[`${item.key}_bg`].src})`,
}"
@click="toDetail(item)">
<div class="flex items-center gap-x-2 mt-[73px]">
<span class="text-[rgba(255,255,255,0.8)] text-[20px] font-bold">{{
item.name
}}</span>
<span
v-if="item.is_hot"
class="text-xs text-white px-2 py-1 rounded-md bg-primary"
>热门工具</span
>
<span
v-else-if="item.is_new"
class="text-xs text-white px-2 py-1 rounded-md bg-[#FF4906]"
>最新上线</span
>
</div>
<div class="mt-3 text-xs text-[rgba(255,255,255,0.5)]">
{{ item.desc }}
</div>
<div class="app-more-btn text-[rgba(255,255,255,0.8)] mt-5" @click="toDetail(item)">
了解更多<Icon name="local-icon-arrow_right" :size="12"></Icon>
</div>
</div>
</template>
</div>
</div>
<div class="mt-8">
<div class="text-xl font-bold">热门内容</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">
挖掘深层价值精准洞察需求快速实现业务增长
</div>
<div class="mt-6 bg-white rounded-xl p-3">
<template v-for="(item, index) in hotApp">
<div
v-if="item.key == AppKeyEnum.PERSON_WECHAT"
class="bg-black rounded-2xl p-6 mb-[14px] bg-cover bg-center cursor-pointer"
:style="{
backgroundImage: `url(${appImageData[`${item.key}_bg`].src})`,
}"
@click="toDetail(item)">
<img :src="item.src" class="w-12 h-12 mt-4" />
<div class="mt-4 text-[rgba(255,255,255,0.8)] text-[20px] font-bold">
{{ item.name }}
</div>
<div class="mt-3 text-xs text-[rgba(255,255,255,0.5)] w-[306px]">
{{ item.desc }}
</div>
<div class="app-more-btn text-[rgba(255,255,255,0.8)] mt-9" @click="toDetail(item)">
了解更多<Icon name="local-icon-arrow_right" :size="12"></Icon>
</div>
</div>
<app-card v-else :item="item" @click="toDetail(item)" />
</template>
</div>
</div>
<div class="mt-8">
<div class="text-xl font-bold">更多工具</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">
通过持续开发与迭代更新我们让功能更强大体验更流畅场景适应性更全面助力企业在竞争中始终保持技术领先
</div>
<div class="mt-6 bg-white rounded-xl p-3">
<app-more
:list="getMoreApp"
:count="moreApp.length"
:page-size="morePageSize"
:page="morePage"
@handle="toDetail"
@load-more="loadMore" />
</div>
</div>
</div>
</div>
</ElScrollbar>
</div>
<app-intro v-if="showAppIntro" ref="appIntroRef" :name="appName" @close="showAppIntro = false" />
<popup ref="followPopRef" width="412" confirm-button-text="" cancel-button-text="" :show-close="false">
<div class="-mb-10">
<div
class="absolute right-4 top-4 w-6 h-6 rounded-full bg-[#F2F2F2] flex items-center justify-center cursor-pointer"
@click="closeFollowPop">
<Icon name="el-icon-Close"></Icon>
</div>
<div class="text-[24px] font-bold">
{{ currTag.name }}
</div>
<div class="text-xs mt-[15px] text-[rgba(0,0,0,0.5)]">
{{ currTag.desc }}
</div>
<div class="mt-[26px] flex flex-col gap-y-1">
<div
v-for="(item, index) in getFollowList(currTag.key)"
:key="index"
class="app-item-card"
@click="toDetail(item)">
<div class="h-full hover:bg-[rgba(0,0,0,0.03)] flex items-center px-[10px] rounded-[10px] gap-x-3">
<img :src="item.src" class="w-12 h-12" />
<div class="flex-1">
<div class="flex items-center gap-x-2">
<div class="font-bold">{{ item.name }}</div>
<div
v-if="!item.is_online"
class="text-[11px] bg-[rgba(0,0,0,0.05)] rounded px-2 py-[2px] text-[rgba(0,0,0,0.5)]">
开发进行中
</div>
</div>
<div class="mt-1 flex items-center">
<span class="text-[#00000080] text-xs">
{{ item.desc }}
</span>
<span class="app-more-btn ml-2" @click="toDetail(item)"> 了解更多 </span>
</div>
</div>
<div class="flex-shrink-0">
<ElButton
type="primary"
round
class="!w-[100px] !h-[36px] !text-xs"
@click="toggleFollow(item.key)"
>{{ isFollow(item.key) ? "取消关注" : "关注" }}
</ElButton>
</div>
</div>
</div>
</div>
<div class="flex justify-center mt-5">
<div class="text-xs flex justify-center mb-8 gap-2 items-center p-1 bg-[#00000008] rounded-full">
<Icon name="local-icon-tips2" :size="16"></Icon>
<span class="text-[#0000004d] text-xs"
>我们正不断拓展智能AI工具的边界及解决方案敬请期待</span
>
</div>
</div>
</div>
</popup>
<app-live-popup v-if="showLivePop" ref="livePopRef" @close="showLivePop = false" />
</template>
<script setup lang="ts">
import { getBaseUrl } from "@/utils/env";
import { useFollowStore } from "@/stores/follow";
import { ClickOutside as vClickOutside } from "element-plus";
import { applications } from "@/config/common";
import { AppKeyEnum, appKeyNameMap, FollowTypeEnum } from "@/enums/appEnums";
import { cloneDeep } from "lodash-es";
import AppIntro from "./app/_components/app-intro.vue";
import AppMore from "./app/_components/app-more.vue";
import AppCard from "./app/_components/app-card.vue";
import AppLivePopup from "./app/_components/app-live-popup.vue";
const followStore = useFollowStore();
const { toggleFollow, isFollow } = followStore;
const appImages = {
...import.meta.glob("./app/_assets/images/*.png", { eager: true }),
};
const appImageData = Object.keys(appImages).reduce((acc, key) => {
const name = key.split("/").pop()?.split(".")[0];
if (name) {
acc[name] = {
name,
src: (appImages[key] as any).default,
};
}
return acc;
}, {} as Record<string, { name: string; src: string }>);
const tagList = [
{
key: FollowTypeEnum.INTERNAL,
name: "内务工具",
name_en: "Internal Affairs",
desc: "智能提醒、任务分配、自动归档,一站式提升团队效率与组织力,让时间与精力专注于真正重要的事。",
},
{
key: FollowTypeEnum.SMART_MARKETING,
name: "智能拓客",
name_en: "Smart Prospecting",
desc: "快速锁定潜在客户,数据驱动、智能推荐,让拓客更高效,成交更简单,助力企业业绩稳步增长!",
},
{
key: FollowTypeEnum.CUSTOMER_MANAGEMENT,
name: "客户管理",
name_en: "Engagement Hub",
desc: "精准洞察客户需求,自动跟进潜在商机,提升转化效率。帮你打造更贴心、更高效的客户体验。",
},
{
key: FollowTypeEnum.CONTENT_MARKETING,
name: "内容营销",
name_en: "Content Marketing",
desc: "快速锁定潜在客户,数据驱动、智能推荐,让拓客更高效,成交更简单,助力企业业绩稳步增长!",
},
];
const currTag = ref<any>(null);
//
const APP_CONFIG = {
//
RECOMMEND: [AppKeyEnum.REDBOOK, AppKeyEnum.MEETING_MINUTES, AppKeyEnum.INTERVIEW],
//
HOT: [AppKeyEnum.PERSON_WECHAT, AppKeyEnum.LIVE, AppKeyEnum.SERVICE, AppKeyEnum.TELEMARKETING],
//
MORE: [
AppKeyEnum.PERSON_WECHAT,
AppKeyEnum.REDBOOK,
AppKeyEnum.LADDER_PLAYER,
AppKeyEnum.INTERVIEW,
AppKeyEnum.MEETING_MINUTES,
AppKeyEnum.DRAWING,
AppKeyEnum.SERVICE,
AppKeyEnum.MIND_MAP,
AppKeyEnum.DOUBYIN,
AppKeyEnum.KUAISHOU,
AppKeyEnum.SPH,
AppKeyEnum.TELEMARKETING,
AppKeyEnum.TAX,
AppKeyEnum.LAW,
AppKeyEnum.WORD,
AppKeyEnum.PPT,
AppKeyEnum.COMPANY_WECHAT,
AppKeyEnum.STATEMENT,
AppKeyEnum.POSTER,
AppKeyEnum.CONTRACT,
AppKeyEnum.LIVE,
],
} as const;
const followPopRef = shallowRef();
const handleTagClick = (key: FollowTypeEnum) => {
currTag.value = tagList.find((item) => item.key === key);
followPopRef.value.open();
};
const closeFollowPop = () => {
followPopRef.value.close();
};
const getTagAppList = (key: FollowTypeEnum) => {
const list: any = Object.keys(applications).map((key) => ({
...applications[key],
key,
}));
return list.filter((item: any) => item.followType === key);
};
const getFollowList = computed(() => {
return (key: FollowTypeEnum) => getTagAppList(key);
});
//
const recommendApp = computed(() => {
return APP_CONFIG.RECOMMEND.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
is_new: key == AppKeyEnum.REDBOOK,
is_hot: [AppKeyEnum.MEETING_MINUTES, AppKeyEnum.INTERVIEW].includes(key),
}));
});
//
const hotApp = computed(() => {
return APP_CONFIG.HOT.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
}));
});
//
const morePageSize = ref(6);
const morePage = ref(1);
const moreApp = computed(() => {
return APP_CONFIG.MORE.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
}));
});
const getMoreApp = computed(() => {
return moreApp.value.slice(0, morePage.value * morePageSize.value);
});
const loadMore = () => {
morePage.value++;
};
const searchInputRef = shallowRef();
const searchPopRef = shallowRef();
const showSearchPop = ref(false);
const onClickOutside = () => {
unref(searchPopRef).popperRef?.delayHide?.();
};
const searchValue = ref("");
const searchApp = ref<any[]>(cloneDeep(moreApp.value));
const searchPage = ref(1);
const searchCount = ref(searchApp.value.length);
const isSearchActive = ref(false);
const getSearchApp = computed(() => {
const lists = cloneDeep(searchApp.value).slice(0, !searchValue.value ? searchPage.value * 4 : moreApp.value.length);
return lists.filter((item) => item.name.toLowerCase().indexOf(searchValue.value.trim().toLowerCase()) > -1);
});
const loadSearchMore = () => {
searchPage.value++;
};
const handleSearch = (value: string) => {
if (!value.trim()) {
searchCount.value = moreApp.value.length;
return;
}
searchCount.value = getSearchApp.value.length;
};
const appIntroRef = ref<InstanceType<typeof AppIntro> | null>(null);
const showAppIntro = ref(false);
const livePopRef = shallowRef();
const showLivePop = ref(false);
const appName = ref("");
const toDetail = async (item: any) => {
const { key, name } = item;
switch (key) {
case AppKeyEnum.DRAWING:
case AppKeyEnum.MEETING_MINUTES:
case AppKeyEnum.MIND_MAP:
case AppKeyEnum.INTERVIEW:
case AppKeyEnum.REDBOOK:
case AppKeyEnum.SERVICE:
window.open(`${getBaseUrl()}/app/${key}`, "_blank");
break;
case AppKeyEnum.LADDER_PLAYER:
appName.value = name;
showAppIntro.value = true;
await nextTick();
appIntroRef.value?.open("ladder_player");
break;
case AppKeyEnum.PERSON_WECHAT:
window.open(`${getBaseUrl()}/app/person_wechat/chat`, "_blank");
break;
case AppKeyEnum.LIVE:
showLivePop.value = true;
await nextTick();
livePopRef.value?.open();
break;
default:
feedback.notifyWarning("功能正在开发中,敬请期待!");
break;
}
};
</script>
<style scoped lang="scss">
.search-bar {
@apply rounded-xl border bg-white p-2 flex items-center;
&.is-active {
box-shadow: 0px 0px 0px 2px rgba(0, 101, 251, 0.2);
border-color: var(--color-primary);
background: rgba(0, 101, 251, 0.03);
}
}
:deep(.search-input) {
.el-input__wrapper {
box-shadow: none;
background-color: transparent;
.el-input__inner {
&::placeholder {
@apply text-[rgba(0,0,0,0.5)] text-base;
}
}
}
}
.tag-style {
@apply cursor-pointer rounded-full px-3 py-1 text-[11px] text-[rgba(0,0,0,0.5)] bg-[rgba(0,0,0,0.03)] hover:bg-[rgba(0,0,0,0.05)];
}
</style>

View File

@ -1,481 +1,3 @@
<template> <template>
<div class="w-full h-full"> <div>Hello, Nuxt is working.</div>
<ElScrollbar>
<div class="w-[720px] mx-auto py-[60px]">
<div>
<div class="flex">
<div
class="border border-token-primary rounded-full px-3 flex items-center justify-center gap-x-[6px] h-[28px]">
<Icon name="local-icon-beautify"></Icon>
<span>热门工具</span>
</div>
</div>
<div class="font-bold text-[32px] mt-[10px]">打造智能化新体验</div>
<div class="mt-[10px] text-xs text-[rgba(0,0,0,0.5)]">
智能AI工具结合先进算法与智能学习覆盖内容生成客户服务图像设计语音处理等多元场景帮助企业降本增效提升生产力让复杂问题变得简单可控
</div>
</div>
<div class="mt-6">
<div class="search-bar border-token-primary" :class="{ 'is-active': isSearchActive }">
<div class="bg-black text-[rgba(255,255,255,0.8)] px-[25px] py-[8px] rounded-md">全部</div>
<div class="mx-4 text-[rgba(0,0,0,0.5)]">精选推荐</div>
<ElDivider direction="vertical" class="!mx-0" />
<div class="flex-1" v-click-outside="onClickOutside">
<ElInput
ref="searchInputRef"
class="w-full search-input"
v-model="searchValue"
placeholder="您需要什么帮助"
@input="handleSearch"
@focus="
isSearchActive = true;
showSearchPop = true;
"
@blur="
isSearchActive = false;
showSearchPop = false;
" />
</div>
<ElPopover
:visible="showSearchPop"
trigger="click"
ref="searchPopRef"
virtual-triggering
popper-class="!w-[720px] !border-none !rounded-xl !p-0"
:trigger-keys="[]"
:show-arrow="false"
:popper-options="{
modifiers: [
{
name: 'offset',
options: {
offset: [-80, 20],
},
},
],
}"
:virtual-ref="searchInputRef">
<div class="p-2">
<div
class="max-h-[500px] overflow-y-auto overflow-x-hidden dynamic-scroller"
v-if="getSearchApp.length">
<app-more
:list="getSearchApp"
:count="searchCount"
:page-size="4"
:page="searchPage"
@handle="toDetail"
@load-more="loadSearchMore" />
</div>
<ElEmpty v-else description="暂无搜索结果" :image-size="100" />
</div>
</ElPopover>
</div>
<div class="mt-3 flex items-center gap-2">
<div
class="tag-style"
v-for="item in tagList"
:key="item.key"
@click="handleTagClick(item.key)">
{{ item.name }}·{{ item.name_en }}
</div>
</div>
</div>
<div class="mt-8 flex flex-col">
<div>
<div class="text-xl font-bold">精选推荐</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">让智能为你赋能驱动无限可能</div>
<div class="mt-6 grid gap-3">
<template v-for="item in recommendApp" :key="item.key">
<div
class="bg-black rounded-2xl p-6 bg-cover bg-center cursor-pointer"
:style="{
backgroundImage: `url(${appImageData[`${item.key}_bg`].src})`,
}"
@click="toDetail(item)">
<div class="flex items-center gap-x-2 mt-[73px]">
<span class="text-[rgba(255,255,255,0.8)] text-[20px] font-bold">{{
item.name
}}</span>
<span
v-if="item.is_hot"
class="text-xs text-white px-2 py-1 rounded-md bg-primary"
>热门工具</span
>
<span
v-else-if="item.is_new"
class="text-xs text-white px-2 py-1 rounded-md bg-[#FF4906]"
>最新上线</span
>
</div>
<div class="mt-3 text-xs text-[rgba(255,255,255,0.5)]">
{{ item.desc }}
</div>
<div class="app-more-btn text-[rgba(255,255,255,0.8)] mt-5" @click="toDetail(item)">
了解更多<Icon name="local-icon-arrow_right" :size="12"></Icon>
</div>
</div>
</template>
</div>
</div>
<div class="mt-8">
<div class="text-xl font-bold">热门内容</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">
挖掘深层价值精准洞察需求快速实现业务增长
</div>
<div class="mt-6 bg-white rounded-xl p-3">
<template v-for="(item, index) in hotApp">
<div
v-if="item.key == AppKeyEnum.PERSON_WECHAT"
class="bg-black rounded-2xl p-6 mb-[14px] bg-cover bg-center cursor-pointer"
:style="{
backgroundImage: `url(${appImageData[`${item.key}_bg`].src})`,
}"
@click="toDetail(item)">
<img :src="item.src" class="w-12 h-12 mt-4" />
<div class="mt-4 text-[rgba(255,255,255,0.8)] text-[20px] font-bold">
{{ item.name }}
</div>
<div class="mt-3 text-xs text-[rgba(255,255,255,0.5)] w-[306px]">
{{ item.desc }}
</div>
<div class="app-more-btn text-[rgba(255,255,255,0.8)] mt-9" @click="toDetail(item)">
了解更多<Icon name="local-icon-arrow_right" :size="12"></Icon>
</div>
</div>
<app-card v-else :item="item" @click="toDetail(item)" />
</template>
</div>
</div>
<div class="mt-8">
<div class="text-xl font-bold">更多工具</div>
<div class="text-xs text-[rgba(0,0,0,0.5)] mt-[10px]">
通过持续开发与迭代更新我们让功能更强大体验更流畅场景适应性更全面助力企业在竞争中始终保持技术领先
</div>
<div class="mt-6 bg-white rounded-xl p-3">
<app-more
:list="getMoreApp"
:count="moreApp.length"
:page-size="morePageSize"
:page="morePage"
@handle="toDetail"
@load-more="loadMore" />
</div>
</div>
</div>
</div>
</ElScrollbar>
</div>
<app-intro v-if="showAppIntro" ref="appIntroRef" :name="appName" @close="showAppIntro = false" />
<popup ref="followPopRef" width="412" confirm-button-text="" cancel-button-text="" :show-close="false">
<div class="-mb-10">
<div
class="absolute right-4 top-4 w-6 h-6 rounded-full bg-[#F2F2F2] flex items-center justify-center cursor-pointer"
@click="closeFollowPop">
<Icon name="el-icon-Close"></Icon>
</div>
<div class="text-[24px] font-bold">
{{ currTag.name }}
</div>
<div class="text-xs mt-[15px] text-[rgba(0,0,0,0.5)]">
{{ currTag.desc }}
</div>
<div class="mt-[26px] flex flex-col gap-y-1">
<div
v-for="(item, index) in getFollowList(currTag.key)"
:key="index"
class="app-item-card"
@click="toDetail(item)">
<div class="h-full hover:bg-[rgba(0,0,0,0.03)] flex items-center px-[10px] rounded-[10px] gap-x-3">
<img :src="item.src" class="w-12 h-12" />
<div class="flex-1">
<div class="flex items-center gap-x-2">
<div class="font-bold">{{ item.name }}</div>
<div
v-if="!item.is_online"
class="text-[11px] bg-[rgba(0,0,0,0.05)] rounded px-2 py-[2px] text-[rgba(0,0,0,0.5)]">
开发进行中
</div>
</div>
<div class="mt-1 flex items-center">
<span class="text-[#00000080] text-xs">
{{ item.desc }}
</span>
<span class="app-more-btn ml-2" @click="toDetail(item)"> 了解更多 </span>
</div>
</div>
<div class="flex-shrink-0">
<ElButton
type="primary"
round
class="!w-[100px] !h-[36px] !text-xs"
@click="toggleFollow(item.key)"
>{{ isFollow(item.key) ? "取消关注" : "关注" }}
</ElButton>
</div>
</div>
</div>
</div>
<div class="flex justify-center mt-5">
<div class="text-xs flex justify-center mb-8 gap-2 items-center p-1 bg-[#00000008] rounded-full">
<Icon name="local-icon-tips2" :size="16"></Icon>
<span class="text-[#0000004d] text-xs"
>我们正不断拓展智能AI工具的边界及解决方案敬请期待</span
>
</div>
</div>
</div>
</popup>
<app-live-popup v-if="showLivePop" ref="livePopRef" @close="showLivePop = false" />
</template> </template>
<script setup lang="ts">
import { getBaseUrl } from "@/utils/env";
import { useFollowStore } from "@/stores/follow";
import { ClickOutside as vClickOutside } from "element-plus";
import { applications } from "@/config/common";
import { AppKeyEnum, appKeyNameMap, FollowTypeEnum } from "@/enums/appEnums";
import { cloneDeep } from "lodash-es";
import AppIntro from "./app/_components/app-intro.vue";
import AppMore from "./app/_components/app-more.vue";
import AppCard from "./app/_components/app-card.vue";
import AppLivePopup from "./app/_components/app-live-popup.vue";
const followStore = useFollowStore();
const { toggleFollow, isFollow } = followStore;
const appImages = {
...import.meta.glob("./app/_assets/images/*.png", { eager: true }),
};
const appImageData = Object.keys(appImages).reduce((acc, key) => {
const name = key.split("/").pop()?.split(".")[0];
if (name) {
acc[name] = {
name,
src: (appImages[key] as any).default,
};
}
return acc;
}, {} as Record<string, { name: string; src: string }>);
const tagList = [
{
key: FollowTypeEnum.INTERNAL,
name: "内务工具",
name_en: "Internal Affairs",
desc: "智能提醒、任务分配、自动归档,一站式提升团队效率与组织力,让时间与精力专注于真正重要的事。",
},
{
key: FollowTypeEnum.SMART_MARKETING,
name: "智能拓客",
name_en: "Smart Prospecting",
desc: "快速锁定潜在客户,数据驱动、智能推荐,让拓客更高效,成交更简单,助力企业业绩稳步增长!",
},
{
key: FollowTypeEnum.CUSTOMER_MANAGEMENT,
name: "客户管理",
name_en: "Engagement Hub",
desc: "精准洞察客户需求,自动跟进潜在商机,提升转化效率。帮你打造更贴心、更高效的客户体验。",
},
{
key: FollowTypeEnum.CONTENT_MARKETING,
name: "内容营销",
name_en: "Content Marketing",
desc: "快速锁定潜在客户,数据驱动、智能推荐,让拓客更高效,成交更简单,助力企业业绩稳步增长!",
},
];
const currTag = ref<any>(null);
//
const APP_CONFIG = {
//
RECOMMEND: [AppKeyEnum.REDBOOK, AppKeyEnum.MEETING_MINUTES, AppKeyEnum.INTERVIEW],
//
HOT: [AppKeyEnum.PERSON_WECHAT, AppKeyEnum.LIVE, AppKeyEnum.SERVICE, AppKeyEnum.TELEMARKETING],
//
MORE: [
AppKeyEnum.PERSON_WECHAT,
AppKeyEnum.REDBOOK,
AppKeyEnum.LADDER_PLAYER,
AppKeyEnum.INTERVIEW,
AppKeyEnum.MEETING_MINUTES,
AppKeyEnum.DRAWING,
AppKeyEnum.SERVICE,
AppKeyEnum.MIND_MAP,
AppKeyEnum.DOUBYIN,
AppKeyEnum.KUAISHOU,
AppKeyEnum.SPH,
AppKeyEnum.TELEMARKETING,
AppKeyEnum.TAX,
AppKeyEnum.LAW,
AppKeyEnum.WORD,
AppKeyEnum.PPT,
AppKeyEnum.COMPANY_WECHAT,
AppKeyEnum.STATEMENT,
AppKeyEnum.POSTER,
AppKeyEnum.CONTRACT,
AppKeyEnum.LIVE,
],
} as const;
const followPopRef = shallowRef();
const handleTagClick = (key: FollowTypeEnum) => {
currTag.value = tagList.find((item) => item.key === key);
followPopRef.value.open();
};
const closeFollowPop = () => {
followPopRef.value.close();
};
const getTagAppList = (key: FollowTypeEnum) => {
const list: any = Object.keys(applications).map((key) => ({
...applications[key],
key,
}));
return list.filter((item: any) => item.followType === key);
};
const getFollowList = computed(() => {
return (key: FollowTypeEnum) => getTagAppList(key);
});
//
const recommendApp = computed(() => {
return APP_CONFIG.RECOMMEND.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
is_new: key == AppKeyEnum.REDBOOK,
is_hot: [AppKeyEnum.MEETING_MINUTES, AppKeyEnum.INTERVIEW].includes(key),
}));
});
//
const hotApp = computed(() => {
return APP_CONFIG.HOT.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
}));
});
//
const morePageSize = ref(6);
const morePage = ref(1);
const moreApp = computed(() => {
return APP_CONFIG.MORE.map((key) => ({
key,
...applications[key],
desc: applications[key].desc2,
name: appKeyNameMap[key],
}));
});
const getMoreApp = computed(() => {
return moreApp.value.slice(0, morePage.value * morePageSize.value);
});
const loadMore = () => {
morePage.value++;
};
const searchInputRef = shallowRef();
const searchPopRef = shallowRef();
const showSearchPop = ref(false);
const onClickOutside = () => {
unref(searchPopRef).popperRef?.delayHide?.();
};
const searchValue = ref("");
const searchApp = ref<any[]>(cloneDeep(moreApp.value));
const searchPage = ref(1);
const searchCount = ref(searchApp.value.length);
const isSearchActive = ref(false);
const getSearchApp = computed(() => {
const lists = cloneDeep(searchApp.value).slice(0, !searchValue.value ? searchPage.value * 4 : moreApp.value.length);
return lists.filter((item) => item.name.toLowerCase().indexOf(searchValue.value.trim().toLowerCase()) > -1);
});
const loadSearchMore = () => {
searchPage.value++;
};
const handleSearch = (value: string) => {
if (!value.trim()) {
searchCount.value = moreApp.value.length;
return;
}
searchCount.value = getSearchApp.value.length;
};
const appIntroRef = ref<InstanceType<typeof AppIntro> | null>(null);
const showAppIntro = ref(false);
const livePopRef = shallowRef();
const showLivePop = ref(false);
const appName = ref("");
const toDetail = async (item: any) => {
const { key, name } = item;
switch (key) {
case AppKeyEnum.DRAWING:
case AppKeyEnum.MEETING_MINUTES:
case AppKeyEnum.MIND_MAP:
case AppKeyEnum.INTERVIEW:
case AppKeyEnum.REDBOOK:
case AppKeyEnum.SERVICE:
window.open(`${getBaseUrl()}/app/${key}`, "_blank");
break;
case AppKeyEnum.LADDER_PLAYER:
appName.value = name;
showAppIntro.value = true;
await nextTick();
appIntroRef.value?.open("ladder_player");
break;
case AppKeyEnum.PERSON_WECHAT:
window.open(`${getBaseUrl()}/app/person_wechat/chat`, "_blank");
break;
case AppKeyEnum.LIVE:
showLivePop.value = true;
await nextTick();
livePopRef.value?.open();
break;
default:
feedback.notifyWarning("功能正在开发中,敬请期待!");
break;
}
};
</script>
<style scoped lang="scss">
.search-bar {
@apply rounded-xl border bg-white p-2 flex items-center;
&.is-active {
box-shadow: 0px 0px 0px 2px rgba(0, 101, 251, 0.2);
border-color: var(--color-primary);
background: rgba(0, 101, 251, 0.03);
}
}
:deep(.search-input) {
.el-input__wrapper {
box-shadow: none;
background-color: transparent;
.el-input__inner {
&::placeholder {
@apply text-[rgba(0,0,0,0.5)] text-base;
}
}
}
}
.tag-style {
@apply cursor-pointer rounded-full px-3 py-1 text-[11px] text-[rgba(0,0,0,0.5)] bg-[rgba(0,0,0,0.03)] hover:bg-[rgba(0,0,0,0.05)];
}
</style>