Kaynağa Gözat

Merge remote-tracking branch 'origin/master_bly' into master_bly

yfh 3 gün önce
ebeveyn
işleme
5e5de3b341

+ 17 - 0
src/api/course/courseRedPacketLog.js

@@ -85,3 +85,20 @@ export function exportCourseRedPacketLog(query) {
     params: query
   })
 }
+
+//会员流失人数统计
+export function courseLost(query) {
+  return request({
+    url: '/course/courseRedPacketLog/courseLost',
+    method: 'get',
+    params: query
+  })
+}
+//企微流失人数统计
+export function qwCourseLost(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/courseLost',
+    method: 'post',
+    data: query
+  })
+}

+ 9 - 0
src/api/course/qw/courseWatchLog.js

@@ -126,3 +126,12 @@ export function listBytrainingCampId(query) {
     params: query
   })
 }
+
+
+export function courseLost(query) {
+  return request({
+    url: '/qw/course/courseWatchLog/statisticsList',
+    method: 'get',
+    params: query
+  })
+}

+ 8 - 1
src/api/course/userCourseCamp.js

@@ -8,7 +8,14 @@ export function listCamp(query) {
     params: query
   })
 }
-
+//训练营列表(不分页 不带参)
+export function shortListCamp(query) {
+  return request({
+    url: '/course/trainingCamp/shortList',
+    method: 'get',
+    params: query
+  })
+}
 // 新增训练营
 export function addCamp(data) {
   return request({

+ 6 - 0
src/api/hisStore/storeOrder.js

@@ -153,3 +153,9 @@ export function getCreateOrderType() {
   })
 }
 
+export const updateDeliveryTime = (id) => {
+  return request({
+    url: '/store/store/storeOrder/updateDeliveryTime/'+ id,
+    method: 'get',
+  })
+}

+ 309 - 0
src/views/course/courseLost/index.vue

@@ -0,0 +1,309 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="营期" prop="courseId">
+        <el-select
+          filterable
+          v-model="queryParams.courseId"
+          placeholder="请选择营期"
+          clearable
+          size="small"
+          style="width: 200px"
+        >
+          <el-option
+            v-for="dict in campData"
+            :key="dict.trainingCampId"
+            :label="dict.trainingCampName"
+            :value="dict.trainingCampId"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" 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" @selection-change="handleSelectionChange">
+      <el-table-column label="营期名称" align="center" prop="periodName" />
+      <el-table-column label="总人数" align="center" prop="total" />
+      <el-table-column label="流失人数" align="center" prop="num" />
+      <el-table-column label="营期开始时间" align="center" prop="periodStartingTime" />
+      <el-table-column label="营期结束时间" align="center" prop="periodEndTime" />
+
+    </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="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="用户头像" prop="avatar">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="form.avatar" width="80">
+            <img :src="form.avatar" style="max-width: 80px;">
+          </el-popover>
+        </el-form-item>
+        <el-form-item label="会员昵称" prop="nickname">
+          <el-input v-model="form.nickname" disabled placeholder="请输入用户昵称" />
+        </el-form-item>
+        <el-form-item label="手机号码" prop="phone">
+          <el-input v-model="form.phone" disabled placeholder="请输入手机号码" />
+        </el-form-item>
+        <el-form-item label="最后一次登录ip" prop="lastIp">
+          <el-input v-model="form.lastIp" disabled placeholder="请输入最后一次登录ip" />
+        </el-form-item>
+        <!-- <el-form-item label="用户余额" prop="nowMoney">
+          <el-input v-model="form.nowMoney" disabled placeholder="请输入用户余额" />
+        </el-form-item> -->
+        <el-form-item label="积分" prop="integral">
+          <el-input v-model="form.integral" disabled placeholder="请输入用户剩余积分" />
+        </el-form-item>
+        <el-form-item label="用户备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入用户备注" />
+        </el-form-item>
+        <!-- <el-form-item label="状态">
+            <el-radio-group v-model="form.status">
+              <el-radio :label="item.dictValue" v-for="item in statusOptions" >{{item.dictLabel}}</el-radio>
+            </el-radio-group>
+        </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>
+  </div>
+</template>
+
+<script>
+import { myList,updateUser,getUser,addUser } from "@/api/users/user";
+import { shortListCamp } from '@/api/course/userCourseCamp'
+import { courseLost } from '@/api/course/courseRedPacketLog'
+export default {
+  name: "User",
+  data() {
+    return {
+      statusOptions:[],
+      //课程数据
+      campData: null,
+      // 遮罩层
+      loading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户表格数据
+      userList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        courseId: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        nowMoney: [
+          { required: true, message: "用户余额不能为空", trigger: "blur" }
+        ],
+        brokeragePrice: [
+          { required: true, message: "佣金金额不能为空", trigger: "blur" }
+        ],
+        integral: [
+          { required: true, message: "用户剩余积分不能为空", trigger: "blur" }
+        ],
+        signNum: [
+          { required: true, message: "连续签到天数不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "1为正常,0为禁止不能为空", trigger: "blur" }
+        ],
+        level: [
+          { required: true, message: "等级不能为空", trigger: "blur" }
+        ],
+        userType: [
+          { required: true, message: "用户类型不能为空", trigger: "change" }
+        ],
+        isPromoter: [
+          { required: true, message: "是否为推广员不能为空", trigger: "blur" }
+        ],
+        addres: [
+          { required: true, message: "详细地址不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    shortListCamp().then(response => {
+      this.campData = response.data;
+    })
+    this.getDicts("user_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    //this.getList();
+  },
+  methods: {
+    /** 查询用户列表 */
+    getList() {
+      this.loading = true;
+      if(this.queryParams.courseId===null){
+        this.loading = false
+        this.userList=null
+        return;
+      }
+      courseLost(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      })
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        userId: null,
+        username: null,
+        password: null,
+        realName: null,
+        birthday: null,
+        idCard: null,
+        mark: null,
+        nickname: null,
+        avatar: null,
+        phone: null,
+        createTime: null,
+        updateTime: null,
+        lastIp: null,
+        nowMoney: null,
+        brokeragePrice: null,
+        integral: null,
+        signNum: null,
+        status: 0,
+        level: null,
+        spreadUserId: null,
+        spreadTime: null,
+        userType: null,
+        isPromoter: null,
+        payCount: null,
+        spreadCount: null,
+        addres: null,
+        wxProfile: null,
+        isDel: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加用户";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const userId = row.userId || this.ids
+      getUser(userId).then(response => {
+        this.form = response.data;
+        this.form.status = response.data.status.toString();
+        this.open = true;
+        this.title = "修改用户";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.userId != null) {
+            updateUser(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addUser(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const userIds = row.userId || this.ids;
+      this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delUser(userIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有用户数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return exportUser(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+      }).catch(function() {});
+    }
+  }
+};
+</script>

+ 327 - 0
src/views/course/courseLost/qw/index.vue

@@ -0,0 +1,327 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="SOP任务" prop="channel" label-width="70px">
+        <date-range
+          v-model="selectedMultipleTasks"
+          :raw-data="channelList"
+          placeholder="请选择多个任务"
+          :parentSelectable="true"
+          :multiple="true"
+          component-width="300px"
+          :max-display-tags="3"
+          :check-strictly="false"
+          :return-leaf-only="false"
+          :show-node-detail="true"
+          @change="handleMultiChange"
+          @dateRangeChange="dateRangeChange"
+        ></date-range>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="cyan" 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" @selection-change="handleSelectionChange">
+<!--      <el-table-column label="营期名称" align="center" prop="periodName" />-->
+      <el-table-column label="营期id" align="center" prop="userLogsId" />
+      <el-table-column label="总人数" align="center" prop="total" />
+      <el-table-column label="流失人数" align="center" prop="num" />
+<!--      <el-table-column label="营期开始时间" align="center" prop="periodStartingTime" />
+      <el-table-column label="营期结束时间" align="center" prop="periodEndTime" />-->
+
+    </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="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="用户头像" prop="avatar">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="form.avatar" width="80">
+            <img :src="form.avatar" style="max-width: 80px;">
+          </el-popover>
+        </el-form-item>
+        <el-form-item label="会员昵称" prop="nickname">
+          <el-input v-model="form.nickname" disabled placeholder="请输入用户昵称" />
+        </el-form-item>
+        <el-form-item label="手机号码" prop="phone">
+          <el-input v-model="form.phone" disabled placeholder="请输入手机号码" />
+        </el-form-item>
+        <el-form-item label="最后一次登录ip" prop="lastIp">
+          <el-input v-model="form.lastIp" disabled placeholder="请输入最后一次登录ip" />
+        </el-form-item>
+        <!-- <el-form-item label="用户余额" prop="nowMoney">
+          <el-input v-model="form.nowMoney" disabled placeholder="请输入用户余额" />
+        </el-form-item> -->
+        <el-form-item label="积分" prop="integral">
+          <el-input v-model="form.integral" disabled placeholder="请输入用户剩余积分" />
+        </el-form-item>
+        <el-form-item label="用户备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入用户备注" />
+        </el-form-item>
+        <!-- <el-form-item label="状态">
+            <el-radio-group v-model="form.status">
+              <el-radio :label="item.dictValue" v-for="item in statusOptions" >{{item.dictLabel}}</el-radio>
+            </el-radio-group>
+        </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>
+  </div>
+</template>
+
+<script>
+import { myList,updateUser,getUser,addUser } from "@/api/users/user";
+import { shortListCamp } from '@/api/course/userCourseCamp'
+import { courseLost, qwCourseLost } from '@/api/course/courseRedPacketLog'
+import { getSOPTaskData } from '@/api/system/employeeStats'
+import dateRange from '@/components/TreeSelect/dateRange.vue'
+import SelectTree from '@/components/TreeSelect/index.vue'
+export default {
+  name: "CourseLostQw",
+  components: {dateRange,SelectTree},
+  data() {
+    return {
+      statusOptions:[],
+      selectedMultipleTasks: [],
+      // 频道列表
+      channelList: [],
+      //课程数据
+      campData: null,
+      // 遮罩层
+      loading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 用户表格数据
+      userList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        periodList: []
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        nowMoney: [
+          { required: true, message: "用户余额不能为空", trigger: "blur" }
+        ],
+        brokeragePrice: [
+          { required: true, message: "佣金金额不能为空", trigger: "blur" }
+        ],
+        integral: [
+          { required: true, message: "用户剩余积分不能为空", trigger: "blur" }
+        ],
+        signNum: [
+          { required: true, message: "连续签到天数不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "1为正常,0为禁止不能为空", trigger: "blur" }
+        ],
+        level: [
+          { required: true, message: "等级不能为空", trigger: "blur" }
+        ],
+        userType: [
+          { required: true, message: "用户类型不能为空", trigger: "change" }
+        ],
+        isPromoter: [
+          { required: true, message: "是否为推广员不能为空", trigger: "blur" }
+        ],
+        addres: [
+          { required: true, message: "详细地址不能为空", trigger: "blur" }
+        ],
+      }
+    };
+  },
+  created() {
+    getSOPTaskData({}).then(response => {
+      this.channelList = response.data;
+    });
+    this.getDicts("user_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    //this.getList();
+  },
+  methods: {
+    /** 查询用户列表 */
+    getList() {
+      this.loading = true;
+      if(this.queryParams.periodList===null){
+        return;
+      }
+      qwCourseLost(this.queryParams).then(response => {
+        this.userList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      })
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    handleMultiChange(e){
+
+    },
+    dateRangeChange(e){
+      getSOPTaskData({
+        startDate: e[0],
+        endDate: e[1]
+      }).then(response => {
+        this.channelList = response.data;
+      });
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        userId: null,
+        username: null,
+        password: null,
+        realName: null,
+        birthday: null,
+        idCard: null,
+        mark: null,
+        nickname: null,
+        avatar: null,
+        phone: null,
+        createTime: null,
+        updateTime: null,
+        lastIp: null,
+        nowMoney: null,
+        brokeragePrice: null,
+        integral: null,
+        signNum: null,
+        status: 0,
+        level: null,
+        spreadUserId: null,
+        spreadTime: null,
+        userType: null,
+        isPromoter: null,
+        payCount: null,
+        spreadCount: null,
+        addres: null,
+        wxProfile: null,
+        isDel: null
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+      this.queryParams.periodList = this.selectedMultipleTasks;
+      //this.getStatisticsData();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.userId)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加用户";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const userId = row.userId || this.ids
+      getUser(userId).then(response => {
+        this.form = response.data;
+        this.form.status = response.data.status.toString();
+        this.open = true;
+        this.title = "修改用户";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.userId != null) {
+            updateUser(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("修改成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          } else {
+            addUser(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess("新增成功");
+                this.open = false;
+                this.getList();
+              }
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const userIds = row.userId || this.ids;
+      this.$confirm('是否确认删除用户编号为"' + userIds + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delUser(userIds);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(function() {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有用户数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return exportUser(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+      }).catch(function() {});
+    }
+  }
+};
+</script>

+ 35 - 4
src/views/course/courseWatchLog/deptWatchLog.vue

@@ -261,6 +261,12 @@
         </el-date-picker>
       </el-form-item>
 
+      <el-form-item label="完课时间" prop="finishTime">
+        <el-date-picker v-model="finishTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="finishTimeChange"></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>
@@ -480,6 +486,7 @@ export default {
   name: "CourseWatchLog",
   data() {
     return {
+      finishTime:null,
       companyName:process.env.VUE_APP_COURSE_COMPANY_NAME,
       sopSearchText: '', // SOP搜索框显示的文本
       selectedSopId: null, // 选中的SOP ID
@@ -602,6 +609,8 @@ export default {
         qecETime:null,
         periodSTime:null,
         periodETime:null,
+        finishSTime:null,
+        finishETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
@@ -706,8 +715,9 @@ export default {
       // xgb 看课数据量太大必须限制时间if (this.isEmptyArray(this.createTimeText) &&
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText)  &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
 
@@ -742,6 +752,7 @@ export default {
         logType: null,
         createTime: null,
         updateTime: null,
+        finishTime:null,
         qwExternalContactId: null,
         externalUserName:null,
         duration: null,
@@ -776,6 +787,9 @@ export default {
       this.queryParams.periodDTime = null;
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
+      this.queryParams.finishSTime = null;
+      this.queryParams.finishETime = null;
+      this.finishTime=null;
       this.queryParams.sopId = null; // 重置SOP ID
       this.scheduleTime=null;
       this.updateTime=null;
@@ -851,8 +865,9 @@ export default {
       // xgb 看课数据量太大必须限制时间
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText) &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
 
@@ -990,7 +1005,23 @@ export default {
         this.queryParams.upETime = null;
       }
     },
+    finishTimeChange(finishTime){
+      if (finishTime && finishTime.length >= 2) {
+        if(!this.checkDateRangeLimit(finishTime)){
+          this.finishTime = null;
+          this.queryParams.finishSTime=null;
+          this.queryParams.finishETime=null;
+          return;
+        }
 
+        this.queryParams.finishSTime = finishTime[0] || null;
+        this.queryParams.finishETime = finishTime[1] || null;
+      } else {
+        this.finishTime = [];
+        this.queryParams.finishSTime = null;
+        this.queryParams.finishETime = null;
+      }
+    },
     // 进线时间
     qecCreateTimeChange(qecCreateTime) {
       if (qecCreateTime && qecCreateTime.length >= 2) {

+ 196 - 6
src/views/course/courseWatchLog/index.vue

@@ -264,6 +264,10 @@
           :default-time="['00:00:00', '23:59:59']">
         </el-date-picker>
       </el-form-item>
+      <el-form-item label="完课时间" prop="finishTime">
+        <el-date-picker v-model="finishTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="finishTimeChange"></el-date-picker>
+      </el-form-item>
 
 <!--      <el-form-item label="是否注册" prop="isVip">-->
 <!--        <el-select-->
@@ -372,6 +376,11 @@
       <el-table-column label="完课时间" align="center" prop="finishTime" />
       <el-table-column label="营期时间" align="center" prop="campPeriodTime" />
       <el-table-column label="进线时间" align="center" prop="qecCreateTime" />
+      <el-table-column label="沟通内容" show-overflow-tooltip align="center" prop="interflowContent" >
+        <template slot-scope="scope">
+          <el-button @click="showInterflowContentFun(scope.row)" type="text" size="small">详情</el-button>
+        </template>
+      </el-table-column>
       <el-table-column label="是否领奖" align="center" prop="rewardType" >
         <template slot-scope="scope">
           <el-tag
@@ -387,6 +396,7 @@
         label="操作"
         width="100">
         <template slot-scope="scope">
+          <el-button @click="editInterflowContentFun(scope.row)" type="text" size="small">修改沟通内容</el-button>
           <el-button @click="openAnswerLogFun(scope.row)" type="text" size="small">答题记录</el-button>
           <el-button @click="openRedLogFun(scope.row)" type="text" size="small">红包记录</el-button>
         </template>
@@ -597,7 +607,55 @@
         <el-button @click="resultDialogVisible = false">关闭</el-button>
       </span>
     </el-dialog>
+    <!-- 修改沟通内容弹窗 -->
+    <el-dialog
+      title="修改沟通内容"
+      :visible.sync="editContentDialogVisible"
+      width="500px"
+      append-to-body
+    >
+      <el-form
+        ref="editContentForm"
+        :model="editContentForm"
+        :rules="editContentRules"
+        label-width="80px"
+      >
+        <el-form-item label="沟通内容" prop="interflowContent">
+          <el-input
+            v-model="editContentForm.interflowContent"
+            type="textarea"
+            :rows="6"
+            placeholder="请输入沟通内容"
+            maxlength="500"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="editContentDialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="submitEditContent" :loading="editContentLoading">确 定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 沟通内容详情弹窗 -->
+    <el-dialog
+      title="沟通内容详情"
+      :visible.sync="showContentDialogVisible"
+      width="500px"
+      append-to-body
+    >
+      <div class="content-detail">
+        <div class="content-section">
+          <div class="content-text">
+            {{ showContentDetail || '暂无沟通内容' }}
+          </div>
+        </div>
+      </div>
 
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="showContentDialogVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -640,6 +698,7 @@ export default {
       scheduleTime: [],  // 改为数组
       createTime: [],    // 改为数组
       updateTime: [],    // 改为数组
+      finishTime:null,
       qecCreateTime: [], // 改为数组
       periodTime: [], // 改为数组
 
@@ -780,6 +839,8 @@ export default {
         periodETime:null,
         scheduleStartTime: null,
         scheduleEndTime: null,
+        finishSTime:null,
+        finishETime:null,
         sendType:process.env.VUE_APP_COURSE_DEFAULT,
         isVip: null,
         sopId: null, // sopId
@@ -809,6 +870,21 @@ export default {
         pageSize: 10
       },
       qwUserOptionsLoading: false,
+
+      editContentDialogVisible: false, // 弹窗显示状态
+      editContentLoading: false, // 提交加载状态
+      editContentForm: {
+        logId: null,
+        interflowContent: ''
+      },
+      editContentRules: {
+        interflowContent: [
+          { required: true, message: '沟通内容不能为空', trigger: 'blur' },
+          { max: 500, message: '长度不能超过500个字符', trigger: 'blur' }
+        ]
+      },
+      showContentDialogVisible: false, // 详情弹窗显示状态
+      showContentDetail: '' // 沟通内容详情
     };
   },
   created() {
@@ -1101,7 +1177,23 @@ export default {
         this.queryParams.upETime = null;
       }
     },
+    finishTimeChange(finishTime){
+      if (finishTime && finishTime.length >= 2) {
+        if(!this.checkDateRangeLimit(finishTime)){
+          this.finishTime = null;
+          this.queryParams.finishSTime=null;
+          this.queryParams.finishETime=null;
+          return;
+        }
 
+        this.queryParams.finishSTime = finishTime[0] || null;
+        this.queryParams.finishETime = finishTime[1] || null;
+      } else {
+        this.finishTime = [];
+        this.queryParams.finishSTime = null;
+        this.queryParams.finishETime = null;
+      }
+    },
     // 进线时间
     qecCreateTimeChange(qecCreateTime) {
       if (qecCreateTime && qecCreateTime.length >= 2) {
@@ -1219,8 +1311,9 @@ export default {
       // xgb 看课数据量太大必须限制时间
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText) &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
 
@@ -1255,6 +1348,7 @@ export default {
         externalUserName:null,
         duration: null,
         qwUserId: null,
+        finishTime:null,
         companyUserId: null,
         companyId: null,
         courseId: null,
@@ -1292,8 +1386,9 @@ export default {
       this.queryParams.scheduleStartTime = null;
       this.queryParams.scheduleEndTime = null;
       this.queryParams.sopId = null; // 重置SOP ID
-
-
+      this.queryParams.finishSTime = null;
+      this.queryParams.finishETime = null;
+      this.finishTime=null;
       // 重置SOP搜索
       this.handleSopClear();
 
@@ -1365,8 +1460,9 @@ export default {
       // xgb 看课数据量太大必须限制时间
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText) &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
       const queryParams = this.queryParams;
@@ -1382,7 +1478,66 @@ export default {
           this.exportLoading = false;
         }).catch(() => {});
     },
+    /**
+     * 修改沟通内容
+     * @param row 当前行数据
+     */
+    editInterflowContentFun(row) {
+      this.editContentForm = {
+        logId: row.logId,
+        interflowContent: row.interflowContent || ''
+      };
+
+      this.editContentDialogVisible = true;
+
+      // 清空表单验证
+      this.$nextTick(() => {
+        if (this.$refs.editContentForm) {
+          this.$refs.editContentForm.clearValidate();
+        }
+      });
+    },
+    /**
+     * 查看沟通内容详情
+     * @param row 当前行数据
+     */
+    showInterflowContentFun(row) {
+      console.log('查看沟通内容详情:', row.logId, row.interflowContent);
 
+      // 设置沟通内容详情
+      this.showContentDetail = row.interflowContent || '暂无沟通内容';
+
+      // 打开弹窗
+      this.showContentDialogVisible = true;
+    },
+    /**
+     * 提交修改沟通内容
+     */
+    submitEditContent() {
+      this.$refs.editContentForm.validate(valid => {
+        if (!valid) return;
+
+        this.editContentLoading = true;
+
+        // 调用后端API更新沟通内容
+        updateCourseWatchLog({
+          logId: this.editContentForm.logId,
+          interflowContent: this.editContentForm.interflowContent
+        })
+          .then(response => {
+            this.$message.success('修改成功');
+            this.editContentDialogVisible = false;
+            this.getList(); // 刷新列表
+          })
+          .catch(error => {
+            console.error('修改失败:', error);
+            this.$message.error('修改失败');
+          })
+          .finally(() => {
+            this.editContentLoading = false;
+          });
+      });
+    },
     openAnswerLogFun(row) {
       this.openAnswerLog = true;
       this.answerLogQueryParams.watchLogId = row.logId;
@@ -1752,4 +1907,39 @@ export default {
   background: rgba(0, 0, 0, 0.2);
   border-radius: 3px;
 }
+
+.content-detail {
+  padding: 10px;
+}
+
+.content-section {
+  margin-top: 10px;
+}
+
+.content-text {
+  padding: 12px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  border: 1px solid #ebeef5;
+  line-height: 1.6;
+  min-height: 100px;
+  max-height: 300px;
+  overflow-y: auto;
+  white-space: pre-wrap; /* 保留换行和空格 */
+  word-break: break-word; /* 自动换行 */
+}
+
+/* 美化滚动条 */
+.content-text::-webkit-scrollbar {
+  width: 6px;
+}
+
+.content-text::-webkit-scrollbar-thumb {
+  background: #c0c4cc;
+  border-radius: 3px;
+}
+
+.content-text::-webkit-scrollbar-track {
+  background: #f5f7fa;
+}
 </style>

+ 141 - 4
src/views/course/courseWatchLog/qw/statistics.vue

@@ -70,6 +70,8 @@
       <el-table-column label="上线率" align="center" prop="onLineRate" />
       <el-table-column label="完课率" align="center" prop="finishedRate" />
 <!--      <el-table-column label="消耗红包金额" align="center" prop="redAmount" />-->
+      <el-table-column label="上线客户完播率" align="center" prop="onLineCompleteRate" />
+<!--      <el-table-column label="流失人数" align="center" prop="onLineNum" />-->
     </el-table>
 
     <pagination
@@ -159,7 +161,7 @@ export default {
     });
   },
   methods: {
-     getSummaries(param) {
+     bfGetSummaries(param) {//以前的代码备份
       let totalNum = 0;
       const { columns, data } = param;
       const sums = [];
@@ -180,7 +182,7 @@ export default {
               return prev;
             }
           }, 0);
-          if(index > 3 && totalNum != 0){
+          if(index > 3 && totalNum !== 0){
               sums[index] = sums[index] + "("+ ((sums[index] / totalNum) * 100).toFixed(2)  + "%)"
           }
           if( column.property=="videoName"){
@@ -196,12 +198,13 @@ export default {
             column.property === "noUserWaitNumber" ||
             column.property === "onLineRate" ||
             column.property === "finishedRate" ||
-            column.property === "redAmount"
+            column.property === "redAmount" ||
+            column.property === "onLineCompleteRate"
           ) {
           }
         } else {
           sums[index] = "";
-          if(index === 12 || index === 13){
+          if(index === 12 || index === 13 || index===14){
             //  let numbers = data.map(item => {
             //   return parseFloat(item[column.property].replace('%', '')) || 0; // 处理空值或无效值
             // });
@@ -210,15 +213,149 @@ export default {
             if(index === 12 && !!sums[9]){
               let sumsNum = sums[6] + sums[7] + sums[8];
               sums[index] = (sumsNum * 100 / sums[9]).toFixed(2) + '%';
+
+
             } else if(index === 13 && !!sums[9]){
               sums[index] =  (sums[7] * 100 / sums[9]).toFixed(2) + '%';
             }
+            else if(index === 14 && !!sums[9]){
+              sums[index] =  (sums[7] * 100 / sums[9]).toFixed(2) + '%';
+            }
           }
         }
       });
       console.log(sums);
       return sums;
     },
+    getSummaries(param) {
+      const { columns, data } = param;
+      const sums = [];
+
+      // 计算各种总数
+      let totalSendNumber = 0;
+      let totalType1 = 0;
+      let totalType2 = 0;
+      let totalType3 = 0;
+      let totalType4 = 0;
+      let totalIsUserWaitNumber = 0;
+      let totalNoUserWaitNumber = 0;
+      let totalOnLineNum = 0;
+
+      // 百分比直接累加(不是平均)
+      let totalOnLineRate = 0;     // 上线率合计
+      let totalFinishedRate = 0;   // 完课率合计
+      let totalOnLineCompleteRate = 0; // 上线客户完播率合计
+
+      // 遍历数据
+      data.forEach(item => {
+        // 数字字段累加
+        totalSendNumber += Number(item.sendNumber) || 0;
+        totalType1 += Number(item.type1) || 0;
+        totalType2 += Number(item.type2) || 0;
+        totalType3 += Number(item.type3) || 0;
+        totalType4 += Number(item.type4) || 0;
+        totalIsUserWaitNumber += Number(item.isUserWaitNumber) || 0;
+        totalNoUserWaitNumber += Number(item.noUserWaitNumber) || 0;
+
+        // 计算本行的上线人数
+        const itemOnLineNum = (Number(item.type1) || 0) + (Number(item.type2) || 0) + (Number(item.type4) || 0);
+        totalOnLineNum += itemOnLineNum;
+
+        // 提取百分比字段的值(去除%号)
+        const parsePercentage = (value) => {
+          if (!value && value !== 0) return 0;
+          if (typeof value === 'string') {
+            // 处理"5.00%"格式
+            const num = parseFloat(value.replace('%', ''));
+            return isNaN(num) ? 0 : num;
+          }
+          return Number(value) || 0;
+        };
+
+        // 累加上线率
+        totalOnLineRate += parsePercentage(item.onLineRate);
+
+        // 累加完课率
+        totalFinishedRate += parsePercentage(item.finishedRate);
+
+        // 累加上线客户完播率
+        totalOnLineCompleteRate += parsePercentage(item.onLineCompleteRate);
+      });
+
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = "总计";
+          return;
+        }
+
+        const property = column.property;
+
+        // 处理特殊字段
+        if (property === "videoName" || property === "courseName" ||
+          property === "qwUserName" || property === "createTime") {
+          sums[index] = "";
+          return;
+        }
+
+        // 处理百分比字段 - 直接累加的结果
+        if (property === "onLineRate") {
+          sums[index] = totalOnLineRate.toFixed(2) + '%';
+          return;
+        } else if (property === "finishedRate") {
+          sums[index] = totalFinishedRate.toFixed(2) + '%';
+          return;
+        } else if (property === "onLineCompleteRate") {
+          sums[index] = totalOnLineCompleteRate.toFixed(2) + '%';
+          return;
+        }
+
+        // 处理普通数字字段
+        switch (property) {
+          case "type1":
+            sums[index] = totalType1;
+            break;
+          case "type2":
+            sums[index] = totalType2;
+            break;
+          case "type3":
+            sums[index] = totalType3;
+            break;
+          case "type4":
+            sums[index] = totalType4;
+            break;
+          case "sendNumber":
+            sums[index] = totalSendNumber;
+            break;
+          case "isUserWaitNumber":
+            sums[index] = totalIsUserWaitNumber;
+            break;
+          case "noUserWaitNumber":
+            sums[index] = totalNoUserWaitNumber;
+            break;
+          case "redAmount":
+            // 如果需要计算红包金额总和
+            const redAmounts = data.map(item => Number(item.redAmount) || 0);
+            sums[index] = redAmounts.reduce((sum, curr) => sum + curr, 0);
+            break;
+          default:
+            sums[index] = "";
+        }
+      });
+
+      console.log("统计结果:", {
+        发课数: totalSendNumber,
+        看课中: totalType1,
+        已完课: totalType2,
+        待看课: totalType3,
+        看课中断: totalType4,
+        上线人数: totalOnLineNum,
+        上线率合计: totalOnLineRate.toFixed(2) + '%',
+        完课率合计: totalFinishedRate.toFixed(2) + '%',
+        上线完播率合计: totalOnLineCompleteRate.toFixed(2) + '%'
+      });
+
+      return sums;
+    },
     courseChange(row){
       this.queryParams.videoId=null;
       if(row === ''){

+ 34 - 4
src/views/course/courseWatchLog/watchLog.vue

@@ -239,6 +239,11 @@
         </el-date-picker>
       </el-form-item>
 
+      <el-form-item label="完课时间" prop="finishTime">
+        <el-date-picker v-model="finishTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange"
+                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="finishTimeChange"></el-date-picker>
+      </el-form-item>
+
 <!--      <el-form-item label="是否注册" prop="isVip">-->
 <!--        <el-select-->
 <!--          filterable-->
@@ -703,6 +708,7 @@ export default {
       updateTime: [],    // 改为数组
       qecCreateTime: [], // 改为数组
       periodTime: [], // 改为数组
+      finishTime:null,
 
       // 控制日历显隐
       showScheduleCalendar: false,
@@ -836,6 +842,8 @@ export default {
         eTime: null,
         upSTime:null,
         upETime:null,
+        finishSTime:null,
+        finishETime:null,
         qecSTime:null,
         qecETime:null,
         periodSTime:null,
@@ -1054,7 +1062,23 @@ export default {
         this.queryParams.upETime = null;
       }
     },
+    finishTimeChange(finishTime){
+      if (finishTime && finishTime.length >= 2) {
+        if(!this.checkDateRangeLimit(finishTime)){
+          this.finishTime = null;
+          this.queryParams.finishSTime=null;
+          this.queryParams.finishETime=null;
+          return;
+        }
 
+        this.queryParams.finishSTime = finishTime[0] || null;
+        this.queryParams.finishETime = finishTime[1] || null;
+      } else {
+        this.finishTime = [];
+        this.queryParams.finishSTime = null;
+        this.queryParams.finishETime = null;
+      }
+    },
     // 进线时间
     qecCreateTimeChange(qecCreateTime) {
       if (qecCreateTime && qecCreateTime.length >= 2) {
@@ -1160,8 +1184,9 @@ export default {
       // xgb 看课数据量太大必须限制时间if (this.isEmptyArray(this.createTimeText) &&
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText) &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
 
@@ -1192,6 +1217,7 @@ export default {
         logType: null,
         createTime: null,
         updateTime: null,
+        finishTime:null,
         qwExternalContactId: null,
         externalUserName:null,
         duration: null,
@@ -1235,6 +1261,9 @@ export default {
       this.queryParams.isVip = null; // 重置 isVip 状态
       this.queryParams.qwUserId = null; // 重置 qwUserId
       this.queryParams.externalUserName=null;
+      this.queryParams.finishSTime = null;
+      this.queryParams.finishETime = null;
+      this.finishTime=null;
       // 重置SOP搜索
       this.handleSopClear();
       // 统一重置日历组件
@@ -1310,8 +1339,9 @@ export default {
       // xgb 看课数据量太大必须限制时间
       if (this.isEmptyArray(this.createTimeText) &&
         this.isEmptyArray(this.updateTimeText) &&
-        this.isEmptyArray(this.scheduleTimeText)) {
-        this.$message.warning('请选择创建时间或营期时间或最新更新时间');
+        this.isEmptyArray(this.scheduleTimeText) &&
+        this.isEmptyArray(this.finishTime)) {
+        this.$message.warning('请选择创建时间或营期时间或最新更新时间或完课时间');
         return;
       }
       const queryParams = this.queryParams;

+ 62 - 3
src/views/hisStore/components/productOrder.vue

@@ -305,6 +305,26 @@
                     />
             </el-select>
         </el-form-item>
+
+        <el-form-item label="物流状态" prop="deliveryStatus" >
+
+          <el-select  style="width:200px" v-model="editForm.deliveryStatus" placeholder="请选择物流状态" clearable size="small" >
+            <!-- 添加一个空选项,用于显示空值 -->
+            <el-option
+              v-if="editForm.deliveryStatus===''"
+              :key="''"
+              :label="''"
+              :value="editForm.deliveryStatus"
+              style="display: none;"
+            />
+            <el-option
+              v-for="item in deliveryStatusOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+            />
+          </el-select>
+        </el-form-item>
         <el-form-item label="档期归属" prop="scheduleId"  >
             <el-select filterable style="width: 200px" v-model="editForm.scheduleId" placeholder="请选择档期" clearable size="small" >
               <el-option
@@ -325,6 +345,22 @@
               />
           </el-select>
         </el-form-item>
+        <!--  订单手机号      -->
+        <el-form-item label="订单手机号" prop="userPhone"  >
+          <el-input v-model="editForm.userPhone" placeholder="请输入手机号" style="width:200px"/>
+        </el-form-item>
+        <!--  线下支付金额      -->
+        <el-form-item label="线下支付金额" prop="offlinePayAmount"  >
+          <el-input
+            v-model.number="editForm.offlinePayAmount"
+            type="number"
+            step="0.01"
+            placeholder="请输入"
+            :min="0"
+            @blur="formatAmount"
+            style="width:200px"
+          />
+        </el-form-item>
         <el-form-item label="备注" prop="mark"  >
           <el-input v-model="editForm.mark" placeholder="请输入备注" />
         </el-form-item>
@@ -584,13 +620,21 @@ export default {
       editForm:{
         orderType:null,
         scheduleId:null,
+        deliveryStatus:null,
+        oldDeliveryStatus:null,
         orderVisit:null,
         mark:"",
+        userPhone:null,
+        offlinePayAmount: 0.00,
       },
-      editRules:{
-
+      editRules: {
+        userPhone: [
+          { required: false, message: '请输入手机号', trigger: 'blur' },
+          { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
+        ]
       },
       createTypeOptions:[],
+      deliveryStatusOptions:[],
       orderTypeOptions:[],
       payTypeOptions:[],
       statusOptions:[],
@@ -621,6 +665,9 @@ export default {
     this.getDicts("store_order_create_type").then((response) => {
       this.createTypeOptions = response.data;
     });
+    this.getDicts("store_order_delivery_status").then((response) => {
+      this.deliveryStatusOptions = response.data;
+    });
     getTcmScheduleList().then(response => {
       this.scheduleOptions = response.data;
     });
@@ -630,7 +677,11 @@ export default {
       return this.$store.state.user.user;
     }
   },
-  methods: {
+  methods: {formatAmount() {
+    if (typeof this.editForm.offlinePayAmount === 'number') {
+        this.editForm.offlinePayAmount = parseFloat(this.editForm.offlinePayAmount.toFixed(2));
+      }
+    },
     closeSms(){
       this.addSms.open=false;
     },
@@ -714,6 +765,7 @@ export default {
             });
     },
     showExpress(){
+      this.traces=null;
       this.expressDialog.open=true;
       getExpress(this.orderId).then(response => {
           this.express = response.data;
@@ -830,6 +882,9 @@ export default {
     submitEditForm(){
         this.$refs["editForm"].validate(valid => {
         if (valid) {
+          if ((this.editForm.deliveryStatus===0||this.editForm.deliveryStatus==='0') && (this.editForm.oldDeliveryStatus===null||this.editForm.oldDeliveryStatus===''||this.editForm.oldDeliveryStatus==='null'||this.editForm.oldDeliveryStatus===0||this.editForm.oldDeliveryStatus==='0') ){
+            this.editForm.deliveryStatus=this.editForm.oldDeliveryStatus
+          }
           updateStoreOrder(this.editForm).then(response => {
             if (response.code === 200) {
               this.msgSuccess("操作成功");
@@ -843,8 +898,12 @@ export default {
     },
     editOrder(){
         this.edit.open=true;
+        this.editForm.offlinePayAmount=this.order.offlinePayAmount;
         this.editForm.mark=this.order.mark
         this.editForm.id=this.order.id;
+        this.editForm.userPhone=null;
+        this.editForm.deliveryStatus=this.order.deliveryStatus===null?'0':this.order.deliveryStatus+'';
+        this.editForm.oldDeliveryStatus=this.order.deliveryStatus
         if(this.order.orderType!=null){
           this.editForm.orderType=this.order.orderType.toString();
         }

+ 100 - 53
src/views/hisStore/statistics/storeProduct.vue

@@ -4,7 +4,7 @@
       <div class="title">
         产品销售统计
       </div>
-      
+
       <el-form class="search-form" :inline="true">
         <el-row :gutter="20">
           <el-col :span="6">
@@ -48,81 +48,114 @@
               </el-select>
             </el-form-item>
           </el-col>
-        </el-row> 
-        
-       
-        
-    
-        
-        
-        <el-form-item label="筛选日期" prop="createTime">
-          <el-date-picker clearable size="small" style="width: 205.4px"
-            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="cyan" icon="el-icon-search" @click="storeProduct">搜索</el-button>
-        </el-form-item>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="6">
+            <el-form-item label="下单时间:">
+              <el-select v-model="queryType" placeholder="查询方式" size="small">
+                <el-option
+                  v-for="item in queryTypes"
+                  :key="item.dictLabel"
+                  :label="item.dictLabel"
+                  :value="item.dictValue">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="筛选日期" prop="createTime">
+              <el-date-picker clearable size="small" style="width: 205.4px"
+                              v-model="dateRange"
+                              type="daterange"
+                              value-format="yyyy-MM-dd"
+                              start-placeholder="开始日期" end-placeholder="结束日期">
+              </el-date-picker>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+          </el-col>
+          <el-col :span="6">
+            <el-form-item>
+              <el-button type="cyan" icon="el-icon-search" @click="storeProduct">搜索</el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
-      
+
       <div class="data-box">
-        <div class="echart-box">
+        <div class="echart-box" hidden="hidden">
           <div id="echart-customer"></div>
         </div>
-        
+
         <!-- 产品销售统计数据表格 -->
         <div class="table-container">
           <h3 class="table-title">产品销售汇总</h3>
           <!-- <div class="export">
             <el-button type="primary" @click="handleExport">导出数据</el-button>
           </div> -->
-          <el-table 
+          <el-table
             :data="tableData"
             border
             stripe
             style="width: 100%; margin-top: 20px;"
             :row-class-name="tableRowClassName"
             :span-method="objectSpanMethod">
-            
+
             <!-- 产品名称列 -->
             <el-table-column prop="name" label="产品名称" width="100" align="center" class-name="sticky-column">
               <template slot-scope="scope">
                 <span :class="{'group-name': scope.row.isGroup}">{{ scope.row.name }}</span>
               </template>
             </el-table-column>
-            
+            <!-- 汇总数据-->
+            <el-table-column label="汇总" align="center">
+              <el-table-column
+                label="数量"
+                width="60"
+                align="center"> <template slot-scope="scope">
+                {{ scope.row.totalCount || 0 }}
+              </template>
+              </el-table-column>
+              <!-- 金额子列 -->
+              <el-table-column
+                label="金额"
+                width="80"
+                align="center">
+                <template slot-scope="scope">
+                  {{ scope.row.totalAmount ? scope.row.totalAmount.toFixed(2) : '0.00' }}
+                </template>
+              </el-table-column>
+            </el-table-column>
             <!-- 动态生成订单类型列 - 使用多级表头 -->
-            <el-table-column 
-              v-for="orderType in orderTypeOptions" 
+            <el-table-column
+              v-for="orderType in orderTypeOptions"
               :key="orderType.dictValue"
-              :label="orderType.dictLabel" 
+              :label="orderType.dictLabel"
               align="center">
-              
+
               <!-- 数量子列 -->
-              <el-table-column 
-                label="数量" 
-                width="60" 
+              <el-table-column
+                label="数量"
+                width="60"
                 align="center">
                 <template slot-scope="scope">
                   {{ getOrderData(scope.row, orderType.dictValue, 'count') }}
                 </template>
               </el-table-column>
-              
+
               <!-- 金额子列 -->
-              <el-table-column 
-                label="金额" 
-                width="80" 
+              <el-table-column
+                label="金额"
+                width="80"
                 align="center">
                 <template slot-scope="scope">
                   {{ getOrderData(scope.row, orderType.dictValue, 'amount') }}
                 </template>
               </el-table-column>
-              
+
             </el-table-column>
-            
+
           </el-table>
         </div>
       </div>
@@ -150,6 +183,11 @@ export default {
   },
   data() {
     return {
+      queryTypes:[
+        { dictLabel: '下单时间', dictValue: 1 },
+        { dictLabel: '发货时间', dictValue: 2 }
+      ],
+      queryType: 2,
       companys: [],
       deptOptions: [],
       companyId: undefined,
@@ -208,7 +246,7 @@ export default {
         this.getTreeselect();
       }
     });
-    
+
     // 获取订单类型字典
     this.getDicts("store_order_type").then((response) => {
       this.orderTypeOptions = response.data;
@@ -269,27 +307,31 @@ export default {
         data.startTime = this.dateRange[0]
         data.endTime = this.dateRange[1]
       }
-      
+      if(this.queryType){
+        data.queryType = this.queryType
+      }
+
       storeProduct(data).then((response) => {
         this.dates = response.dates;
         this.orderCount = response.orderCount;
         this.payPrice = response.payPrice;
-        
+
         // 处理表格数据,补全缺失的订单类型数据
         this.tableData = this.processTableData(response.tableData || []);
-        
+
         setTimeout(() => {
           this.initEchart();
         }, 500);
       });
     },
-    
+
     // 新增方法:处理表格数据,为每个产品补全所有订单类型的数据
     processTableData(rawTableData) {
       return rawTableData.map(item => {
         // 创建一个新的 productCounts 对象,包含所有订单类型
         const processedProductCounts = {};
-        
+        let totalCount = 0;
+        let totalAmount = 0.0;
         // 遍历所有订单类型选项,为每个类型设置默认值
         this.orderTypeOptions.forEach(orderType => {
           const dictValue = orderType.dictValue;
@@ -303,15 +345,20 @@ export default {
               amount: 0.0
             };
           }
+          // 累加汇总数据
+          totalCount += processedProductCounts[dictValue].count;
+          totalAmount += processedProductCounts[dictValue].amount;
         });
-        
+
         return {
           ...item,
-          productCounts: processedProductCounts
+          productCounts: processedProductCounts,
+          totalCount: totalCount,
+          totalAmount: totalAmount
         };
       });
     },
-    
+
     initEchart() {
       var option = {
         tooltip: {
@@ -365,7 +412,7 @@ export default {
       this.chart = echarts.init(document.getElementById("echart-customer"));
       this.chart.setOption(option, true);
     },
-    
+
     // 修改获取订单数据的方法,适配新的数据结构
     getOrderData(row, orderType, dataType) {
       // 使用 productCounts 而不是 orderData
@@ -378,7 +425,7 @@ export default {
       }
       return dataType === 'amount' ? '0.00' : 0;
     },
-    
+
     // 表格行样式
     tableRowClassName({ row, rowIndex }) {
       if (row.isGroup) {
@@ -471,7 +518,7 @@ export default {
 
 ::v-deep .el-table {
   font-size: 12px;
-  
+
   .el-table__header-wrapper {
     th {
       background-color: #f5f7fa;
@@ -479,7 +526,7 @@ export default {
       color: #333;
     }
   }
-  
+
   .el-table__body-wrapper {
     td {
       padding: 8px 0;
@@ -492,7 +539,7 @@ export default {
   left: 0;
   z-index: 2;
   background-color: white;
-  
+
   // 为表头也添加样式
   &.is-header-column {
     background-color: #f5f7fa;
@@ -509,4 +556,4 @@ export default {
 .vue-treeselect__control {
   display: block;
 }
-</style>
+</style>

+ 62 - 1
src/views/hisStore/storeOrder/list.vue

@@ -133,6 +133,17 @@
               end-placeholder="结束日期">
             </el-date-picker>
           </el-form-item>
+          <el-form-item label="发货时间" prop="deliverySendTimeRange">
+            <el-date-picker
+              style="width:220px"
+              clearable size="small"
+              v-model="deliverySendTimeRange"
+              type="daterange"
+              value-format="yyyy-MM-dd"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期">
+            </el-date-picker>
+          </el-form-item>
           <el-form-item label="回单时间" prop="deliveryImportTimeRange">
             <el-date-picker
             style="width:220px"
@@ -207,6 +218,14 @@
               v-hasPermi="['store:storeOrderAudit:auditAgain']"
             >重新提交审核</el-button>
           </el-col>
+          <el-col :span="1.5">
+            <el-button
+              type="warning"
+              icon="el-icon-download"
+              size="mini"
+              @click="handleGetSendDeliveryTime"
+            >获取发货时间</el-button>
+          </el-col>
         <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
         <el-tabs type="card" v-model="activeName" @tab-click="handleClick">
@@ -257,7 +276,13 @@
                   <span v-if="scope.row.payPrice!=null">{{scope.row.payPrice.toFixed(2)}}</span>
               </template>
           </el-table-column>
+          <el-table-column label="线下支付金额" align="center" prop="offlinePayAmount" >
+            <template slot-scope="scope">
+              <span v-if="scope.row.offlinePayAmount!=null">{{scope.row.offlinePayAmount.toFixed(2)}}</span>
+            </template>
+          </el-table-column>
           <el-table-column label="下单时间" align="center" prop="createTime" width="100"/>
+          <el-table-column label="发货时间" align="center" prop="deliverySendTime" width="100"/>
           <!-- <el-table-column label="支付状态" align="center" prop="paid" /> -->
           <el-table-column label="支付时间" align="center" prop="payTime" width="100">
           </el-table-column>
@@ -498,7 +523,7 @@
 </template>
 
 <script>
-import {exportStoreOrderItems, createUserOrder,listStoreOrder, getStoreOrder, delStoreOrder, addStoreOrder, updateStoreOrder, exportStoreOrder } from "@/api/hisStore/storeOrder";
+import {exportStoreOrderItems, createUserOrder,listStoreOrder, getStoreOrder, updateDeliveryTime, addStoreOrder, updateStoreOrder, exportStoreOrder } from "@/api/hisStore/storeOrder";
 import { getUserList } from "@/api/users/user";
 import { getAddressList } from "@/api/users/userAddress";
 import { getTcmScheduleList } from "@/api/company/tcmScheduleReport";
@@ -602,6 +627,7 @@ export default {
       open: false,
       createTimeRange:[],
       payTimeRange:[],
+      deliverySendTimeRange:[],
       deliveryImportTimeRange:[],
       scheduleOptions:[],
       // 查询参数
@@ -725,6 +751,18 @@ export default {
         this.getList();
         this.open = false;
       });
+    },
+    handleGetSendDeliveryTime(){
+      if (this.ids.length !== 1) {
+        this.$message.warning("请选择一条数据");
+        return;
+      }
+
+      updateDeliveryTime(this.ids[0]).then(response => {
+        this.$message.success("获取发货时间成功");
+        this.getList();
+        this.open = false;
+      });
     },
      /** 查询部门下拉树结构 */
      getTreeselect() {
@@ -872,6 +910,14 @@ export default {
       else{
         this.queryParams.deliveryImportTimeRange=null;
       }
+      if(this.deliverySendTimeRange!=null&&this.deliverySendTimeRange.length==2){
+        this.queryParams.deliverySendTimeRange=this.deliverySendTimeRange[0]+"--"+this.deliverySendTimeRange[1]
+      }
+      else {
+        this.queryParams.deliverySendTimeRange=null;
+      }
+
+
       this.loading = true;
       listStoreOrder(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
         this.storeOrderList = response.rows;
@@ -970,6 +1016,14 @@ export default {
       else{
         this.queryParams.payTimeRange=null;
       }
+      if (this.deliverySendTimeRange!=null && this.deliverySendTimeRange.length==2){
+        this.queryParams.deliverySendTimeRange=this.deliverySendTimeRange[0]+"--"+this.deliverySendTimeRange[1]
+
+      }
+      else{
+        this.queryParams.deliverySendTimeRange=null;
+      }
+
       if(this.deliveryImportTimeRange!=null&&this.deliveryImportTimeRange.length==2){
         this.queryParams.deliveryImportTimeRange=this.deliveryImportTimeRange[0]+"--"+this.deliveryImportTimeRange[1]
       }
@@ -1004,6 +1058,13 @@ export default {
       else{
         this.queryParams.payTimeRange=null;
       }
+      if(this.deliverySendTimeRange!=null&&this.deliverySendTimeRange.length==2){
+        this.queryParams.deliverySendTimeRange=this.deliverySendTimeRange[0]+"--"+this.deliverySendTimeRange[1]
+      }
+      else{
+        this.queryParams.deliverySendTimeRange=null;
+      }
+
       if(this.deliveryImportTimeRange!=null&&this.deliveryImportTimeRange.length==2){
         this.queryParams.deliveryImportTimeRange=this.deliveryImportTimeRange[0]+"--"+this.deliveryImportTimeRange[1]
       }

+ 5 - 0
src/views/hisStore/storeOrder/myList.vue

@@ -205,6 +205,11 @@
               <span v-if="scope.row.payPrice!=null">{{scope.row.payPrice.toFixed(2)}}</span>
           </template>
       </el-table-column>
+      <el-table-column label="线下支付金额" align="center" prop="offlinePayAmount" >
+        <template slot-scope="scope">
+          <span v-if="scope.row.offlinePayAmount!=null">{{scope.row.offlinePayAmount.toFixed(2)}}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="下单时间" align="center" prop="createTime" width="100"/>
       <!-- <el-table-column label="支付状态" align="center" prop="paid" /> -->
       <el-table-column label="支付时间" align="center" prop="payTime" width="100">

+ 5 - 0
src/views/hisStore/storeOrderAudit/audit.vue

@@ -220,6 +220,11 @@
           {{ scope.row.totalPrice ? parseFloat(scope.row.totalPrice).toFixed(2) : '0.00' }}
         </template>
       </el-table-column>
+      <el-table-column label="线下支付金额" align="center" prop="offlinePayAmount" >
+        <template slot-scope="scope">
+          <span v-if="scope.row.offlinePayAmount!=null">{{scope.row.offlinePayAmount.toFixed(2)}}</span>
+        </template>
+      </el-table-column>
       <el-table-column label="手机号" align="center" prop="userPhone"  width="120"/>
       <el-table-column label="审核时间" align="center" prop="companyAuditTime"  width="160"/>
       <el-table-column label="审核人" align="center" prop="companyAuditUserName" />

+ 12 - 0
src/views/member/list.vue

@@ -173,6 +173,11 @@
         </template>
       </el-table-column>
       <el-table-column label="手机号码" align="center" prop="phone" />
+      <el-table-column label="会员等级" align="center" prop="level" >
+          <template slot-scope="scope">
+            <span>{{ scope.row.level === 1 ? 'vip会员' : '普通会员' }}</span>
+          </template>
+      </el-table-column>
       <el-table-column label="积分" align="center" prop="integral" v-if="false" />
       <el-table-column label="看课数量" align="center" prop="watchCourseCount" />
       <el-table-column label="缺课数量" align="center" prop="missCourseCount" />
@@ -255,6 +260,12 @@
         <el-form-item label="手机号码" prop="phone">
           <el-input v-model="form.phone" disabled placeholder="请输入手机号码" />
         </el-form-item>
+        <el-form-item label="是否成为会员">
+          <el-radio-group v-model="form.level">
+            <el-radio :label=1>是</el-radio>
+            <el-radio :label=0>否</el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="最后登录IP" prop="lastIp" v-if="form.lastIp">
           <el-input v-model="form.lastIp" disabled placeholder="最后一次登录IP" />
         </el-form-item>
@@ -518,6 +529,7 @@ export default {
         nickname: null,
         avatar: null,
         phone: null,
+        isVip: null,
         status: 1,
         tagIds: [],
         companyUserId: null,

+ 4 - 4
src/views/store/storeOrder/list.vue

@@ -52,7 +52,7 @@
               />
         </el-select>
       </el-form-item>
-		
+
       <el-form-item label="结算状态" prop="deliveryPayStatus">
         <el-select style="width:220px" v-model="queryParams.deliveryPayStatus" placeholder="请选择物流结算状态" clearable size="small" >
         <el-option
@@ -140,7 +140,7 @@
           @keyup.enter.native="handleQuery"
         />
       </el-form-item>
-	
+
       <el-form-item label="加密电话" prop="userPhoneMk">
         <el-input
           v-model="queryParams.userPhoneMk"
@@ -221,8 +221,8 @@
 	<el-form-item label="入账时间" prop="tuiMoneyTime">
 	      <el-date-picker v-model="tuiMoneyTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" 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>