|
|
@@ -0,0 +1,242 @@
|
|
|
+<template>
|
|
|
+ <div class="home-container">
|
|
|
+ <div
|
|
|
+ class="icon-expand"
|
|
|
+ :style="{
|
|
|
+ backgroundColor: drawerVisible ? '#44449c' : '#7F70C840',
|
|
|
+ left: drawerVisible ? '18%' : '0'
|
|
|
+ }"
|
|
|
+ @click="toggleDrawer"
|
|
|
+ >
|
|
|
+ <span class="vertical-lines" :style="{ color: drawerVisible ? '#8a78d0' : 'white' }">||</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <LeftPanel ref="leftPanelRef" v-show="drawerVisible" />
|
|
|
+
|
|
|
+ <div class="tool-content">
|
|
|
+ <div class="title-box">
|
|
|
+ <div class="box-icon" @click="goBack">
|
|
|
+ <el-icon class="left-icon"><ArrowLeftBold /></el-icon>
|
|
|
+ 返回首页|小程序工具
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-loading="loading" class="content-box">
|
|
|
+ <div v-for="tool in toolList" :key="tool.id" class="small-box">
|
|
|
+ <button
|
|
|
+ class="tool-card"
|
|
|
+ type="button"
|
|
|
+ :title="`打开${tool.name}`"
|
|
|
+ @click="openTool(tool)"
|
|
|
+ >
|
|
|
+ <img v-if="tool.cover" :src="tool.cover" :alt="tool.name" />
|
|
|
+ <div v-else class="cover-placeholder">暂无封面</div>
|
|
|
+ </button>
|
|
|
+ <div class="additional-text">{{ tool.name }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-empty v-if="!loading && toolList.length === 0" description="暂无可用的小程序工具" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { onMounted, ref } from 'vue'
|
|
|
+import { ArrowLeftBold } from '@element-plus/icons-vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import LeftPanel from '@/components/LeftPanel.vue'
|
|
|
+import { getMiniProgramToolList } from '@/api/ai/miniProgramTool.js'
|
|
|
+import { homeRoutes } from '@/router/index.js'
|
|
|
+
|
|
|
+const leftPanelRef = ref(null)
|
|
|
+const drawerVisible = ref(true)
|
|
|
+const loading = ref(false)
|
|
|
+const toolList = ref([])
|
|
|
+const router = useRouter()
|
|
|
+
|
|
|
+const toggleDrawer = () => {
|
|
|
+ drawerVisible.value = !drawerVisible.value
|
|
|
+}
|
|
|
+
|
|
|
+const goBack = () => {
|
|
|
+ router.push(homeRoutes.home)
|
|
|
+}
|
|
|
+
|
|
|
+const normalizeTool = (tool) => ({
|
|
|
+ id: tool.id ?? tool.toolId ?? tool.url,
|
|
|
+ name: tool.name ?? tool.title ?? tool.toolName ?? '未命名工具',
|
|
|
+ cover: tool.image ?? tool.cover ?? tool.coverUrl ?? tool.imageUrl ?? '',
|
|
|
+ url: tool.url ?? tool.linkUrl ?? tool.routeUrl ?? tool.targetUrl ?? ''
|
|
|
+})
|
|
|
+
|
|
|
+const fetchToolList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const response = await getMiniProgramToolList()
|
|
|
+ const list = response?.data?.list ?? response?.data ?? []
|
|
|
+ toolList.value = Array.isArray(list) ? list.map(normalizeTool) : []
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取小程序工具列表失败:', error)
|
|
|
+ toolList.value = []
|
|
|
+ ElMessage.error('获取小程序工具列表失败,请稍后重试')
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const openTool = (tool) => {
|
|
|
+ if (!tool.url) {
|
|
|
+ ElMessage.warning('该工具暂未配置访问地址')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let targetUrl
|
|
|
+ try {
|
|
|
+ targetUrl = new URL(tool.url, window.location.origin)
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('该工具的访问地址无效')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!['http:', 'https:'].includes(targetUrl.protocol)) {
|
|
|
+ ElMessage.error('仅支持打开 HTTP 或 HTTPS 地址')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const newWindow = window.open(targetUrl.href, '_blank', 'noopener,noreferrer')
|
|
|
+ if (newWindow) newWindow.opener = null
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(fetchToolList)
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+@use 'sass:math';
|
|
|
+
|
|
|
+@function rpx($px) {
|
|
|
+ @return math.div($px, 750) * 100vw;
|
|
|
+}
|
|
|
+
|
|
|
+.home-container {
|
|
|
+ position: fixed;
|
|
|
+ inset: 0;
|
|
|
+ display: flex;
|
|
|
+ background: linear-gradient(to bottom, #e2ddfc, #f1effd);
|
|
|
+}
|
|
|
+
|
|
|
+.icon-expand {
|
|
|
+ width: rpx(8);
|
|
|
+ height: rpx(35);
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ z-index: 9999;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ clip-path: polygon(0 0, 100% 15%, 100% 85%, 0 100%);
|
|
|
+ transform: translateY(-50%);
|
|
|
+ transition: all .3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.vertical-lines {
|
|
|
+ font-size: rpx(10);
|
|
|
+}
|
|
|
+
|
|
|
+.tool-content {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ flex-direction: column;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.title-box {
|
|
|
+ height: rpx(35);
|
|
|
+}
|
|
|
+
|
|
|
+.box-icon {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 100%;
|
|
|
+ padding-left: rpx(15);
|
|
|
+ color: #000;
|
|
|
+ font-size: rpx(10);
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.left-icon {
|
|
|
+ margin: 0 rpx(5) 0 rpx(10);
|
|
|
+}
|
|
|
+
|
|
|
+.content-box {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-content: flex-start;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.content-box :deep(.el-empty) {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.small-box {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 calc(33.333% - rpx(10));
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ margin: rpx(3) 0 0 rpx(10);
|
|
|
+ color: #000;
|
|
|
+ font-size: rpx(8);
|
|
|
+}
|
|
|
+
|
|
|
+.tool-card {
|
|
|
+ width: rpx(150);
|
|
|
+ height: rpx(80);
|
|
|
+ margin-top: rpx(5);
|
|
|
+ padding: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ border: 1px solid #fff;
|
|
|
+ border-radius: rpx(10);
|
|
|
+ background: #d9d5ef;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.tool-card:hover,
|
|
|
+.tool-card:focus-visible {
|
|
|
+ box-shadow: 0 4px 8px rgb(0 0 0 / 50%);
|
|
|
+ outline: none;
|
|
|
+}
|
|
|
+
|
|
|
+.tool-card img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+}
|
|
|
+
|
|
|
+.cover-placeholder {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ color: #666;
|
|
|
+ font-size: rpx(9);
|
|
|
+}
|
|
|
+
|
|
|
+.additional-text {
|
|
|
+ margin: rpx(2) 0 rpx(4);
|
|
|
+ font-size: rpx(8);
|
|
|
+}
|
|
|
+
|
|
|
+.content-box::-webkit-scrollbar {
|
|
|
+ width: rpx(2);
|
|
|
+}
|
|
|
+
|
|
|
+.content-box::-webkit-scrollbar-thumb {
|
|
|
+ border-radius: rpx(3);
|
|
|
+ background: linear-gradient(to bottom, hsl(230 100% 21%), #8a78d0);
|
|
|
+}
|
|
|
+</style>
|