zhangyaowen 3 luni în urmă
părinte
comite
8474851ccb

+ 7 - 4
.env

@@ -2,12 +2,15 @@
 SHOPRO_VERSION=v2.4.1
 
 # 后端接口 - 正式环境(通过 process.env.NODE_ENV 非 development)
-# SHOPRO_BASE_URL=http://192.168.10.17:9095
-SHOPRO_BASE_URL=http://42.194.163.46:9095
+SHOPRO_BASE_URL=https://zjkdywd.com:9018
+# SHOPRO_BASE_URL=https://192.168.10.17:9095
+# SHOPRO_BASE_URL=http://42.194.163.46:9095
 
 # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development)
-# SHOPRO_DEV_BASE_URL=http://192.168.10.17:9095
-SHOPRO_DEV_BASE_URL=http://42.194.163.46:9095
+# 正式环境
+SHOPRO_DEV_BASE_URL=https://zjkdywd.com:9018
+# SHOPRO_DEV_BASE_URL=https://192.168.10.17:9095
+# SHOPRO_DEV_BASE_URL=http://42.194.163.46:9095
 
 ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc
 

+ 65 - 0
keys/STAR.zjkdywd.com.crt

@@ -0,0 +1,65 @@
+-----BEGIN CERTIFICATE-----
+MIIGaDCCBVCgAwIBAgIQTVuBDxGG440QYM6u1MZL4TANBgkqhkiG9w0BAQsFADCB
+hTELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu
+QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEpMCcG
+A1UEAxMgQ2VydHVtIERvbWFpbiBWYWxpZGF0aW9uIENBIFNIQTIwHhcNMjUwNDI4
+MDMwMjE1WhcNMjYwNTI4MDMwMjE0WjAYMRYwFAYDVQQDDA0qLnpqa2R5d2QuY29t
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsGl82VggUNvPpUtvFIa/
+iCBvWfHZa98bYLPOI9bEs/HR3wpHZUZAh4lS39vNjH/F+GNngNtXb8+6P1DCJodX
+ZZyQoVNjsWbdD70YhzaJU5DEiNCjpYYNzWzyg0/emm5OGvfeVOIvFa8QbYsVRDOF
+RJSFqs0EMevlJEVvA8dON8oTFZETuG5kaMwJUQgdk7Hm+8cMSEMGP0sSm9bnMEJw
+jX37UMBx3HczThE0Ej5u5S9tucYeEC3anXxlTjMuBiWwWYCvxDPq/QFMT8j1AQ5W
+Zx1G5xnE6lc0yKwtL1vEuXzIFy1/oaz3yIAeMFHUvZwwo4SV1EvcSUD9Flpn7WjR
+ZwIDAQABo4IDPjCCAzowDAYDVR0TAQH/BAIwADAyBgNVHR8EKzApMCegJaAjhiFo
+dHRwOi8vY3JsLmNlcnR1bS5wbC9kdmNhc2hhMi5jcmwwcQYIKwYBBQUHAQEEZTBj
+MCsGCCsGAQUFBzABhh9odHRwOi8vZHZjYXNoYTIub2NzcC1jZXJ0dW0uY29tMDQG
+CCsGAQUFBzAChihodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvZHZjYXNoYTIu
+Y2VyMB8GA1UdIwQYMBaAFOUxrb86EZb0g7xQPNS3kJuQ7t4lMB0GA1UdDgQWBBSA
+r1335VLGhijFgY3OVdEiLx84tDAdBgNVHRIEFjAUgRJkdmNhc2hhMkBjZXJ0dW0u
+cGwwSwYDVR0gBEQwQjAIBgZngQwBAgEwNgYLKoRoAYb2dwIFAQMwJzAlBggrBgEF
+BQcCARYZaHR0cHM6Ly93d3cuY2VydHVtLnBsL0NQUzAdBgNVHSUEFjAUBggrBgEF
+BQcDAQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMCUGA1UdEQQeMByCDSouempr
+ZHl3ZC5jb22CC3pqa2R5d2QuY29tMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkA
+dwAOV5S8866pPjMbLJkHs/eQ35vCPXEyJd0hqSWsYcVOIQAAAZZ6WGoKAAAEAwBI
+MEYCIQCA4/s49w7jPDZxu8XNbyqh1sLtKgiTSdaI2UHyJwehNgIhAJ0Bm8ifrlO1
+66ar5WZ7TKAQdC5Egu4PcfIyhbrKd1s+AHUAGYbUxyiqb/66A294Kk0BkarOLXIx
+D67OXXBBLSVMx9QAAAGWelhrDwAABAMARjBEAiB7BJobjwohBFL2i18p81VT0dko
+VwHPOSSfs1jfl1L5owIgRl8x7RVZzbLH9Xms8G0xMoI9U2827bRUu45Px/nlM4wA
+dwBkEcRspBLsp4kcogIuALyrTygH1B41J6vq/tUDyX3N8AAAAZZ6WGunAAAEAwBI
+MEYCIQDmbXnPq8H/iL6M53ktz/LyoLSfPVBAx1sBGx02Y01GfwIhAOxNaHTc+4Lm
+kPmCa7Vl/hHp6GhAHIIgWYLEOSpzpRQCMA0GCSqGSIb3DQEBCwUAA4IBAQBiU+7x
+0krk8SCL4SFS9gUbpJelbMP5dS1gRlV425bUrHOQizZneptLaUAd+rvXhfIw34cJ
+UyvzlNNYe/r8bdwFhqIzCNyYFud1z4xCb6Aa0YRRpCDvnHvFErI0YpM5dcRaT5YC
+jkvmWCJlDc3g/wwgqSzPaQ5hyuixoWa/IunxiJbKyh8HvCPeqUGeBLSRGSUNreTE
+C6Kk69tNro5+mMUgP91t+7w4i4S1oYh4N49EeA3NWGQwjwznwlxKdzTvN9o773oz
+ssCVkXvaWGlb1QxWHCBSTRWsd/xk4xpRCiwQZfCYnY47G9b6myDDn97wfPacX6lW
+aRfnJMW+bqA6J2HJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEzjCCA7agAwIBAgIQJt3SK0bJxE1aaU05gH5yrTANBgkqhkiG9w0BAQsFADB+
+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5B
+LjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIwIAYD
+VQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMB4XDTE0MDkxMTEyMDAwMFoX
+DTI3MDYwOTEwNDYzOVowgYUxCzAJBgNVBAYTAlBMMSIwIAYDVQQKExlVbml6ZXRv
+IFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv
+biBBdXRob3JpdHkxKTAnBgNVBAMTIENlcnR1bSBEb21haW4gVmFsaWRhdGlvbiBD
+QSBTSEEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoSVj343kIAfZ
+VNHRBPYX4j5H+8N0JbjEvxISvOBw0TkFwhez94JwoE4H/hAq/9sNRl4klKOLRZ8Y
+m85CxK7bgzO8wru0MLanN4d4e0jLJSyCuwpIEmB2ieyOzI8eUkjphgJawrCKfIU9
+2f9gTzNspqGgheHXU/LqJz1lqXLBCIPMsCWcEUYk4D70p+/tUbFlk0K09uaGChB5
+MjZYsmuo3NV6Hp0U7kDnskZMvZopwuz4MMFiAiriHINi0IU2GoPeEoQpZe/SMr4x
+YEKoz/jd6tBWRx29dpYkE+e+2Zkr+jBk8Yo4eqbhKpYCsJ262I9tTnqUaX2wk6p0
+5ZOQE/qimQIDAQABo4IBPjCCATowDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
+5TGtvzoRlvSDvFA81LeQm5Du3iUwHwYDVR0jBBgwFoAUCHbNywf/JPbFze27kLzi
+hDdGdfcwDgYDVR0PAQH/BAQDAgEGMC8GA1UdHwQoMCYwJKAioCCGHmh0dHA6Ly9j
+cmwuY2VydHVtLnBsL2N0bmNhLmNybDBrBggrBgEFBQcBAQRfMF0wKAYIKwYBBQUH
+MAGGHGh0dHA6Ly9zdWJjYS5vY3NwLWNlcnR1bS5jb20wMQYIKwYBBQUHMAKGJWh0
+dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdG5jYS5jZXIwOQYDVR0gBDIwMDAu
+BgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93d3cuY2VydHVtLnBsL0NQUzAN
+BgkqhkiG9w0BAQsFAAOCAQEAur/w4d1NK0JDZFjfZPP/gBpfVr47qbJ291R6TDDB
+mSRLctLK1PoIxpDeiBLt+JD5/KmE/ZLyeOXbySJXq0EwQmsLn9dzM/sBZxxCXI8n
+Z8duBwONDpbLCgPMPviHPDUwzRiM1XHdzd1hsBOjZEZO/nFOa2XpFATyP6i9DDY9
+Kl2eB/LCT5DFXk0YN9EnKICkNuXKk2plDviTua9SWEt6cdi68+/S8/ail+RdFAKa
+y+WutpPhI5+bP0b37o6hAFtmwx5oI4YPXXe6U635UvtwFcV16895rUl88nZirkQv
+xV9RNCVBahIKX46uEMRDiTX97P8x5uweh+k6fClQRUGjFA==
+-----END CERTIFICATE-----

+ 28 - 0
keys/STAR.zjkdywd.com.key

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwaXzZWCBQ28+l
+S28Uhr+IIG9Z8dlr3xtgs84j1sSz8dHfCkdlRkCHiVLf282Mf8X4Y2eA21dvz7o/
+UMImh1dlnJChU2OxZt0PvRiHNolTkMSI0KOlhg3NbPKDT96abk4a995U4i8VrxBt
+ixVEM4VElIWqzQQx6+UkRW8Dx043yhMVkRO4bmRozAlRCB2Tseb7xwxIQwY/SxKb
+1ucwQnCNfftQwHHcdzNOETQSPm7lL225xh4QLdqdfGVOMy4GJbBZgK/EM+r9AUxP
+yPUBDlZnHUbnGcTqVzTIrC0vW8S5fMgXLX+hrPfIgB4wUdS9nDCjhJXUS9xJQP0W
+WmftaNFnAgMBAAECggEAJI1G1BXBZF+XD42or10YFFF8IOvOe95XYtr+jEh0q0Ag
+lxEfJ+m9dxfJ9x4o4cKbt1KJYJaR8FXfJj1KbQc/4YrmILcRgfFpPliU23c+TvVA
+84QfVC2JcJXxTanf2qmeP1I6W+YdkWWbhsb5mg7+vu3zICeohdt1t+U3ElZaa453
+9pUgCQL+PzgmbRxJOV3AIgV9IB2ipeadOwwSFKi/J6736APKjZm7iNQpP4iljQC0
+p+1zIqPjtJQ9niKOlcaYvoYAqrf/H69Y9weJlKazS9k25clc1stVML7Vs5NQP/VV
+JbM4EwmqQh3pGSChkfAJQRjzRqoQh0Px1KayygsNsQKBgQDbwYVuGt5nHx+Uoj6c
+vTU/iFlmd8LUTBzxaRDoy93FQd36XFhtqT623Ajrdls9Y+YsiIuMdIlxrJFxXeOB
+YwCZVjpOB3JXwt055EZY9+BM2A8C//5mxtzpiAx3+VwX9Qr9wsVqK339Si47tlh9
+rc9/goB2tWPnEoqWFfMjtghjXwKBgQDNgemMujfKuJOFOv/n2u/HG9iYkYmWk0DF
+wCTISkkcLUnuKLC9OzpGSkJPg8bk21ci+nqNr1e/orL+miuTq4y7XisUXxCbvFQO
++08EZohmJxLjkNUSw0T6nkOy0+h9SpHuntRz++QvOndNmLCmZtmCEfyXolJIesfN
+MMTv2woW+QKBgHeuUWjBoVYwERb1tXvPX+yV6Xpwxt6wBSNEwTzHqqP/V1E+cbpn
+bgxQkVsLPm2UtoqSYs2/ugr6nMtNAtY4gN8BowdT7uX07l7U/6r8dF/lW2ea24ZF
+z5Iv0fqVB57UiYPiS3mi7h7AA6XpjRAFVPhwrl34M8xowKk0kxJj1PWpAoGAdFLc
+x/OAMRdZKUjEIZEjuChi6bk3kCu5tjWUnGqwB2r7NfuCTxp/WdoevrNwmYouQHZX
+eGuP18FYYBUUJXotJsGe0OEenYh0bCaEC5GeE3R6lKUedohhOxrqZwbbyk+zWsY8
+q4ohFzqY3StYklgVNjLIL2yJ9szI27SHRN1PUMECgYAk0gPbFp7LyyQ4qThDDoFY
+wdZ/mOz0nLJm7SVvls0nmg3Pwhg+maA6APdmNtPDyHt2lLiD+MBbXMyasP4k8/A4
+tiye0B9O9FUhvy5P1QWIHkpSZf0VOT102qZtt5+nsx11PY9dH9utudcPZb0swfjd
+7hWQ6D4KDrA67dhFvrvtVA==
+-----END PRIVATE KEY-----

+ 2 - 1
package.json

@@ -88,13 +88,14 @@
     }
   },
   "dependencies": {
-    "vue": "3.5.12",
+    "@microsoft/fetch-event-source": "^2.0.1",
     "dayjs": "^1.11.7",
     "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",
     "luch-request": "^3.0.8",
     "pinia": "^2.0.33",
     "pinia-plugin-persist-uni": "^1.2.0",
+    "vue": "3.5.12",
     "weixin-js-sdk": "^1.6.0"
   },
   "devDependencies": {

+ 100 - 0
pages/chat/components/eventSource.vue

@@ -0,0 +1,100 @@
+<!--
+ * @Author: HTangtang 1539880046@qq.com
+ * @Date: 2025-01-21 14:41:20
+ * @LastEditors: HTangtang 1539880046@qq.com
+ * @LastEditTime: 2025-03-06 17:49:40
+ * @FilePath: \dam_app_project\pages\smartAi\components\eventSource.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <view id="event-source" :props="mergeProps" :change:props="eventSource.renderPropsChange" v-show="false">
+  </view>
+</template>
+
+<script>
+export default {
+  props: {
+    url: {
+      type: String,
+      default: "",
+    },
+    options: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  data() {
+    return {
+      isSend: false
+    }
+  },
+  computed: {
+    // 合并传入renderjs的数据
+    mergeProps({ url, options, isSend }) {
+      return {
+        url,
+        options,
+        isSend
+      };
+    },
+  },
+  methods: {
+    // 发送
+    send() {
+      this.isSend = true;
+      this.$nextTick(() => {
+        this.isSend = false;
+      });
+    },
+    // 处理renderjs发回的数据
+    emits(e) {
+      this.$emit("callback", { ...e });
+    },
+  },
+};
+</script>
+
+<script module="eventSource" lang="renderjs">
+import { fetchEventSource } from "@microsoft/fetch-event-source";
+export default {
+	methods: {
+		// 传入数据变更
+    renderPropsChange(nVal) {
+      const { url, isSend } = nVal || {};
+      if (!isSend) return;
+      if (!url) return this.handleEmitData({ type: "tip", msg: "URL cannot be empty." });
+      this.$nextTick(() => {
+        this.handleSSE(nVal);
+      });
+    },
+		// 发送数据到service层
+		handleEmitData(data = {}) {
+			this.$ownerInstance.callMethod('emits', data);
+		},
+    // 处理SSE
+		 handleSSE(opts = {}) {
+      const that = this;
+      // 检查浏览器是否支持SSE
+			if (!('EventSource' in window)) return this.handleEmitData({ type: "tip", msg: "The current device does not support EventSource." });
+      const { url, options = {} } = opts || {};
+      // console.log("options", JSON.stringify(options));
+      fetchEventSource(url, {
+        ...options,
+       async onopen() {
+          that.handleEmitData({ type: "onopen", msg: "EventSource onopen." });
+        },
+        onmessage(res) {
+          //  console.log(res,'resres')
+          that.handleEmitData({ type: "onmessage", msg: "EventSource onmessage.", data: res.data });
+        },
+        onclose() {
+          that.handleEmitData({ type: "onclose", msg: "EventSource onclose." });
+        },
+        onerror(error) {
+          that.handleEmitData({ type: "onerror", msg: "EventSource onerror.", data: JSON.stringify(error) });
+        }
+      });
+		}
+	}
+}
+</script>

+ 80 - 76
pages/chat/components/messageInput.vue

@@ -1,102 +1,106 @@
 <template>
   <view class="send-wrap ss-flex">
     <view class="left ss-flex ss-flex-1">
-      <uni-easyinput
-        class="ss-flex-1 ss-p-l-22"
-        :inputBorder="false"
-        :clearable="false"
-        v-model="message"
-        placeholder="请输入你要咨询的问题"
-      ></uni-easyinput>
+      <uni-easyinput class="ss-flex-1 ss-p-l-22" :inputBorder="false" :clearable="false" v-model="message"
+        placeholder="请输入你要咨询的问题"></uni-easyinput>
     </view>
     <text class="sicon-basic bq" @tap.stop="onTools('emoji')"></text>
-    <text
-      v-if="!message"
-      class="sicon-edit"
-      :class="{ 'is-active': toolsMode === 'tools' }"
-      @tap.stop="onTools('tools')"
-    ></text>
-    <button v-if="message" class="ss-reset-button send-btn" @tap="sendMessage">
-      发送
+    <text v-if="!message" class="sicon-edit" :class="{ 'is-active': toolsMode === 'tools' }"
+      @tap.stop="onTools('tools')"></text>
+    <button v-if="message" class="ss-reset-button send-btn" :disabled="loading" @tap="sendMessage">
+      {{ loading ? '发送中...' : '发送' }}
     </button>
   </view>
 </template>
 
 <script setup>
-  import { computed } from 'vue';
-  /**
-   * 消息发送组件
-   */
-  const props = defineProps({
-    // 消息
-    modelValue: {
-      type: String,
-      default: '',
-    },
-    // 工具模式
-    toolsMode: {
-      type: String,
-      default: '',
-    },
-  });
-  const emits = defineEmits(['update:modelValue', 'onTools', 'sendMessage']);
-  const message = computed({
-    get() {
-      return props.modelValue;
-    },
-    set(newValue) {
-      emits(`update:modelValue`, newValue);
-    }
-  });
-
+import { computed, ref } from 'vue';
+/**
+ * 消息发送组件
+ */
+const props = defineProps({
+  // 消息
+  modelValue: {
+    type: String,
+    default: '',
+  },
+  // 工具模式
+  toolsMode: {
+    type: String,
+    default: '',
+  },
+  loading: {
+    type: Boolean,
+    default: false,
+  },
+});
 
-  // 打开工具菜单
-  function onTools(mode) {
-    emits('onTools', mode);
+const emits = defineEmits(['update:modelValue', 'onTools', 'sendMessage']);
+const message = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(newValue) {
+    emits(`update:modelValue`, newValue);
   }
+});
+
 
-  // 发送消息
-  function sendMessage() {
-    emits('sendMessage');
+// 打开工具菜单
+function onTools(mode) {
+  emits('onTools', mode);
+}
+
+// 发送消息
+async function sendMessage() {
+  if (props.loading) return;
+  try {
+    await emits('sendMessage');
+  } finally {
   }
+}
 </script>
 
 <style scoped lang="scss">
-  .send-wrap {
-    padding: 18rpx 20rpx;
-    background: #fff;
+.send-wrap {
+  padding: 18rpx 20rpx;
+  background: #fff;
 
-    .left {
-      height: 64rpx;
-      border-radius: 32rpx;
-      background: var(--ui-BG-1);
-    }
+  .left {
+    height: 64rpx;
+    border-radius: 32rpx;
+    background: var(--ui-BG-1);
+  }
 
-    .bq {
-      font-size: 50rpx;
-      margin-left: 10rpx;
-    }
+  .bq {
+    font-size: 50rpx;
+    margin-left: 10rpx;
+  }
 
-    .sicon-edit {
-      font-size: 50rpx;
-      margin-left: 10rpx;
-      transform: rotate(0deg);
-      transition: all linear 0.2s;
+  .sicon-edit {
+    font-size: 50rpx;
+    margin-left: 10rpx;
+    transform: rotate(0deg);
+    transition: all linear 0.2s;
 
-      &.is-active {
-        transform: rotate(45deg);
-      }
+    &.is-active {
+      transform: rotate(45deg);
     }
+  }
+
+  .send-btn {
+    width: 100rpx;
+    height: 60rpx;
+    line-height: 60rpx;
+    border-radius: 30rpx;
+    background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
+    font-size: 26rpx;
+    color: #fff;
+    margin-left: 11rpx;
 
-    .send-btn {
-      width: 100rpx;
-      height: 60rpx;
-      line-height: 60rpx;
-      border-radius: 30rpx;
-      background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
-      font-size: 26rpx;
-      color: #fff;
-      margin-left: 11rpx;
+    &:disabled {
+      opacity: 0.6;
     }
   }
+}
 </style>

+ 29 - 1
pages/chat/components/messageList.vue

@@ -127,5 +127,33 @@ const onScrollToUpper = () => {
   }
   showNewMessageTip.value = false;
 };
-defineExpose({ getMessageList, refreshMessageList });
+// 暴露方法 修改消息列表指定的消息
+const updateMessage = (items, messageId = '') => {
+  messageList.value = messageList.value.map((item) => {
+    if (item.ids == 1) {
+      delete item.ids
+      item.content = items.content
+      item.messageId = items.messageId
+      item.isAi = true
+      return item;
+    } else {
+      if (messageId == "") {
+        if (item.messageId == items.messageId) {
+          item.content += items.content
+        }
+      } else {
+        if (item.messageId == messageId) {
+          item.isAi = false
+        }
+      }
+      return item;
+    }
+  });
+}
+// 通过id 获取对应的头像
+const getAvatar = (id) => {
+  return messageList.value[messageList.value.length - 1]
+}
+
+defineExpose({ getMessageList, refreshMessageList, updateMessage, messageList, getAvatar });
 </script>

+ 10 - 2
pages/chat/components/messageListItem.vue

@@ -49,6 +49,14 @@
           <OrderItem :orderData="getMessageContent(message)"
             @tap="sheep.$router.go('/pages/order/detail', { id: getMessageContent(message).id })" />
         </template>
+        <template v-if="message.contentType === KeFuMessageContentTypeEnum.AI">
+          <view class="message-box" :class="{ admin: message.senderId === UserTypeEnum.ADMIN }">
+            <mp-html :content="replaceEmoji(getMessageContent(message).text || message.content)" />
+            <!-- {{ message.messageId }}----
+            {{ loadingId }} -->
+            <text v-if="loadingInput && message.isAi">...</text>
+          </view>
+        </template>
         <!-- user头像 -->
         <image v-if="message.senderId === userInfo.id" class="chat-avatar ss-m-l-24" :src="message.receiverAvatar ||
           default1
@@ -60,7 +68,7 @@
 </template>
 
 <script setup>
-import { computed, unref } from 'vue';
+import { computed, unref, inject } from 'vue';
 
 import default1 from '/static/default.jpg';
 import dayjs from 'dayjs';
@@ -70,7 +78,7 @@ import sheep from '@/sheep';
 import { formatDate, jsonParse } from '@/sheep/util';
 import GoodsItem from '@/pages/chat/components/goods.vue';
 import OrderItem from '@/pages/chat/components/order.vue';
-
+const loadingInput = inject('loadingInput');
 const props = defineProps({
   // 消息
   message: {

+ 99 - 5
pages/chat/index.vue

@@ -10,7 +10,8 @@
     <!--  聊天区域  -->
     <MessageList ref="messageListRef">
       <template #bottom>
-        <message-input v-model="chat.msg" @on-tools="onTools" @send-message="onSendMessage"></message-input>
+        <message-input :loading="loadingInput" v-model="chat.msg" @on-tools="onTools"
+          @send-message="onSendMessage"></message-input>
       </template>
     </MessageList>
     <!--  聊天工具  -->
@@ -20,12 +21,14 @@
     </tools-popup>
     <!--  产品订单选择  -->
     <SelectPopup :mode="chat.selectMode" :show="chat.showSelect" @select="onSelect" @close="chat.showSelect = false" />
+    <EventSource ref="EventSourceRef" :url="eventSourceUrl" :options="eventSourceOptions" @callback="handleCallback" />
   </s-layout>
 </template>
 
 <script setup>
 import MessageList from '@/pages/chat/components/messageList.vue';
-import { reactive, ref, toRefs, computed } from 'vue';
+import EventSource from '@/pages/chat/components/eventSource.vue';
+import { reactive, ref, toRefs, computed, provide } from 'vue';
 import sheep from '@/sheep';
 import ToolsPopup from '@/pages/chat/components/toolsPopup.vue';
 import MessageInput from '@/pages/chat/components/messageInput.vue';
@@ -39,6 +42,48 @@ import KeFuApi from '@/sheep/api/promotion/kefu';
 import { useWebSocket } from '@/sheep/hooks/useWebSocket';
 import { jsonParse } from '@/sheep/util';
 import { onLoad } from '@dcloudio/uni-app';
+const EventSourceRef = ref(null); //AIref
+
+const eventSourceUrl = import.meta.env.SHOPRO_BASE_URL + "/app-api/promotion/kefu-message/sendStream"; //ai客服URL
+// const eventSourceUrl = "https://192.168.10.17:9095/app-api/infra/ai-dify/chat-messages-stream"; //ai客服URL
+// ai客服流式上传参数
+const eventSourceOptions = computed(() => {
+  return {
+    headers: {
+      "content-type": "application/json",
+      Accept: "text/event-stream",
+      "tenant-id": 1,
+      Authorization: "Bearer " + uni.getStorageSync('token'),
+    },
+    method: "POST",
+    body: JSON.stringify({
+      "contentType": 22,
+      "content": chat.msg,
+      "relUserId": route.value.relUserId
+      // type: "律师咨询",
+      // query: chat.msg
+    }), // 请求体
+  };
+});
+const answerArr = ref('');
+const loadingInput = ref(false); // 加载状态
+provide('loadingInput', loadingInput); // 依赖注入加载状态
+const loadingId = ref(""); // 加载的id
+// ai客服发送消息接收回调
+const handleCallback = async (e) => {
+  const { type, msg, data } = e || {};
+  if (type == "onmessage") {
+    const datas = JSON.parse(data);
+    answerArr.value += datas.content;
+    loadingId.value = datas.messageId
+    await messageListRef.value.updateMessage(datas);
+  }
+  if (type == "onclose") {
+    loadingInput.value = false;
+    await messageListRef.value.updateMessage({}, loadingId.value);
+    answerArr.value = ""
+  }
+};
 const sys_navBar = sheep.$platform.navbar;
 const options1 = [{
   label: 'ai',
@@ -66,23 +111,72 @@ const route = ref({})
 onLoad((options) => {
   route.value = options
 });
-
+const userInfo = computed(() => sheep.$store('user').userInfo);
 // 发送消息
 async function onSendMessage() {
   if (!chat.msg) return;
   try {
+    loadingInput.value = true;
+    const idArr = ['1', '2', '3', '4']
     const data = {
       conversationId: route.value.conversationId,
       contentType: KeFuMessageContentTypeEnum.TEXT,
       content: JSON.stringify({ text: chat.msg }),
       relUserId: route.value.relUserId
     };
-    await KeFuApi.sendKefuMessageNew(data);
-    await messageListRef.value.refreshMessageList();
+    // 如果在线就走直接发消息  如果不在线就走ai回复
+    const res = await KeFuApi.checkUserId({
+      userId: route.value.relUserId
+    })
+    if (res.data) {
+      await KeFuApi.sendKefuMessageNew(data);
+    } else {
+      if (idArr.includes(route.value.relUserId)) {
+        console.log(messageListRef.value.getAvatar(), 555222233)
+        const avatarObj = messageListRef.value.getAvatar()
+        const params = {
+          id: 1,
+          conversationId: route.value.conversationId,
+          senderId: userInfo.value.id,
+          senderAvatar: avatarObj.senderAvatar,
+          senderType: 1,
+          receiverId: route.value.relUserId,
+          receiverAvatar: avatarObj.receiverAvatar,
+          receiverType: null,
+          contentType: 1,
+          content: JSON.stringify({ text: chat.msg }),
+          readStatus: true,
+          createTime: 1745546275000
+        }
+        await messageListRef.value.refreshMessageList(params);
+        const params1 = {
+          ids: 1,
+          isAi: true,
+          isLoading: true,
+          conversationId: route.value.conversationId,
+          senderId: route.value.relUserId,
+          senderAvatar: avatarObj.receiverAvatar,
+          senderType: 1,
+          receiverId: userInfo.value.id,
+          receiverAvatar: avatarObj.senderAvatar,
+          receiverType: null,
+          contentType: 22,
+          content: '',
+          readStatus: true,
+          createTime: 1745546275000
+        }
+        console.log(params1, params, 555222233)
+        await messageListRef.value.refreshMessageList(params1);
+        await EventSourceRef.value.send(data);
+      } else {
+        await KeFuApi.sendKefuMessageNew(data);
+      }
+    }
     chat.msg = '';
   } finally {
     chat.showTools = false;
   }
+
 }
 
 const messageListRef = ref();

+ 1 - 0
pages/chat/util/constants.js

@@ -7,6 +7,7 @@ export const KeFuMessageContentTypeEnum = {
   // ========== 商城特殊消息 ==========
   PRODUCT: 10, //  产品消息
   ORDER: 11, //  订单消息"
+  AI: 22, // AI消息
 };
 export const UserTypeEnum = {
   MEMBER: 1, // 会员 面向 c 端,普通用户

+ 35 - 0
sheep/api/promotion/kefu.js

@@ -29,6 +29,41 @@ const KeFuApi = {
       },
     });
   },
+  sendStream: (data) => {
+    return request({
+      url: '/promotion/kefu-message/sendStream',
+      method: 'POST',
+      data,
+      custom: {
+        auth: true,
+        showLoading: true,
+        loadingMsg: '发送中',
+        showSuccess: true,
+        successMsg: '发送成功',
+      },
+    });
+  },
+  checkUserId: (params) => {
+    return request({
+      url: '/promotion/kefu-message/checkUserId',
+      method: 'GET',
+      params,
+    });
+  },
+  chatMessagesStream: (data) => {
+    return request({
+      url: '/infra/ai-dify/chat-messages-stream',
+      method: 'POST',
+      data,
+      custom: {
+        auth: true,
+        showLoading: true,
+        loadingMsg: '发送中',
+        showSuccess: true,
+        successMsg: '发送成功',
+      },
+    });
+  },
   getKefuMessageList: (params) => {
     return request({
       url: '/promotion/kefu-message/list',

+ 1 - 0
sheep/components/s-goods-card/s-goods-card.vue

@@ -65,6 +65,7 @@
     <view v-if="layoutType === LayoutTypeEnum.ONE_COL_SMALL_IMG && state.goodsList.length" class="goods-lg-box">
       <view class="goods-box" :style="[{ marginBottom: data.space + 'px' }]" v-for="item in state.goodsList"
         :key="item.id">
+
         <s-goods-column class="goods-card" size="lg" :goodsFields="data.fields" :data="item" :tagStyle="data.badge"
           :titleColor="data.fields.name?.color" :subTitleColor="data.fields.introduction.color"
           :topRadius="data.borderRadiusTop" :bottomRadius="data.borderRadiusBottom"

+ 61 - 61
sheep/components/s-menu-grid/s-menu-grid.vue

@@ -28,77 +28,77 @@
 </template>
 
 <script setup>
-	import sheep from '@/sheep';
-	import {
-		computed
-	} from 'vue';
+import sheep from '@/sheep';
+import {
+	computed
+} from 'vue';
 
-	const props = defineProps({
-		// 装修数据
-		data: {
-			type: Object,
-			default: () => ({}),
-		},
-		// 装修样式
-		styles: {
-			type: Object,
-			default: () => ({}),
-		},
-	});
-	// 设置背景样式
-	const bgStyle = computed(() => {
-		// 直接从 props.styles 解构
-		const {
-			bgType,
-			bgImg,
-			bgColor
-		} = props.styles;
+const props = defineProps({
+	// 装修数据
+	data: {
+		type: Object,
+		default: () => ({}),
+	},
+	// 装修样式
+	styles: {
+		type: Object,
+		default: () => ({}),
+	},
+});
+// 设置背景样式
+const bgStyle = computed(() => {
+	// 直接从 props.styles 解构
+	const {
+		bgType,
+		bgImg,
+		bgColor
+	} = props.styles;
 
-		// 根据 bgType 返回相应的样式
-		return {
-			background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
-		};
-	});
+	// 根据 bgType 返回相应的样式
+	return {
+		background: bgType === 'img' ? `url(${bgImg}) no-repeat top center / 100% 100%` : bgColor
+	};
+});
 </script>
 
 <style lang="scss" scoped>
-	.menu-image {
-		width: 24px;
-		height: 24px;
-	}
+.menu-image {
+	width: 24px;
+	height: 24px;
+}
 
-	.grid-item-box {
-		flex: 1;
-		display: flex;
-		flex-direction: column;
-		align-items: center;
-		justify-content: center;
-		height: 100%;
+.grid-item-box {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	height: 100%;
 
-		.img-box {
-			position: relative;
+	.img-box {
+		position: relative;
 
-			.tag-box {
-				position: absolute;
-				z-index: 2;
-				top: 0;
-				right: 0;
-				font-size: 2em;
-				line-height: 1;
-				padding: 0.4em 0.6em 0.3em;
-				transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
-				transform-origin: 100% 0;
-				border-radius: 200rpx;
-				white-space: nowrap;
-			}
+		.tag-box {
+			position: absolute;
+			z-index: 2;
+			top: 0;
+			right: 0;
+			font-size: 2em;
+			line-height: 1;
+			padding: 0.4em 0.6em 0.3em;
+			transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
+			transform-origin: 100% 0;
+			border-radius: 200rpx;
+			white-space: nowrap;
 		}
+	}
 
-		.title-box {
-			.grid-tip {
-				font-size: 24rpx;
-				white-space: nowrap;
-				text-align: center;
-			}
+	.title-box {
+		.grid-tip {
+			font-size: 24rpx;
+			white-space: nowrap;
+			text-align: center;
 		}
 	}
+}
 </style>

+ 28 - 24
vite.config.js

@@ -4,31 +4,35 @@ import path from 'path';
 // import viteCompression from 'vite-plugin-compression';
 import uniReadPagesV3Plugin from './sheep/router/utils/uni-read-pages-v3';
 import mpliveMainfestPlugin from './sheep/libs/mplive-manifest-plugin';
-
+import fs from 'fs';
 
 // https://vitejs.dev/config/
 export default (command, mode) => {
-	const env = loadEnv(mode, __dirname, 'SHOPRO_');
-	return {
-		envPrefix: "SHOPRO_",
-		plugins: [
-			uni(),
-			// viteCompression({
-			// 	verbose: false
-			// }),
-			uniReadPagesV3Plugin({
-				pagesJsonDir: path.resolve(__dirname, './pages.json'),
-				includes: ['path', 'aliasPath', 'name', 'meta'],
-			}),
-			mpliveMainfestPlugin(env.SHOPRO_MPLIVE_ON)
-		],
-		server: {
-			host: true,
-			// open: true,
-			port: env.SHOPRO_DEV_PORT,
-			hmr: {
-				overlay: true,
-			},
-		},
-	};
+  const env = loadEnv(mode, __dirname, 'SHOPRO_');
+  return {
+    envPrefix: 'SHOPRO_',
+    plugins: [
+      uni(),
+      // viteCompression({
+      // 	verbose: false
+      // }),
+      uniReadPagesV3Plugin({
+        pagesJsonDir: path.resolve(__dirname, './pages.json'),
+        includes: ['path', 'aliasPath', 'name', 'meta'],
+      }),
+      mpliveMainfestPlugin(env.SHOPRO_MPLIVE_ON),
+    ],
+    server: {
+      host: true,
+      // open: true,
+      port: env.SHOPRO_DEV_PORT,
+      hmr: {
+        overlay: true,
+      },
+      https: {
+        cert: fs.readFileSync(path.join(__dirname, 'keys/STAR.zjkdywd.com.crt')),
+        key: fs.readFileSync(path.join(__dirname, 'keys/STAR.zjkdywd.com.key')),
+      },
+    },
+  };
 };