| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- <template>
- <div class="help-container">
- <div class="help-header">
- <div class="header-left">
- <el-select
- v-model="selectedTabValue"
- placeholder="请选择帮助主题"
- size="default"
- style="width: 250px; margin-right: 20px;"
- @change="handleTabSelect"
- >
- <el-option
- v-for="tab in tabOptions"
- :key="tab.value"
- :label="tab.label"
- :value="tab.value"
- />
- </el-select>
- <h1>使用帮助</h1>
- </div>
- <el-button type="info" plain @click="openSwagger">
- <el-icon style="margin-right: 5px"><Link /></el-icon>
- API 文档 (Swagger)
- </el-button>
- </div>
-
- <div class="tabs-container" v-if="tabs.length > 0">
- <div class="tabs-wrapper">
- <div
- v-for="tab in tabs"
- :key="tab.id"
- class="tab-item"
- :class="{ active: activeTabId === tab.id }"
- @click="switchTab(tab.id)"
- >
- <span class="tab-label">{{ tab.label }}</span>
- <el-icon
- class="tab-close"
- @click.stop="closeTab(tab.id)"
- v-if="tabs.length > 1"
- >
- <Close />
- </el-icon>
- </div>
- </div>
- </div>
- <div class="tab-content">
- <component
- :is="getComponentByValue(activeTabValue)"
- v-if="activeTabValue"
- />
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, computed, provide } from 'vue'
- import { Link, Close } from '@element-plus/icons-vue'
- import { useHelpDocs } from '../composables/useHelpDocs'
- // Import new components
- import IntegrationOverview from './help/IntegrationOverview.vue'
- import FastIntegration from './help/FastIntegration.vue'
- import CustomLogin from './help/CustomLogin.vue'
- import TicketExchange from './help/TicketExchange.vue'
- import ApiComparison from './help/ApiComparison.vue'
- import AccountSync from './help/AccountSync.vue'
- import UserSyncPull from './help/UserSyncPull.vue'
- import MessageIntegration from './help/MessageIntegration.vue'
- import MinIOFilePermissions from './help/MinIOFilePermissions.vue'
- import AccountManagement from './help/AccountManagement.vue'
- import OidcIntegration from './help/OidcIntegration.vue'
- import ClientApi from './help/ClientApi.vue'
- import Tbd from './help/Tbd.vue'
- interface Tab {
- id: string
- value: string
- label: string
- }
- const selectedTabValue = ref('')
- const tabs = ref<Tab[]>([])
- const activeTabId = ref<string>('')
- const { openSwagger } = useHelpDocs()
- // Tab选项配置
- const tabOptions = [
- { label: '平台对接概述', value: 'integration-overview' },
- { label: '快速对接 (Redirect)', value: 'fast-integration' },
- { label: '自定义登录页面', value: 'custom-login' },
- { label: '票据交互', value: 'ticket-exchange' },
- { label: '接口对比', value: 'api-comparison' },
- { label: '账号同步 (M2M)', value: 'account-sync' },
- { label: '全量用户同步', value: 'user-sync-pull' },
- { label: '消息中心对接', value: 'message-integration' },
- { label: '客户端接口 API', value: 'client-api' },
- { label: '文件存储权限控制', value: 'minio-file-permissions' },
- { label: '平台账号管理', value: 'account-management' },
- { label: 'OIDC 集成指南', value: 'oidc-integration' },
- { label: '其他帮助 (待定)', value: 'tbd' }
- ]
- // 组件映射
- const componentMap: Record<string, any> = {
- 'integration-overview': IntegrationOverview,
- 'fast-integration': FastIntegration,
- 'custom-login': CustomLogin,
- 'ticket-exchange': TicketExchange,
- 'api-comparison': ApiComparison,
- 'account-sync': AccountSync,
- 'user-sync-pull': UserSyncPull,
- 'message-integration': MessageIntegration,
- 'client-api': ClientApi,
- 'minio-file-permissions': MinIOFilePermissions,
- 'account-management': AccountManagement,
- 'oidc-integration': OidcIntegration,
- 'tbd': Tbd
- }
- // 当前激活的标签页值
- const activeTabValue = computed(() => {
- const activeTab = tabs.value.find(tab => tab.id === activeTabId.value)
- return activeTab?.value || ''
- })
- // 根据value获取组件
- const getComponentByValue = (value: string) => {
- return componentMap[value] || null
- }
- // 处理下拉框选择
- const handleTabSelect = (value: string) => {
- // 检查是否已存在该标签页
- const existingTab = tabs.value.find(tab => tab.value === value)
-
- if (existingTab) {
- // 如果已存在,切换到该标签页
- activeTabId.value = existingTab.id
- } else {
- // 如果不存在,创建新标签页
- const tabOption = tabOptions.find(opt => opt.value === value)
- if (tabOption) {
- const newTab: Tab = {
- id: `tab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
- value: value,
- label: tabOption.label
- }
- tabs.value.push(newTab)
- activeTabId.value = newTab.id
- }
- }
-
- // 清空下拉框选择(可选,让用户知道已经添加了)
- selectedTabValue.value = ''
- }
- // 切换标签页
- const switchTab = (tabId: string) => {
- activeTabId.value = tabId
- }
- // 关闭标签页
- const closeTab = (tabId: string) => {
- const index = tabs.value.findIndex(tab => tab.id === tabId)
- if (index !== -1) {
- tabs.value.splice(index, 1)
-
- // 如果关闭的是当前激活的标签页,切换到其他标签页
- if (activeTabId.value === tabId) {
- if (tabs.value.length > 0) {
- // 优先切换到右侧的标签页,如果没有则切换到左侧
- const newIndex = index < tabs.value.length ? index : index - 1
- activeTabId.value = tabs.value[newIndex].id
- } else {
- activeTabId.value = ''
- }
- }
- }
- }
- // 初始化时添加默认标签页
- const initDefaultTab = () => {
- const defaultTab: Tab = {
- id: `tab-${Date.now()}`,
- value: 'integration-overview',
- label: '平台对接概述'
- }
- tabs.value.push(defaultTab)
- activeTabId.value = defaultTab.id
- }
- // 组件挂载时初始化
- initDefaultTab()
- // 提供给子组件使用,用于切换tab
- provide('switchHelpTab', handleTabSelect)
- </script>
- <style scoped>
- .help-container {
- padding: 30px;
- max-width: 1000px;
- margin: 0 auto;
- background-color: #fff;
- border-radius: 8px;
- min-height: 80vh;
- box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
- }
- .help-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- border-bottom: 1px solid #eaecef;
- margin-bottom: 20px;
- padding-bottom: 15px;
- }
- .header-left {
- display: flex;
- align-items: center;
- flex: 1;
- }
- h1 {
- font-size: 28px;
- color: #1f2f3d;
- font-weight: 600;
- margin: 0;
- padding: 0;
- border-bottom: none;
- }
- .tabs-container {
- margin-bottom: 20px;
- border-bottom: 1px solid #eaecef;
- }
- .tabs-wrapper {
- display: flex;
- gap: 4px;
- overflow-x: auto;
- padding-bottom: 0;
- }
- .tabs-wrapper::-webkit-scrollbar {
- height: 6px;
- }
- .tabs-wrapper::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 3px;
- }
- .tabs-wrapper::-webkit-scrollbar-thumb {
- background: #c1c1c1;
- border-radius: 3px;
- }
- .tabs-wrapper::-webkit-scrollbar-thumb:hover {
- background: #a8a8a8;
- }
- .tab-item {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 10px 16px;
- background-color: #f5f7fa;
- border: 1px solid #e4e7ed;
- border-bottom: none;
- border-radius: 4px 4px 0 0;
- cursor: pointer;
- transition: all 0.3s;
- white-space: nowrap;
- position: relative;
- min-width: 120px;
- max-width: 250px;
- }
- .tab-item:hover {
- background-color: #ecf5ff;
- border-color: #b3d8ff;
- }
- .tab-item.active {
- background-color: #fff;
- border-color: #409eff;
- border-bottom-color: #fff;
- color: #409eff;
- font-weight: 500;
- z-index: 1;
- margin-bottom: -1px;
- }
- .tab-label {
- flex: 1;
- overflow: hidden;
- text-overflow: ellipsis;
- font-size: 14px;
- }
- .tab-close {
- font-size: 14px;
- color: #909399;
- cursor: pointer;
- padding: 2px;
- border-radius: 2px;
- transition: all 0.2s;
- flex-shrink: 0;
- }
- .tab-close:hover {
- background-color: #f56c6c;
- color: #fff;
- }
- .tab-item.active .tab-close {
- color: #409eff;
- }
- .tab-item.active .tab-close:hover {
- background-color: #f56c6c;
- color: #fff;
- }
- .tab-content {
- margin-top: 20px;
- }
- </style>
|