Browse Source

IM消息批量发送

三七 3 weeks ago
parent
commit
9f38abcd19
4 changed files with 1071 additions and 2 deletions
  1. 1 1
      .env.prod-kyt
  2. 9 0
      src/api/user/fsUser.js
  3. 1025 0
      src/components/course/userCoursePeriod.vue
  4. 36 1
      src/views/member/mylist.vue

+ 1 - 1
.env.prod-kyt

@@ -1,5 +1,5 @@
 # 页面标题
-VUE_APP_TITLE =互联网医院管理系统
+VUE_APP_TITLE =宽益堂SCRM销售端
 # 公司名称
 VUE_APP_COMPANY_NAME =云联融智互联网医院有限公司
 # ICP备案号

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

@@ -94,3 +94,12 @@ export function getQwRepeatData(data){
     data: data
   })
 }
+
+// 批量发送IM
+export function batchSendCourse(data) {
+  return request({
+    url: '/user/fsUser/batchSendCourse',
+    method: 'post',
+    data: data
+  })
+}

+ 1025 - 0
src/components/course/userCoursePeriod.vue

@@ -0,0 +1,1025 @@
+<template>
+    <div class="app-container">
+        <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+            <el-form-item label="营期名称" prop="periodName">
+                <el-input v-model="queryParams.periodName" placeholder="请输入营期名称" clearable size="small"
+                    @keyup.enter.native="handleQuery" />
+            </el-form-item>
+            <el-form-item label="公司" prop="companyIdList">
+                <el-select v-model="queryParams.companyIdList" placeholder="请选择公司" clearable size="small" multiple>
+                    <el-option v-for="item in companyOptions" :key="item.companyId" :label="item.companyName"
+                        :value="item.companyId" />
+                </el-select>
+            </el-form-item>
+            <el-form-item label="开营日期开始" prop="periodStartingTime" label-width="120px">
+                <el-date-picker clearable size="small" style="width: 200px" v-model="queryParams.periodStartingTime"
+                    type="date" value-format="yyyy-MM-dd" placeholder="请选择开营日期开始时间">
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item label="开营日期结束" prop="periodEndTime" label-width="120px">
+                <el-date-picker clearable size="small" style="width: 300px" v-model="queryParams.periodEndTime"
+                    type="date" value-format="yyyy-MM-dd" placeholder="请选择开营日期结束时间">
+                </el-date-picker>
+            </el-form-item>
+            <el-form-item>
+                <el-button type="cyan" icon="el-icon-search" size="mini" @click="handleQuery"
+                    v-hasPermi="['course:period:list']">搜索</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="periodList" @selection-change="handleSelectionChange" border>
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column label="营期名称" align="center" prop="periodName" />
+            <el-table-column label="公司名称" align="center" prop="companyName" />
+            <el-table-column label="营期状态" align="center" prop="periodStatus" width="100"
+                :formatter="periodStatusFormatter" />
+            <el-table-column label="营期线" align="center" prop="periodLine" width="180" />
+            <el-table-column label="开营开始时间" align="center" prop="periodStartingTime" width="180" />
+            <el-table-column label="开营结束时间" align="center" prop="periodEndTime" width="180" />
+            <el-table-column label="创建时间" align="center" prop="createTime" width="180" />
+            <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-setting"
+                        @click="handlePeriodSettings(scope.row)">选择视频</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-drawer title="营期相关设置" :visible.sync="periodSettingsVisible" direction="rtl" size="74%"
+            :destroy-on-close="true" append-to-body custom-class="period-settings-drawer">
+            <div class="drawer-content" style="margin-left: 25px">
+                <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+                    <el-tab-pane label="课程管理" name="course">
+                        <el-table ref="courseTable" v-loading="course.loading" :data="course.list"
+                            @selection-change="handleSelectionCourseChange" border>
+                            <el-table-column type="selection" width="55" align="center" />
+                            <el-table-column label="课程" align="center" prop="courseName" width="180" />
+                            <el-table-column label="小节" align="center" prop="videoName" />
+                            <el-table-column label="开课状态" align="center" prop="status" width="100"
+                                :formatter="courseStatusFormatter" />
+                            <el-table-column label="营期时间" align="center" prop="dayDate" width="100" />
+                            <el-table-column label="开始时间" align="center" prop="startDateTime" width="100">
+                                <!--              <template slot-scope="scope">-->
+                                <!--                <el-tag>{{scope.row.startDateTime}}</el-tag>-->
+                                <!--              </template>-->
+                            </el-table-column>
+                            <el-table-column label="结束时间" align="center" prop="endDateTime" width="100">
+                                <!--              <template slot-scope="scope">-->
+                                <!--                <el-tag type="success">{{parseTime(scope.row.endDateTime, '{h}:{i}:{s}')}}</el-tag>-->
+                                <!--              </template>-->
+                            </el-table-column>
+                            <el-table-column label="领取红包时间" align="center" prop="lastJoinTime" width="160">
+                                <template slot-scope="scope">
+                                    <el-tag type="danger">{{ scope.row.lastJoinTime }}</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="handleSend(scope.row)"
+                                        v-hasPermi="['course:period:courseMove']">发送</el-button>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                        <pagination v-show="course.total > 0" :total="course.total"
+                            :page.sync="course.queryParams.pageNum" :limit.sync="course.queryParams.pageSize"
+                            @pagination="getCourseList" style="height: 40px" />
+                    </el-tab-pane>
+
+                </el-tabs>
+            </div>
+        </el-drawer>
+
+        <el-dialog title="IM发送" :visible.sync="imOpen" width="800px" append-to-body>
+            <el-form ref="imSendForm" :model="imSendForm" :rules="imRules" label-width="220px">
+                <el-form-item label="标题" prop="title">
+                    <el-input v-model="imSendForm.title" placeholder="请输入标题" />
+                </el-form-item>
+                <el-form-item label="发送消息时间" prop="sendTime">
+                    <el-date-picker value-format="yyyy-MM-dd HH:mm:ss" v-model="imSendForm.sendTime" type="datetime" placeholder="选择日期时间">
+                    </el-date-picker>
+                </el-form-item>
+                <el-form-item label="有效时长(分钟)" prop="effectiveDuration">
+                    <el-input-number v-model="imSendForm.effectiveDuration" :min="0"
+                        label="链接有效时长(分钟)"></el-input-number>
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="imSubmit">确 定</el-button>
+                <el-button @click="imCancel">取 消</el-button>
+            </div>
+        </el-dialog>
+
+    </div>
+</template>
+
+
+<script>
+import { addPeriod, delPeriod, exportPeriod, getPeriod, pagePeriod, updatePeriod, getDays, addCourse, delPeriodDay, updateCourseTime, updateCourseDate, updateListCourseData, periodCourseMove, closePeriod } from "@/api/course/userCoursePeriod";
+import { getCompanyList } from "@/api/company/company";
+import { batchSendCourse } from "@/api/user/fsUser";
+import { listCamp, addCamp, editCamp, delCamp, copyCamp } from "@/api/course/userCourseCamp";
+import { courseList, videoList } from '@/api/course/courseRedPacketLog'
+import Da from "element-ui/src/locale/lang/da";
+import { getConfigByKey } from '@/api/system/config'
+export default {
+    props: {
+        userIds: {
+            type: Array,
+            default: () => []
+        },
+        companyId: {
+            type: Number,
+            default: 0
+        },
+        companyUserId: {
+            type: Number,
+            default: 0
+        }
+    },
+    name: "Period",
+    components: {
+    },
+    data() {
+        return {
+            imSendForm: {
+
+            },
+            imOpen: false,
+            // 遮罩层
+            loading: true,
+            updateDateOpen: false,
+            // 左侧遮罩层
+            leftLoading: true,
+            // 选中数组
+            ids: [],
+            // 非单个禁用
+            single: true,
+            // 非多个禁用
+            multiple: true,
+            // 显示搜索条件
+            showSearch: true,
+            // 总条数
+            total: 0,
+            // 左侧总条数
+            leftTotal: 0,
+            // 会员营期表格数据
+            periodList: [],
+            // 左侧列表数据
+            leftList: [],
+            videoList: [],
+            // 弹出层标题
+            title: "",
+            isDisabledDateRange: false, //是否禁用开营日期
+            // 是否显示弹出层
+            open: false,
+            // 查询参数
+            queryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                periodName: null,
+                periodStartingTime: null,
+                periodEndTime: null,
+                companyIdList: []
+            },
+            // 左侧查询参数
+            leftQueryParams: {
+                pageNum: 1,
+                pageSize: 10,
+                hasNextPage: false,
+                scs: 'order_number(desc),training_camp_id(desc)',
+                trainingCampName: null
+            },
+            // 表单参数
+            form: {},
+            // 课程相关数据
+            course: {
+                open: false,
+                row: {},
+                list: [],
+                queryParams: {
+                    pageNum: 1,
+                    pageSize: 10,
+                },
+                loading: true,
+                total: 0,
+                addOpen: false,
+                form: {},
+            },
+            //修改营期时间参数
+            updatePeriodDate: {
+                open: false,
+                loading: true,
+                ids: [],
+                form: {},
+            },
+            joinTimeSwitch: true,
+            updateCourse: {
+                open: false,
+                loading: true,
+                ids: [],
+                form: {
+                    timeRange: null,
+                    joinTime: null
+                },
+            },
+            imRules: {
+                title: [
+                    { required: true, message: "发送标题不能为空", trigger: "blur" }
+                ],
+            },
+            // 表单校验
+            rules: {
+                periodName: [
+                    { required: true, message: '营期名称不能为空', trigger: 'blur' }
+                ],
+                companyId: [
+                    { required: true, message: '公司不能为空', trigger: 'change' }
+                ],
+                courseStyle: [
+                    { required: true, message: '课程风格不能为空', trigger: 'change' }
+                ],
+                // redPacketGrantMethod: [
+                //   { required: true, message: '红包发放方式不能为空', trigger: 'change' }
+                // ],
+                periodType: [
+                    { required: true, message: '营期类型不能为空', trigger: 'change' }
+                ],
+                maxViewNum: [
+                    { required: true, message: '销售可查看天数不能为空', trigger: 'blur' }
+                ],
+                periodStartingTime: [
+                    { required: true, message: '开营日期不能为空', trigger: 'change' }
+                ]
+            },
+            // 公司选项
+            companyOptions: [],
+            // 训练营列表
+            campList: [],
+            // 激活的训练营索引
+            activeCampIndex: null,
+            // 训练营对话框是否显示
+            campDialogVisible: false,
+            courseList: false,
+            // 训练营表单
+            campForm: {
+                trainingCampId: null,
+                trainingCampName: ''
+            },
+            // 训练营表单校验
+            campRules: {
+                trainingCampName: [
+                    { required: true, message: '训练营名称不能为空', trigger: 'blur' },
+                    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+                ]
+            },
+            // 添加课程表单校验
+            courseAddRules: {
+                courseId: [
+                    { required: true, message: '请选择课程', trigger: 'change' }
+                ],
+                videoIds: [
+                    { required: true, message: '请选择小节', trigger: 'change' },
+                    { type: 'array', min: 1, message: '请至少选择一个小节', trigger: 'change' }
+                ]
+            },
+            // 修改看课时间表单校验
+            courseUpdateRules: {
+                timeRange: [
+                    {
+                        required: true,
+                        validator: (rule, value, callback) => {
+                            if (!value || !Array.isArray(value) || value.length !== 2) {
+                                callback(new Error('请选择完整的看课时间范围'));
+                            } else {
+                                // 检查时间格式是否正确(yyyy-MM-dd HH:mm:ss)
+                                const timeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
+                                if (!timeRegex.test(value[0]) || !timeRegex.test(value[1])) {
+                                    callback(new Error('时间格式不正确'));
+                                } else if (value[0] >= value[1]) {
+                                    callback(new Error('结束时间必须大于开始时间'));
+                                } else {
+                                    callback();
+                                }
+                            }
+                        },
+                        trigger: 'change'
+                    }
+                ],
+                joinTime: [
+                    {
+                        required: true,
+                        validator: (rule, value, callback) => {
+                            if (!value) {
+                                callback(new Error('请选择领取红包时间'));
+                            } else {
+                                // 检查时间格式是否正确(yyyy-MM-dd HH:mm:ss)
+                                const timeRegex = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
+                                if (!timeRegex.test(value)) {
+                                    callback(new Error('时间格式不正确'));
+                                } else {
+                                    // 检查领取红包时间是否在看课时间范围内
+                                    const timeRange = this.updateCourse.form.timeRange;
+                                    if (timeRange && Array.isArray(timeRange) && timeRange.length === 2) {
+                                        if (value < timeRange[0] || value > timeRange[1]) {
+                                            callback(new Error('领取红包时间必须在看课时间范围内'));
+                                        } else {
+                                            callback();
+                                        }
+                                    } else {
+                                        callback();
+                                    }
+                                }
+                            }
+                        },
+                        trigger: 'change'
+                    }
+                ]
+            },
+            // 滚动节流标志
+            scrollThrottle: false,
+            // 加载更多状态
+            loadingMore: false,
+            // 设置红包对话框
+            redPacketVisible: false,
+            periodCompanyList: [],
+            currentRedPacketData: {
+                periodId: '',
+                videoId: ''
+            },
+            // 营期相关设置抽屉
+            periodSettingsVisible: false,
+            activeTab: 'course',
+            periodSettingsData: {},
+            companyList: [],
+            courseDialogVisible: false,
+            redPacketList: [],
+            currentCompany: null,
+            // 选中的营期数据
+            selectedPeriods: [],
+            // 批量设置红包按钮是否禁用
+            batchSetRedPacketDisabled: true,
+
+        };
+    },
+    created() {
+        courseList().then(response => {
+            this.courseList = response.list;
+        });
+        // this.getList();
+        this.getLeftList();
+        this.getCompanyList();
+
+    },
+    methods: {
+        imSubmit() {
+            console.log("imSendForm:",this.imSendForm)
+            this.$refs["imSendForm"].validate(valid => {
+                batchSendCourse(this.imSendForm).then(res=>{
+                    this.imCancel();
+                    this.$message.success('发送成功')
+                })
+            });
+        },
+        handleSend(row) {
+            this.imOpen = true;
+            this.imSendForm = {
+                userIds:this.userIds,
+                companyUserId: this.companyUserId,
+                companyId: this.companyId,
+                id:row.id,
+                periodId:row.periodId,
+                courseId:row.courseId,
+                videoId:row.videoId
+            }
+        },
+        imCancel() {
+            this.imOpen = false;
+            this.imRest();
+        },
+        imRest() {
+            this.imSendForm = {
+                userIds: [],
+                companyUserId: null,
+                companyId: null,
+                periodId: null,
+                courseId: null,
+                videoId: null,
+                tagIds: [],
+                sendTime: null,
+                title: null,
+                effectiveDuration: null,
+                id: null,
+            };
+            this.resetForm("imSendForm")
+        },
+        /** 删除按钮操作 */
+        async handleDeleteCourse(row) {
+            const periodDayIds = row.id || this.updateCourse.ids;
+            try {
+                await this.$confirm('是否确认删除该课程?', "提示", {
+                    confirmButtonText: "确定",
+                    cancelButtonText: "取消",
+                    type: "warning"
+                });
+
+                const res = await delPeriodDay(periodDayIds);
+
+                if (res && res.code === 200) {
+                    this.getCourseList();// 刷新列表
+                    this.$message.success('删除成功');
+                } else {
+                    this.$message.error(res.msg);
+                }
+            } catch (error) {
+
+            }
+        },
+        /** 查询会员营期列表 */
+        getList() {
+            this.loading = true;
+            const params = { ...this.queryParams };
+            params.trainingCampId = null,
+            pagePeriod(params).then(response => {
+                this.periodList = response.rows;
+                this.total = response.total;
+                this.loading = false;
+            });
+        },
+        /** 查询左侧列表 */
+        getLeftList() {
+            this.leftLoading = true;
+            // 重置页码和加载更多状态
+            this.leftQueryParams.pageNum = 1;
+            this.loadingMore = false;
+
+            // 训练营数据
+            listCamp(this.leftQueryParams).then(response => {
+                if (response && response.code === 200) {
+                    this.campList = response.data.list || [];
+                    this.leftQueryParams.hasNextPage = response.data.hasNextPage;
+                    this.activeCampIndex = this.campList.length > 0 ? 0 : null;
+                    this.selectCamp(this.activeCampIndex);
+                    // 如果当前显示的列表高度不足以触发滚动,但还有更多数据,自动加载下一页
+                    this.$nextTick(() => {
+                        const scrollEl = this.$refs.campList;
+                        if (scrollEl && this.leftQueryParams.hasNextPage && scrollEl.scrollHeight <= scrollEl.clientHeight) {
+                            this.loadMoreCamps();
+                        }
+                    });
+                } else {
+                    this.$message.error(response.msg || '获取训练营列表失败');
+                    this.campList = [];
+                    this.leftQueryParams.hasNextPage = false;
+                }
+                this.leftLoading = false;
+            }).catch(error => {
+                console.error('获取训练营列表失败:', error);
+                this.$message.error('获取训练营列表失败');
+                this.campList = [];
+                this.leftQueryParams.hasNextPage = false;
+                this.leftLoading = false;
+            });
+        },
+        /** 搜索按钮操作 */
+        handleQuery() {
+            this.queryParams.pageNum = 1;
+            this.getList();
+        },
+        /** 左侧搜索按钮操作 */
+        handleLeftQuery() {
+            // 重置页码和列表
+            this.leftQueryParams.pageNum = 1;
+            this.campList = [];
+            this.getLeftList();
+        },
+        /** 重置按钮操作 */
+        resetQuery() {
+            this.resetForm("queryForm");
+            this.queryParams.companyIdList = [];
+            this.handleQuery();
+        },
+        /** 多选框选中数据 */
+        handleSelectionChange(selection) {
+            this.ids = selection.map(item => item.periodId)
+            this.single = selection.length !== 1
+            this.multiple = !selection.length
+            // 更新批量设置红包相关数据
+            this.selectedPeriods = selection;
+            this.batchSetRedPacketDisabled = selection.length === 0;
+        },
+        handleSelectionCourseChange(selection) {
+            this.updateCourse.ids = selection.map(item => item.id)
+        },
+
+
+
+
+
+
+
+        /** 获取公司下拉列表*/
+        getCompanyList() {
+            this.loading = true;
+            getCompanyList().then(response => {
+                this.companyOptions = response.data;
+                this.loading = false;
+            });
+        },
+        // 取消按钮
+        cancel() {
+            this.open = false;
+            this.reset();
+        },
+        // 表单重置
+        reset() {
+            this.form = {
+                periodId: null,
+                periodName: null,
+                companyId: null,
+                courseId: null,
+                videoId: null,
+                trainingCampId: null,
+                createTime: null,
+                updateTime: null,
+                courseStyle: null,
+                liveRoomStyle: null,
+                redPacketGrantMethod: 1,
+                periodType: 1,
+                periodStartingTime: null,
+                dateRange: [],
+                date: null,
+                days: [],
+                maxViewNum: 0,
+                periodEndTime: null,
+                timeRange: [], // 看课时间范围
+                viewStartTime: null, // 看课开始时间
+                viewEndTime: null, // 看课结束时间
+                lastJoinTime: null // 领取红包时间
+            };
+            this.resetForm("form");
+        },
+
+
+
+
+        /** 重置训练营表单 */
+        resetCampForm() {
+            this.campForm = {
+                trainingCampId: null,
+                trainingCampName: ''
+            };
+            // 如果表单已经创建,则重置校验结果
+            if (this.$refs.campForm) {
+                this.$refs.campForm.resetFields();
+            }
+        },
+
+
+
+        /** 选中训练营 */
+        selectCamp(index) {
+            if (index == null || index == undefined) return;
+            this.activeCampIndex = index;
+            // 加载对应的训练营营期数据
+            const selectedCamp = this.campList[index];
+            this.queryParams.trainingCampId = selectedCamp.trainingCampId;
+            this.getList();
+        },
+        /** 处理滚动事件,实现滚动到底部加载更多 */
+        handleScroll() {
+            // 如果正在节流中或者正在加载中,则不处理
+            if (this.scrollThrottle || this.loadingMore) return;
+
+            // 设置节流,200ms内不再处理滚动事件
+            this.scrollThrottle = true;
+            setTimeout(() => {
+                this.scrollThrottle = false;
+            }, 200);
+
+            const scrollEl = this.$refs.campList;
+            if (!scrollEl) return;
+
+            // 判断是否滚动到底部:滚动高度 + 可视高度 >= 总高度 - 30(添加30px的容差,提前触发加载)
+            const isBottom = scrollEl.scrollTop + scrollEl.clientHeight >= scrollEl.scrollHeight - 30;
+
+            // 如果滚动到底部,且有下一页数据,且当前不在加载中,则加载更多
+            if (isBottom && this.leftQueryParams.hasNextPage && !this.leftLoading && !this.loadingMore) {
+                this.loadMoreCamps();
+            }
+        },
+        /** 加载更多训练营数据 */
+        loadMoreCamps() {
+            // 已在加载中,防止重复加载
+            if (this.leftLoading || this.loadingMore) return;
+
+            // 设置加载状态
+            this.loadingMore = true;
+
+            // 页码加1
+            this.leftQueryParams.pageNum += 1;
+
+            // 加载下一页数据
+            listCamp(this.leftQueryParams).then(response => {
+                if (response && response.code === 200) {
+                    // 将新数据追加到列表中
+                    const newList = response.data.list || [];
+                    if (newList.length > 0) {
+                        this.campList = [...this.campList, ...newList];
+                    }
+
+                    // 更新是否有下一页的标志
+                    this.leftQueryParams.hasNextPage = response.data.hasNextPage;
+
+                    // 如果当前显示的列表高度不足以触发滚动,但还有更多数据,自动加载下一页
+                    this.$nextTick(() => {
+                        const scrollEl = this.$refs.campList;
+                        if (scrollEl && this.leftQueryParams.hasNextPage && scrollEl.scrollHeight <= scrollEl.clientHeight) {
+                            // 延迟一点再加载下一页,避免过快加载
+                            setTimeout(() => {
+                                this.loadMoreCamps();
+                            }, 300);
+                        }
+                    });
+                } else {
+                    this.$message.error(response.msg || '加载更多训练营失败');
+                }
+                this.loadingMore = false;
+            }).catch(error => {
+                console.error('加载更多训练营失败:', error);
+                this.$message.error('加载更多训练营失败');
+                this.loadingMore = false;
+            });
+        },
+        timeChange(type) {
+            if (type == 1) {
+                this.form.periodStartingTime = this.form.dateRange[0];
+                this.form.periodEndTime = this.form.dateRange[1];
+                if (!Array.isArray(this.form.days)) {
+                    this.form.days = [];
+                }
+                this.form.days = [];
+                let days = this.getDiff(this.form.periodStartingTime, this.form.periodEndTime);
+                for (let i = 0; i < days; i++) {
+                    this.form.days.push({ lesson: i + 1 });
+                }
+            }
+            if (type == 2) {
+                this.form.periodStartingTime = this.form.date;
+                this.form.periodEndTime = this.form.date;
+                this.form.days = [];
+            }
+        },
+        getDiff(start, end) {
+            if (start == null || start == undefined || start == '') return 0;
+            if (end == null || end == undefined || end == '') return 0;
+            if (start == end) 1;
+            const startDate = this.getUTCDate(start);
+            const endDate = this.getUTCDate(end);
+
+            const timeDiff = endDate - startDate;
+            return (Math.floor(timeDiff / (1000 * 3600 * 24))) + 1; // 直接取整
+        },
+        getUTCDate(dateStr) {
+            const [year, month, day] = dateStr.split('-').map(Number);
+            return new Date(Date.UTC(year, month - 1, day)); // 月份从0开始
+        },
+
+        getCourseList() {
+            this.course.loading = true;
+            getDays(this.course.queryParams).then(e => {
+                this.course.list = e.rows;
+                this.course.total = e.total;
+                this.course.loading = false;
+            });
+        },
+
+
+
+
+
+
+
+
+
+        handlePeriodSettings(row) {
+            console.log("公司id:",this.companyId,this.companyUserId)
+            this.periodSettingsData = row;
+            this.periodSettingsVisible = true;
+            // 初始化课程列表
+            this.course.queryParams.periodId = row.periodId;
+            // 根据当前激活的tab加载对应数据
+            this.handleTabClick({ name: this.activeTab });
+        },
+
+
+        /** 处理tab切换 */
+        handleTabClick(tab) {
+            if (tab.name === 'course') {
+                this.getCourseList();
+            } else if (tab.name === 'company') {
+                this.redPacketVisible = true;
+            }
+        },
+
+
+        /** 营期状态格式化 */
+        periodStatusFormatter(row) {
+            const statusMap = {
+                1: '未开始',
+                2: '进行中',
+                3: '已结束'
+            };
+            return statusMap[row.periodStatus] || '未知状态';
+        },
+        /** 开课状态格式化 */
+        courseStatusFormatter(row) {
+            const statusMap = {
+                0: '未开始',
+                1: '进行中',
+                2: '已结束'
+            };
+            return statusMap[row.status] || '未知状态';
+        },
+
+
+
+
+
+    },
+};
+</script>
+
+<style scoped>
+.left-aside {
+    background-color: #fff;
+    border-right: 1px solid #EBEEF5;
+    padding: 0;
+    display: flex;
+    flex-direction: column;
+    height: 800px;
+}
+
+.left-header {
+    padding: 10px;
+    border-bottom: 1px solid #EBEEF5;
+}
+
+.left-header-top {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 10px;
+}
+
+.search-btn {
+    width: 50%;
+    height: 36px;
+    background-color: #409EFF;
+    color: white;
+    border: none;
+}
+
+.search-input-wrapper {
+    margin-bottom: 10px;
+}
+
+.sort-wrapper {
+    display: flex;
+    align-items: center;
+    margin-bottom: 10px;
+}
+
+.sort-label {
+    width: 70px;
+    font-size: 14px;
+    font-weight: 600;
+    color: #909399;
+}
+
+.sort-select {
+    margin-left: 10px;
+    width: 280px;
+}
+
+.color-wrapper {
+    display: flex;
+    align-items: center;
+    margin-bottom: 10px;
+}
+
+.color-label {
+    width: 70px;
+    color: #606266;
+}
+
+.color-hint {
+    margin-left: 10px;
+    color: #606266;
+    font-size: 12px;
+}
+
+.color-help {
+    margin-left: 5px;
+    color: #909399;
+    cursor: pointer;
+}
+
+.hint-text {
+    color: #606266;
+    font-size: 12px;
+    margin-top: 5px;
+}
+
+.camp-list {
+    flex: 1;
+    overflow-y: auto;
+    padding: 3px;
+}
+
+.camp-item {
+    margin-bottom: 5px;
+    padding: 15px;
+    background-color: #ffffff;
+    position: relative;
+    cursor: pointer;
+    display: flex;
+    justify-content: space-between;
+    border: 1px solid #eaedf2;
+}
+
+.camp-item:last-child {
+    margin-bottom: 0;
+}
+
+.camp-item:hover {
+    background-color: #f5f9ff;
+}
+
+.camp-item.active {
+    background-color: #eaf4ff;
+    border-left: 1px solid #75b8fc;
+}
+
+.camp-content {
+    flex: 1;
+    padding-right: 10px;
+}
+
+.camp-title {
+    font-weight: bold;
+    font-size: 16px;
+    margin-bottom: 8px;
+    color: #333;
+    display: flex;
+    align-items: center;
+}
+
+.camp-icon {
+    font-size: 16px;
+    margin-right: 6px;
+    color: #409EFF;
+}
+
+.camp-info {
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 8px;
+    font-size: 12px;
+    color: #c4c1c1;
+    line-height: 1.5;
+}
+
+.camp-stats {
+    display: flex;
+    justify-content: space-between;
+    font-size: 12px;
+    color: #666;
+    background-color: #f5f9ff;
+    padding: 6px 10px;
+    border-radius: 4px;
+    line-height: 1.5;
+}
+
+.stat-item {
+    display: flex;
+    align-items: center;
+}
+
+.stat-item i {
+    margin-right: 4px;
+    font-size: 14px;
+    color: #409EFF;
+}
+
+.camp-actions {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-end;
+    gap: 8px;
+    border-left: 1px dashed #eaedf2;
+    padding-left: 12px;
+    min-width: 50px;
+}
+
+.action-btn {
+    padding: 2px 5px;
+    font-size: 12px;
+    border-radius: 4px;
+    transition: all 0.2s;
+}
+
+.action-btn:hover {
+    background-color: rgba(255, 255, 255, 0.8);
+}
+
+.delete-btn {
+    color: #f56c6c;
+}
+
+.delete-btn:hover {
+    background-color: rgba(245, 108, 108, 0.1);
+}
+
+.copy-btn {
+    color: #409EFF;
+}
+
+.copy-btn:hover {
+    background-color: rgba(64, 158, 255, 0.1);
+}
+
+.warning-icon {
+    color: #E6A23C;
+    font-size: 16px;
+    margin-left: 5px;
+}
+
+.el-main {
+    padding: 10px;
+}
+
+/* 添加训练营表单样式 */
+.drawer-footer {
+    /* position: absolute; */
+    bottom: 0;
+    left: 0;
+    right: 0;
+    padding: 20px;
+    background: #fff;
+    text-align: right;
+    border-top: 1px solid #e8e8e8;
+}
+
+.el-input-number {
+    width: 100%;
+}
+
+/* 加载更多样式 */
+.loading-more {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 12px 0;
+    color: #909399;
+    font-size: 14px;
+}
+
+.loading-more i {
+    margin-right: 5px;
+    font-size: 16px;
+}
+
+/* 无更多数据提示 */
+.no-more-data {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 12px 0;
+    color: #c0c4cc;
+    font-size: 13px;
+}
+
+.no-more-data span {
+    position: relative;
+    display: flex;
+    align-items: center;
+}
+</style>

+ 36 - 1
src/views/member/mylist.vue

@@ -137,6 +137,17 @@
       <!--          v-hasPermi="['user:fsUser:export']"-->
       <!--        >导出</el-button>-->
       <!--      </el-col>-->
+            <el-col :span="1.5">
+              <el-button
+              type="success"
+              plain
+              icon="el-icon-edit"
+              size="mini"
+              :disabled="multiple"
+              @click="batchSend"
+              v-hasPermi="['course:userVideo:audit']"
+              >批量IM发送</el-button>
+            </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -296,6 +307,16 @@
       <userDetails  ref="userDetails" />
     </el-drawer>
 
+    <el-dialog
+      title="营期"
+      :visible.sync="dialogVisible"
+      width="80%"
+      :before-close="handleClose">
+      <userCoursePeriod :userIds="ids" :companyId="companyId" :companyUserId="companyUserId"/>
+    </el-dialog>
+
+   
+
   </div>
 </template>
 
@@ -304,11 +325,13 @@ import { listUser, getUser, addUser, updateUser, delUser, exportUser, auditUser
 import {transferUser} from "@/api/users/user";
 import {getUserList} from "@/api/company/companyUser";
 import userDetails from '@/views/store/components/userDetails.vue';
+import userCoursePeriod from '../../components/course/userCoursePeriod.vue'
 export default {
   name: "FsUser",
-  components: {userDetails},
+  components: {userDetails,userCoursePeriod},
   data() {
     return {
+      
       show:{
         title:"会员详情",
         open:false,
@@ -317,6 +340,7 @@ export default {
         targetUserId: [{required: true, message: '请选择转移至销售', trigger: 'change'}],
         content: [{required: true, message: '请选择转移至销售', trigger: 'change'}]
       },
+      dialogVisible:false,
       companyUserList: [],
       openTransferDialog: false,
       transferForm: {
@@ -327,6 +351,8 @@ export default {
       loading: true,
       // 选中数组
       ids: [],
+      companyUserId:null,
+      companyId:null,
       // 非单个禁用
       single: true,
       // 非多个禁用
@@ -559,6 +585,8 @@ export default {
       this.ids = selection.map(item => item.userId);
       this.single = selection.length !== 1;
       this.multiple = !selection.length;
+      this.companyId = selection[0].companyId;
+      this.companyUserId = selection[0].companyUserId;
     },
 
     /** 新增按钮操作 */
@@ -663,6 +691,13 @@ export default {
     getProjectLabel(projectId) {
       return this.projectOptions.find(item => parseInt(item.dictValue) === projectId)?.dictLabel;
     },
+
+    batchSend(){
+      this.dialogVisible = true;
+    },
+    handleClose(){
+      this.dialogVisible = false;
+    }
   }
 };
 </script>