UserManualModal.vue 5.93 KB
<template>
  <transition name="help-doc-fade">
    <div v-if="visible" class="help-doc-overlay">
      <div class="help-doc-panel">
        <!-- 头部 -->
        <header class="help-doc-header">
          <button
            v-if="selectedManual"
            class="help-doc-back"
            type="button"
            @click="selectedManualId = null"
          >
            ← 返回使用手册列表
          </button>
          <button
            v-else
            class="help-doc-back"
            type="button"
            @click="handleBack"
          >
            ← 返回客服中心
          </button>
          <h2 class="help-doc-title">
            {{ selectedManual ? selectedManual.name : 'LinkMed 使用手册' }}
          </h2>
        </header>

        <!-- 主体内容:列表视图 + 手册详情视图 -->
        <main class="help-doc-main">
          <!-- 手册详情视图 -->
          <template v-if="selectedManual">
            <section class="manual-content">
              <iframe
                class="manual-iframe"
                :src="selectedManual.url"
                frameborder="0"
              ></iframe>
              <p class="manual-tip">
                当前为线上预览地址,后续可替换为对应分类的正式使用手册链接。
              </p>
            </section>
          </template>

          <!-- 手册列表视图 -->
          <template v-else>
            <!-- 手册类型标签 -->
            <nav class="manual-tabs">
              <button
                v-for="manual in manualTypes"
                :key="manual.id"
                type="button"
                class="manual-tab"
                :class="{ active: manual.id === selectedManualId }"
                @click="openManual(manual.id)"
              >
                <span class="manual-tab-icon">❤</span>
                <span class="manual-tab-text">{{ manual.name }}</span>
              </button>
            </nav>
          </template>
        </main>
      </div>
    </div>
  </transition>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

interface ManualType {
  id: string;
  name: string;
  url: string;
}

const DEFAULT_MANUAL_URL = 'https://www.example.com/';

const props = defineProps<{
  visible: boolean;
}>();

const emit = defineEmits<{
  (e: 'back'): void;
}>();

const manualTypes: ManualType[] = [
  {
    id: 'basic',
    name: '基础使用',
    url: DEFAULT_MANUAL_URL,
  },
  {
    id: 'common',
    name: '常见功能',
    url: DEFAULT_MANUAL_URL,
  },
  {
    id: 'demo',
    name: '上手演示',
    url: DEFAULT_MANUAL_URL,
  },
  {
    id: 'permission',
    name: '权限管理',
    url: DEFAULT_MANUAL_URL,
  },
  {
    id: 'settings',
    name: '配置与设置',
    url: DEFAULT_MANUAL_URL,
  },
  {
    id: 'ops-security',
    name: '运维与安全',
    url: DEFAULT_MANUAL_URL,
  },
];

const selectedManualId = ref<string | null>(null);

const selectedManual = computed<ManualType | null>(() => {
  if (!selectedManualId.value) return null;
  const found = manualTypes.find((m) => m.id === selectedManualId.value);
  return found ?? null;
});

const openManual = (id: string) => {
  selectedManualId.value = id;
};

const handleBack = () => {
  emit('back');
};
</script>

<style scoped>
.help-doc-overlay {
  pointer-events: auto;
  position: fixed;
  inset: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background: rgba(0, 0, 0, 0.25);
  z-index: 3999;
}

.help-doc-panel {
  width: 760px;
  max-width: calc(100vw - 80px);
  height: 480px;
  max-height: calc(100vh - 80px);
  display: flex;
  flex-direction: column;
  background: #f5f7fa;
  border-radius: 16px;
  box-shadow: 0 18px 45px rgba(0, 0, 0, 0.35);
  overflow: hidden;
}

.help-doc-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 20px;
  border-bottom: 1px solid #ebeef5;
  background: #ffffff;
}

.help-doc-back {
  border: none;
  background: #f2f3f5;
  color: #606266;
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 13px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.help-doc-back:hover {
  background: #e4e7ed;
}

.help-doc-title {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}

.help-doc-main {
  padding: 20px 24px 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
  background: #f5f7fa;
}

/* 手册类型标签条 */
.manual-tabs {
  display: flex;
  gap: 8px;
  padding: 4px 4px 0;
  overflow-x: auto;
}

.manual-tab {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: 12px;
  border: none;
  background: linear-gradient(135deg, #edf5ff, #e0eeff);
  color: #3a7bd5;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease,
    box-shadow 0.15s ease;
}

.manual-tab-icon {
  font-size: 13px;
}

.manual-tab-text {
  white-space: nowrap;
}

.manual-tab.active {
  background: linear-gradient(135deg, #3a7bd5, #5dade2);
  color: #ffffff;
  box-shadow: 0 4px 10px rgba(58, 123, 213, 0.35);
}

.manual-tab.active .manual-tab-icon {
  opacity: 0.95;
}

.manual-tab:hover {
  transform: translateY(-1px);
}

/* 手册内容区域 */
.manual-content {
  margin-top: 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.manual-iframe {
  flex: 1;
  width: 100%;
  border: none;
  border-radius: 10px;
  background: #ffffff;
}

.manual-tip {
  margin: 0;
  font-size: 12px;
  color: #909399;
}

/* 动画,与客服弹窗风格保持一致 */
.help-doc-fade-enter-active,
.help-doc-fade-leave-active {
  transition: opacity 0.18s ease;
}

.help-doc-fade-enter-from,
.help-doc-fade-leave-to {
  opacity: 0;
}

@media (max-width: 1024px) {
  .help-doc-panel {
    width: calc(100vw - 40px);
    max-height: calc(100vh - 40px);
  }
}

@media (max-width: 768px) {
  .help-doc-panel {
    width: calc(100vw - 24px);
    max-height: calc(100vh - 24px);
  }
}
</style>