自己的网页加一个搜索框,调用deepseek的API

发布于:2025-03-04 ⋅ 阅读:(17) ⋅ 点赞:(0)

一切源于一个学习黑马程序员视频的突发奇想
在网页悬浮一个搜索按钮,点击可以实现调用deepseek文本模型回答你的问题

前端实现

前端使用vue实现的

首先是整体页面:AIWidget.vue

<template>
  <div>
    <!-- 悬浮 AI 按钮 -->
    <el-button class="floating-button" @click="dialogVisible = true">
      <el-icon><Search /></el-icon>
    </el-button>

    <!-- AI 搜索框 -->
    <el-dialog v-model="dialogVisible" title="AI 搜索" width="400px">
      <el-input
        v-model="query"
        placeholder="请输入搜索内容..."
        @keyup.enter="handleSearch"
      />
      <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>

      <el-divider />

      <el-scrollbar height="200px">
        <ul v-if="results.length">
          <li v-for="(item, index) in results" :key="index">{{ item }}</li>
        </ul>
        <p v-else class="no-result">暂无搜索结果</p>
      </el-scrollbar>
    </el-dialog>
  </div>
</template>

<script>
import { ref, watch } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ElMessage } from "element-plus";
import { aiSearch } from "@/api/aiSearch";
import { debounce } from "lodash-es";

export default {
  components: { Search },
  setup() {
    const dialogVisible = ref(false);
    const query = ref("");
    const results = ref([]);
    const loading = ref(false);

    const handleSearch = debounce(async () => {
      const cleanQuery = query.value.trim().replace(/<[^>]*>?/gm, "");
      if (!cleanQuery) return;
      
      loading.value = true;
      try {
        const response = await aiSearch(cleanQuery);
        results.value = response.data || [];
      } catch (error) {
        ElMessage.error("搜索失败:" + error.message);
        results.value = [];
      } finally {
        loading.value = false;
      }
    }, 500);

    watch(dialogVisible, (val) => {
      if (!val) {
        query.value = "";
        results.value = [];
      }
    });

    return { dialogVisible, query, results, loading, handleSearch };
  },
};
</script>

<style scoped>
.floating-button {
  position: fixed;
  bottom: 20px;
  right: 20px;
  width: 50px;
  height: 50px;
  font-size: 20px;
  border-radius: 50%;
  background-color: #409eff;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
  cursor: pointer;
  transition: background-color 0.3s;
}

.floating-button:hover {
  background-color: #66b1ff;
}

.search-btn {
  margin-top: 10px;
  width: 100%;
}

.no-result {
  text-align: center;
  color: gray;
}
</style>

直接放在src/commpoents
接下来是JS文件,直接放在 src/api/
aiSearch.js

import request from '@/utils/request';

// AI 搜索 API 请求
export function aiSearch(query) {
  return request({
    url: '/ai-search',
    method: 'get',
    params: { q: query }
  });
}

为了在每个页面的右下角都显示这个搜索框,我们直接导入组件到App.vue
App.vue

<template>
   <div>
    <router-view />
    <AIWidget /> <!-- 悬浮 AI 按钮 -->
  </div>

</template>

<script setup>
import useSettingsStore from '@/store/modules/settings'
import { handleThemeStyle } from '@/utils/theme'
import AIWidget from "@/components/AIWidget.vue"; // 导入 AI 搜索组件

onMounted(() => {
  nextTick(() => {
    // 初始化主题样式
    handleThemeStyle(useSettingsStore().theme)
  })
})

</script>

后端实现

首先在 application.yml 添加配置:

siliconflow:
  api:
    url: https://api.siliconflow.cn/v1/chat/completions
    token: your_token_here  # 替换为实际token
    model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B

创建请求/响应 DTO 对象:
AiSearchReq.java

package com.dkd.manage.controller.AI;

import com.dkd.common.core.domain.R;
import com.dkd.manage.service.AI.IAiSearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/ai-search")
public class AiSearchController {

    @Autowired
    private IAiSearchService aiSearchService;

    @GetMapping("")
    public R<List<String>> search(@RequestParam String q) {
        return R.ok(aiSearchService.searchAI(q));
    }
}

ChatCompletionReq.java

package com.dkd.manage.domain.dto.AI;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

@Data
public class ChatCompletionReq {
    private String model;
    private List<Message> messages;
    private boolean stream = false;
    private int max_tokens = 512;
    private double temperature = 0.7;
    private double top_p = 0.7;
    private int top_k = 50;
    private double frequency_penalty = 0.5;
    private int n = 1;
    private ResponseFormat response_format = new ResponseFormat("text");
    //private List<Tool> tools = new ArrayList<>();

    @Data
    @AllArgsConstructor
    public static class Message {
        private String role;
        private String content;
    }

    @Data
    @AllArgsConstructor
    public static class ResponseFormat {
        private String type;
    }

    @Data
    public static class Tool {
        private String type = "function";
        private ToolFunction function = new ToolFunction();
    }

    @Data
    public static class ToolFunction {
        private String description = "";
        private String name = "";
        private Object parameters = new Object();
        private boolean strict = false;
    }
}

ChatCompletionResp.java

package com.dkd.manage.domain.dto.AI;

import lombok.Data;

import java.util.List;

@Data
public class ChatCompletionResp {
    private List<Choice> choices;

    @Data
    public static class Choice {
        private Message message;
    }

    @Data
    public static class Message {
        private String content;
    }
}

Controller 层:
AiSearchController.java

package com.dkd.manage.controller.AI;

import com.dkd.common.core.domain.R;
import com.dkd.manage.service.AI.IAiSearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/ai-search")
public class AiSearchController {

    @Autowired
    private IAiSearchService aiSearchService;

    @GetMapping("")
    public R<List<String>> search(@RequestParam String q) {
        return R.ok(aiSearchService.searchAI(q));
    }
}

Service 接口:
IAiSearchService.java

package com.dkd.manage.service.AI;

import java.util.List;

public interface IAiSearchService {
    List<String> searchAI(String query);
}

Service 实现:
AiSearchServiceImpl.java

package com.dkd.manage.service.impl;

import com.dkd.common.exception.ServiceException;
import com.dkd.manage.domain.dto.AI.ChatCompletionReq;
import com.dkd.manage.domain.dto.AI.ChatCompletionResp;
import com.dkd.manage.service.AI.IAiSearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Service
public class IAiSearchServiceImpl implements IAiSearchService {

    @Value("${siliconflow.api.url}")
    private String apiUrl;

    @Value("${siliconflow.api.token}")
    private String apiToken;

    @Value("${siliconflow.api.model}")
    private String model;

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public List<String> searchAI(String query) {
        // 1. 构建请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setBearerAuth(apiToken);

        // 2. 构建请求体
        ChatCompletionReq request = new ChatCompletionReq();
        request.setModel(model);
        request.setMessages(Collections.singletonList(
                new ChatCompletionReq.Message("user", query)
        ));

        // 3. 发送请求
        HttpEntity<ChatCompletionReq> entity = new HttpEntity<>(request, headers);
        ResponseEntity<ChatCompletionResp> response = restTemplate.exchange(
                apiUrl,
                HttpMethod.POST,
                entity,
                ChatCompletionResp.class
        );

        // 4. 处理响应
        if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
            String content = response.getBody().getChoices().get(0).getMessage().getContent();
            return Arrays.asList(content.split("\\n"));
        }
        throw new ServiceException("AI 服务调用失败");
    }
}

配置 RestTemplate(如果尚未配置):
RestTemplateConfig.java

放在framework/src/main/java/com/

package com.dkd.framework.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}