Ver código fonte

企业微信数据智能专区-获取企微会话功能

cgp 2 dias atrás
pai
commit
653fcbf237

+ 1 - 0
package.json

@@ -90,6 +90,7 @@
     "@huaweicloud/huaweicloud-sdk-core": "^3.1.105",
     "@huaweicloud/huaweicloud-sdk-vod": "^3.1.105",
     "@riophae/vue-treeselect": "0.4.0",
+    "@wecom/jssdk": "^2.4.0",
     "axios": "0.21.0",
     "chart.js": "^2.9.4",
     "clipboard": "2.0.6",

+ 1 - 0
public/index.html

@@ -5,6 +5,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
     <meta name="renderer" content="webkit">
     <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
     <!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico"> -->
     <!-- <title><%= webpackConfig.name %></title> -->
     <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->

+ 84 - 0
src/api/qw/companySession.js

@@ -0,0 +1,84 @@
+// src/api/qw/companySession.js
+// 统一封装企微会话相关接口
+
+import request from '@/utils/request';
+
+// ---------- 登录 & 签名(走 SCRM 后端) ----------
+
+/**
+ * 企微扫码登录(用 code 换取用户信息)
+ * @param {object} param { code: string }
+ * @returns {Promise}
+ */
+export function qwLogin(param) {
+  return request({
+    url: '/weChatSpace/login',
+    method: 'post',
+    data: param
+  });
+}
+
+/**
+ * 获取 agentConfig 签名
+ * @param {object} query { url: string }
+ * @returns {Promise}
+ */
+export function qwSignature(query) {
+  return request({
+    url: '/weChatSpace/getAgentConfigSignature',
+    method: 'get',
+    params: query
+  });
+}
+
+// ----------获取企微专区会话配置 ----------
+export function getQwSessionConfig() {
+  return request({
+    url: '/weChatSpace/getQwSessionConfig',
+    method: 'get'
+  });
+}
+
+
+// ---------- 会话记录(暂走测试服务器代理) ----------
+
+/**
+ * 获取会话记录列表
+ * @param {object} params
+ * @returns {Promise}
+ */
+// export async function qwConversations({ seq = 0, limit = 50, customerId, staffUserId } = {}) {
+//   const BASE = '/wecom-api';
+//   let url = `${BASE}/api/conversations?seq=${seq}&limit=${limit}&customerId=${customerId}`;
+//   if (staffUserId) {
+//     url += `&staffUserId=${staffUserId}`;
+//   }
+//   const res = await fetch(url);
+//   const data = await res.json();
+//   if (data.errcode !== 0) {
+//     throw new Error(data.errmsg || '获取会话失败');
+//   }
+//   return data;
+// }
+
+// 改用 request 调用中转接口
+export async function qwConversations(params = {}) {
+  const res = await request({
+    url: '/weChatSpace/conversations',
+    method: 'get',
+    params: {
+      seq: params.seq || 0,
+      limit: params.limit || 50,
+      proxy: params.proxy || 0,
+      timeout: params.timeout || 30,
+      customerId: params.customerId,
+      staffUserId: params.staffUserId
+    }
+  });
+  const resp = res.data || res;
+  if (resp.errcode !== 0) {
+    throw new Error(resp.errmsg || '获取会话失败');
+  }
+  return resp;
+}
+

+ 9 - 0
src/api/qw/user.js

@@ -33,6 +33,15 @@ export function userList(query) {
     params: query
   })
 }
+
+// 查询全部企微用户列表
+export function listAllQwUserList(query) {
+  return request({
+    url: '/qw/user/listAllQwUserList',
+    method: 'get',
+    params: query
+  })
+}
 export function qwUserList(id) {
   return request({
     url: '/qw/user/qwUserList/' + id,

+ 393 - 0
src/views/qw/companySession/ConversationPanel.vue

@@ -0,0 +1,393 @@
+<template>
+  <div class="conversation-panel">
+    <!-- 配置未加载 -->
+    <div v-if="!configReady" class="loading-tip">
+      <i class="el-icon-loading"></i>
+      <p>正在加载配置...</p>
+    </div>
+
+    <!-- 未登录 -->
+    <div v-else-if="!isLoggedIn" class="login-area">
+      <div class="login-tip">请先扫码登录企微</div>
+      <div id="login-container"></div>
+    </div>
+
+    <!-- 已登录但未选择员工 -->
+    <div v-else-if="!staffUserId" class="empty-tip">
+      <i class="el-icon-info"></i>
+      <p>请在左侧选择一个企微员工</p>
+    </div>
+
+    <!-- 已登录但未选择客户 -->
+    <div v-else-if="!customerId" class="empty-tip">
+      <i class="el-icon-info"></i>
+      <p>请在左侧选择一个客户查看会话</p>
+    </div>
+
+    <!-- 加载中 -->
+    <div v-else-if="!isReady" class="loading-tip">
+      <i class="el-icon-loading"></i>
+      <p>正在加载会话记录...</p>
+    </div>
+
+    <!-- 会话记录为空 -->
+    <div v-else-if="msgList.length === 0" class="empty-tip">
+      <i class="el-icon-info"></i>
+      <p>暂无数据</p>
+    </div>
+
+    <!-- 聊天窗口 -->
+    <div v-else id="chat-container"></div>
+  </div>
+</template>
+
+<script>
+import * as ww from '@wecom/jssdk';
+import defaultStaffAvatar from '@/assets/images/user.png';
+import { qwLogin, qwSignature, qwConversations, getQwSessionConfig } from '@/api/qw/companySession';
+
+const LOGIN_STORAGE_KEY = 'wecom_session_expire';
+
+export default {
+  name: 'ConversationPanel',
+  props: {
+    customerId: { type: String, default: null },
+    customerAvatar: { type: String, default: '' },
+    staffUserId: { type: String, default: null }
+  },
+  data() {
+    return {
+      isLoggedIn: false,
+      isReady: false,
+      msgList: [],
+      chatInstance: null,
+      _sdkInited: false,
+      defaultStaffAvatar: defaultStaffAvatar,
+      // 从后台获取的企微配置
+      config: {
+        corpid: '',
+        agentid: '',
+        agentSecret: '',
+        domain:''
+      },
+      configReady: false,
+    };
+  },
+  watch: {
+    customerId(newId, oldId) {
+      if (newId && newId !== oldId && this.isLoggedIn && this.staffUserId && this.configReady) {
+        this.reloadChat();
+      }
+    },
+    staffUserId(newStaffId, oldStaffId) {
+      if (newStaffId && newStaffId !== oldStaffId && this.customerId && this.isLoggedIn && this.configReady) {
+        this.reloadChat();
+      }
+    }
+  },
+  async mounted() {
+    // 第一步:获取配置
+    try {
+      const configRes = await getQwSessionConfig();
+      const configData = this._extractResponse(configRes);
+      if (!configData.corpid || !configData.agentid) {
+        throw new Error('配置数据不完整');
+      }
+      this.config = {
+        corpid: configData.corpid,
+        agentid: String(configData.agentid),
+        agentSecret: configData.agentSecret || '',
+        domain: configData.domain || ''
+      };
+      this.configReady = true;
+    } catch (e) {
+      console.error('获取企微配置失败:', e);
+      this.$message.error('获取企微配置失败,请刷新重试');
+      return;
+    }
+
+    // 第二步:后续登录流程
+    const urlParams = new URLSearchParams(window.location.search);
+    const code = urlParams.get('code');
+
+    if (code) {
+      const newUrl = window.location.origin + window.location.pathname;
+      window.history.replaceState({}, '', newUrl);
+      await this.handleLogin(code);
+      this.storeLoginState();
+      if (this.customerId && this.staffUserId) {
+        await this.loadAndRender();
+      }
+    } else {
+      if (this.checkLoginState()) {
+        this.isLoggedIn = true;
+        if (this.customerId && this.staffUserId) {
+          await this.loadAndRender();
+        }
+      } else {
+        this.$nextTick(() => this.createLoginPanel());
+      }
+    }
+  },
+  async activated() {
+    if (this.isLoggedIn && this.configReady && this.customerId && this.staffUserId) {
+      await this.loadAndRender();
+    }
+  },
+  methods: {
+    // 刷新并重新渲染(客户/员工切换时)
+    async reloadChat() {
+      this.isReady = false;
+      await this.fetchMsgList(this.customerId, this.staffUserId);
+      this.isReady = true;
+      this.$nextTick(() => this.renderOrUpdateChat());
+    },
+    // 从接口加载数据并渲染(activated / 首次加载)
+    async loadAndRender() {
+      this.isReady = false;
+      try {
+        await this.fetchMsgList(this.customerId, this.staffUserId);
+        this.isReady = true;
+        await this.$nextTick();
+        const container = document.getElementById('chat-container');
+        if (!container || container.offsetHeight === 0) {
+          await new Promise(resolve => setTimeout(resolve, 300));
+          await this.$nextTick();
+          const retryContainer = document.getElementById('chat-container');
+          if (!retryContainer || retryContainer.offsetHeight === 0) {
+            console.error('容器不可见,放弃渲染');
+            return;
+          }
+        }
+        this._sdkInited = false;
+        await this.renderOrUpdateChat();
+      } catch (err) {
+        console.error('初始化失败', err);
+        this.isReady = true;
+      }
+    },
+
+    // ---------- 登录相关 ----------
+    async handleLogin(code) {
+      const res = await qwLogin({ code });
+      const resp = this._extractResponse(res);
+      if (resp.errcode !== 0) {
+        throw new Error(resp.errmsg || '登录失败');
+      }
+      this.isLoggedIn = true;
+    },
+    createLoginPanel() {
+      //const redirectUri = 'http://sestest.ylrzcloud.com/companySale/companySession';
+      const redirectUri = this.config.domain+'/companySale/companySession';
+      console.log("回调地址:",redirectUri);
+      ww.createWWLoginPanel({
+        el: document.getElementById('login-container'),
+        params: {
+          login_type: 'CorpApp',
+          appid: this.config.corpid,
+          agentid: this.config.agentid,
+          redirect_uri: redirectUri,
+          redirect_type: 'callback',
+          state: 'state_' + Date.now()
+        },
+        onLoginSuccess: async ({ code }) => {
+          await this.handleLogin(code);
+          this.storeLoginState();
+          if (this.customerId && this.staffUserId) {
+            await this.loadAndRender();
+          }
+        },
+        onLoginError: (err) => {
+          console.error('登录面板错误:', err);
+          this.clearLoginState();
+        }
+      });
+    },
+
+    // ---------- 本地存储 ----------
+    checkLoginState() {
+      const stored = localStorage.getItem(LOGIN_STORAGE_KEY);
+      if (!stored) return false;
+      return Date.now() < parseInt(stored, 10);
+    },
+    storeLoginState() {
+      localStorage.setItem(LOGIN_STORAGE_KEY, (Date.now() + 30 * 60 * 1000).toString());
+    },
+    clearLoginState() {
+      localStorage.removeItem(LOGIN_STORAGE_KEY);
+    },
+
+    // ---------- 数据获取 ----------
+    async fetchMsgList(customerId, staffUserId) {
+      try {
+        const data = await qwConversations({ customerId, staffUserId });
+        this.msgList = data.msgList || [];
+      } catch (e) {
+        console.error('获取会话记录失败:', e);
+        this.msgList = [];
+      }
+    },
+
+    // ---------- 签名 ----------
+    async getAgentConfigSignature() {
+      const currentUrl = window.location.href.split('#')[0];
+      const res = await qwSignature({ url: currentUrl });
+      const data = this._extractResponse(res);
+      return {
+        timestamp: data.timestamp,
+        nonceStr: data.nonceStr,
+        signature: data.signature
+      };
+    },
+
+    // ---------- SDK 初始化 ----------
+    async initSDKOnce() {
+      if (this._sdkInited) return true;
+      try {
+        await ww.register({
+          corpId: this.config.corpid,
+          agentId: this.config.agentid,
+          jsApiList: ['selectExternalContact', 'shareAppMessage', 'wwapp.invokeJsApiByCallInfo'],
+          getAgentConfigSignature: () => this.getAgentConfigSignature()
+        });
+        await ww.initOpenData();
+        this._sdkInited = true;
+        return true;
+      } catch (e) {
+        console.error('SDK 初始化失败:', e);
+        return false;
+      }
+    },
+
+    // ---------- 渲染聊天组件 ----------
+    async renderOrUpdateChat() {
+      const container = document.getElementById('chat-container');
+      if (!container) return;
+
+      if (this.chatInstance) {
+        try {
+          this.chatInstance.setData({ msgList: this.msgList });
+          return;
+        } catch (e) {
+          console.warn('setData 失败,重建组件', e);
+          this.chatInstance = null;
+        }
+      }
+
+      const ok = await this.initSDKOnce();
+      if (!ok) return;
+
+      const factory = ww.createOpenDataFrameFactory();
+      if (!factory) return;
+
+      const templateData = {
+        msgList: this.msgList,
+        customerAvatar: this.customerAvatar,
+        defaultStaffAvatar: this.defaultStaffAvatar
+      };
+
+      this.chatInstance = factory.createOpenDataFrame({
+        el: container,
+        template: `
+          <view wx:for="{{data.msgList}}" wx:for-item="msg" wx:key="msgid" style="margin-bottom: 15px;">
+            <view style="text-align: center; margin-bottom: 8px;">
+              <text style="background:red; color:white; padding:4px;">{{ msg.displayTime }}</text>
+            </view>
+            <view
+              style="display: flex; flex-direction: row; {{msg.sender.type == 1 ? 'justify-content: flex-end;' : 'justify-content: flex-start;'}}">
+              <image wx:if="{{msg.sender.type == 2}}" src="{{data.customerAvatar}}"
+                     style="width: 36px; height: 36px; border-radius: 4px; margin-right: 10px; background: #fff;"></image>
+              <view
+                style="max-width: 70%; padding: 10px 14px; border-radius: 8px; word-break: break-all; background: {{msg.sender.type == 1 ? '#95ec69' : '#ffffff'}}; box-shadow: 0 1px 3px rgba(0,0,0,0.1); display: flex; align-items: center;">
+                <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
+              </view>
+              <image wx:if="{{msg.sender.type == 1}}" src="{{data.defaultStaffAvatar}}"
+                     style="width: 36px; height: 36px; border-radius: 4px; margin-left: 10px; background: #fff;"></image>
+            </view>
+          </view>
+        `,
+        data: templateData,
+        error: (e) => {
+          console.error('[企微组件] 错误', e);
+          if (e && e.errCode === 42006) {
+            this.clearLoginState();
+            this.isLoggedIn = false;
+            this.isReady = false;
+            this.$nextTick(() => this.createLoginPanel());
+          }
+        },
+        handleModal({ modalUrl }) {
+          window.open(modalUrl, '_blank');
+          return true;
+        }
+      });
+    },
+
+    // 通用提取:兼容多种若依返回结构
+    _extractResponse(res) {
+      if (!res) return {};
+      // 直接就是目标对象(包含 errcode、timestamp 或 corpid)
+      if (res.errcode !== undefined || res.timestamp || res.corpid) return res;
+      // res.data 是目标对象
+      if (res.data && (res.data.errcode !== undefined || res.data.timestamp || res.data.corpid)) return res.data;
+      // res.data.data 是目标对象(少数多层包装)
+      if (res.data && res.data.data && (res.data.data.errcode !== undefined || res.data.data.timestamp || res.data.data.corpid)) return res.data.data;
+      // 兜底
+      return res.data || res;
+    }
+  }
+};
+</script>
+
+<style>
+#chat-container,
+#chat-container > div,
+#chat-container iframe {
+  width: 100% !important;
+  height: 100% !important;
+  min-width: 100% !important;
+  min-height: 100% !important;
+  overflow: hidden;
+}
+
+.conversation-panel {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  background: #f5f6f7;
+  position: relative;
+}
+
+.login-area, .empty-tip, .loading-tip {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  flex: 1;
+}
+
+.login-tip {
+  font-size: 18px;
+  color: #666;
+  margin-bottom: 24px;
+}
+
+.empty-tip, .loading-tip {
+  color: #999;
+  font-size: 16px;
+}
+
+.empty-tip i, .loading-tip i {
+  font-size: 48px;
+  margin-bottom: 16px;
+}
+
+#chat-container {
+  flex: 1;
+  width: 100%;
+  height: 100%;
+  background: #f0f2f5;
+}
+</style>

+ 244 - 0
src/views/qw/companySession/index.vue

@@ -0,0 +1,244 @@
+<template>
+  <div class="session-layout">
+    <!-- 左侧:客户列表 -->
+    <div class="left-panel">
+      <div class="left-panel-content">
+        <el-form :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
+          <el-form-item label="企微主体" prop="corpId">
+            <el-select v-model="queryParams.corpId" placeholder="企微主体" size="small" clearable @change="handleCorpChange">
+              <el-option
+                v-for="corp in corpList"
+                :key="corp.corpId"
+                :label="corp.corpName"
+                :value="corp.corpId"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="企微员工" prop="qwUserId">
+            <el-select
+              v-model="queryParams.qwUserId"
+              placeholder="请选择员工"
+              size="small"
+              clearable
+              filterable
+              :disabled="!queryParams.corpId"
+            >
+              <el-option
+                v-for="staff in staffList"
+                :key="staff.id"
+                :label="staff.qwUserName"
+                :value="staff.id"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+          </el-form-item>
+        </el-form>
+
+        <div class="left-table-wrapper">
+          <el-table
+            v-loading="loading"
+            :data="externalContactList"
+            border
+            highlight-current-row
+            @row-click="handleRowClick"
+            height="100%"
+          >
+            <el-table-column label="头像" align="center" width="80px">
+              <template slot-scope="scope">
+                <el-popover placement="right" trigger="hover">
+                  <img slot="reference" :src="scope.row.avatar" width="40px" style="border-radius:50%" />
+                  <img :src="scope.row.avatar" style="max-width:200px" />
+                </el-popover>
+              </template>
+            </el-table-column>
+            <el-table-column label="客户名称" prop="name" width="100px" />
+            <el-table-column label="销售昵称" prop="qwUserName" width="100px" />
+            <el-table-column label="添加时间" prop="createTime" width="100px" />
+          </el-table>
+        </div>
+
+        <pagination
+          v-show="total > 0"
+          :total="total"
+          :page.sync="queryParams.pageNum"
+          :limit.sync="queryParams.pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
+
+    <!-- 右侧:会话详情面板 -->
+    <div class="right-panel">
+      <ConversationPanel
+        :key="conversationPanelKey"
+        :customerId="selectedCustomerId"
+        :customerAvatar="selectedCustomerAvatar"
+        :staffUserId="selectedStaffQwUserId"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import {listExternalContact} from "@/api/qw/externalContact";
+import {getCompanyList} from "@/api/company/company";
+import {getAllUserlist} from "@/api/company/companyUser";
+import {allCorp} from "@/api/qw/qwCompany";
+import {listAllQwUserList} from "@/api/qw/user";
+import ConversationPanel from "./ConversationPanel.vue";
+
+export default {
+  name: "CompanySession",
+  components: {ConversationPanel},
+  data() {
+    return {
+      loading: false,
+      total: 0,
+      externalContactList: [],
+      companyUserNameList: [],
+      corpList: [],
+      selectedCustomerId: null,
+      selectedCustomerAvatar: null,
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        companyId: null,
+        companyUserName: null,
+        corpId: null,
+        extId: null,
+        qwUserId: null
+      },
+      qwCompanyList: [],
+      staffList: [],
+      // 用于强制重建子组件
+      conversationPanelKey: 0
+    };
+  },
+  computed: {
+    selectedStaffQwUserId() {
+      if (!this.queryParams.qwUserId) return null;
+      const staff = this.staffList.find(s => s.id === this.queryParams.qwUserId);
+      return staff ? staff.qwUserId : null;
+    }
+  },
+  created() {
+    getCompanyList().then(response => {
+      this.qwCompanyList = response.data;
+      if (this.qwCompanyList && this.qwCompanyList.length > 0) {
+        this.queryParams.companyId = this.qwCompanyList[0].companyId;
+        this.getAllUserlist(this.queryParams.companyId);
+      }
+    });
+    allCorp().then(response => {
+      this.corpList = response.data || [];
+      if (this.corpList.length > 0) {
+        this.queryParams.corpId = this.corpList[0].corpId;
+        this.handleCorpChange(this.queryParams.corpId);
+      }
+    });
+  },
+  // 当从其他页面切回时,强制重建右侧会话面板
+  activated() {
+    this.conversationPanelKey++;
+  },
+  methods: {
+    handleCorpChange(corpId) {
+      this.queryParams.qwUserId = null;
+      if (corpId) {
+        this.fetchQwUserList(corpId);
+      } else {
+        this.staffList = [];
+      }
+    },
+    async fetchQwUserList(corpId) {
+      try {
+        const res = await listAllQwUserList({corpId});
+        if (res.code === 200) {
+          this.staffList = res.data || [];
+        } else if (Array.isArray(res)) {
+          this.staffList = res;
+        } else {
+          this.staffList = [];
+          console.warn('获取员工列表失败', res.msg);
+        }
+      } catch (e) {
+        console.error('获取员工列表异常', e);
+        this.staffList = [];
+      }
+    },
+    getAllUserlist(companyId) {
+      if (companyId) {
+        getAllUserlist({companyId}).then(response => {
+          this.companyUserNameList = response.data;
+        });
+      }
+    },
+    getList() {
+      this.loading = true;
+      listExternalContact(this.queryParams).then(response => {
+        this.externalContactList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    handleQuery() {
+      if (!this.queryParams.qwUserId) {
+        this.$message.warning('请先选择企微员工');
+        return;
+      }
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    handleRowClick(row) {
+      this.selectedCustomerId = row.externalUserId;
+      this.selectedCustomerAvatar = row.avatar;
+    },
+  }
+};
+</script>
+
+<style scoped>
+.session-layout {
+  display: flex;
+  height: calc(100vh - 90px);
+  overflow: hidden;
+}
+
+.left-panel {
+  width: 420px;
+  min-width: 380px;
+  flex-shrink: 0;
+  border-right: 1px solid #e8e8e8;
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+}
+
+.left-panel-content {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  padding: 16px;
+  box-sizing: border-box;
+}
+
+.left-table-wrapper {
+  flex: 1;
+  overflow: hidden;
+  margin-bottom: 12px;
+}
+
+.right-panel {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  position: relative;
+}
+</style>

+ 55 - 0
src/views/system/config/config.vue

@@ -3059,6 +3059,35 @@
         </el-form>
       </el-tab-pane>
 
+      <el-tab-pane label="企微专区会话配置" name="qw.sessionConfig">
+        <el-form ref="form41" :model="form41" label-width="160px">
+          <el-form-item label="企业ID" prop="corpid">
+            <el-input v-model="form41.corpid" placeholder="请输入企业ID" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="自建应用ID" prop="agentid">
+            <el-input v-model="form41.agentid" placeholder="请输入自建应用ID" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="自建应用可信域名" prop="domain">
+            <el-input v-model="form41.domain" placeholder="请输入自建应用可信域名" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="自建应用密钥" prop="agentSecret">
+            <el-input v-model="form41.agentSecret" placeholder="请输入自建应用密钥" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="专区程序id" prop="programId">
+            <el-input v-model="form41.programId" placeholder="请输入专区程序id" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="程序查询会话能力id" prop="fetchConversationAbilityId">
+            <el-input v-model="form41.fetchConversationAbilityId" placeholder="请输入查询会话能力id" style="width:400px"></el-input>
+          </el-form-item>
+          <el-form-item label="程序能力action" prop="abilityAction">
+            <el-input v-model="form41.abilityAction" placeholder="请输入专区程序能力action" style="width:400px"></el-input>
+          </el-form-item>
+          <div class="footer">
+            <el-button type="primary" @click="submitQwSessionConfig41">提交</el-button>
+          </div>
+        </el-form>
+      </el-tab-pane>
+
     </el-tabs>
 
 
@@ -3281,6 +3310,15 @@ export default {
         endIndex: 11,
         numberCalls:1,
       },
+      form41: {
+        corpid: '',
+        agentid: '',
+        domain: '',
+        agentSecret: '',
+        fetchConversationAbilityId: '',
+        programId: '',
+        abilityAction: '',
+      },
       storeProductScrmColumns:[],
       storeScrmColumns: [],
       photoArr: [],
@@ -3773,6 +3811,11 @@ export default {
         if (key == 'qw:config') {
           this.form15 = JSON.parse(response.data.configValue)
         }
+        if (key == 'qw.sessionConfig') {
+          if (response.data && response.data.configValue) {
+            this.form41 = JSON.parse(response.data.configValue);
+          }
+        }
         if (key == 'his.brand') {
           this.form16 = JSON.parse(response.data.configValue)
           console.log(this.form16)
@@ -4322,6 +4365,18 @@ export default {
 
       this.saveConfig40();
     },
+    submitQwSessionConfig41() {
+      const param = {
+        configId: this.configId,
+        configKey: this.configKey,
+        configValue: JSON.stringify(this.form41)
+      }
+      updateConfigByKey(param).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess('修改成功')
+        }
+      })
+    },
 
     saveConfig40() {
       const param = {

+ 1 - 0
vue.config.js

@@ -13,6 +13,7 @@ const port = process.env.port || process.env.npm_config_port || 80 // 端口
 //官方vue.config.js 参考文档 https://cli.vuejs.org/zh/config/#css-loaderoptions
 // 这里只列一部分,具体配置参考文档
 module.exports = {
+  transpileDependencies: ['@wecom/jssdk'],
   // 部署生产环境和开发环境下的URL。
   // 默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上
   // 例如 https://www.test.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.test.vip/admin/,则设置 baseUrl 为 /admin/。