代码调用浏览器麦克风api;根据说的话跳转相应的页面;(vue2环境下)

在业务中,出现个需求

当点击页面上某个麦克风点击,进行识别的时候,会转成文字,然后根据相应的文字跳转相应的路由;

但是现在有个问题,浏览器的麦克风api,只能在https协议下进行请求,以及本机的https方法。这点要注意;

现在做了个另外的。就是调用windows本机的麦克风按钮,然后数据到input框进行展示。当鼠标点击到input 然后进行ctrl+h;唤起麦克风,然后说出话语,跳转路由;

js 复制代码
// 在https环境下进行请求
<template>
  <div class="voice-assistant-input">
    <!-- 输入框:用于显示语音识别结果 / 指令 -->
    <div class="input-container">
      <input
        ref="commandInput"
        v-model="commandText"
        type="text"
        placeholder="语音识别结果将显示在这里,或手动输入指令..."
        class="command-input"
        readonly
      />
    </div>

    <!-- 操作按钮:麦克风 + 发送 -->
    <div class="button-container">
      <!-- 麦克风按钮 -->
      <button @click="startVoiceRecognition" class="mic-btn" title="点击开始语音指令">
        🎤
      </button>

      <!-- 发送按钮(可用于手动输入指令测试,或提交识别内容) -->
      <button @click="handleSendCommand" class="send-btn" title="执行当前指令">
        📤
      </button>
    </div>

    <!-- 提示信息 -->
    <p v-if="message" :class="['message', messageType]">
      {{ message }}
    </p>
  </div>
</template>

<script>
export default {
  name: 'VoiceAssistantInput',
  props: {
    // 后端返回的菜单数据,每个菜单项需包含 name/title 和 path
    menuData: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      commandText: '', // 输入框中显示的指令(语音识别结果 or 手动输入)
      message: '',     // 提示信息
      messageType: 'info', // info / error
    };
  },
  methods: {
    // 开始语音识别
    async startVoiceRecognition() {
      try {
        // 1. 检查 HTTPS 环境
        if (location.protocol !== 'https:' && location.hostname !== 'localhost') {
          this.showMessage('语音识别需要在 HTTPS 环境下使用,请使用 HTTPS 访问', 'error');
          return;
        }

        // 2. 检查浏览器支持
        if (!('SpeechRecognition' in window) && !('webkitSpeechRecognition' in window)) {
          this.showMessage('您的浏览器不支持语音识别,请使用 Chrome 浏览器', 'error');
          return;
        }

        // 3. 请求麦克风权限
        try {
          await navigator.mediaDevices.getUserMedia({ audio: true });
        } catch (permissionError) {
          console.error('麦克风权限被拒绝:', permissionError);
          this.showMessage('请允许访问麦克风权限,然后重试', 'error');
          return;
        }

        // 4. 创建语音识别实例
        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
        const recognition = new SpeechRecognition();

        // 5. 配置识别参数
        recognition.lang = 'zh-CN';
        recognition.interimResults = false;
        recognition.maxAlternatives = 1;
        recognition.continuous = false;

        // 6. 事件处理
        recognition.onstart = () => {
          this.commandText = '';
          this.showMessage('正在监听中,请说话...', 'info');
        };

        recognition.onresult = (event) => {
          const text = event.results[0][0].transcript;
          this.commandText = text;
          console.log('语音识别结果:', text);
          this.showMessage('识别完成,正在处理指令...', 'info');
          this.handleCommand(text);
        };

        recognition.onerror = (event) => {
          console.error('语音识别错误:', event.error);
          let errorMessage = '语音识别出错';
          
          switch (event.error) {
            case 'no-speech':
              errorMessage = '没有检测到语音,请重试';
              break;
            case 'audio-capture':
              errorMessage = '无法访问麦克风,请检查设备连接';
              break;
            case 'not-allowed':
              errorMessage = '麦克风权限被拒绝,请在浏览器设置中允许访问';
              break;
            case 'network':
              errorMessage = '网络错误,请检查网络连接';
              break;
            case 'service-not-allowed':
              errorMessage = '语音识别服务不可用';
              break;
            default:
              errorMessage = `语音识别出错: ${event.error}`;
          }
          
          this.showMessage(errorMessage, 'error');
        };

        recognition.onend = () => {
          console.log('语音识别结束');
        };

        // 7. 开始识别
        recognition.start();
        
      } catch (error) {
        console.error('启动语音识别失败:', error);
        this.showMessage('启动语音识别失败,请重试', 'error');
      }
    },

    // 处理指令(无论是语音还是手动输入)
    handleCommand(command) {
      if (!command.trim()) {
        this.showMessage('指令为空,请重试', 'error');
        return;
      }

      if (!this.menuData || this.menuData.length === 0) {
        this.showMessage('菜单数据未加载,无法识别页面', 'error');
        return;
      }

      console.log('原始指令:', command);
      console.log('菜单数据:', this.menuData);

      // 提取关键字(改进版本)
      const keywords = this.extractKeywords(command);
      console.log('提取的关键词:', keywords);

      if (keywords.length === 0) {
        this.showMessage('未识别到有效的页面关键词,请重试', 'error');
        return;
      }

      // 查找匹配的菜单项
      const matchedMenus = this.findMenusByKeywords(this.menuData, keywords);
      console.log('匹配结果:', matchedMenus);

      if (matchedMenus.length === 0) {
        this.showMessage(`未找到与"${keywords.join('、')}"相关的页面,请尝试说:数据监测、实时数据、财务报表等`, 'error');
        return;
      }

      // 选择最佳匹配
      const bestMatch = this.selectBestMatch(matchedMenus, keywords);
      this.navigateToPage(bestMatch);
    },

    // 改进的关键词提取方法
    extractKeywords(command) {
      // 1. 移除常见的语音指令词汇
      let cleanCommand = command
        .replace(/帮我|请|跳转到|页面|我想|查看|进入|那个|啥|的|打开|去|到|访问|进去/gi, '')
        .replace(/\s+/g, '')
        .trim();

      console.log('清理后的指令:', cleanCommand);

      // 2. 如果清理后为空,使用原始指令
      if (!cleanCommand) {
        cleanCommand = command.trim();
      }

      // 3. 分词处理(简单版本)
      const keywords = [];
      
      // 常见的页面关键词映射
      const keywordMap = {
        '数据': ['数据监测', '数据', '监测'],
        '实时': ['实时数据', '实时', '数据'],
        '财务': ['财务报表', '财务', '报表'],
        '销售': ['销售报表', '销售', '报表'],
        '用户': ['用户管理', '用户', '管理'],
        '角色': ['角色管理', '角色', '管理'],
        '菜单': ['菜单管理', '菜单', '管理'],
        '系统': ['系统管理', '系统', '管理'],
        '报表': ['报表', '财务报表', '销售报表'],
        '管理': ['管理', '用户管理', '角色管理', '菜单管理', '系统管理']
      };

      // 4. 检查是否包含关键词
      for (const [key, values] of Object.entries(keywordMap)) {
        if (cleanCommand.includes(key)) {
          keywords.push(...values);
        }
      }

      // 5. 如果没有匹配到预定义关键词,直接使用清理后的指令
      if (keywords.length === 0) {
        keywords.push(cleanCommand);
      }

      // 6. 去重
      return [...new Set(keywords)];
    },

    // 改进的菜单搜索方法
    findMenusByKeywords(menus, keywords) {
      const result = [];
      const search = (items) => {
        for (const item of items) {
          // 计算匹配分数
          const score = this.calculateMatchScore(item, keywords);
          if (score > 0) {
            if (item.path) {
              result.push({ ...item, matchScore: score });
            }
          }
          if (item.children && item.children.length > 0) {
            search(item.children);
          }
        }
      };
      search(menus);
      
      // 按匹配分数排序
      return result.sort((a, b) => b.matchScore - a.matchScore);
    },

    // 计算匹配分数
    calculateMatchScore(item, keywords) {
      let score = 0;
      const itemName = item.name || '';
      const itemTitle = item.title || '';
      
      for (const keyword of keywords) {
        // 完全匹配得分最高
        if (itemName === keyword || itemTitle === keyword) {
          score += 10;
        }
        // 包含匹配
        else if (itemName.includes(keyword) || itemTitle.includes(keyword)) {
          score += 5;
        }
        // 部分匹配
        else if (keyword.includes(itemName) || keyword.includes(itemTitle)) {
          score += 2;
        }
      }
      
      return score;
    },

    // 选择最佳匹配
    selectBestMatch(matchedMenus, keywords) {
      if (matchedMenus.length === 1) {
        return matchedMenus[0];
      }
      
      // 优先选择完全匹配的项目
      for (const keyword of keywords) {
        const exactMatch = matchedMenus.find(item => 
          item.name === keyword || item.title === keyword
        );
        if (exactMatch) {
          return exactMatch;
        }
      }
      
      // 返回分数最高的
      return matchedMenus[0];
    },

    // 导航到指定页面
    navigateToPage(target) {
      if (!target.path) {
        this.showMessage(`找到了"${target.name}",但未配置路由`, 'error');
        return;
      }

      try {
        // 检查路由是否存在
        const route = this.$router.resolve(target.path);
        if (route.resolved.matched.length > 0) {
            console.log(target.path,'-=-=-');
            
          this.$router.push(target.path);
          this.showMessage(`已跳转到:${target.name || target.title}(匹配分数:${target.matchScore})`, 'info');
        } else {
          this.showMessage(`路由 "${target.path}" 不存在,请检查路由配置`, 'error');
          console.error('路由不存在:', target.path);
        }
      } catch (error) {
        console.error('路由跳转失败:', error);
        this.showMessage(`跳转失败:${error.message}`, 'error');
      }
    },

    // 简单提取关键字(保留作为备用方法)
    extractKeyword(command) {
      return command
        .replace(/帮我|请|跳转到|页面|我想|查看|进入|那个|啥|的/gi, '')
        .replace(/\s+/g, '')
        .trim() || command.slice(-6).trim(); // fallback
    },

    // 从菜单数据中查找包含关键字的菜单项(保留原方法作为备用)
    findMenusByKeyword(menus, keyword) {
      const result = [];
      const search = (items) => {
        for (const item of items) {
          if (
            (item.name && item.name.includes(keyword)) ||
            (item.title && item.title.includes(keyword))
          ) {
            if (item.path) {
              console.log(item, 'item===');
              result.push(item);
            }
          }
          if (item.children && item.children.length > 0) {
            search(item.children);
            console.log(item, item.children, 'item.children===');
          }
        }
      };
      search(menus);
      return result;
    },

    // 处理发送按钮(比如手动输入指令后点击发送)
    handleSendCommand() {
      if (!this.commandText.trim()) {
        this.showMessage('请输入或识别一条指令', 'error');
        return;
      }
      this.handleCommand(this.commandText);
    },

    // 显示提示信息
    showMessage(text, type = 'info') {
      this.message = text;
      this.messageType = type;
      setTimeout(() => {
        this.message = '';
      }, 3000);
    },
  },
};
</script>

<style scoped>
.voice-assistant-input {
  max-width: 500px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  font-family: Arial, sans-serif;
  text-align: center;
  background:#34537C;
}

.input-container {
  margin-bottom: 16px;
}

.command-input {
  width: 100%;
  padding: 12px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 6px;
  box-sizing: border-box;
  text-align: left;
  background-color: #f9f9f9;
}

.button-container {
  display: flex;
  justify-content: center;
  gap: 12px;
}

.mic-btn,
.send-btn {
  padding: 10px 16px;
  font-size: 18px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.mic-btn {
  background-color: #28a745;
  color: white;
}

.mic-btn:hover {
  background-color: #218838;
}

.send-btn {
  background-color: #007bff;
  color: white;
}

.send-btn:hover {
  background-color: #0056b3;
}

.message {
  margin-top: 12px;
  font-weight: bold;
}

.message.info {
  color: #007bff;
}

.message.error {
  color: #dc3545;
}
</style>
js 复制代码
// 唤起windows本身的麦克风,然后说话展示话术。
<template>
  <div class="voice-assistant-input">
    <!-- 输入框:用于显示语音识别结果 / 指令 -->
    <div class="input-container">
      <input
        ref="commandInput"
        v-model="commandText"
        type="text"
        placeholder="请使用Windows语音识别或手动输入指令..."
        class="command-input"
        @keyup.enter="handleSendCommand"
      />
    </div>

    <!-- 操作按钮:语音输入提示 + 发送 -->
    <div class="button-container">
      <!-- 语音输入提示按钮 -->
      <!-- <button @click="showVoiceInputGuide" class="voice-guide-btn" title="查看语音输入使用指南">
        🎤 语音输入
      </button> -->

      <!-- 清空按钮 -->
      <button @click="clearInput" class="clear-btn" title="清空输入框">
        🗑️ 清空
      </button>

      <!-- 发送按钮 -->
      <button @click="handleSendCommand" class="send-btn" title="执行当前指令">
        📤 执行
      </button>
    </div>

    <!-- 语音输入指南 -->
    <div v-if="showGuide" class="voice-guide">
      <h4>🎤 Windows语音输入使用指南</h4>
      <div class="guide-content">
        <p><strong>方法一:Windows语音识别</strong></p>
        <ol>
          <li>按 <kbd>Win + H</kbd> 开启Windows语音输入</li>
          <li>对着麦克风说出指令(如:"数据监测"、"用户管理"、"跳转运行视图")</li>
          <li>语音识别结果会自动填入上方输入框并自动执行</li>
          <li>如果没有自动跳转,请手动点击"执行"按钮</li>
        </ol>
        
        <p><strong>方法二:第三方语音输入软件</strong></p>
        <ul>
          <li>讯飞输入法:按住语音键说话</li>
          <li>搜狗输入法:Shift + 空格 启用语音输入</li>
          <li>百度输入法:Ctrl + Shift + S 语音输入</li>
        </ul>
        
        <p><strong>支持的指令示例:</strong></p>
        <div class="command-examples">
          <span class="example-tag">数据监测</span>
          <span class="example-tag">实时数据</span>
          <span class="example-tag">用户管理</span>
          <span class="example-tag">角色管理</span>
          <span class="example-tag">菜单管理</span>
          <span class="example-tag">系统管理</span>
          <span class="example-tag">财务报表</span>
          <span class="example-tag">运行视图</span>
        </div>
      </div>
      <button @click="showGuide = false" class="close-guide-btn">关闭指南</button>
    </div>

    <!-- 提示信息 -->
    <p v-if="message" :class="['message', messageType]">
      {{ message }}
    </p>
  </div>
</template>

<script>
export default {
  name: 'VoiceAssistantInput',
  props: {
    // 后端返回的菜单数据,每个菜单项需包含 name/title 和 path
    menuData: {
      type: Array,
      required: true,
    },
  },
  data() {
    return {
      commandText: '', // 输入框中显示的指令(语音识别结果 or 手动输入)
      message: '',     // 提示信息
      messageType: 'info', // info / error / success
      showGuide: false, // 是否显示语音输入指南
      lastCommandText: '', // 记录上一次的指令文本
    };
  },
  mounted() {
    // 组件加载时显示欢迎信息
    this.showMessage('💡 提示:您可以使用Windows语音输入(Win+H)或手动输入指令进行页面导航', 'info');
    
    // 监听输入框变化,实现自动执行
    this.setupAutoExecution();
  },
  methods: {
    // 设置自动执行监听
    setupAutoExecution() {
      // 监听输入框内容变化
      this.$watch('commandText', (newVal, oldVal) => {
        // 如果输入框内容发生变化且不为空
        if (newVal && newVal !== oldVal && newVal !== this.lastCommandText) {
          this.lastCommandText = newVal;
          
          // 延迟执行,给用户一点时间看到识别结果
          setTimeout(() => {
            if (this.commandText === newVal) { // 确保内容没有再次变化
              console.log('自动执行语音指令:', newVal);
              this.handleCommand(newVal);
            }
          }, 1000); // 1秒后自动执行
        }
      });
    },

    // 显示语音输入使用指南
    showVoiceInputGuide() {
      this.showGuide = !this.showGuide;
      if (this.showGuide) {
        this.showMessage('📖 语音输入指南已展开,请查看下方说明', 'info');
      }
    },

    // 清空输入框
    clearInput() {
      this.commandText = '';
      this.lastCommandText = '';
      this.showMessage('🗑️ 输入框已清空', 'info');
      this.$refs.commandInput.focus();
    },

    // 处理指令(无论是语音还是手动输入)
    handleCommand(command) {
      if (!command.trim()) {
        this.showMessage('指令为空,请重试', 'error');
        return;
      }

      if (!this.menuData || this.menuData.length === 0) {
        this.showMessage('菜单数据未加载,无法识别页面', 'error');
        return;
      }

      console.log('原始指令:', command);
      console.log('菜单数据:', this.menuData);

      // 提取关键字(改进版本)
      const keywords = this.extractKeywords(command);
      console.log('提取的关键词:', keywords);

      if (keywords.length === 0) {
        this.showMessage('未识别到有效的页面关键词,请重试', 'error');
        return;
      }

      // 查找匹配的菜单项
      const matchedMenus = this.findMenusByKeywords(this.menuData, keywords);
      console.log('匹配结果:', matchedMenus);

      if (matchedMenus.length === 0) {
        this.showMessage(`未找到与"${keywords.join('、')}"相关的页面,请尝试说:数据监测、实时数据、财务报表、运行视图等`, 'error');
        return;
      }

      // 选择最佳匹配
      const bestMatch = this.selectBestMatch(matchedMenus, keywords);
      this.navigateToPage(bestMatch);
    },

    // 改进的关键词提取方法
    extractKeywords(command) {
      // 1. 移除常见的语音指令词汇
      let cleanCommand = command
        .replace(/帮我|请|跳转到|页面|我想|查看|进入|那个|啥|的|打开|去|到|访问|进去/gi, '')
        .replace(/\s+/g, '')
        .trim();

      console.log('清理后的指令:', cleanCommand);

      // 2. 如果清理后为空,使用原始指令
      if (!cleanCommand) {
        cleanCommand = command.trim();
      }

      // 3. 分词处理(简单版本)
      const keywords = [];
      
      // 常见的页面关键词映射
      const keywordMap = {
        '数据': ['数据监测', '数据', '监测'],
        '实时': ['实时数据', '实时', '数据'],
        '财务': ['财务报表', '财务', '报表'],
        '销售': ['销售报表', '销售', '报表'],
        '用户': ['用户管理', '用户', '管理'],
        '角色': ['角色管理', '角色', '管理'],
        '菜单': ['菜单管理', '菜单', '管理'],
        '系统': ['系统管理', '系统', '管理'],
        '报表': ['报表', '财务报表', '销售报表'],
        '管理': ['管理', '用户管理', '角色管理', '菜单管理', '系统管理'],
        // 新增运行视图相关关键词
        '运行': ['运行视图', '运行', '视图', '运行监控', '运行状态'],
        '视图': ['运行视图', '视图', '运行', '监控视图', '状态视图'],
        '监控': ['运行监控', '监控', '运行视图', '状态监控'],
        '状态': ['运行状态', '状态', '运行视图', '状态监控']
      };

      // 4. 检查是否包含关键词
      for (const [key, values] of Object.entries(keywordMap)) {
        if (cleanCommand.includes(key)) {
          keywords.push(...values);
        }
      }

      // 5. 如果没有匹配到预定义关键词,直接使用清理后的指令
      if (keywords.length === 0) {
        keywords.push(cleanCommand);
      }

      // 6. 去重
      return [...new Set(keywords)];
    },

    // 改进的菜单搜索方法
    findMenusByKeywords(menus, keywords) {
      const result = [];
      const search = (items) => {
        for (const item of items) {
          // 计算匹配分数
          const score = this.calculateMatchScore(item, keywords);
          if (score > 0) {
            if (item.path) {
              result.push({ ...item, matchScore: score });
            }
          }
          if (item.children && item.children.length > 0) {
            search(item.children);
          }
        }
      };
      search(menus);
      
      // 按匹配分数排序
      return result.sort((a, b) => b.matchScore - a.matchScore);
    },

    // 计算匹配分数
    calculateMatchScore(item, keywords) {
      let score = 0;
      const itemName = item.name || '';
      const itemTitle = item.title || '';
      
      for (const keyword of keywords) {
        // 完全匹配得分最高
        if (itemName === keyword || itemTitle === keyword) {
          score += 10;
        }
        // 包含匹配
        else if (itemName.includes(keyword) || itemTitle.includes(keyword)) {
          score += 5;
        }
        // 部分匹配
        else if (keyword.includes(itemName) || keyword.includes(itemTitle)) {
          score += 2;
        }
      }
      
      return score;
    },

    // 选择最佳匹配
    selectBestMatch(matchedMenus, keywords) {
      if (matchedMenus.length === 1) {
        return matchedMenus[0];
      }
      
      // 优先选择完全匹配的项目
      for (const keyword of keywords) {
        const exactMatch = matchedMenus.find(item => 
          item.name === keyword || item.title === keyword
        );
        if (exactMatch) {
          return exactMatch;
        }
      }
      
      // 返回分数最高的
      return matchedMenus[0];
    },

    // 导航到指定页面
    navigateToPage(target) {
      if (!target.path) {
        this.showMessage(`找到了"${target.name}",但未配置路由`, 'error');
        return;
      }

      try {
        // 检查路由是否存在
        const route = this.$router.resolve(target.path);
        if (route.resolved.matched.length > 0) {
          console.log(target.path, '-=-=-');
          
          this.$router.push(target.path);
          this.showMessage(`✅ 已跳转到:${target.name || target.title}(匹配分数:${target.matchScore})`, 'success');
          
          // 跳转成功后清空输入框
          setTimeout(() => {
            this.commandText = '';
          }, 1000);
        } else {
          this.showMessage(`路由 "${target.path}" 不存在,请检查路由配置`, 'error');
          console.error('路由不存在:', target.path);
        }
      } catch (error) {
        console.error('路由跳转失败:', error);
        this.showMessage(`跳转失败:${error.message}`, 'error');
      }
    },

    // 处理发送按钮(手动输入指令后点击发送)
    handleSendCommand() {
      if (!this.commandText.trim()) {
        this.showMessage('请输入指令或使用语音输入', 'error');
        this.$refs.commandInput.focus();
        return;
      }
      this.handleCommand(this.commandText);
    },

    // 显示提示信息
    showMessage(text, type = 'info') {
      this.message = text;
      this.messageType = type;
      
      // 根据消息类型设置不同的显示时间
      const timeout = type === 'error' ? 5000 : 3000;
      setTimeout(() => {
        this.message = '';
      }, timeout);
    },
  },
};
</script>

<style scoped>
.voice-assistant-input {
  max-width: 600px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  font-family: Arial, sans-serif;
  text-align: center;
  background: #34537C;
  color: white;
}

.input-container {
  margin-bottom: 16px;
}

.command-input {
  width: 100%;
  padding: 12px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 6px;
  box-sizing: border-box;
  text-align: left;
  background-color: #f9f9f9;
  color: #333;
}

.command-input:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 5px rgba(0, 123, 255, 0.3);
}

.button-container {
  display: flex;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

.voice-guide-btn,
.clear-btn,
.send-btn {
  padding: 10px 16px;
  font-size: 14px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s;
  font-weight: bold;
}

.voice-guide-btn {
  background-color: #17a2b8;
  color: white;
}

.voice-guide-btn:hover {
  background-color: #138496;
  transform: translateY(-1px);
}

.clear-btn {
  background-color: #6c757d;
  color: white;
}

.clear-btn:hover {
  background-color: #545b62;
  transform: translateY(-1px);
}

.send-btn {
  background-color: #28a745;
  color: white;
}

.send-btn:hover {
  background-color: #218838;
  transform: translateY(-1px);
}

.voice-guide {
  margin-top: 20px;
  padding: 20px;
  background-color: rgba(255, 255, 255, 0.95);
  border-radius: 8px;
  text-align: left;
  color: #333;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.voice-guide h4 {
  margin-top: 0;
  color: #007bff;
  text-align: center;
}

.guide-content {
  line-height: 1.6;
}

.guide-content ol,
.guide-content ul {
  padding-left: 20px;
}

.guide-content li {
  margin-bottom: 8px;
}

.guide-content kbd {
  background-color: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 3px;
  padding: 2px 6px;
  font-size: 12px;
  color: #495057;
}

.command-examples {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.example-tag {
  background-color: #e9ecef;
  color: #495057;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  border: 1px solid #ced4da;
}

.close-guide-btn {
  display: block;
  margin: 15px auto 0;
  padding: 8px 16px;
  background-color: #dc3545;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.close-guide-btn:hover {
  background-color: #c82333;
}

.message {
  margin-top: 12px;
  font-weight: bold;
  padding: 10px;
  border-radius: 4px;
}

.message.info {
  color: #0c5460;
  background-color: #d1ecf1;
  border: 1px solid #bee5eb;
}

.message.error {
  color: #721c24;
  background-color: #f8d7da;
  border: 1px solid #f5c6cb;
}

.message.success {
  color: #155724;
  background-color: #d4edda;
  border: 1px solid #c3e6cb;
}

/* 响应式设计 */
@media (max-width: 768px) {
  .voice-assistant-input {
    margin: 10px;
    padding: 15px;
  }
  
  .button-container {
    flex-direction: column;
    align-items: center;
  }
  
  .voice-guide-btn,
  .clear-btn,
  .send-btn {
    width: 100%;
    max-width: 200px;
  }
}
</style>
js 复制代码
//父组件进行展示
    <div v-if="isDivVisible" class="znkuzsBox">
     <VoiceAssistantInput :menuData="menuList" />
    </div>
      data(){
    return{
      menuList: [
        {
          name: '数据监测',
          children: [
            { name: '数据监测', path: '/xxxx/xxxx', title: '数据监测' },
          ],
        },
                {
          name: '运行视图',
          children: [
            { name: '运行视图', path: '/xxxx/xxxxx', title: '运行视图' }
          ],
        },
      ],
    }
  },
相关推荐
爱分享的程序员11 分钟前
前端面试专栏-工程化:29.微前端架构设计与实践
前端·javascript·面试
上单带刀不带妹14 分钟前
Vue3递归组件详解:构建动态树形结构的终极方案
前端·javascript·vue.js·前端框架
-半.16 分钟前
Collection接口的详细介绍以及底层原理——包括数据结构红黑树、二叉树等,从0到彻底掌握Collection只需这篇文章
前端·html
90后的晨仔36 分钟前
📦 Vue CLI 项目结构超详细注释版解析
前端·vue.js
@大迁世界37 分钟前
用CSS轻松调整图片大小,避免拉伸和变形
前端·css
一颗不甘坠落的流星37 分钟前
【JS】获取元素宽高(例如div)
前端·javascript·react.js
白开水都有人用39 分钟前
VUE目录结构详解
前端·javascript·vue.js
if时光重来1 小时前
axios统一封装规范管理
前端·vue.js
m0dw1 小时前
js迭代器
开发语言·前端·javascript
烛阴1 小时前
别再让 JavaScript 卡死页面!Web Workers 零基础上手指南
前端·javascript