CzRger 4 päivää sitten
vanhempi
commit
9aaf998838

+ 4 - 0
package.json

@@ -12,16 +12,20 @@
   "dependencies": {
     "@types/node": "^20.17.11",
     "axios": "^1.7.9",
+    "crypto-js": "^4.2.0",
     "default-passive-events": "^2.0.0",
     "dompurify": "^3.2.5",
     "echarts": "^5.6.0",
     "element-plus": "^2.9.7",
     "fast-glob": "^3.3.3",
     "highlight.js": "^11.11.1",
+    "jsencrypt": "^3.3.2",
+    "json-bigint": "^1.0.0",
     "lodash-es": "^4.17.21",
     "markdown-it": "^14.1.0",
     "marked": "^15.0.8",
     "pinia": "^3.0.1",
+    "qs": "^6.14.0",
     "rollup-plugin-visualizer": "^5.14.0",
     "sass": "^1.83.1",
     "sortablejs": "^1.15.6",

+ 2 - 2
src/api/interceptors.ts

@@ -1,6 +1,6 @@
 import axios from 'axios';
 import {ElMessage} from "element-plus";
-import {toLogin} from "@/utils/permissions";
+// import {toLogin} from "@/utils/permissions";
 export class Interceptors {
   public instance: any
 
@@ -72,7 +72,7 @@ export class Interceptors {
     switch (res.status) {
       case 401:
         ElMessage.warning(res.data.msg);
-        toLogin()
+        // toLogin()
         break;
       case 403:
         break;

+ 11 - 0
src/api/modules/cms/chat.ts

@@ -0,0 +1,11 @@
+import { handle } from '../../index'
+
+const suffix = 'cms-api'
+
+// 查询字典项
+export const cmsAiQueryHotThemelist = (params) => handle({
+  url: `/${suffix}/szface/cmsAi/queryHotThemelist`,
+  method: 'post',
+  params
+})
+

+ 0 - 11
src/api/modules/dify/chat.ts

@@ -1,11 +0,0 @@
-import { handle } from '../../index'
-
-const suffix = 'sww-api'
-
-// 查询字典项
-export const dictGetAllSysDictsByValue = (params) => handle({
-  url: `/${suffix}/api/dict/getAllSysDictsByValue`,
-  method: 'get',
-  params
-})
-

+ 8 - 0
src/views/smart-ask-answer/assistant/chat.vue

@@ -140,10 +140,17 @@ const initChat = () => {
           type: 'answer',
           welcome: true,
         },
+        {
+          type: 'answer',
+          content: 'asdasd',
+        },
       ]
     }
   })
 }
+const setText = (text: string) => {
+  state.text = text
+}
 onMounted(() => {
   initChat()
   ref_text.value.addEventListener('input', (e) => {
@@ -152,6 +159,7 @@ onMounted(() => {
     textarea.style.height = Math.min(textarea.scrollHeight + 2, 200) + 'px';
   });
 })
+defineExpose({setText})
 </script>
 
 <style lang="scss" scoped>

+ 64 - 0
src/views/smart-ask-answer/assistant/cms/api.ts

@@ -0,0 +1,64 @@
+import { post } from './request'
+
+const suffix = 'cms-api'
+
+// 热门推荐-热门推荐-主题信息
+export const cmsAiQueryHotThemelist = (params) => post(
+  `/${suffix}/szface/cmsAi/queryHotThemelist`,
+  params,
+  {
+    headers: {
+      isEncrypt: true
+    }
+  },
+  'json'
+)
+
+// 热门推荐-热门推荐-热点问题列表
+export const cmsAiQueryHotReclist = (params) => post(
+  `/${suffix}/szface/cmsAi/queryHotReclist`,
+  params,
+  {
+    headers: {
+      isEncrypt: true
+    }
+  },
+  'json'
+)
+
+// 智能问答-问题推荐列表
+export const cmsAiQueryQuestionReclist = (params) => post(
+  `/${suffix}/szface/cmsAi/queryQuestionReclist`,
+  params,
+  {
+    headers: {
+      isEncrypt: true
+    }
+  },
+  'json'
+)
+
+// 政务服务-事项列表
+export const matterQueryMatterlist = (params) => post(
+  `/${suffix}/szface/matter/queryMatterlist`,
+  params,
+  {
+    headers: {
+      isEncrypt: true
+    }
+  },
+  'json'
+)
+
+// 政务资讯-政策文件列表
+export const policyInfoQueryPolicyInfolist = (params) => post(
+  `/${suffix}/szface/policyInfo/queryPolicyInfolist`,
+  params,
+  {
+    headers: {
+      isEncrypt: true
+    }
+  },
+  'json'
+)
+

+ 66 - 0
src/views/smart-ask-answer/assistant/cms/crypto.ts

@@ -0,0 +1,66 @@
+import CryptoJS from "crypto-js";
+
+/**
+ * 随机生成32位的字符串
+ * @returns {string}
+ */
+const generateRandomString = (): string => {
+  const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+  let result = "";
+  const charactersLength = characters.length;
+  for (let i = 0; i < 32; i++) {
+    result += characters.charAt(Math.floor(Math.random() * charactersLength));
+  }
+  return result;
+};
+
+/**
+ * 随机生成aes 密钥
+ * @returns {string}
+ */
+export const generateAesKey = (): CryptoJS.lib.WordArray => {
+  return CryptoJS.enc.Utf8.parse(generateRandomString());
+};
+
+/**
+ * 加密base64
+ * @returns {string}
+ */
+export const encryptBase64 = (str: CryptoJS.lib.WordArray): string => {
+  return CryptoJS.enc.Base64.stringify(str);
+};
+
+/**
+ * 解密base64
+ */
+export const decryptBase64 = (str: string) => {
+  return CryptoJS.enc.Base64.parse(str);
+};
+
+/**
+ * 使用密钥对数据进行加密
+ * @param message
+ * @param aesKey
+ * @returns {string}
+ */
+export const encryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray): string => {
+  const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
+    mode: CryptoJS.mode.ECB,
+    padding: CryptoJS.pad.Pkcs7,
+  });
+  return encrypted.toString();
+};
+
+/**
+ * 使用密钥对数据进行解密
+ * @param message
+ * @param aesKey
+ * @returns {string}
+ */
+export const decryptWithAes = (message: string, aesKey: CryptoJS.lib.WordArray): string => {
+  const decrypted = CryptoJS.AES.decrypt(message, aesKey, {
+    mode: CryptoJS.mode.ECB,
+    padding: CryptoJS.pad.Pkcs7,
+  });
+  return decrypted.toString(CryptoJS.enc.Utf8);
+};

+ 22 - 0
src/views/smart-ask-answer/assistant/cms/jsencrypt.ts

@@ -0,0 +1,22 @@
+import { JSEncrypt } from "jsencrypt";
+
+// 密钥对生成 http://web.chacuo.net/netrsakeypair
+
+const publicKey: string = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK5yfMEMCBDVsLL9j63VJ3tCqi8pUAyW+eDXuU4xbBe+78IbVmblZ3KBgGDcTjqnM2desI5ZitpLa2/jFXn5Mf0CAwEAAQ=='
+
+// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大
+const privateKey: string = 'MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA7yLWhkYx6y/iQrgAj8cQVYIm9hKmdXlRZE8q1uP35rlVujnWM9bO3AIlDbEKzbmCnAJ68sr3Oj2zD3SAvN8SewIDAQABAkEAx3RhRaFqpWVM7KUYItO/9fIWmQu5NyY3EtlNO+rsq8ywrvSttVY2er3tBDoTVtAeOTkJWBwKoNi3u8FHFiOmoQIhAP1TWNTb2q5cd7wFK8itFuSei2A+WaUdvDNl8SaaMIyTAiEA8akkQTSZGBxO9hXChxI9wI5e74m3AVREwzi73dVWe3kCIB3EbnrMvtygRv2UCfoRxM/mhXAww23wmY3cm8KyeaP7AiEAhKZ5tikvGCMB3ObY3tfOedIsnoQTpnEhRZ/wz7X5QNECIBBc4WS/hqncMeNEC2v2vtYJAPX2UzLwV16o4GqJjgzF'
+
+// 加密
+export const encrypt = (txt: string) => {
+  const encryptor = new JSEncrypt();
+  encryptor.setPublicKey(publicKey); // 设置公钥
+  return encryptor.encrypt(txt); // 对数据进行加密
+};
+
+// 解密
+export const decrypt = (txt: string) => {
+  const encryptor = new JSEncrypt();
+  encryptor.setPrivateKey(privateKey); // 设置私钥
+  return encryptor.decrypt(txt); // 对数据进行解密
+};

+ 385 - 0
src/views/smart-ask-answer/assistant/cms/request.ts

@@ -0,0 +1,385 @@
+import axios, {
+  AxiosError,
+  AxiosInstance,
+  AxiosRequestHeaders,
+  AxiosResponse,
+  InternalAxiosRequestConfig,
+} from "axios";
+import qs from "qs";
+import JSONbig from "json-bigint";
+// import Vue from "vue";
+import { merge } from "lodash-es";
+
+import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from "./crypto";
+import { encrypt, decrypt } from "./jsencrypt";
+// mock 不需要加密请求
+const encryptHeader = process.env.VUE_APP_MOCK ? "" : "cprams-encrypt-key"; //
+JSONbig({ storeAsString: true }); //所有超出安全范围的整数以字符串的形式存储
+// import Logger from "@/utils/Logger";
+// import errorCode from "./errorCode";
+// 需要忽略的提示。忽略后,自动 Promise.reject('error')
+const ignoreMsgs = [
+  "无效的刷新令牌", // 刷新令牌被删除时,不用提示
+  "刷新令牌已过期", // 使用刷新令牌,刷新获取新的访问令牌时,结果因为过期失败,此时需要忽略。否则,会导致继续 401,无法跳转到登出界面
+];
+// 默认超时时间
+axios.defaults.timeout = process.env.NODE_ENV === "development" ? 60 * 1000 : 20 * 1000;
+// 相对路径设置
+axios.defaults.baseURL = process.env.VUE_APP_API_PREFIX;
+
+//数据未加密时生效
+axios.defaults.transformResponse = [
+  function (data) {
+    try {
+      // 如果转换成功则返回转换的数据结果
+      return JSONbig.parse(data);
+    } catch (err) {
+      // 如果转换失败,则包装为统一数据格式并返回
+      return data;
+    }
+  },
+];
+
+axios.defaults.transformRequest = [
+  function (data) {
+    return JSONbig.stringify(data);
+  },
+];
+//数据未加密时生效end
+
+// http request 拦截器
+axios.interceptors.request.use(
+  async (config) => {
+    // 设置参数
+    if (!config.headers["Content-Type"]) {
+      config.headers["Content-Type"] = "application/json";
+    }
+
+    const params = config.params || {};
+    const data = config.data || false;
+
+    // 是否需要加密
+    // Logger.log("请求拦截器: config", config);
+
+    let isEncrypt = (config.headers || {})?.isEncrypt === "true";
+    //  JSONbig.stringify(data);
+
+    if (
+      config.method?.toUpperCase() === "POST" &&
+      (config.headers as AxiosRequestHeaders)["Content-Type"] === "application/x-www-form-urlencoded"
+    ) {
+      config.data = qs.stringify(data);
+    }
+    // get参数编码
+    if (config.method?.toUpperCase() === "GET" && params) {
+      let url = config.url + "?";
+      for (const propName of Object.keys(params)) {
+        const value = params[propName];
+        if (value) {
+          if (typeof value === "object") {
+            for (const val of Object.keys(value)) {
+              const params = propName + "[" + val + "]";
+              const subPart = encodeURIComponent(params) + "=";
+              url += subPart + encodeURIComponent(value[val]) + "&";
+            }
+          } else {
+            url += `${propName}=${encodeURIComponent(value)}&`;
+          }
+        }
+      }
+      // 给 get 请求加上时间戳参数,避免从缓存中拿数据
+      // const now = new Date().getTime()
+      // params = params.substring(0, url.length - 1) + `?_t=${now}`
+      url = url.slice(0, -1);
+      config.params = {};
+      config.url = url;
+    }
+    // 当开启参数加密
+    // Logger.log("开启参数加密:isEncrypt:" + isEncrypt);
+    console.log(config.data);
+    if (isEncrypt && encryptHeader && (config.method === "post" || config.method === "put")) {
+      // 生成一个 AES 密钥
+      const aesKey = generateAesKey();
+      const mergeKey = encrypt(encryptBase64(aesKey)) as any;
+      config.headers[encryptHeader] = mergeKey;
+      config.data =
+        typeof config.data === "object"
+          ? encryptWithAes(JSONbig.stringify(config.data), aesKey)
+          : encryptWithAes(config.data, aesKey);
+    }
+    Reflect.deleteProperty(config.headers, "isEncrypt");
+
+    return config;
+  },
+  (err) => {
+    return Promise.reject(err);
+  }
+);
+// response 拦截器
+axios.interceptors.response.use(
+  async (response: AxiosResponse<any>) => {
+    const { data, status } = response;
+    // 加密后的 AES 秘钥
+    const keyStr = response.headers[encryptHeader];
+    // 加密
+    if (keyStr) {
+      const data = response.data;
+      // 请求体 AES 解密
+      const base64Str = decrypt(keyStr);
+      // base64 解码 得到请求头的 AES 秘钥
+      const aesKey = decryptBase64(base64Str.toString());
+      // console.log('---aesKey', base64Str,data, aesKey)
+      // aesKey 解码 data
+      const decryptData = decryptWithAes(data, aesKey);
+      // console.log('---decryptData', decryptData)
+      // 将结果 (得到的是 JSON 字符串) 转为 JSON
+      //转繁体
+      response.data = JSONbig.parse(decryptData);
+      console.log(response.config.url, response.data);
+    }
+    if (!data) {
+      // 返回“[HTTP]请求没有返回值”;
+      console.log("!data");
+      throw new Error();
+    }
+    // const { t } = useI18n()
+    // 未设置状态码则默认成功状态
+    const code = data.status || 200;
+    // 二进制数据则直接返回
+    if (response.request.responseType === "blob" || response.request.responseType === "arraybuffer") {
+      return response.data;
+    }
+    // 获取错误信息
+    const msg = data.msg
+      // || errorCode[code] || errorCode["default"];
+    if (ignoreMsgs.indexOf(msg) !== -1) {
+      // 如果是忽略的错误码,直接返回 msg 异常
+      return Promise.reject(msg);
+    } else if (code === 401) {
+      // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
+      // Vue.prototype.$message({
+      //   message: msg || "令牌过期",
+      //   type: "error",
+      // });
+    } else if (code === 500) {
+      // ElMessage.error(t('sys.api.errMsg500'))
+      // Vue.prototype.$message({
+      //   message: msg || "接口异常",
+      //   type: "error",
+      // });
+      return Promise.reject(new Error(msg));
+    } else if (code === -2) {
+      return Promise.reject(new Error(code));
+    } else {
+      return response;
+    }
+  },
+  (error: any) => {
+    // 中断请求不提示
+    if (axios.isCancel(error)) {
+      return new Promise(() => {});
+    }
+    if (error.name === "AxiosError") {
+      console.error(error.message);
+    }
+    return Promise.reject({ status: 500 });
+
+    if (error.response) {
+      switch (error.response.status) {
+        case 401:
+          // router.replace({
+          //   path: publicPath + "login",
+          //   query: { redirect: router.currentRoute.fullPath }
+          // });
+          // window.localStorage.clear();
+          break;
+        case 500:
+          // Vue.prototype.$message({
+          //   message: error.response.data.resp_msg || "服务器异常",
+          //   type: "error",
+          // });
+          break;
+        case 502:
+          // Vue.prototype.$message({
+          //   message: error.response.data.resp_msg || "接口异常",
+          //   type: "error",
+          // });
+          break;
+        case 400:
+          // Vue.prototype.$message({
+          //   message: error.response.data.resp_msg || "请求失败",
+          //   type: "error",
+          //   timeout: 1300, // 显示毫秒数
+          // });
+          break;
+        default:
+      }
+    }
+    return Promise.reject(error);
+  }
+);
+export function put(url, data = {}, config = {}) {
+  return new Promise((resolve, reject) => {
+    axios.put(url, data, config).then(
+      (response) => {
+        if (response.status === 200) {
+          resolve(response.data);
+        } else {
+          // Vue.prototype.$message({
+          //   message: response.statusText || "接口异常",
+          //   type: "error",
+          // });
+          // Vue.prototype.$message.error(response.statusText)
+        }
+      },
+      (err) => {
+        return reject(err);
+      }
+    );
+  });
+}
+
+function initConfig({ config, reqType }) {
+  let contentType = ``;
+  switch (reqType) {
+    case "json":
+      contentType = "application/json";
+      break;
+    case "file":
+      contentType = "multipart/form-data";
+      break;
+    default:
+      contentType = "application/x-www-form-urlencoded;charset=utf-8";
+  }
+
+  config = merge(
+    {
+      headers: {
+        "Content-Type": contentType,
+      },
+    },
+    config
+  );
+  return config;
+}
+
+export function post(url, data = {}, config = {}, reqType) {
+  config = initConfig({ reqType, config });
+
+  if (data instanceof FormData === false) {
+    data = reqType === "json" ? JSONbig.stringify(data) : qs.stringify({ ...data });
+  }
+  return new Promise((resolve, reject) => {
+    axios.post(url, data, config).then(
+      (response) => {
+        if (response.status === 200) {
+          resolve(response.data);
+        } else {
+          // Vue.prototype.$message({
+          //   message: response.statusText || "接口异常",
+          //   type: "error",
+          // });
+          reject(response);
+          // Vue.prototype.$message.error(response.statusText)
+        }
+      },
+      (err) => {
+        reject(err);
+      }
+    );
+  });
+}
+
+export function postRaw(url, data = {}, config: any = {}, reqType = "json") {
+  config = initConfig({ reqType, config });
+  // JSONbig.stringify(data)
+  data = JSONbig.stringify(data);
+
+  return axios.post(url, data, config).then(
+    (response) => {
+      if (response.status === 200) {
+        return response.data;
+      } else {
+        // Vue.prototype.$message({
+        //   message: response.statusText || "接口异常",
+        //   type: "error",
+        // });
+        // Vue.prototype.$message.error(response.statusText)
+      }
+    },
+    (err) => {
+      // return Promise.reject(err);
+    }
+  );
+}
+
+export function get(url, data = {}, config = {}) {
+  return new Promise((resolve, reject) => {
+    axios.get(url + "?" + qs.stringify(data?.data || {}), config).then(
+      (response) => {
+        if (response.status === 200) {
+          resolve(response.data);
+        } else {
+          // Vue.prototype.$message({
+          //   message: response.statusText || "接口异常",
+          //   type: "error",
+          // });
+          // Vue.prototype.$message.error(response.statusText)
+        }
+      },
+      (err) => {
+        reject(err);
+      }
+    );
+  });
+}
+
+export function del(url, data = {}, config = {}) {
+  return new Promise((resolve, reject) => {
+    axios.delete(url, { data, ...config }).then(
+      (response) => {
+        if (response.status === 200) {
+          resolve(response.data);
+        } else {
+          // Vue.prototype.$message({
+          //   message: response.statusText || "接口异常",
+          //   type: "error",
+          // });
+          // Vue.prototype.$message.error(response.statusText)
+        }
+      },
+      (err) => {
+        reject(err);
+      }
+    );
+  });
+}
+
+export function form(url: string, formData: object = {}, config: object = {}) {
+  let allConfig = Object.assign(
+    {
+      headers: {
+        "Content-Type": "multipart/form-data",
+      },
+    },
+    config
+  );
+  return new Promise((resolve, reject) => {
+    axios.post(url, formData, allConfig).then(
+      (response) => {
+        if (response.status === 200) {
+          resolve(response.data);
+        } else {
+          // Vue.prototype.$message({
+          //   message: response.statusText || "接口异常",
+          //   type: "error",
+          // });
+          // Vue.prototype.$message.error(response.statusText)
+        }
+      },
+      (err) => {
+        return Promise.reject(err);
+      }
+    );
+  });
+}

BIN
src/views/smart-ask-answer/assistant/imgs/bad.png


BIN
src/views/smart-ask-answer/assistant/imgs/copy.png


BIN
src/views/smart-ask-answer/assistant/imgs/good.png


BIN
src/views/smart-ask-answer/assistant/imgs/play.png


+ 96 - 37
src/views/smart-ask-answer/assistant/index.vue

@@ -21,7 +21,7 @@
           <div class="list_1">
             <template v-for="(item, index) in state.askList">
               <div class="item">
-                <span class="__hover">{{item}}</span>
+                <span class="__hover" @click="ref_chat.setText(item)">{{item}}</span>
                 <div class="del __hover" @click="onDelAsk(index)">删除</div>
               </div>
             </template>
@@ -32,7 +32,7 @@
             <div class="title_1">热门主题</div>
             <div class="list">
               <template v-for="item in state.hotList.theme">
-                <div class="item __hover">{{item}}</div>
+                <div class="item __hover" :class="{active: state.hotList.themeId == item.id}" @click="initQuestion(item.id)">{{item.themeName}}</div>
               </template>
             </div>
           </div>
@@ -41,7 +41,7 @@
             <div class="list_1">
               <template v-for="item in state.hotList.question">
                 <div class="item">
-                  <span class="__hover">{{item}}</span>
+                  <span class="__hover" @click="ref_chat.setText(item.hotContent)">{{item.hotContent}}</span>
                 </div>
               </template>
             </div>
@@ -60,14 +60,14 @@
           <div class="list_1">
             <template v-for="item in state.adviseList[state.adviseList.value]">
               <div class="item">
-                <span class="__hover">{{item}}</span>
+                <span class="__hover" @click="ref_chat.setText(item)">{{item}}</span>
               </div>
             </template>
           </div>
         </div>
       </div>
       <div class="assistant-main-content">
-        <chatCom @getText="getText"/>
+        <chatCom ref="ref_chat" @getText="getText"/>
       </div>
     </div>
     <CzrDialog
@@ -155,9 +155,14 @@
 </template>
 
 <script setup lang="ts">
-import {computed, getCurrentInstance, onMounted, reactive, watch} from "vue";
+import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from "vue";
 import CzrDialog from "@/components/czr-ui/CzrDialog.vue";
 import chatCom from './chat.vue'
+import {
+  cmsAiQueryHotReclist,
+  cmsAiQueryHotThemelist,
+  cmsAiQueryQuestionReclist, matterQueryMatterlist, policyInfoQueryPolicyInfolist
+} from "@/views/smart-ask-answer/assistant/cms/api";
 
 const askSplit = 'd95839a9-1b75-8ba3-06e7-8fc46aff233b'
 const askKey = 'assistant_askList'
@@ -167,19 +172,9 @@ const state: any = reactive({
   showDisclaimers: false,
   askList: localStorage.getItem(askKey) ? localStorage.getItem(askKey).split(askSplit) : [],
   hotList: {
-    theme: [
-      '进入口通关',
-      '单证状态',
-      '税率',
-      '企业服务',
-      '口岸',
-      '进入口通关'
-    ],
-    question: [
-      '出口退税查询',
-      '出口退税查询',
-      '出口退税查询'
-    ],
+    theme: [],
+    themeId: '',
+    question: [],
   },
   adviseList: {
     loading: false,
@@ -189,25 +184,15 @@ const state: any = reactive({
       {label: '相关政策', value: 'policy'},
       {label: '相关事项', value: 'item'},
     ],
-    question: [
-      '出口退税查询question',
-      '出口退税查询question',
-      '出口退税查询question',
-    ],
-    policy: [
-      '出口退税查询policy',
-      '出口退税查询policy',
-      '出口退税查询policy',
-    ],
-    item: [
-      '出口退税查询item',
-      '出口退税查询item',
-      '出口退税查询item',
-    ],
+    question: [],
+    policy: [],
+    item: [],
   },
 })
+const ref_chat = ref()
 const getText = (text: string) => {
   setAskList(text)
+  initRelation(text)
 }
 const setAskList = (text: string) => {
   const nowAsk = localStorage.getItem(askKey) ? localStorage.getItem(askKey).split(askSplit) : []
@@ -223,7 +208,76 @@ const onClearAsk = () => {
   state.askList = []
   localStorage.setItem(askKey, state.askList.join(askSplit))
 }
+const initTheme = () => {
+  const params = {
+    data: {
+      pageIndex: 1,
+      pageSize: 10,
+    }
+  }
+  cmsAiQueryHotThemelist(params).then(res => {
+    state.hotList.theme = res?.data?.list || []
+    if (state.hotList.theme.length > 0) {
+      initQuestion(state.hotList.theme[0].id)
+    }
+  })
+}
+const initQuestion = (id) => {
+  state.hotList.themeId = id
+  const params = {
+    data: {
+      pageIndex: 1,
+      pageSize: 10,
+      condition: {
+        themeId: id
+      }
+    }
+  }
+  cmsAiQueryHotReclist(params).then(res => {
+    state.hotList.question = res?.data?.list || []
+  })
+}
+const initRelation = (text = '') => {
+  const params1 = {
+    data: {
+      pageIndex: 1,
+      pageSize: 10,
+      condition: {
+        questionContent: text
+      }
+    }
+  }
+  cmsAiQueryQuestionReclist(params1).then(res => {
+    state.adviseList.question = res?.data?.list.map(v => v.questionContent) || []
+  })
+  const params2 = {
+    data: {
+      pageIndex: 1,
+      pageSize: 10,
+      condition: {
+        mattersName: text
+      }
+    }
+  }
+  matterQueryMatterlist(params2).then(res => {
+    state.adviseList.item = res?.data?.list.map(v => v.mattersName) || []
+  })
+  const params3 = {
+    data: {
+      pageIndex: 1,
+      pageSize: 10,
+      condition: {
+        contentTitle: text
+      }
+    }
+  }
+  policyInfoQueryPolicyInfolist(params3).then(res => {
+    state.adviseList.policy = res?.data?.list.map(v => v.contentTitle) || []
+  })
+}
 onMounted(() => {
+  initTheme()
+  initRelation()
 })
 </script>
 
@@ -243,10 +297,9 @@ onMounted(() => {
   height: 126px;
   display: flex;
   flex-direction: column;
-  overflow: hidden;
+  //overflow: hidden;
+  overflow-y: auto;
   .item {
-    height: 47px;
-    min-height: 47px;
     border-bottom: 1px dashed #D8DAE5;
     display: flex;
     align-items: center;
@@ -254,6 +307,12 @@ onMounted(() => {
     font-weight: 400;
     font-size: 16px;
     color: #111111;
+    >span {
+      min-height: 47px;
+      display: flex;
+      align-items: center;
+      flex: 1;
+    }
     &:before {
       content: '';
       width: 6px;

+ 9 - 0
vite.config.ts

@@ -60,9 +60,18 @@ export default defineConfig(({mode, command}) => {
             return path.replace(/^\/dify-api/, '')
           }
         },
+        '/cms-api': {
+          target: 'http://192.168.5.98:8090/',
+          // target: 'http://192.168.4.12:8090/',
+          changeOrigin: true,
+          rewrite: (path) => {
+            return path.replace(/^\/cms-api/, '')
+          }
+        },
       }
     },
     build: {
+      target: 'esnext',
       outDir: "smart-ask-answer-web",
       rollupOptions: {//分包优化
         output: {