Kaynağa Gözat

今正迁移
-企业标签
-ai 会话记录,聊天记录
-好友欢迎语

lk 2 gün önce
ebeveyn
işleme
5bfaedc944

+ 12 - 0
src/api/company/companyUser.js

@@ -120,3 +120,15 @@ export function getCompanyUserListPage(query) {
     params: query
   })
 }
+export function getQwAllUserList(id,companyId) {
+  return request({
+    url: '/company/companyUser/getQwAllUserList/'+id+'/'+companyId,
+    method: 'get'
+  })
+}
+export function getCompanyListByCorpId(id) {
+  return request({
+    url: '/company/companyUser/getCompanyList/'+id,
+    method: 'get'
+  })
+}

+ 53 - 0
src/api/fastGpt/fastGptChatMsg.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询聊天记录列表
+export function listFastGptChatMsg(query) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询聊天记录详细
+export function getFastGptChatMsg(msgId) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg/' + msgId,
+    method: 'get'
+  })
+}
+
+// 新增聊天记录
+export function addFastGptChatMsg(data) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改聊天记录
+export function updateFastGptChatMsg(data) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除聊天记录
+export function delFastGptChatMsg(msgId) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg/' + msgId,
+    method: 'delete'
+  })
+}
+
+// 导出聊天记录
+export function exportFastGptChatMsg(query) {
+  return request({
+    url: '/fastGpt/fastGptChatMsg/export',
+    method: 'get',
+    params: query
+  })
+}

+ 60 - 0
src/api/fastGpt/fastGptChatMsgLogs.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 查询聊天记录日志列表
+export function listFastGptChatMsgLogs(query) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getChatMsgLogsList(query) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs/logsList',
+    method: 'get',
+    params: query
+  })
+}
+// 查询聊天记录日志详细
+export function getFastGptChatMsgLogs(logsId) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs/' + logsId,
+    method: 'get'
+  })
+}
+
+// 新增聊天记录日志
+export function addFastGptChatMsgLogs(data) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改聊天记录日志
+export function updateFastGptChatMsgLogs(data) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除聊天记录日志
+export function delFastGptChatMsgLogs(logsId) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs/' + logsId,
+    method: 'delete'
+  })
+}
+
+// 导出聊天记录日志
+export function exportFastGptChatMsgLogs(query) {
+  return request({
+    url: '/fastGpt/fastGptChatMsgLogs/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/fastGpt/fastGptChatSession.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询对话关系列表
+export function listFastGptChatSession(query) {
+  return request({
+    url: '/fastGpt/fastGptChatSession/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询对话关系详细
+export function getFastGptChatSession(sessionId) {
+  return request({
+    url: '/fastGpt/fastGptChatSession/' + sessionId,
+    method: 'get'
+  })
+}
+
+// 新增对话关系
+export function addFastGptChatSession(data) {
+  return request({
+    url: '/fastGpt/fastGptChatSession',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改对话关系
+export function updateFastGptChatSession(data) {
+  return request({
+    url: '/fastGpt/fastGptChatSession',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除对话关系
+export function delFastGptChatSession(sessionId) {
+  return request({
+    url: '/fastGpt/fastGptChatSession/' + sessionId,
+    method: 'delete'
+  })
+}
+
+// 导出对话关系
+export function exportFastGptChatSession(query) {
+  return request({
+    url: '/fastGpt/fastGptChatSession/export',
+    method: 'get',
+    params: query
+  })
+}

+ 31 - 0
src/api/qw/friendWelcome.js

@@ -16,3 +16,34 @@ export function getFriendWelcome(id) {
     method: 'get'
   })
 }
+// 删除好友欢迎语
+export function delFriendWelcome(id) {
+  return request({
+    url: '/qw/friendWelcome/' + id,
+    method: 'delete'
+  })
+}
+// 新增好友欢迎语
+export function addFriendWelcome(data) {
+  return request({
+    url: '/qw/friendWelcome',
+    method: 'post',
+    data: data
+  })
+}
+// 修改好友欢迎语
+export function updateFriendWelcome(data) {
+  return request({
+    url: '/qw/friendWelcome',
+    method: 'put',
+    data: data
+  })
+}
+// 导出好友欢迎语
+export function exportFriendWelcome(query) {
+  return request({
+    url: '/qw/friendWelcome/export',
+    method: 'get',
+    params: query
+  })
+}

+ 294 - 0
src/views/fastGpt/fastGptChatMsg/index.vue

@@ -0,0 +1,294 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="用户昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入用户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="客服角色" prop="roleId">
+        <el-select v-model="queryParams.roleId" placeholder="请选择客服角色" clearable size="small">
+          <el-option
+                v-for="item in roles"
+                :key="item.roleId"
+                :label="item.roleName"
+                :value="item.roleId"
+              />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="消息类型" prop="msgType">
+          <el-select v-model="queryParams.msgType" placeholder="请选择类型" clearable size="small">
+           <el-option
+             v-for="item in typeOptions"
+               :key="item.dictValue"
+               :label="item.dictLabel"
+               :value="item.dictValue"
+             />
+          </el-select>
+      </el-form-item>
+
+      <el-form-item label="发送时间" prop="createTime">
+        <el-date-picker
+           style="width:220px"
+           clearable size="small"
+           v-model="dateRange"
+           type="daterange"
+           value-format="yyyy-MM-dd"
+           start-placeholder="开始日期"
+           end-placeholder="结束日期">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['chat:chatMsg:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="chatMsgList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="ID" align="center" prop="msgId" />
+      <el-table-column label="用户" align="center" prop="nickName" />
+      <el-table-column label="头像" width="150" align="center">
+        <template slot-scope="scope">
+          <img :src="scope.row.avatar" style="height: 80px">
+        </template>
+      </el-table-column>
+      <el-table-column label="AI客服" align="center" prop="roleName" />
+      <el-table-column show-overflow-tooltip label="消息内容" align="center" prop="content" />
+      <el-table-column  label="消息类型"  align="center" prop="msgType">
+            <template slot-scope="scope">
+                <el-tag prop="type" v-for="(item, index) in typeOptions"    v-if="scope.row.msgType==item.dictValue">{{item.dictLabel}}</el-tag>
+              </template>
+      </el-table-column>
+      <el-table-column  label="发送方式"  align="center" prop="sendType">
+            <template slot-scope="scope">
+                <el-tag prop="type" v-for="(item, index) in sendTypeOptions"    v-if="scope.row.sendType==item.dictValue">{{item.dictLabel}}</el-tag>
+            </template>
+      </el-table-column>
+
+      <el-table-column label="发送时间" align="center" prop="createTime" />
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listFastGptChatMsg, getFastGptChatMsg, delFastGptChatMsg, addFastGptChatMsg, updateFastGptChatMsg, exportFastGptChatMsg } from "@/api/fastGpt/fastGptChatMsg";
+import { getAllRoleList } from "@/api/fastGpt/fastGptRole";
+
+export default {
+  name: "ChatMsg",
+  data() {
+    return {
+      show:{
+              title:"聊天详情",
+              open:false,
+            },
+      roles:[],
+      dateRange:[],
+      typeOptions:[],
+      sendTypeOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 聊天消息记录表格数据
+      chatMsgList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        chatId: null,
+        content: null,
+        msgType: null,
+        sendType: null,
+        companyId: null,
+        roleId: null,
+        companyUserId: null,
+        msgJson: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getDicts("chat_msg_type").then((response) => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("chat_msg_send_type").then((response) => {
+      this.sendTypeOptions = response.data;
+    });
+    getAllRoleList().then(response => {
+        this.roles = response.data;
+    });
+    this.getList();
+  },
+  methods: {
+    handleShow(){
+
+    },
+    /** 查询聊天消息记录列表 */
+    getList() {
+      this.loading = true;
+      listFastGptChatMsg(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.chatMsgList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        msgId: null,
+        chatId: null,
+        content: null,
+        msgType: null,
+        sendType: null,
+        companyId: null,
+        roleId: null,
+        companyUserId: null,
+        createTime: null,
+        msgJson: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.dateRange=null
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.msgId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加聊天消息记录";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const msgId = row.msgId || this.ids
+      getFastGptChatMsg(msgId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改聊天消息记录";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.msgId != null) {
+            updateFastGptChatMsg(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addFastGptChatMsg(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const msgIds = row.msgId || this.ids;
+      this.$confirm('是否确认删除聊天消息记录编号为"' + msgIds + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delFastGptChatMsg(msgIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有聊天消息记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportFastGptChatMsg(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style lang="css">
+  .el-tooltip__popper{
+    max-width:40%
+  }
+</style>

+ 200 - 0
src/views/fastGpt/fastGptChatSession/fastGptChatMsgDetails.vue

@@ -0,0 +1,200 @@
+<template>
+    <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+      <div style="padding: 20px; background-color: #fff;">
+         查看聊天记录
+      </div>
+      <div class="contentx" v-if="item!=null">
+          <div class="desct"> 会话详情</div>
+          <el-descriptions title="" :column="3" border>
+            <el-descriptions-item label="会话标识"><span v-if="item!=null">{{item.chatId}}</span></el-descriptions-item>
+            <el-descriptions-item label="咨询客户"><span v-if="item!=null">{{item.nickName}}</span></el-descriptions-item>
+            <el-descriptions-item label="客服账号" span="3"><span v-if="item!=null">{{item.roleName}}</span></el-descriptions-item>
+            <el-descriptions-item label="接待时间"><span v-if="item!=null">{{item.createTime}}</span></el-descriptions-item>
+            <el-descriptions-item label="结束时间"><span v-if="item!=null">{{item.updateTime}}</span></el-descriptions-item>
+            <el-descriptions-item label="服务状态">
+              <span v-if="item!=null"><dict-tag :options="sessionStatusOptions" :value="item.status"/> </span>
+            </el-descriptions-item>
+          </el-descriptions>
+      </div>
+      <div class="contentx" v-if="item!=null">
+          <div class="desct"> 查看聊天记录</div>
+          <div class="block">
+            <el-timeline>
+              <el-timeline-item v-for="(i, index) in msgList" :key="index" :timestamp="i.createTime" placement="top">
+                <el-card>
+                  <h4 v-if="i.sendType==1">{{ item.nickName }}</h4>
+                  <h4 v-else>{{item.roleName}}</h4>
+                  <p>{{i.content}}</p>
+                 <!-- <p v-if="i.sendType==2" style="color: #c8cacb;">{{i.status==1?'正确':'未标记'}}</p>
+                  <el-link type="primary" :underline="false" v-if="i.sendType==2" class="ivu-pl-8" @click="updateLogs(i.msgId)" style="float: right; margin-right: 20px; margin-bottom: 20px;">修改记录</el-link>
+                  <el-link type="primary" :underline="false" v-if="i.sendType==2" class="ivu-pl-8" @click="updateMsgStatus(i.msgId)" style="float: right; margin-right: 20px; ">√</el-link>
+                  <el-link type="primary" :underline="false" v-if="i.sendType==2" class="ivu-pl-8" @click="updateMsg(i.msgId)" style="float: right; margin-right: 20px; ">X</el-link> -->
+                </el-card>
+              </el-timeline-item>
+            </el-timeline>
+          </div>
+      </div>
+    <!-- 添加或修改聊天消息记录对话框 -->
+    <el-dialog title="修改聊天消息" :visible.sync="open" width="700px" append-to-body>
+      <el-form ref="form" :model="form"  label-width="80px">
+        <el-form-item label="消息内容">
+          <el-input type="textarea" v-model="form.content" :rows="12"/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+    <el-dialog title="修改记录" :visible.sync="logsOpen" width="800px" append-to-body>
+      <el-timeline>
+        <el-timeline-item v-for="(i, index) in chatMsgLogsList" :key="index" :timestamp="i.createTime" placement="top">
+          <el-card>
+            <h4 v-if="i.logsType==1">标记正确</h4>
+            <h4 v-else>修改回复</h4>
+            <p style="float: right;">{{i.nickName}}</p>
+            <p>{{i.content}}</p>
+          </el-card>
+        </el-timeline-item>
+      </el-timeline>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="logsCancel">确 定</el-button>
+      </div>
+    </el-dialog>
+
+
+    </div>
+</template>
+
+<script>
+import { getFastGptChatMsg, updateFastGptChatMsg } from "@/api/fastGpt/fastGptChatMsg";
+
+import { getFastGptChatSession } from "@/api/fastGpt/fastGptChatSession";
+import { getAllRoleList } from "@/api/fastGpt/fastGptRole";
+import { listFastGptChatMsgLogs} from "@/api/fastGpt/fastGptChatMsgLogs";
+  export default {
+    name: "fastGptChatMsgDetails",
+    data() {
+      return {
+        sessionStatusOptions:[],
+        open:false,
+        logsOpen:false,
+        // 方剂类型字典
+        typeOptions:[],
+        sendTypeOptions:[],
+        roles:[],
+        msgList:[],
+        chatMsgLogsList:[],
+        // 状态字典
+        statusOptions: [],
+        item:null,
+        form: {},
+      }
+    },
+    created() {
+      this.getDicts("sys_chat_session_status").then(response => {
+        this.sessionStatusOptions = response.data;
+      });
+      this.getDicts("chat_msg_type").then((response) => {
+        this.typeOptions = response.data;
+      });
+      this.getDicts("chat_msg_send_type").then((response) => {
+        this.sendTypeOptions = response.data;
+      });
+      getAllRoleList().then(response => {
+          this.roles = response.data;
+      });
+    },
+    methods: {
+      getDetails(id) {
+        this.item=null;
+        getFastGptChatSession(id).then(response => {
+            this.item = response.data;
+            this.msgList = response.list;
+        });
+      },
+      updateMsg(row){
+         this.form = null;
+          getFastGptChatMsg(row).then(response => {
+          this.open = true;
+          this.form = response.data;
+        });
+      },
+      updateMsgStatus(row){
+        var statusForm={
+          msgId:row,
+          status:1
+        }
+        this.$confirm('是否确认标记?', "警告", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning"
+          }).then(() => {
+            this.exportLoading = true;
+            return updateFastGptChatMsg(statusForm);
+          }).then(response => {
+            this.msgSuccess("修改成功");
+            this.open = false;
+          var status=  this.msgList.find(item => item.msgId ==row);
+          status.status=1;
+          }).catch(() => {});
+
+
+      },
+      updateLogs(row){
+        this.logsOpen=true;
+        var queryParams= {
+          msgId: row
+        }
+        listFastGptChatMsgLogs(queryParams).then(response => {
+          this.chatMsgLogsList = response.rows;
+        });
+      },
+      logsCancel(){
+        this.logsOpen=false;
+      },
+      submitForm(){
+        var submitForm={
+          msgId:this.form.msgId,
+          content:this.form.content
+        }
+        updateFastGptChatMsg(submitForm).then(response => {
+          this.msgSuccess("修改成功");
+          this.open = false;
+          var status=  this.msgList.find(item => item.msgId ==this.form.msgId);
+          status.content=this.form.content;
+        });
+      },
+      cancel(){
+          this.open = false;
+      }
+    }
+  }
+</script>
+<style>
+  .contentx{
+      height: 100%;
+      background-color: #fff;
+      padding: 0px 20px 20px;
+
+
+      margin: 20px;
+  }
+  .el-descriptions-item__label.is-bordered-label{
+    font-weight: normal;
+  }
+  .el-descriptions-item__content {
+    max-width: 150px;
+    min-width: 100px;
+  }
+  .desct{
+      padding-top: 20px;
+      padding-bottom: 20px;
+      color: #524b4a;
+      font-weight: bold;
+    }
+  .padding-a{
+    padding-right: 10px;
+
+  }
+</style>

+ 480 - 0
src/views/fastGpt/fastGptChatSession/index.vue

@@ -0,0 +1,480 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="会话标识" prop="chatId">
+        <el-input
+          v-model="queryParams.chatId"
+          placeholder="请输入会话标识"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="客户昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入客户昵称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+	  <el-form-item label="角色昵称" prop="roleName">
+	    <el-input
+	      v-model="queryParams.roleName"
+	      placeholder="请输入角色昵称"
+	      clearable
+	      size="small"
+	      @keyup.enter.native="handleQuery"
+	    />
+	  </el-form-item>
+	  <el-form-item label="企微账号" prop="qwUserName">
+	    <el-input
+	      v-model="queryParams.qwUserName"
+	      placeholder="请输入企微账号"
+	      clearable
+	      size="small"
+	      @keyup.enter.native="handleQuery"
+	    />
+	  </el-form-item>
+      <el-form-item label="会话状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
+          <el-option
+            v-for="item in sessionStatusOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+		<el-form-item label="是否人工" prop="isArtificial">
+		  <el-select v-model="queryParams.isArtificial" placeholder="请选择状态" clearable size="small">
+		    <el-option
+		      v-for="item in orOptions"
+		      :key="item.dictValue"
+		      :label="item.dictLabel"
+		      :value="item.dictValue"
+		    />
+		  </el-select>
+		</el-form-item>
+      <el-form-item label="查看状态" prop="isLook">
+        <el-select v-model="queryParams.isLook" placeholder="请选择" clearable size="small">
+          <el-option
+            v-for="item in sessionLookOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="接待时间" prop="createTime">
+        <el-date-picker
+          style="width:220px"
+          clearable size="small"
+          v-model="dateRange"
+          type="daterange"
+          value-format="yyyy-MM-dd"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期">
+          @change="changeTime"
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="primary"-->
+<!--          plain-->
+<!--          icon="el-icon-plus"-->
+<!--          size="mini"-->
+<!--          @click="handleAdd"-->
+<!--          v-hasPermi="['fastGpt:fastGptChatSession:add']"-->
+<!--        >新增</el-button>-->
+<!--      </el-col>-->
+<!--      <el-col :span="1.5">-->
+<!--        <el-button-->
+<!--          type="success"-->
+<!--          plain-->
+<!--          icon="el-icon-edit"-->
+<!--          size="mini"-->
+<!--          :disabled="single"-->
+<!--          @click="handleUpdate"-->
+<!--          v-hasPermi="['fastGpt:fastGptChatSession:edit']"-->
+<!--        >修改</el-button>-->
+<!--      </el-col>-->
+    <el-col :span="1.5">
+       <el-button
+         type="danger"
+         plain
+         icon="el-icon-delete"
+         size="mini"
+         :disabled="multiple"
+         @click="handleDelete"
+         v-hasPermi="['fastGpt:fastGptChatSession:remove']"
+       >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['fastGpt:fastGptChatSession:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" border :data="fastGptChatSessionList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="会话标识" align="center" prop="chatId" />
+      <el-table-column label="咨询客户" align="center" prop="nickName" />
+      <el-table-column label="头像" align="center" prop="avatar" >
+        <template slot-scope="scope">
+          <el-image
+            v-if="scope.row.avatar != null"
+            :src="scope.row.avatar"
+            :preview-src-list="[scope.row.avatar]"
+            :style="{ width: '100px', height: '100px' }"
+          ></el-image>
+        </template>
+      </el-table-column>
+      <el-table-column label="角色昵称" align="center" prop="roleName" />
+	  <el-table-column label="企微账号" align="center" prop="qwUserName" />
+	  
+      <el-table-column label="接待时间" align="center" prop="createTime" >
+        <template slot-scope="scope">
+          <span v-if="!scope.row.updateTime && scope.row.status === 1">
+            {{ scope.row.createTime }} - 未结束
+          </span>
+          <span v-else>
+            {{ scope.row.createTime }} - {{ scope.row.updateTime }}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="会话状态" align="center" prop="status" >
+        <template slot-scope="scope">
+          <el-tag prop="status" v-for="(item, index) in sessionStatusOptions"    v-if="scope.row.status==item.dictValue">{{item.dictLabel}}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="查看状态" align="center" prop="isLook" >
+        <template slot-scope="scope">
+          <el-tag prop="isLook" v-for="(item, index) in sessionLookOptions"    v-if="scope.row.isLook==item.dictValue">{{item.dictLabel}}</el-tag>
+        </template>
+      </el-table-column>
+	  <el-table-column label="是否人工" align="center" prop="isArtificial" >
+	    <template slot-scope="scope">
+	      <el-tag prop="isArtificial" v-for="(item, index) in orOptions"    v-if="scope.row.isArtificial==item.dictValue">{{item.dictLabel}}</el-tag>
+	    </template>
+	  </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleDetails(scope.row)"
+          >查看</el-button>
+		  <el-button v-if="scope.row.isArtificial==0"
+		    size="mini"
+		    type="text"
+		    @click="Artificial(scope.row,1)"
+		  >转人工</el-button>
+		  <el-button v-if="scope.row.isArtificial==1"
+		    size="mini"
+		    type="text"
+		    @click="Artificial(scope.row,0)"
+		  >转Ai</el-button>
+        </template>
+
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+<!--    &lt;!&ndash; 添加或修改对话关系对话框 &ndash;&gt;-->
+<!--    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>-->
+<!--      <el-form ref="form" :model="form" :rules="rules" label-width="80px">-->
+<!--        <el-form-item label="聊天id" prop="chatId">-->
+<!--          <el-input v-model="form.chatId" placeholder="请输入聊天id" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="客户ID uid" prop="userId">-->
+<!--          <el-input v-model="form.userId" placeholder="请输入客户ID uid" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="客服ID 应用id?" prop="kfId">-->
+<!--          <el-input v-model="form.kfId" placeholder="请输入客服ID 应用id?" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="状态 1会话中 2已结束">-->
+<!--          <el-radio-group v-model="form.status">-->
+<!--            <el-radio label="1">请选择字典生成</el-radio>-->
+<!--          </el-radio-group>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="公司ID" prop="companyId">-->
+<!--          <el-input v-model="form.companyId" placeholder="请输入公司ID" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="是否查看" prop="isLook">-->
+<!--          <el-input v-model="form.isLook" placeholder="请输入是否查看" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="用户类型 1微信用户 2小程序用户 3销售用户" prop="userType">-->
+<!--          <el-select v-model="form.userType" placeholder="请选择用户类型 1微信用户 2小程序用户 3销售用户">-->
+<!--            <el-option label="请选择字典生成" value="" />-->
+<!--          </el-select>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="客户昵称" prop="nickName">-->
+<!--          <el-input v-model="form.nickName" placeholder="请输入客户昵称" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="头像" prop="avatar">-->
+<!--          <el-input v-model="form.avatar" placeholder="请输入头像" />-->
+<!--        </el-form-item>-->
+<!--      </el-form>-->
+<!--      <div slot="footer" class="dialog-footer">-->
+<!--        <el-button type="primary" @click="submitForm">确 定</el-button>-->
+<!--        <el-button @click="cancel">取 消</el-button>-->
+<!--      </div>-->
+<!--    </el-dialog>-->
+    <el-drawer
+      :with-header="false"
+      size="75%"  @close="handleDrawerClose"
+      :title="show.title" :visible.sync="show.open">
+      <fastGptChatMsgDetails  ref="Details" />
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { listFastGptChatSession, getFastGptChatSession, delFastGptChatSession, addFastGptChatSession, updateFastGptChatSession, exportFastGptChatSession } from "@/api/fastGpt/fastGptChatSession";
+import fastGptChatMsgDetails from "@/views/fastGpt/fastGptChatSession/fastGptChatMsgDetails.vue";
+
+export default {
+  name: "FastGptChatSession",
+  components: {fastGptChatMsgDetails},
+  data() {
+    return {
+      show:{
+        title:"聊天详情",
+        open:false,
+      },
+      //接待时间
+      dateRange:[],
+      //会话状态
+      sessionStatusOptions:[],
+      //查看状态
+      sessionLookOptions:[],
+	  orOptions:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 对话关系表格数据
+      fastGptChatSessionList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        chatId: null,
+        userId: null,
+        kfId: null,
+        status: null,
+        companyId: null,
+        isLook: null,
+        userType: null,
+        nickName: null,
+        avatar: null,
+		qwUserName: null,
+		roleName: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+    this.getDicts("sys_chat_session_status").then(response => {
+      this.sessionStatusOptions = response.data;
+    });
+    this.getDicts("sys_chat_session_look").then(response => {
+      this.sessionLookOptions = response.data;
+    });
+	this.getDicts("sys_company_or").then(response => {
+	  this.orOptions = response.data;
+	});
+    this.getList();
+  },
+  methods: {
+	  Artificial(row,id){
+		  this.form.sessionId=row.sessionId
+		  this.form.isArtificial=id
+		  updateFastGptChatSession(this.form).then(response => {
+		    this.msgSuccess("修改成功");
+		    this.open = false;
+		    this.getList();
+		  });
+	  },
+    /** 查询对话关系列表 */
+    getList() {
+      this.loading = true;
+      listFastGptChatSession(this.queryParams).then(response => {
+        this.fastGptChatSessionList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    //查看按钮
+    handleDetails(row){
+      this.show.open=true;
+      setTimeout(() => {
+        this.$refs.Details.getDetails(row.sessionId);
+      }, 500);
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        sessionId: null,
+        chatId: null,
+        userId: null,
+        kfId: null,
+        createTime: null,
+        updateTime: null,
+        status: 0,
+        companyId: null,
+        isLook: null,
+        userType: null,
+        nickName: null,
+        avatar: null
+      };
+      this.resetForm("form");
+    },
+    //关闭
+    handleDrawerClose(){
+      this.getList();
+    },
+    /**
+    * 会话时间
+    */
+    changeTime(){
+      if(this.dateRange!=null){
+        this.queryParams.beginTime=this.dateRange[0];
+        this.queryParams.endTime=this.dateRange[1];
+      }else{
+        this.queryParams.beginTime=null;
+        this.queryParams.endTime=null;
+      }
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.sessionId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加对话关系";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const sessionId = row.sessionId || this.ids
+      getFastGptChatSession(sessionId).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改对话关系";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.sessionId != null) {
+            updateFastGptChatSession(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addFastGptChatSession(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const sessionIds = row.sessionId || this.ids;
+      this.$confirm('是否确认删除对话关系编号为"' + sessionIds + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delFastGptChatSession(sessionIds);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有对话关系数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportFastGptChatSession(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

+ 205 - 0
src/views/qw/friendWelcome/ImageUploadWeclome.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="component-upload-image">
+    <el-upload
+      :action="uploadUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :limit="limit"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      name="file"
+      :on-remove="handleRemove"
+      :show-file-list="true"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :class="{hide: this.fileList.length >= this.limit}"
+    >
+      <i class="el-icon-plus"></i>
+    </el-upload>
+
+    <el-dialog
+      :visible.sync="dialogVisible"
+      title="预览"
+      width="800"
+      append-to-body
+    >
+      <img
+        :src="dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+
+export default {
+  name: "ImageUpload",
+  props: {
+    value: [String, Object, Array],
+    // 图片数量限制
+    limit: {
+      type: Number,
+      default: 1,
+    },
+    // 大小限制(MB)
+    fileSize: {
+       type: Number,
+      default: 10,
+    },
+    // 文件类型, 例如['png', 'jpg', 'jpeg']
+    fileType: {
+      type: Array,
+      default: () => ["png", "jpg", "jpeg"],
+    },
+    // 是否显示提示
+    isShowTip: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      dialogImageUrl: "",
+      dialogVisible: false,
+      hideUpload: false,
+      baseUrl:"",
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      headers: {
+        Authorization: "Bearer " + getToken(),
+      },
+      fileList: []
+    };
+  },
+  watch: {
+    value: {
+      handler(val) {
+        if (val) {
+          // 首先将值转为数组
+          const list = Array.isArray(val) ? val : this.value.split(',');
+          // 然后将数组转为对象数组
+          this.fileList = list.map(item => {
+            if (typeof item === "string") {
+              if (item.indexOf(this.baseUrl) === -1) {
+                  item = { name: item, url: item };
+              } else {
+                  item = { name: item, url: item };
+              }
+            }
+            return item;
+          });
+        } else {
+          this.fileList = [];
+          return [];
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  computed: {
+    // 是否显示提示
+    showTip() {
+      return this.isShowTip && (this.fileType || this.fileSize);
+    },
+  },
+  methods: {
+    // 删除图片
+    handleRemove(file, fileList) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name);
+      if(findex > -1) {
+        this.fileList.splice(findex, 1);
+        this.$emit("input", this.listToString(this.fileList));
+      }
+    },
+    // 上传成功回调
+    handleUploadSuccess(res,file) {
+      this.fileList.push({ name: res.url, url: res.url });
+      this.$emit("input", this.listToString(this.fileList));
+      this.loading.close();
+    },
+    // 上传前loading加载
+    handleBeforeUpload(file) {
+      let isImg = false;
+      if (this.fileType.length) {
+        let fileExtension = "";
+        if (file.name.lastIndexOf(".") > -1) {
+          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
+        }
+        isImg = this.fileType.some(type => {
+          if (file.type.indexOf(type) > -1) return true;
+          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+          return false;
+        });
+      } else {
+        isImg = file.type.indexOf("image") > -1;
+      }
+
+      if (!isImg) {
+        this.$message.error(
+          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
+        );
+        return false;
+      }
+      if (this.fileSize) {
+        const isLt = file.size / 1024 / 1024 < this.fileSize;
+        if (!isLt) {
+          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
+          return false;
+        }
+      }
+      this.loading = this.$loading({
+        lock: true,
+        text: "上传中",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+    },
+    // 文件个数超出
+    handleExceed() {
+      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
+    },
+    // 上传失败
+    handleUploadError() {
+      this.$message({
+        type: "error",
+        message: "上传失败",
+      });
+      this.loading.close();
+    },
+    // 预览
+    handlePictureCardPreview(file) {
+      console.log(file)
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    // 对象转成指定字符串分隔
+    listToString(list, separator) {
+      let strs = "";
+      separator = separator || ",";
+      for (let i in list) {
+        strs += list[i].url.replace(this.baseUrl, "") + separator;
+      }
+      return strs != '' ? strs.substr(0, strs.length - 1) : '';
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+// .el-upload--picture-card 控制加号部分
+::v-deep.hide .el-upload--picture-card {
+    display: none;
+}
+// 去掉动画效果
+::v-deep .el-list-enter-active,
+::v-deep .el-list-leave-active {
+    transition: all 0s;
+}
+
+::v-deep .el-list-enter, .el-list-leave-active {
+    opacity: 0;
+    transform: translateY(0);
+}
+</style>
+

+ 1418 - 0
src/views/qw/friendWelcome/indexNew.vue

@@ -0,0 +1,1418 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="120px">
+      <el-form-item label="企微公司" prop="corpId">
+          <el-select v-model="queryParams.corpId" placeholder="企微公司"  size="small" @change="updateCorpId()">
+            <el-option
+              v-for="dict in myQwCompanyList"
+              :key="dict.dictValue"
+              :label="dict.dictLabel"
+              :value="dict.dictValue"
+            />
+          </el-select>
+      </el-form-item>
+      <el-form-item label="公司" prop="companyId">
+          <el-select v-model="queryParams.companyId" placeholder="公司"  size="small" @change="updateCompanyId()">
+            <el-option
+              v-for="dict in myCompanyList"
+              :key="dict.companyId"
+              :label="dict.companyName"
+              :value="dict.companyId"
+            />
+          </el-select>
+      </el-form-item>
+      <el-form-item label="id" prop="id">
+        <el-input
+          v-model="queryParams.id"
+          placeholder="请输入ID"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="使用的成员" >
+        <el-select filterable v-model="queryParams.qwUserIds" filterable  clearable placeholder="公司员工"   size="small">
+          <el-option
+             v-for="dict in companyUserList"
+             :key="dict.id"
+             :label="dict.qwUserName+'('+dict.qwUserId+')'"
+             :value="dict.id">
+          </el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="是否开启分时段" prop="isDayparting">
+        <el-select v-model="queryParams.isDayparting" placeholder="请选择" clearable size="small">
+          <el-option v-for="dict in allowSelectOptions" :key="dict.dictValue" :label="dict.dictLabel"  :value="dict.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createdTime">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.createTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择创建时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="更新时间" prop="updateTieme">
+        <el-date-picker clearable size="small"
+          v-model="queryParams.updateTime"
+          type="date"
+          value-format="yyyy-MM-dd"
+          placeholder="选择更新时间">
+        </el-date-picker>
+      </el-form-item>
+      <el-form-item label="是否发送欢迎语" prop="isSendMsg">
+        <el-select v-model="queryParams.isSendMsg" placeholder="请选择" clearable size="small" >
+          <el-option v-for="dict in allowSelectOptions" :key="dict.dictValue" :label="dict.dictLabel"  :value="dict.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          icon="el-icon-plus"
+          @click="handleAdd"
+          v-hasPermi="['qw:friendWelcome:add']"
+        >新增</el-button>
+      </el-form-item>
+    </el-form>
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+
+    <el-table v-loading="loading" :data="friendWelcomeList" @selection-change="handleSelectionChange" border>
+      <el-table-column label="id" align="center" prop="id" width="180"/>
+      <el-table-column label="标题" align="center" prop="welcomeTitle"/>
+      <el-table-column label="消息内容" align="left" prop="welcomeText"  width="400px" >
+        <template slot-scope="scope">
+          <span style="color:rgb(19, 154, 50);" v-if="scope.row.isDayparting==='1'">[共 {{JSON.parse(scope.row.daypartingItemlist).length+1}} 时段]</span>
+          <span style="color:rgb(19, 154, 50);" v-else >[默认时段]</span>
+          <el-tooltip class="item" effect="dark" :content="scope.row.welcomeText" placement="top">
+            <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
+              <span>{{ scope.row.welcomeText }}</span>
+            </div>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+      <el-table-column label="使用成员" align="center" prop="qwUserIds">
+      <template slot-scope="scope">
+          <div v-for="id in JSON.parse(scope.row.qwUserIds)" :key="id" style="display: inline;" class="text-container">
+            <el-tag type="success" v-for="list in companyUserList" :key="list.qwUserId" style="margin: 3px;" v-if="list.id==id">{{list.qwUserName}}</el-tag>
+          </div>
+      </template>
+      </el-table-column>
+      <el-table-column label="是否开启分时段" align="center" prop="isDayparting">
+        <template slot-scope="scope">
+          <dict-tag :options="allowSelectOptions" :value="scope.row.isDayparting"></dict-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="是否发送欢迎语" align="center" prop="isSendMsg" >
+        <template slot-scope="scope">
+          <dict-tag :options="allowSelectOptions" :value="scope.row.isSendMsg"></dict-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="更新时间" align="center" prop="updateTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['qw:friendWelcome:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['qw:friendWelcome:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改好友欢迎语对话框 -->
+    <el-dialog :title="title" :visible.sync="open" v-loading="loading" width="1700px" append-to-body>
+      <div style="width: 1130px" class="app-container">
+        <div>
+          <span style="font-size: 15px">基础信息</span>
+          <el-divider></el-divider>
+          <el-alert
+            title="注意事项"
+            type="warning"
+            description="这是提示~根据特色还不知道写点啥"
+            :closable="false"
+            show-icon>
+          </el-alert>
+        </div>
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+
+          <el-form-item label="选择使用成员:" prop="qwUserIds" style="margin-top: 2%">
+            <div>
+              <el-button
+                size="medium"
+                icon="el-icon-circle-plus-outline"
+                plain
+                @click="handlelistUser">请选择使用成员</el-button>
+            </div>
+            <div>
+              <el-tag
+                style="margin-left: 5px"
+                size="medium"
+                :key="list.id"
+                v-for="list in userSelectList"
+                closable
+                :disable-transitions="false"
+                @close="handleClosegroupUser(list)">
+                {{list.qwUserName}}({{list.nickName}})
+              </el-tag>
+            </div>
+          </el-form-item>
+          <!-- 新增整体标题 -->
+          <el-form-item label="标题:" prop="welcomeTitle">
+            <el-input v-model="form.welcomeTitle" placeholder="请输入欢迎语标题"/>
+            <el-alert style="margin-top: 8px;height: 30px"
+                      title="此标题仅用于欢迎语的用途区分"
+                      type="info"
+                      :closable="false"
+                      show-icon>
+            </el-alert>
+          </el-form-item>
+          <el-form-item label="是否发送欢迎语">
+            <el-switch
+              v-model="form.isSendMsg"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              active-value="1"
+              inactive-value="2">
+            </el-switch>
+            <span v-if="form.isSendMsg == '1'" style="margin-left: 10px;color: #13ce66">允许</span>
+            <span v-if="form.isSendMsg == '2'" style="margin-left: 10px;color: #ff4949">不允许</span>
+
+          </el-form-item>
+          <div>
+            <span style="font-size: 15px">发送欢迎语</span>
+            <el-divider></el-divider>
+          </div>
+          <el-form-item label="默认欢迎语:" prop="welcomeText">
+              <el-input v-model="form.welcomeText" type="textarea"  :rows="12" maxlength="1300" show-word-limit placeholder="请输入消息内容"/>
+            <!-- 附件和链接列表 -->
+            <el-row>
+              <el-col>
+                <div v-for="(item, index) in form.attachments" :key="index" style="background-color: #f5f7fa;padding: 5px;border: 1px solid  #d9d9d9;">
+                  <div slot="header" style="display: flex;justify-content: space-between;align-items: center; ">
+                    <div style="flex: 1;">
+                    <span v-if="item.msgtype === 'image'">【图片】: {{ item.image.pic_url }}</span>
+                    <span v-if="item.msgtype === 'video'">【视频】: {{ item.video.url }}</span>
+                    <span v-if="item.msgtype === 'link'">【链接】: {{ item.link.title }}-{{item.link.desc}}</span>
+                    <span v-if="item.msgtype === 'miniprogram'">【小程序】: {{ item.miniprogram.title }}</span>
+                    </div>
+                    <div style="  display: flex;gap: 10px;">
+                      <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-edit"
+                        style="float: left;"
+                        @click="editFileItem(item,index,-1)"
+                      >修改</el-button>
+                      <el-button
+                        size="mini"
+                        type="text"
+                        icon="el-icon-delete"
+                        style="float: right;"
+                        @click="removeFileItem(item,index,-1)"
+                      >删除</el-button>
+                  </div>
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+
+            <el-dropdown @command="(command) => handleCommand(command, -1)" trigger="click" placement="top-start">
+              <el-dropdown-menu slot="dropdown" style="width: 120px;">
+                <el-dropdown-item command="image">
+                  <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
+                </el-dropdown-item>
+                <el-dropdown-item command="video">
+                  <i class="el-icon-video-camera" style="margin-right: 10px;"></i>视频
+                </el-dropdown-item>
+                <el-dropdown-item command="link">
+                  <i class="el-icon-link" style="margin-right: 10px;"></i>链接
+                </el-dropdown-item>
+                <el-dropdown-item command="miniprogram">
+                  <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                </el-dropdown-item>
+              </el-dropdown-menu>
+
+              <span class="el-dropdown-link">
+                <el-link icon="el-icon-paperclip" type="text" style="color: rgb(24, 144, 255)">
+                  添加附件(最多9个)
+                </el-link>
+              </span>
+            </el-dropdown>
+
+            </el-form-item>
+
+          <el-form-item label="分时段欢迎语">
+            <el-switch
+              v-model="form.isDayparting"
+              active-color="#13ce66"
+              inactive-color="#ff4949"
+              active-value="1"
+              inactive-value="2">
+            </el-switch>
+            <span v-if="form.isDayparting == '1'" style="margin-left: 10px;color: #13ce66">已启用</span>
+            <span v-if="form.isDayparting == '2'" style="margin-left: 10px;color: #ff4949">已禁用</span>
+          </el-form-item>
+
+          <el-form-item v-if="form.isDayparting == '1'" >
+            <div style="background-color:#ecf8fe;width: 100%;border: 1px solid #dcdfe6">
+              <span style="margin-left:20px;font-size: 15px;display: block" >注意:1、员工上下班不同时间段可设置不同欢迎语;</span>
+              <span style="margin-left:65px;font-size: 15px;display: block" >2、分时段之外的时间将发送已允许的默认欢迎语。</span>
+              <span style="margin-left:65px;font-size: 15px;display: block" >3、不设置分时段欢迎语则使用已允许的默认欢迎语</span>
+              <span style="margin-left:65px;font-size: 15px;display: block" >4、若员工没有设置过欢迎语则不会发送</span>
+            </div>
+          </el-form-item>
+          <el-form-item v-if="form.isDayparting == '2'">
+            <div style="background-color:#ecf8fe;width: 100%;border: 1px solid #dcdfe6">
+              <span style="margin-left:20px;font-size: 15px;display: block" >注意:1、新建欢迎语最多可发送1条文字消息和9个附件;</span>
+              <span style="margin-left:65px;font-size: 15px;display: block" >2、文字消息和附件不能同时为空,当两者均填写时用户会收到多条消息;</span>
+              <span style="margin-left:65px;font-size: 15px;display: block" >3、欢迎语将在客户加为好友后20秒内下发,因网络延迟可能造成发送不成功</span>
+            </div>
+          </el-form-item>
+          <div  v-if="form.isDayparting == '1'"  v-for="(item, index) in form.daypartingItemlist"    :key="index" >
+            <el-form-item :label="`时段 ${index + 1}:`">
+                <el-row>
+                  <el-col style="width: 965px">
+                    <div style="background-color: #fbfbfb;padding: 10px;  border: 1px solid #e6e6e6; margin-bottom: 20px;">
+                          <el-form ref="friendWelcomeItemForm"  :rules="itemRules" :model="item">
+                          <div style="display: flex; gap: 10px;">
+                            <el-form-item label="发起时间:" prop="week" style="flex: 8;">
+                              <el-select v-model="item.week" remote multiple placeholder="请选择" filterable style="width: 580px">
+                                <el-option
+                                  v-for="dict in weekOptions"
+                                  :key="dict.value"
+                                  :label="dict.label"
+                                  :value="dict.value">
+                                </el-option>
+                              </el-select>
+                            </el-form-item>
+                            <el-form-item prop="startTime" style="flex: 1;">
+                              <el-time-select style="width: 120px;"
+                                              placeholder="起始时间"
+                                              v-model="item.startTime"
+                                              :picker-options="{
+                                            start: '00:00',
+                                            step: '00:15',
+                                            end: '24:00'
+                                          }">
+                              </el-time-select>
+                            </el-form-item>
+                            <el-form-item prop="endTime" style="flex: 1;">
+                              <el-time-select style="width: 120px;"
+                                              placeholder="结束时间"
+                                              v-model="item.endTime"
+                                              :picker-options="{
+                                            start: '00:00',
+                                            step: '00:15',
+                                            end: '24:00',
+                                            minTime: item.startTime
+                                          }">
+                              </el-time-select>
+                            </el-form-item>
+                          </div>
+
+                        <el-form-item style="margin-top: 20px" prop="welcomeText">
+                          <el-input v-model="item.welcomeText" type="textarea" :rows="12" maxlength="1300" show-word-limit placeholder="请输入消息内容"/>
+                          <!-- 附件和链接列表 -->
+                          <el-row>
+                            <el-col>
+                              <div v-for="(attachment, attachIndex) in item.attachments" :key="attachIndex" style="background-color: #f5f7fa;padding: 5px;border: 1px solid  #d9d9d9;">
+                                <div slot="header" style="  display: flex;justify-content: space-between;align-items: center; ">
+                                  <div style="flex: 1;">
+                                    <span v-if="attachment.msgtype === 'image'">【图片】: {{ attachment.image.pic_url }}</span>
+                                    <span v-if="attachment.msgtype === 'video'">【视频】: {{ attachment.video.url }}</span>
+                                    <span v-if="attachment.msgtype === 'link'">【链接】: {{ attachment.link.title }}-{{attachment.link.desc}}</span>
+                                    <span v-if="attachment.msgtype === 'miniprogram'">【小程序】: {{ attachment.miniprogram.title }}</span>
+                                  </div>
+                                  <div style="  display: flex;gap: 10px;">
+                                    <el-button
+                                      size="mini"
+                                      type="text"
+                                      icon="el-icon-edit"
+                                      style="float: left;"
+                                      @click="editFileItem(attachment,attachIndex,index)"
+                                    >修改</el-button>
+                                    <el-button
+                                      size="mini"
+                                      type="text"
+                                      icon="el-icon-delete"
+                                      style="float: right;"
+                                      @click="removeFileItem(attachment,attachIndex,index)"
+                                    >删除</el-button>
+                                  </div>
+                                </div>
+                              </div>
+                            </el-col>
+                          </el-row>
+
+                          <el-dropdown @command="(command) => handleCommand(command, index)" trigger="click" placement="top-start">
+                            <el-dropdown-menu slot="dropdown" style="width: 120px;">
+                              <el-dropdown-item command="image">
+                                <i class="el-icon-picture" style="margin-right: 10px;"></i>图片
+                              </el-dropdown-item>
+                              <el-dropdown-item command="video">
+                                <i class="el-icon-video-camera" style="margin-right: 10px;"></i>视频
+                              </el-dropdown-item>
+                              <el-dropdown-item command="link">
+                                <i class="el-icon-link" style="margin-right: 10px;"></i>链接
+                              </el-dropdown-item>
+                              <el-dropdown-item command="miniprogram">
+                                <i class="el-icon-link" style="margin-right: 10px;"></i>小程序
+                              </el-dropdown-item>
+                            </el-dropdown-menu>
+
+                            <span class="el-dropdown-link">
+                            <el-link icon="el-icon-paperclip" type="text" style="color: rgb(24, 144, 255)">
+                              添加附件(最多9个)
+                            </el-link>
+                            </span>
+                          </el-dropdown>
+                        </el-form-item>
+                      </el-form>
+                    </div>
+                  </el-col>
+                  <el-col style="width: 15px;">
+                    <el-link v-if="form.daypartingItemlist.length>1" icon="el-icon-delete-solid" @click="delItemList(index)" type="text" style="color: rgb(24, 144, 255);margin-top: 350px" ></el-link>
+                  </el-col>
+                </el-row>
+            </el-form-item>
+          </div>
+          <div  v-if="form.isDayparting == '1'" style="margin-left: 10%">
+            <el-link type="primary" class="el-icon-plus" :underline="false" @click='addItemList()'>添加其他分时段欢迎语</el-link>
+          </div>
+
+        </el-form>
+      </div>
+
+      <div slot="footer" class="dialog-footer" style="text-align: center">
+        <el-button type="primary"  @click="submitForm">确定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 选择成员账号弹窗   -->
+    <el-dialog :title="listUser.title" :visible.sync="listUser.open" width="1600px" append-to-body>
+      <qwUserList ref="QwUserList" @selectUserList="selectUserList"></qwUserList>
+    </el-dialog>
+
+    <el-dialog :title="welcomeItem.title" :visible.sync="welcomeItem.open" width="1300px" append-to-body>
+      <el-form ref="fileFrom" :model="fileFrom" :rules="fuleRules" label-width="110px">
+        <div v-if="welcomeItem.type==='image'">
+          <el-form-item label="图片:" prop="imagePicUrl">
+            <ImageUpload v-model="fileFrom.imagePicUrl"  type="image" :num="10" :width="150" :height="150"  disabled/>
+          </el-form-item>
+        </div>
+        <div v-if="welcomeItem.type==='video'">
+          <el-form-item label="视频:" prop="videoUrl">
+
+            <el-upload
+              v-model="fileFrom.videoUrl"
+              class="avatar-uploader"
+              :action="uploadUrl"
+              :show-file-list="false"
+              :on-success="(res, file) => handleAvatarSuccessVideo(res, file, fileFrom)"
+              :before-upload="beforeAvatarUploadVideo">
+              <i class="el-icon-plus avatar-uploader-icon"></i>
+            </el-upload>
+            {{fileFrom.videoUrl}}
+            <video v-if="fileFrom.videoUrl"
+                   :src="fileFrom.videoUrl"
+                   controls style="width: 200px;height: 100px">
+            </video>
+          </el-form-item>
+        </div>
+        <div v-if="welcomeItem.type==='link'">
+
+          <el-form-item label="选择课程">
+            <el-select  v-model="fileFrom.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable  @change="courseChange(fileFrom,welcomeItem.index,welcomeItem.itemIndex)">
+              <el-option
+                v-for="dict in courseList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+            <el-select  v-model="fileFrom.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" filterable @change="videoIdChange(fileFrom, welcomeItem.index, welcomeItem.itemIndex,welcomeItem.type)" >
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="图文标题:" prop="linkTitle">
+            <el-input v-model="fileFrom.linkTitle" :rows="2" maxlength="42"  show-word-limit placeholder="请输入图文消息标题,最长为42字" />
+          </el-form-item>
+          <el-form-item label="图文封面:" prop="linkPicUrl">
+            <ImageUpload v-model="fileFrom.linkPicUrl"  type="image" :num="10" :width="150" :height="150" />
+          </el-form-item>
+          <el-form-item label="图文的描述:" prop="linkDesc">
+            <el-input v-model="fileFrom.linkDesc" :rows="4" maxlength="170" show-word-limit type="textarea" placeholder="请输入内容,,最长为170字" />
+          </el-form-item>
+          <div v-if="fileFrom.videoId==null" style="margin-top: 1%">
+            <el-form-item label="图文链接:"  label-width="100px" >
+              <el-input v-model="fileFrom.linkUrl" placeholder="请输入链接地址" style="width: 90%;"/>
+            </el-form-item>
+          </div>
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="图文链接:"  label-width="100px" >
+              <el-tag type="warning" v-model="fileFrom.linkUrl='待生成'">选择的课程小节 即为卡片链接地址</el-tag>
+            </el-form-item>
+          </div>
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="课节过期时间" style="margin-top: 1%" required label-width="110px">
+              <el-row>
+                <el-input-number  v-model="fileFrom.expiresDays"  :min="1" :max="9999" ></el-input-number>
+                (天)
+              </el-row>
+              <el-row>
+                <span class="tip">默认为30天</span>
+              </el-row>
+            </el-form-item>
+          </div>
+<!--          <el-form-item label="图文链接:" prop="linkUrl">-->
+<!--            <el-input v-model="fileFrom.linkUrl" :rows="2"  placeholder="选择了课程小节会自动设置地址" />-->
+<!--          </el-form-item>-->
+        </div>
+        <div v-if="welcomeItem.type==='miniprogram'">
+
+          <el-form-item label="选择课程" prop="miniprogramCourseId">
+            <el-select  v-model="fileFrom.courseId" placeholder="请选择课程" style=" margin-right: 10px;" size="mini" filterable @change="courseChange(fileFrom,welcomeItem.index,welcomeItem.itemIndex)">
+              <el-option
+                v-for="dict in courseList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+            <el-select  v-model="fileFrom.videoId" placeholder="请选择小节" size="mini" style=" margin-right: 10px;" filterable @change="videoIdChange(fileFrom, welcomeItem.index, welcomeItem.itemIndex,welcomeItem.type)" >
+              <el-option
+                v-for="dict in videoList"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="parseInt(dict.dictValue)"
+              />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="小程序标题:" prop="miniprogramTitle">
+            <el-input v-model="fileFrom.miniprogramTitle" :rows="2" maxlength="64" placeholder="请输入小程序消息标题,最长为64字节" @input="checkByteLength(fileFrom)" />
+          </el-form-item>
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="小程序链接:"  label-width="100px" >
+              <el-tag type="warning" v-model="fileFrom.miniprogramPage='待生成'">选择的课程小节 即为卡片小程序链接地址</el-tag>
+            </el-form-item>
+          </div>
+          <el-form-item label="appid" prop="miniprogramAppid" v-show="false" >
+            <el-input v-model="fileFrom.miniprogramAppid='wx73f85f8d62769119' " disabled />
+          </el-form-item>
+
+          <div v-if="fileFrom.videoId!=null">
+            <el-form-item label="课节过期时间" style="margin-top: 1%" required label-width="110px">
+              <el-row>
+                <el-input-number  v-model="fileFrom.expiresDays"  :min="1" :max="9999" ></el-input-number>
+                (天)
+              </el-row>
+              <el-row>
+                <span class="tip">默认为30天</span>
+              </el-row>
+            </el-form-item>
+          </div>
+          <!--          <el-form-item label="图文链接:" prop="linkUrl">-->
+          <!--            <el-input v-model="fileFrom.linkUrl" :rows="2"  placeholder="选择了课程小节会自动设置地址" />-->
+          <!--          </el-form-item>-->
+        </div>
+      </el-form>
+      <div slot="footer" class="dialog-footer" style="text-align: center">
+        <el-button type="primary" @click="confirmUpload('fileFrom')">确定</el-button>
+        <el-button type="primary" @click="cancelUpload">取消</el-button>
+      </div>
+    </el-dialog>
+
+  </div>
+</template>
+
+<script>
+import { listFriendWelcome, getFriendWelcome, delFriendWelcome, addFriendWelcome, updateFriendWelcome, exportFriendWelcome } from "@/api/qw/friendWelcome";
+import qwUserList from '@/views/qw/user/qwUserList.vue'
+import ImageUploadWeclome from '@/views/qw/friendWelcome/ImageUploadWeclome.vue'
+import ImageUpload from '@/views/qw/material/ImageUpload.vue'
+import { getQwAllUserList,getCompanyListByCorpId } from '@/api/company/companyUser'
+import { getMyQwUserList,getMyQwCompanyListAll } from "@/api/qw/user";
+import {courseList, videoList} from "@/api/qw/sop";
+export default {
+  name: "FriendWelcome",
+  components: { ImageUpload, qwUserList,ImageUploadWeclome},
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      //公司列表
+      myQwCompanyList:[],
+      myCompanyList:[],
+      //选择成员列表
+      listUser:{
+        title:"",
+        open:false
+      },
+      //选择成员列表
+      userSelectList:[],
+      fileFrom:{
+        imagePicUrl:null,
+        linkTitle:null,
+        linkPicUrl:null,
+        linkDesc:null,
+        linkUrl:null,
+        videoId:null,
+        courseId:null,
+        expiresDays:30,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
+      },
+      uploadUrl: process.env.VUE_APP_BASE_API + "/common/uploadOSS2",
+      courseList:[],
+      videoList:[],
+      fuleRules:{
+        imagePicUrl:[ { required: true, message: "图片不能为空", trigger: "submit" }],
+        linkTitle:[ { required: true, message: "图文标题不能为空", trigger: "submit" }],
+        linkUrl:[ { required: true, message: "图文链接不能为空", trigger: "submit" }],
+        miniprogramTitle:[ { required: true, message: "图文链接不能为空", trigger: "submit" }],
+        miniprogramCourseId:[
+          {
+            required: true,
+            validator: (rule, value, callback) => {
+              // 检查id是否为空
+              if (!this.fileFrom.courseId) {
+                return callback(new Error('请选择课程'));
+              }
+              // 检查ddId是否为空
+              if (!this.fileFrom.videoId) {
+                return callback(new Error('请选择小节'));
+              }
+              // 校验通过
+              callback();
+            },
+            trigger: 'change' // 下拉选择变化时触发校验
+          }
+        ]
+      },
+
+
+      weekOptions: [{
+        value: 1,
+        label: '星期一'
+      }, {
+        value: 2,
+        label: '星期二'
+      }, {
+        value: 3,
+        label: '星期三'
+      }, {
+        value: 4,
+        label: '星期四'
+      }, {
+        value: 5,
+        label: '星期五'
+      }
+        , {
+          value: 6,
+          label: '星期六'
+        }
+        , {
+          value: 7,
+          label: '星期天'
+        }],
+
+      welcomeItem:{
+        open: false,
+        title: '',
+        type: '',
+        index: -1,
+        itemIndex: -1
+      },
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      //是否
+      allowSelectOptions:[],
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 好友欢迎语表格数据
+      friendWelcomeList: [],
+      //账号列表
+      companyUserList:[],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        id: null,
+        qwUserIds: [],
+        companyId: null,
+        welcomeText: null,
+        isDayparting: null,
+        createTime: null,
+        updateTime: null,
+        isSendMsg: null,
+        corpId: null,
+        companyId: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        qwUserIds: [
+          { required: true, message: "发送企业群发消息的成员账号不能为空", trigger: "submit" }
+        ],
+        welcomeText:[
+          { required: true, message: "消息内容不能为空噢", trigger: "submit" }
+        ],
+
+      },
+      itemRules: {
+        week: [
+          { required: true, message: '请选择发起时间的星期', trigger: 'submit' }
+        ],
+        startTime: [
+          { required: true, message: '请选择开始时间', trigger: 'submit' },
+        ],
+        endTime: [
+          { required: true, message: '请选择结束时间', trigger: 'submit' },
+
+        ],
+        welcomeText: [
+          { required: true, message: '消息内容不能为空噢', trigger: 'submit' }
+        ]
+      }
+
+    };
+  },
+  created() {
+    //账号列表
+
+
+    //是否允许发送
+    this.getDicts("sys_qw_allow_select").then(response => {
+      this.allowSelectOptions = response.data;
+    });
+    courseList().then(response => {
+      this.courseList = response.list;
+    });
+    getMyQwCompanyListAll().then(response => {
+
+            this.myQwCompanyList = response.data;
+            if(this.myQwCompanyList!=null){
+              getCompanyListByCorpId(this.myQwCompanyList[0].dictValue).then(response=>{
+                this.myCompanyList = response.data
+                this.queryParams.companyId = this.myCompanyList[0].companyId
+                this.queryParams.corpId=this.myQwCompanyList[0].dictValue
+                getQwAllUserList(this.myQwCompanyList[0].dictValue,this.myCompanyList[0].companyId).then(response => {
+                this.companyUserList = response.data;
+              });
+              this.getList();
+              })
+
+              
+            }
+    });
+  },
+  watch:{
+    userSelectList(newList) {
+      this.form.qwUserIds =newList.map(item =>item.id);
+    }
+  },
+  methods: {
+    updateCorpId(){
+        this.getList();
+        this.queryParams.companyId = "";
+        getCompanyListByCorpId(this.queryParams.corpId).then(response =>{
+          this.myCompanyList = response.data;
+        })
+      //   getQwAllUserList(this.queryParams.corpId).then(response => {
+      //   this.companyUserList = response.data;
+      // });
+     },
+     updateCompanyId(){
+        this.getList();
+        getQwAllUserList(this.queryParams.corpId,this.queryParams.companyId).then(response =>{
+          this.companyUserList = response.data;
+        })
+     },
+    /** 查询好友欢迎语列表 */
+    getList() {
+      this.loading = true;
+
+      listFriendWelcome(this.queryParams).then(response => {
+
+        // 将处理后的数据赋值给 friendWelcomeList
+        this.friendWelcomeList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // // 检查字节长度
+    checkByteLength(fileFrom) {
+      const text = fileFrom.miniprogramTitle;
+      const byteLength = this.getByteLength(text); // 获取当前字节数
+
+      // 如果字节数超过64,截断输入内容
+      if (byteLength > 64) {
+        this.$set(fileFrom, 'miniprogramTitle', this.truncateTextByByteLength(text,64));
+      }
+    },
+
+    // 计算字符串的字节数
+    getByteLength(text) {
+      return new Blob([text]).size; // 使用 Blob 计算字节数
+    },
+
+    // 根据字节数截断字符串
+    truncateTextByByteLength(text, maxByteLength) {
+      let byteLength = 0;
+      let result = "";
+
+      for (let i = 0; i < text.length; i++) {
+        const char = text[i];
+        const charByteLength = this.getByteLength(char); // 获取当前字符的字节数
+
+        // 如果加上当前字符的字节数后不超过限制,则添加到结果中
+        if (byteLength + charByteLength <= maxByteLength) {
+          result += char;
+          byteLength += charByteLength;
+        } else {
+          break; // 超过限制时停止
+        }
+      }
+
+      return result;
+    },
+
+    //选择群发的企业成员账号
+    handlelistUser(){
+      setTimeout(() => {
+        this.$refs.QwUserList.getDetails(this.queryParams.corpId);
+      }, 1);
+      this.listUser.title="选择企业成员"
+      this.listUser.open=true;
+    },
+    //选择的成员账号列表
+    selectUserList(list){
+
+      this.listUser.open=false;
+
+      // 3. 遍历要添加的 list,逐条判断是否存在重复
+      list.forEach(newItem => {
+        // some() 判断是否存在相同 id
+        const isExist = this.userSelectList.some(oldItem => oldItem.id === newItem.id);
+        if (!isExist) {
+          // 不存在重复的,才添加
+          this.userSelectList.push(newItem);
+        }
+      });
+      // //用于显示
+      // this.userSelectList=list;
+
+    },
+    //删除一些选择了的账号
+    handleClosegroupUser(list){
+
+      // 假设 list 对象具有一个 id 属性
+      const index = this.userSelectList.findIndex(t => t.id === list.id);
+      if (index !== -1) {
+        this.userSelectList.splice(index,1);
+      }
+    },
+    //附件选择
+    handleCommand(command,itemIndex){
+
+      if (this.form.attachments.length >=9 && itemIndex===-1) {
+        return this.$message.error('附件数量已达上限,无法添加更多附件');
+      }
+
+      if (this.isDayparting==='1' && this.form.daypartingItemlist[itemIndex].attachments.length>=9){
+        return this.$message.error('附件数量已达上限,无法添加更多附件');
+      }
+
+      this.welcomeItem = {
+        open: true,
+        title: this.getTitleByCommand(command),
+        type: command,
+        index: itemIndex === -1 ? this.form.attachments.length : this.form.daypartingItemlist[itemIndex].attachments.length,
+        itemIndex
+      };
+    },
+
+    getTitleByCommand(command) {
+      switch (command) {
+        case 'image':
+          return '添加图片';
+        case 'link':
+          return '添加链接';
+        case 'miniprogram':
+          return '添加小程序';
+      }
+    },
+
+
+    courseChange(fileFrom,index,itemIndex){
+
+      // 清空 videoId 选择
+      this.$set(fileFrom, 'videoId', null);
+      // 清空 videoList
+      this.videoList = [];
+
+      if (fileFrom.courseId != null) {
+        // 查找选中的课程对应的 label 和 dictImgUrl
+        const selectedCourse = this.courseList.find(course => parseInt(course.dictValue) === fileFrom.courseId);
+
+        if (selectedCourse) {
+          // 设置 linkTitle 和 linkImageUrl
+          this.$set(fileFrom, 'linkTitle', selectedCourse.dictLabel);
+          this.$set(fileFrom, 'linkPicUrl', selectedCourse.dictImgUrl);
+        }
+
+
+        // 获取新的 videoList
+        videoList(fileFrom.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+      }
+      //
+      // // 更新对应的数据层级
+      // if (itemIndex === -1) {
+      //   // 更新 form.attachments
+      //   this.$set(this.form.attachments, index, {
+      //     ...this.form.attachments[index],
+      //     courseId: fileFrom.courseId,
+      //     videoId: null, // 因为已清空
+      //     title: fileFrom.linkTitle,
+      //     picurl: fileFrom.linkPicUrl
+      //   });
+      // } else {
+      //
+      //   this.$set(this.form.daypartingItemlist[itemIndex].attachments, index, {
+      //     ...this.form.daypartingItemlist[itemIndex].attachments[index],
+      //     courseId: fileFrom.courseId,
+      //     videoId: null, // 因为已清空
+      //     title: fileFrom.linkTitle,
+      //     picurl: fileFrom.linkPicUrl
+      //   });
+      // }
+    },
+    videoIdChange(fileFrom,index, itemIndex,type){
+      //选择了课程小节则 默认绑上
+      if (fileFrom.videoId != null) {
+        // 根据 videoId 获取相关信息(假设有相关的 API 调用)
+        let  selectedVideo = this.videoList.find(course => parseInt(course.dictValue) === fileFrom.videoId);
+        if (selectedVideo && type==='link') {
+          this.$set(fileFrom, 'linkDesc', selectedVideo.dictLabel);
+          this.$set(fileFrom, 'expiresDays', 30);
+        }
+        if (selectedVideo && type==='miniprogram') {
+          this.$set(fileFrom, 'miniprogramTitle', this.truncateTextByByteLength(selectedVideo.dictLabel,64));
+          this.$set(fileFrom, 'expiresDays', 30);
+        }
+
+      }
+
+      // // 更新对应的数据层级
+      // if (itemIndex === -1) {
+      //   // 更新 form.attachments
+      //   this.$set(this.form.attachments, index, {
+      //     ...this.form.attachments[index],
+      //     videoId: fileFrom.videoId,
+      //     desc: fileFrom.linkDesc,
+      //   });
+      // } else {
+      //   // 更新 form.daypartingItemlist[itemIndex].attachments
+      //   this.$set(this.form.daypartingItemlist[itemIndex].attachments, index, {
+      //     ...this.form.daypartingItemlist[itemIndex].attachments[index],
+      //     videoId: fileFrom.videoId,
+      //     desc: fileFrom.linkDesc,
+      //   });
+      //
+      // }
+
+    },
+    //修改附件
+    editFileItem(item, index, itemIndex){
+
+      this.welcomeItem = {
+        open: true,
+        title: this.getEditTitleByMsgType(item.msgtype),
+        type: item.msgtype,
+        index,
+        itemIndex
+      };
+      if (item.msgtype === 'image') {
+        this.fileFrom.imagePicUrl = item.image.pic_url;
+      }else if (item.msgtype === 'video') {
+        this.fileFrom.videoUrl = item.video.url;
+      } else if (item.msgtype === 'link') {
+        this.fileFrom.linkTitle = item.link.title;
+        this.fileFrom.linkPicUrl = item.link.picurl;
+        this.fileFrom.linkDesc = item.link.desc;
+        this.fileFrom.linkUrl = item.link.url;
+        this.fileFrom.videoId = item.link.videoId;
+        this.fileFrom.courseId = item.link.courseId;
+        this.fileFrom.expiresDays = item.link.expiresDays;
+
+        videoList(item.link.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+
+      }else if (item.msgtype === 'miniprogram') {
+        this.fileFrom.miniprogramAppid = 'wx73f85f8d62769119';
+        this.fileFrom.miniprogramTitle = item.miniprogram.title;
+        this.fileFrom.miniprogramPicUrl = "待生成";
+        this.fileFrom.miniprogramPage = "待生成";
+        this.fileFrom.videoId = item.miniprogram.videoId;
+        this.fileFrom.courseId = item.miniprogram.courseId;
+        this.fileFrom.expiresDays = item.miniprogram.expiresDays;
+
+        videoList(item.miniprogram.courseId).then(response => {
+          this.videoList = response.list;
+        });
+
+      }
+
+
+    },
+
+
+
+    getEditTitleByMsgType(msgType) {
+      switch (msgType) {
+        case 'image':
+          return '编辑图片';
+        case 'link':
+          return '编辑链接';
+        case 'miniprogram':
+          return '编辑小程序';
+      }
+    },
+
+    //删除附件
+    removeFileItem(data,index, itemIndex) {
+
+      if (itemIndex === -1) {
+        this.form.attachments.splice(index, 1);
+      } else {
+        this.form.daypartingItemlist[itemIndex].attachments.splice(index, 1);
+      }
+    },
+
+    //添加分时段欢迎语
+    addItemList(){
+      this.form.daypartingItemlist.push({welcomeText:null,attachments:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null})
+    },
+
+
+    //删除某一个分时段欢迎语
+    delItemList(index){
+      this.form.daypartingItemlist.splice(index,1)
+    },
+
+    //提交附件
+    confirmUpload(fileFrom) {
+     this.$refs[fileFrom].validate((valid) => {
+          if (valid) {
+            const { type, index, itemIndex } = this.welcomeItem;
+            let attachment = {};
+            if (type === 'image') {
+              attachment = {
+                msgtype: 'image',
+                image: {
+                  pic_url: this.fileFrom.imagePicUrl
+                }
+              };
+            } else if (type === 'video') {
+              attachment = {
+                msgtype: 'video',
+                video: {
+                  url:this.fileFrom.videoUrl,
+                }
+              };
+            } else if (type === 'link') {
+              attachment = {
+                msgtype: 'link',
+                link: {
+                  title: this.fileFrom.linkTitle,
+                  picurl: this.fileFrom.linkPicUrl,
+                  desc: this.fileFrom.linkDesc,
+                  url: this.fileFrom.linkUrl,
+                  courseId:this.fileFrom.courseId,
+                  videoId:this.fileFrom.videoId,
+                  expiresDays:this.fileFrom.expiresDays,
+                }
+              };
+            }else if (type==='miniprogram'){
+              attachment = {
+                msgtype: 'miniprogram',
+                miniprogram: {
+                  title: this.fileFrom.miniprogramTitle,
+                  pic_media_id: "待查询",
+                  appid: "wx73f85f8d62769119",
+                  page: this.fileFrom.miniprogramPage,
+                  courseId:this.fileFrom.courseId,
+                  videoId:this.fileFrom.videoId,
+                  expiresDays:this.fileFrom.expiresDays,
+                }
+              };
+            }
+
+            if (itemIndex === -1) {
+              // 默认欢迎语附件处理
+              if (index < this.form.attachments.length) {
+                // 存在附件则更新
+                this.form.attachments.splice(index, 1, attachment);
+              } else {
+                // 不存在附件则插入
+                this.form.attachments.push(attachment);
+              }
+
+            } else {
+              // 分时段欢迎语附件处理
+              if (index < this.form.daypartingItemlist[itemIndex].attachments.length) {
+                // 存在附件则更新
+                this.form.daypartingItemlist[itemIndex].attachments.splice(index, 1, attachment);
+              } else {
+                // 不存在附件则插入
+                this.form.daypartingItemlist[itemIndex].attachments.push(attachment);
+              }
+            }
+            this.resetFileFrom();
+          } else {
+            console.log('error submit!!');
+            return false;
+          }
+        });
+
+    },
+
+    //取消附件
+    cancelUpload() {
+      this.resetFileFrom();
+      this.welcomeItem.open = false;
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    //重置附件表单
+    resetFileFrom() {
+      this.fileFrom = {
+        imagePicUrl: null,
+        linkTitle: null,
+        linkPicUrl: null,
+        linkDesc: null,
+        linkUrl: null,
+        videoId:null,
+        courseId:null,
+        miniprogramTitle:null,
+        miniprogramPage:null,
+        miniprogramPicUrl:null,
+        miniprogramAppid:null,
+      };
+
+      this.welcomeItem={
+        open: false,
+        title: '',
+        type: '',
+        index: -1,
+        itemIndex: -1
+      } // 重置编辑索引
+
+      this.videoList=[];
+    },
+
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        qwUserIds: [],
+        companyId: null,
+        //默认欢迎语附件
+        attachments: [],
+        createdTime: null,
+        updateTieme: null,
+        isSendMsg:'1',
+        welcomeText: '',
+        isDayparting: '2',
+        //分时段欢迎语
+        daypartingItemlist:[{welcomeText:null,attachments:[],week:[1,2,3,4,5,6,7],startTime:null,endTime:null}],
+      };
+      this.userSelectList=[]
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.queryParams.createTime = null;
+      this.resetForm("queryForm");
+      this.queryParams.corpId= this.myQwCompanyList[0].dictValue
+      getQwAllUserList(this.queryParams.corpId).then(response => {
+        this.companyUserList = response.data;
+      });
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "新建好友欢迎语";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getFriendWelcome(id).then(response => {
+
+
+        let data = response.row;
+
+        // 转换 attachments
+        if (typeof data.attachments === 'string') {
+          data.attachments = JSON.parse(data.attachments);
+        }
+
+        // 转换 daypartingItemlist
+        if (typeof data.daypartingItemlist === 'string') {
+          data.daypartingItemlist = JSON.parse(data.daypartingItemlist);
+        }
+
+        // 转换 daypartingItemlist 中的每个项目的 attachments 和 week
+        if (Array.isArray(data.daypartingItemlist)) {
+          data.daypartingItemlist = data.daypartingItemlist.map(item => {
+            return {
+              ...item,
+              attachments: typeof item.attachments === 'string' ? JSON.parse(item.attachments) : item.attachments,
+              week: typeof item.week === 'string' ? JSON.parse(item.week) : item.week
+            };
+          });
+        }
+
+        // 赋值给表单
+        this.form = data;
+        this.userSelectList=this.form.userSelectList
+
+        this.open = true;
+        this.title = "修改好友欢迎语";
+      });
+    },
+    /** 提交按钮 验证 from表单*/
+    submitForm() {
+
+      this.loading=true;
+
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          this.form.corpId=this.queryParams.corpId;
+          //有分时段欢迎语 验证分时段表单
+          if (this.form.isDayparting==='1'){
+            const itemForms = this.$refs.friendWelcomeItemForm;
+            if (Array.isArray(itemForms)) {
+              let allValid = true;
+
+              itemForms.forEach((itemFormRef, index) => {
+                itemFormRef.validate((itemValid) => {
+                  if (!itemValid) {
+                    this.loading=false;
+                    allValid = false;
+                  } else {
+                    const { startTime, endTime } = itemFormRef.model;
+                    if (startTime && endTime && startTime > endTime) {
+                      this.$message.error(`时段 ${index + 1} 的开始时间不能大于结束时间`);
+                      allValid = false;
+                      this.loading=false;
+
+                    }
+                  }
+
+                  if (index === itemForms.length - 1 && allValid) {
+                    //全表单验证通过
+                    this.commitForm();
+                  }
+                });
+              });
+            }
+          }else {
+            //只有默认的
+            this.commitForm();
+
+          }
+
+        }
+        this.loading = false;
+      });
+    },
+
+    //提交form表单
+    commitForm(){
+
+      // 深拷贝表单数据
+      const requestData = { ...this.form };
+      // 将 `form.attachments` 转为 JSON 字符串
+      requestData.attachments = JSON.stringify(this.form.attachments);
+      requestData.qwUserIds = JSON.stringify(this.form.qwUserIds);
+
+      // 遍历 `daypartingItemlist`,将其中的 `attachments` 和 `week` 转为 JSON 字符串
+      requestData.daypartingItemlist = JSON.stringify(requestData.daypartingItemlist.map(item => ({
+          ...item,
+        }))
+      );
+
+
+      if (this.form.id != null) {
+        updateFriendWelcome(requestData).then(response => {
+          this.msgSuccess("修改成功");
+          this.loading=false;
+          this.open = false;
+          this.getList();
+        }).catch(error => {
+          this.open=false;
+          this.loading = false;
+        });
+      } else {
+        addFriendWelcome(requestData).then(response => {
+          this.msgSuccess("新增成功");
+          this.loading=false;
+          this.open = false;
+          this.getList();
+        }).catch(error => {
+          this.open=false;
+          this.loading = false;
+        });
+      }
+
+
+      // 重置表单 无论成功还是失败-都重置表单
+      this.reset();
+    },
+
+    //处理时间
+    // formatTime(date) {
+    //   const hours = date.getHours().toString().padStart(2, '0');
+    //   const minutes = date.getMinutes().toString().padStart(2, '0');
+    //   const seconds = date.getSeconds().toString().padStart(2, '0');
+    //   return `${hours}:${minutes}:${seconds}`;
+    // },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除好友欢迎语编号为"' + ids + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delFriendWelcome(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有好友欢迎语数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportFriendWelcome(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    },
+
+    handleAvatarSuccessVideo(res, file, content) {
+      if (res.code == 200) {
+        // 使用 $set 确保响应式更新
+        this.$set(content, 'videoUrl', res.url);
+      } else {
+        this.msgError(res.msg);
+      }
+
+    },
+    beforeAvatarUploadVideo(file) {
+      const isLt30M = file.size / 1024 / 1024 < 10;
+      const isMP4 = file.type === 'video/mp4';
+
+      if (!isMP4) {
+        this.$message.error('仅支持上传 MP4 格式的视频文件!');
+        return false;
+      }
+
+      if (!isLt30M) {
+        this.$message.error('上传大小不能超过 10MB!');
+        return false;
+      }
+
+      return true;
+    },
+  }
+};
+</script>
+
+<style>
+.text-container {
+  max-height: 7.5em; /* 设置最大高度为6行,根据字体大小调整 */
+  overflow-y: auto; /* 内容超出时显示滚动条 */
+  line-height: 1.5em; /* 行高设置,确保每行高度一致 */
+}
+</style>

+ 205 - 0
src/views/qw/material/ImageUpload.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="component-upload-image">
+    <el-upload
+      :action="uploadUrl"
+      list-type="picture-card"
+      :on-success="handleUploadSuccess"
+      :before-upload="handleBeforeUpload"
+      :limit="limit"
+      :on-error="handleUploadError"
+      :on-exceed="handleExceed"
+      name="file"
+      :on-remove="handleRemove"
+      :show-file-list="true"
+      :file-list="fileList"
+      :on-preview="handlePictureCardPreview"
+      :class="{hide: this.fileList.length >= this.limit}"
+    >
+      <i class="el-icon-plus"></i>
+    </el-upload>
+
+    <el-dialog
+      :visible.sync="dialogVisible"
+      title="预览"
+      width="800"
+      append-to-body
+    >
+      <img
+        :src="dialogImageUrl"
+        style="display: block; max-width: 100%; margin: 0 auto"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getToken } from "@/utils/auth";
+
+export default {
+  name: "ImageUpload",
+  props: {
+    value: [String, Object, Array],
+    // 图片数量限制
+    limit: {
+      type: Number,
+      default: 1,
+    },
+    // 大小限制(MB)
+    fileSize: {
+        type: Number,
+        default: 10,
+    },
+    // 文件类型, 例如['png', 'jpg', 'jpeg']
+    fileType: {
+      type: Array,
+      default: () => ["png", "jpg", "jpeg"],
+    },
+    // 是否显示提示
+    isShowTip: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      dialogImageUrl: "",
+      dialogVisible: false,
+      hideUpload: false,
+      baseUrl:"",
+      uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      headers: {
+        Authorization: "Bearer " + getToken(),
+      },
+      fileList: []
+    };
+  },
+  watch: {
+    value: {
+      handler(val) {
+        if (val) {
+          // 首先将值转为数组
+          const list = Array.isArray(val) ? val : this.value.split(',');
+          // 然后将数组转为对象数组
+          this.fileList = list.map(item => {
+            if (typeof item === "string") {
+              if (item.indexOf(this.baseUrl) === -1) {
+                  item = { name: item, url: item };
+              } else {
+                  item = { name: item, url: item };
+              }
+            }
+            return item;
+          });
+        } else {
+          this.fileList = [];
+          return [];
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  computed: {
+    // 是否显示提示
+    showTip() {
+      return this.isShowTip && (this.fileType || this.fileSize);
+    },
+  },
+  methods: {
+    // 删除图片
+    handleRemove(file, fileList) {
+      const findex = this.fileList.map(f => f.name).indexOf(file.name);
+      if(findex > -1) {
+        this.fileList.splice(findex, 1);
+        this.$emit("input", this.listToString(this.fileList));
+      }
+    },
+    // 上传成功回调
+    handleUploadSuccess(res,file) {
+      this.fileList.push({ name: res.url, url: res.url });
+      this.$emit("input", this.listToString(this.fileList));
+      this.loading.close();
+    },
+    // 上传前loading加载
+    handleBeforeUpload(file) {
+      let isImg = false;
+      if (this.fileType.length) {
+        let fileExtension = "";
+        if (file.name.lastIndexOf(".") > -1) {
+          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
+        }
+        isImg = this.fileType.some(type => {
+          if (file.type.indexOf(type) > -1) return true;
+          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+          return false;
+        });
+      } else {
+        isImg = file.type.indexOf("image") > -1;
+      }
+
+      if (!isImg) {
+        this.$message.error(
+          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
+        );
+        return false;
+      }
+      if (this.fileSize) {
+        const isLt = file.size / 1024 / 1024 < this.fileSize;
+        if (!isLt) {
+          this.$message.error(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
+          return false;
+        }
+      }
+      this.loading = this.$loading({
+        lock: true,
+        text: "上传中",
+        background: "rgba(0, 0, 0, 0.7)",
+      });
+    },
+    // 文件个数超出
+    handleExceed() {
+      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
+    },
+    // 上传失败
+    handleUploadError() {
+      this.$message({
+        type: "error",
+        message: "上传失败",
+      });
+      this.loading.close();
+    },
+    // 预览
+    handlePictureCardPreview(file) {
+      console.log(file)
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    // 对象转成指定字符串分隔
+    listToString(list, separator) {
+      let strs = "";
+      separator = separator || ",";
+      for (let i in list) {
+        strs += list[i].url.replace(this.baseUrl, "") + separator;
+      }
+      return strs != '' ? strs.substr(0, strs.length - 1) : '';
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+// .el-upload--picture-card 控制加号部分
+::v-deep.hide .el-upload--picture-card {
+    display: none;
+}
+// 去掉动画效果
+::v-deep .el-list-enter-active,
+::v-deep .el-list-leave-active {
+    transition: all 0s;
+}
+
+::v-deep .el-list-enter, .el-list-leave-active {
+    opacity: 0;
+    transform: translateY(0);
+}
+</style>
+

+ 432 - 0
src/views/qw/tagGroup/index.vue

@@ -0,0 +1,432 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="企微公司" prop="corpId">
+                      <el-select v-model="queryParams.corpId" placeholder="企微公司"  size="small" @change="updateCorpId()">
+                        <el-option
+                          v-for="dict in myQwCompanyList"
+                          :key="dict.dictValue"
+                          :label="dict.dictLabel"
+                          :value="dict.dictValue"
+                        />
+                      </el-select>
+      </el-form-item>
+      <el-form-item label="名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入标签组名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          size="mini"
+          @click="handleSync"
+          v-hasPermi="['qw:tagGroup:sync']"
+        >同步</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['qw:tagGroup:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['qw:tagGroup:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['qw:tagGroup:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['qw:tagGroup:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="tagGroupList" @selection-change="handleSelectionChange" border>
+      <el-table-column type="selection" width="55" align="center" />
+
+      <el-table-column label="来源" align="center" prop="groupFrom">
+        <template slot-scope="scope">
+          <span v-if="scope.row.groupFrom==1">本应用创建</span>
+          <span v-else>其他创建</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="名称" align="center" prop="name" />
+      <el-table-column label="排序" align="center" prop="order" />
+      <el-table-column label="标签" align="center" prop="tag" >
+<!--        <template slot-scope="scope" >-->
+<!--            <el-tag type="success" v-for="i in scope.row.tag" :key="i.id" style="margin: 2px 5px 0 0;">{{i.name}}</el-tag>-->
+<!--        </template>-->
+        <template slot-scope="scope">
+          <div class="tag-container">
+            <div class="tag-list">
+              <el-tag type="success" v-for="i in scope.row.tag" :key="i.id" style="margin: 2px 5px 0 0;">{{i.name}}</el-tag>
+            </div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['qw:tagGroup:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['qw:tagGroup:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改企微客户标签组对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入名称"  />
+        </el-form-item>
+        <el-form-item label="排序" prop="order">
+          <el-input-number v-model="form.order" placeholder="请输入排序" />
+        </el-form-item>
+        <el-row>
+           <el-col :span="3"><div style="margin-top: 9px;font-weight: bold;float: right; margin-right: 10px;">标签</div></el-col>
+          <el-col :span="21">
+            <el-table :data="form.tag"   >
+            <el-table-column label="来源" prop="tagFrom" align="center" width="90" >
+              <template slot-scope="scope">
+                  <span v-if="scope.row.tagFrom==1">本应用创建</span>
+                  <span v-if="!scope.row.tagFrom && scope.row.tagId">其他创建</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="名称" prop="name" align="center" >
+              <template slot-scope="scope">
+                <el-input v-model="scope.row.name"   ></el-input>
+              </template>
+            </el-table-column>
+            <el-table-column label="排序" prop="order" align="center" >
+              <template slot-scope="scope">
+                <el-input-number v-model="scope.row.order"  ></el-input-number>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="150px" align="center">
+              <template slot-scope="scope" >
+                  <el-button @click="addRow" size="mini" type="text" >新增</el-button>
+                  <el-button @click="deleteRow(scope.$index,scope.row)"   size="mini" type="text" v-if="form.tag.length>1"   >删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { syncTag,listTagGroup, getTagGroup, delTagGroup, addTagGroup, updateTagGroup, exportTagGroup } from "@/api/qw/tagGroup";
+import { getMyQwUserList,getMyQwCompanyListAll } from "@/api/qw/user";
+export default {
+  name: "TagGroup",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      myQwUserList: [],
+      myQwCompanyList:[],
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微客户标签组表格数据
+      tagGroupList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        groupId: null,
+        name: null,
+        order: null,
+        corpId: null,
+        companyId: null,
+      },
+      tagJson:[],
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+
+        name: [
+          { required: true, message: "名称不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    getMyQwCompanyListAll().then(response => {
+            this.myQwCompanyList = response.data;
+            if(this.myQwCompanyList!=null){
+              this.queryParams.corpId=this.myQwCompanyList[0].dictValue
+              this.getList();
+            }
+    });
+  },
+  methods: {
+
+    updateCorpId(){
+           this.getList();
+     },
+    /** 查询企微客户标签组列表 */
+    getList() {
+      this.loading = true;
+      listTagGroup(this.queryParams).then(response => {
+        this.tagGroupList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        groupId: null,
+        name: null,
+        order: null,
+        corpId: null,
+        companyId: null,
+        createTime: null,
+        updateTime: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    addRow() {
+      this.form.tag.push({ name: "标签", order:0 ,tagFrom:1},);
+    },
+    deleteRow(index,row) {
+
+      if (row.tagFrom==1){
+        this.form.tag.splice(index, 1);
+      }else {
+        this.$message.error("不能删除非本应用创建的标签")
+      }
+    },
+
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.queryParams.corpId= this.myQwCompanyList[0].dictValue;
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.form.tag= [
+        { name: "标签", order:0,tagFrom:1},
+      ];
+      this.title = "添加企微客户标签组";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getTagGroup(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改企微客户标签组";
+      });
+    },
+    handleSync(){
+      this.loading=true;
+      syncTag(this.queryParams.corpId).then(response => {
+        this.msgSuccess("同步成功");
+
+        this.getList();
+      }).finally(
+        () => {
+          this.loading=false;
+        }
+      );
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          this.form.corpId=this.queryParams.corpId
+          if (this.form.id != null) {
+            updateTagGroup(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addTagGroup(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除企微客户标签组名为"' + row.name + '"的数据项?【注意:仅能删除在本后台创建得标签组及标签】', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delTagGroup(ids);
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有企微客户标签组数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportTagGroup(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+<style scoped>
+
+.tag-container {
+  max-height: 300px;
+  overflow-y: auto;
+  padding: 1px;
+  border: 1px solid #ebeef5;
+  border-radius: 1px;
+  background-color: #fafafa;
+}
+.tag-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+.scroll-hint {
+  text-align: center;
+  color: #909399;
+  font-size: 12px;
+  padding: 1px 0;
+}
+.container {
+  max-width: 800px;
+  margin: 0 auto;
+  padding: 10px;
+}
+.title {
+  text-align: center;
+  color: #303133;
+  margin-bottom: 30px;
+}
+.demo-table {
+  width: 100%;
+  margin-bottom: 30px;
+}
+.instructions {
+  background-color: #f5f7fa;
+  padding: 15px;
+  border-radius: 1px;
+  margin-bottom: 20px;
+}
+</style>

+ 218 - 0
src/views/qw/user/qwUserList.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="app-container">
+    <el-alert v-if="type==2 && (sendType==2||sendType==4||sendType==11)"
+      title="注意事项"
+      type="warning"
+      description="只显示含有【允许使用插件】的成员"
+      :closable="false"
+      center
+      show-icon>
+    </el-alert>
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px" @submit.prevent="handleQuery">
+
+      <el-form-item label="后台员工昵称" prop="nickName">
+        <el-input
+          v-model="queryParams.nickName"
+          placeholder="请输入后台员工昵称"
+          clearable
+          size="small"
+          @keydown.enter.native="handleQueryEnter"
+        />
+      </el-form-item>
+      <el-form-item label="企微员工部门" prop="deptName">
+        <el-input
+          v-model="queryParams.deptName"
+          placeholder="请输入企微员工部门"
+          clearable
+          size="small"
+          @keydown.enter.native="handleQueryEnter"
+        />
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">刷新/重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" :data="userList" ref="userList"  @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="企微员工账号" align="center" prop="qwUserId" />
+        <el-table-column label="企微员工昵称" align="center" prop="qwUserName"/>
+        <el-table-column label="企微员工部门" align="center" prop="departmentName"/>
+        <el-table-column label="后台员工昵称" align="center" prop="nickName"/>
+        <el-table-column label="后台员工用户名" align="center" prop="userName" />
+
+    </el-table>
+    <div style="margin-top: 30px;display: flex;justify-content: center">
+      <el-button type="warning" icon="el-icon-search" @click="confirmSelect">确定选择</el-button>
+    </div>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="handlePaginationChange"
+    />
+  </div>
+</template>
+
+<script>
+import { listUser, getUser, delUser, addUser, updateUser, exportUser } from "@/api/qw/user";
+
+
+export default {
+  name: "qwUserList",
+  data() {
+    return {
+      type:null,
+      sendType:null,
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      selectUsers: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 企微用户表格数据
+      userList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        qwUserId: null,
+        companyId: null,
+        companyUserId: null,
+        deptName: null,
+        corpId: null,
+        nickName: null
+      },
+      // 表单参数
+      form: {},
+
+      // 表单校验
+      rules: {
+      }
+    };
+  },
+  created() {
+
+  },
+  methods: {
+
+    getDetails(corpId,type,sendType,isRemark){
+      this.type=type;
+      this.sendType=sendType;
+      if (type!=null&&sendType!=null){
+        this.queryParams.type=type;
+        this.queryParams.sendType=sendType;
+      }
+      this.queryParams.corpId=corpId;
+      this.queryParams.isRemark=isRemark;
+      this.getList();
+    },
+    /** 查询企微用户列表 */
+    getList() {
+      this.loading = true;
+      listUser(this.queryParams).then(response => {
+        // 如果 companyUserId 为 null,移除列
+
+        this.userList = response.rows;
+        this.total =   response.total;
+        this.loading = false;
+
+      });
+    },
+    handlePaginationChange(row) {
+      this.queryParams.pageNum = row.page;
+      this.queryParams.pageSize = row.limit;
+      this.getList();
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        qwUserId: null,
+        companyId: null,
+        companyUserId: null,
+        corpId: null
+      };
+      this.resetForm("form");
+    },
+    //确定选择
+    confirmSelect(){
+      this.$emit("selectUserList",this.selectUsers);
+      this.resetSelect();
+    },
+    //重置选择
+    resetSelect(){
+      this.$refs.userList.clearSelection();
+
+      this.selectUsers=[];
+      //重置
+      this.queryParams={
+        pageNum: 1,
+        pageSize: 5,
+        qwUserId: null,
+        companyId: null,
+        companyUserId: null,
+        corpId: null,
+        nickName: null
+      },
+        this.getList();
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    handleQueryEnter(event){
+      // 确保事件对象存在
+      if (event && event.preventDefault) {
+        event.preventDefault(); // 阻止默认提交行为
+      }
+      this.handleQuery();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+
+      // 保存当前页的选中项
+      const currentPageSelections = selection.map(item => item.id);
+
+      // 合并选中项
+      this.selectUsers = this.selectUsers.filter(item =>
+        this.userList.some(tag => tag.id === item.id) ? currentPageSelections.includes(item.id) : true
+      ).concat(selection.filter(item => !this.selectUsers.some(selected => selected.id === item.id)));
+
+      // 更新 single 和 multiple
+      this.single = this.selectUsers.length !== 1;
+      this.multiple = !this.selectUsers.length;
+
+    },
+
+  }
+};
+</script>