3 Commits 94893e9a2e ... ef789ffd4b

Autor SHA1 Mensaje Fecha
  yfh ef789ffd4b Merge remote-tracking branch 'origin/Payment-Configuration' into Payment-Configuration hace 2 semanas
  yfh 523be614f5 1、迁移舌诊调整 hace 2 semanas
  yfh ceb12e9936 1、迁移奖励配置表信息 hace 3 semanas

+ 53 - 0
src/api/course/reward.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询奖励配置列表
+export function listReward(query) {
+  return request({
+    url: '/course/reward/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询奖励配置详细
+export function getReward(id) {
+  return request({
+    url: '/course/reward/' + id,
+    method: 'get'
+  })
+}
+
+// 新增奖励配置
+export function addReward(data) {
+  return request({
+    url: '/course/reward',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改奖励配置
+export function updateReward(data) {
+  return request({
+    url: '/course/reward',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除奖励配置
+export function delReward(id) {
+  return request({
+    url: '/course/reward/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出奖励配置
+export function exportReward(query) {
+  return request({
+    url: '/course/reward/export',
+    method: 'get',
+    params: query
+  })
+}

+ 53 - 0
src/api/course/rewardRound.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询奖励领取记录列表
+export function listRewardRound(query) {
+  return request({
+    url: '/course/rewardRound/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询奖励领取记录详细
+export function getRewardRound(id) {
+  return request({
+    url: '/course/rewardRound/' + id,
+    method: 'get'
+  })
+}
+
+// 新增奖励领取记录
+export function addRewardRound(data) {
+  return request({
+    url: '/course/rewardRound',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改奖励领取记录
+export function updateRewardRound(data) {
+  return request({
+    url: '/course/rewardRound',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除奖励领取记录
+export function delRewardRound(id) {
+  return request({
+    url: '/course/rewardRound/' + id,
+    method: 'delete'
+  })
+}
+
+// 导出奖励领取记录
+export function exportRewardRound(query) {
+  return request({
+    url: '/course/rewardRound/export',
+    method: 'get',
+    params: query
+  })
+}

+ 187 - 0
src/views/components/his/couponComponent.vue

@@ -0,0 +1,187 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="标题" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          placeholder="请输入标题"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="有效期" prop="limitTime">
+        <el-date-picker v-model="limitTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="change"></el-date-picker>
+      </el-form-item>
+      <el-form-item label="面额" prop="price">
+        <el-input
+          v-model="queryParams.price"
+          placeholder="请输入面额"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" border :data="couponList">
+      <el-table-column label="优惠券名称" align="center" prop="title" width="120px"/>
+      <el-table-column label="面额" align="center" prop="price" />
+      <el-table-column label="折扣" align="center" prop="rate" />
+      <el-table-column label="数量" align="center" prop="number" />
+      <el-table-column label="卷类型 " align="center" prop="couponType" width="120px">
+        <template slot-scope="scope">
+          <dict-tag :options="couponTypeOptions" :value="scope.row.couponType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="最低消费金额可用" align="center" prop="minPrice" />
+      <el-table-column label="剩余数量" align="center" prop="remainNumber" />
+      <el-table-column label="有效期" align="center" prop="limitTime" width="180"/>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="更改时间" align="center" prop="updateTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="150px">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="selectCoupon(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"
+    />
+  </div>
+</template>
+
+<script>
+import { listCoupon } from "@/api/his/coupon";
+export default {
+  name: "CouponComponent",
+  data() {
+    return {
+      limitTime:[],
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 优惠券表格数据
+      couponList: [],
+      statusOptions: [],
+      // 卷类型 1代金券 字典
+      couponTypeOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        title: null,
+        limitTime: null,
+        price: null,
+        number: null,
+        couponType: 6,
+        minPrice: null,
+        remainNumber: null,
+        sTime:null,
+        eTime:null
+      },
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_coupon_type").then(response => {
+      this.couponTypeOptions = response.data;
+    });
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    selectCoupon(row) {
+      this.$emit("select-coupon", {"id": row.couponId, "name": row.title });
+    },
+    /** 查询优惠券列表 */
+    getList() {
+      this.loading = true;
+      listCoupon(this.queryParams).then(response => {
+        this.couponList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        couponId: null,
+        title: null,
+        createTime: null,
+        limitTime: null,
+        price: null,
+        number: null,
+        couponType: null,
+        minPrice: null,
+        remainNumber: null,
+        status:"1",
+        rate:0,
+        limitDay:1,
+        limitCount:1,
+        limitType:null,
+        cateIds:null,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.limitTime=null;
+      this.queryParams.sTime=null;
+      this.queryParams.eTime=null;
+      this.handleQuery();
+    },
+    change(){
+      if(this.limitTime!=null){
+        this.queryParams.sTime=this.limitTime[0];
+        this.queryParams.eTime=this.limitTime[1];
+      }else{
+        this.queryParams.sTime=null;
+        this.queryParams.eTime=null;
+      }
+    },
+  }
+};
+</script>
+<style scoped>
+::v-deep .el-table--scrollable-x .el-table__body-wrapper {
+  height: 70vh;
+}
+</style>

+ 1496 - 0
src/views/course/reward/index.vue

@@ -0,0 +1,1496 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="奖励名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入奖励名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="奖励描述" prop="description">
+        <el-input
+          v-model="queryParams.description"
+          placeholder="请输入奖励描述"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="奖励类型" prop="rewardType">
+        <el-select v-model="queryParams.rewardType" placeholder="请选择奖励类型" clearable size="small">
+          <el-option
+            v-for="item in typeOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['course:reward:add']"
+        >新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="success"
+          plain
+          icon="el-icon-edit"
+          size="mini"
+          :disabled="single"
+          @click="handleUpdate"
+          v-hasPermi="['course:reward:edit']"
+        >修改</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-delete"
+          size="mini"
+          :disabled="multiple"
+          @click="handleDelete"
+          v-hasPermi="['course:reward:remove']"
+        >删除</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:reward:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="rewardList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="主键ID" align="center" prop="id" />
+      <el-table-column label="奖励名称" align="center" prop="name" />
+      <el-table-column label="奖励描述" align="center" prop="description" />
+      <el-table-column label="奖励类型" align="center" prop="rewardType">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.rewardType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="期望值" align="center" prop="expectedValue" />
+      <el-table-column label="实际奖励内容" align="center" prop="actualRewards">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleViewReward(scope.row)"
+          >查看详情</el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['course:reward:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['course:reward:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改奖励配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" :width="width" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="奖励名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入奖励名称" @input="updateRewardItemsName" />
+        </el-form-item>
+        <el-form-item label="奖励类型" prop="rewardType">
+          <el-select v-model="form.rewardType" placeholder="请选择奖励类型" @change="handleRewardTypeChange">
+            <el-option
+              v-for="item in typeOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="期望值" prop="expectedValue">
+          <el-input-number :min="0" :precision="2" :step="1" v-model="form.expectedValue" placeholder="期望值" />
+        </el-form-item>
+        <el-form-item v-if="form.rewardType === '1'" label="宝箱图片">
+          <div class="image-upload-group">
+            <div class="image-upload-item">
+              <span>关闭图片</span>
+              <el-upload
+                v-model="form.closeChestUrl"
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :show-file-list="false"
+                :on-success="handleSuccessClose"
+                :before-upload="beforeUpload">
+                <img v-if="form.closeChestUrl" :src="form.closeChestUrl" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+            </div>
+
+            <div class="image-upload-item">
+              <span>开启图片</span>
+              <el-upload
+                v-model="form.openChestUrl"
+                class="avatar-uploader"
+                :action="uploadUrl"
+                :show-file-list="false"
+                :on-success="handleSuccessOpen"
+                :before-upload="beforeUpload">
+                <img v-if="form.openChestUrl" :src="form.openChestUrl" class="avatar">
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item v-if="form.rewardType === '2'"
+                      label="红包金额" prop="account">
+          <el-input-number v-model="form.account" :min="0" :step="0.1" style="width: 200px;"></el-input-number>
+        </el-form-item>
+        <el-form-item v-if="form.rewardType === '3'"
+                      label="福币数" prop="account">
+          <el-input-number v-model="form.account" :min="0" :step="1" style="width: 200px;"></el-input-number>
+        </el-form-item>
+        <el-form-item v-if="form.rewardType === '1'" label="宝箱奖励" prop="rewardItems">
+          <div class="reward-table-container">
+            <div class="table-header">
+              <span>宝箱奖励配置</span>
+              <el-button type="primary" icon="el-icon-plus" size="mini" @click="addRewardItem">添加奖励项</el-button>
+            </div>
+            <el-table :data="form.rewardItems" border size="small">
+              <el-table-column v-if="false" label="奖励名称" >
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.name" :placeholder="form.name || '请输入奖励名称'"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励模式">
+                <template slot-scope="scope">
+                  <el-select v-model="scope.row.jltype" disabled placeholder="请选择奖励模式" style="width: 100%">
+                    <el-option
+                      v-for="item in jltypeOptions"
+                      :key="item.dictValue"
+                      :label="item.dictLabel"
+                      :value="item.dictValue"
+                    />
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励数量">
+                <template slot-scope="scope">
+                  <el-input-number v-model="scope.row.amount" :min="1" :controls="false" style="width: 100%"></el-input-number>
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" >
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.probability" placeholder="例如: 20%"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column  v-if="false" label="唯一序列号" >
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
+                    <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
+                  </el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" width="80">
+                <template slot-scope="scope">
+                  <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeRewardItem(scope.$index)"></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+
+        <el-form-item v-if="form.rewardType === '4'" label="转盘奖励" prop="rewardItems">
+          <div class="reward-table-container spin-reward">
+            <div class="table-header">
+              <span>转盘奖励配置</span>
+              <el-button type="primary" icon="el-icon-plus" @click="addSpinRewardItem">添加奖励项</el-button>
+            </div>
+            <div class="spin-tips">建议:概率填写为百分比字符串,如 12.5% ,各项之和=100%</div>
+            <el-table :data="form.rewardItems" border stripe>
+              <el-table-column label="图标" align="center" width="72">
+                <template slot-scope="scope">
+                  <el-upload
+                    class="spin-icon-uploader"
+                    :action="uploadUrl"
+                    :show-file-list="false"
+                    :before-upload="beforeUpload"
+                    accept="image/*"
+                    :on-success="(res,file)=>handleSpinIconSuccess(res, scope.row)"
+                  >
+                    <img v-if="scope.row.iconUrl" :src="scope.row.iconUrl" class="spin-icon" />
+                    <i v-else class="el-icon-plus avatar-uploader-icon spin-icon"></i>
+                  </el-upload>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励名称" align="center">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.name" placeholder="请输入奖励名称"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励类型" align="center" width="120">
+                <template slot-scope="scope">
+                  <el-select v-model="scope.row.type" @change="changeType(scope.row)" placeholder="选择类型" style="width: 100%">
+                    <el-option
+                      v-for="opt in spinItemTypeOptions"
+                      :key="opt.dictValue"
+                      :label="opt.dictLabel"
+                      :value="opt.dictValue"
+                    />
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="优惠券" align="center">
+                <template slot-scope="scope">
+                  <el-select
+                    :disabled="scope.row.type !== '4'"
+                    :ref="`customSelect_${scope.row.code}`"
+                    v-model="scope.row.couponId"
+                    placeholder="请选择优惠券"
+                    @click.native.stop="scope.row.type === '4' ? openCouponDrawer(scope.row) : null"
+                    clearable
+                    style="width: 100%;">
+                    <el-option
+                      v-for="item in couponList"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id">
+                    </el-option>
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="商品" align="center">
+                <template slot-scope="scope">
+                  <el-select
+                    :disabled="scope.row.type !== '5'"
+                    :ref="`customSelect_${scope.row.code}`"
+                    v-model="scope.row.goodsId"
+                    placeholder="请选择商品"
+                    @click.native.stop="scope.row.type === '5' ? openGoodsDrawer(scope.row) : null"
+                    clearable
+                    style="width: 100%;">
+                    <el-option
+                      v-for="item in goodsList"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id">
+                    </el-option>
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励数量" align="center" show-overflow-tooltip width="180">
+                <template slot-scope="scope">
+                  <el-input :disabled="scope.row.type === '5'" v-model="scope.row.amount" :controls="false" style="width: 100%"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" align="center" class-name="probability-col" width="120">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.probability" placeholder="例如: 10%"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column v-if="false" label="唯一序列号">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
+                    <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
+                  </el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="80">
+                <template slot-scope="scope">
+                  <el-button type="danger" icon="el-icon-delete" @click="removeRewardItem(scope.$index)"></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+
+        <el-form-item v-if="form.rewardType === '5'" label="保底转盘" prop="rewardItems">
+          <div class="reward-table-container spin-reward">
+            <div class="table-header">
+              <span>保底转盘奖励配置</span>
+              <el-button type="primary" icon="el-icon-plus" @click="addSpinRewardItem2">添加奖励项</el-button>
+            </div>
+            <div class="spin-tips">
+              建议:概率填写为百分比字符串,如 12.5% ,各项之和=100%
+              <br/>
+              保底:保底奖励只能有1个且最后5次才能抽中
+            </div>
+            <el-table :data="form.rewardItems" border stripe>
+              <el-table-column label="图标" align="center" width="72">
+                <template slot-scope="scope">
+                  <el-upload
+                    class="spin-icon-uploader"
+                    :action="uploadUrl"
+                    :show-file-list="false"
+                    :before-upload="beforeUpload"
+                    accept="image/*"
+                    :on-success="(res,file)=>handleSpinIconSuccess(res, scope.row)"
+                  >
+                    <img v-if="scope.row.iconUrl" :src="scope.row.iconUrl" class="spin-icon" />
+                    <i v-else class="el-icon-plus avatar-uploader-icon spin-icon"></i>
+                  </el-upload>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励名称" align="center">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.name" placeholder="请输入奖励名称"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励类型" align="center" width="120">
+                <template slot-scope="scope">
+                  <el-select v-model="scope.row.type" @change="changeType(scope.row)" placeholder="选择类型" style="width: 100%">
+                    <el-option
+                      v-for="opt in spinItemTypeOptions"
+                      :key="opt.dictValue"
+                      :label="opt.dictLabel"
+                      :value="opt.dictValue"
+                    />
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="优惠券" align="center">
+                <template slot-scope="scope">
+                  <el-select
+                    :disabled="scope.row.type !== '4'"
+                    :ref="`customSelect_${scope.row.code}`"
+                    v-model="scope.row.couponId"
+                    placeholder="请选择优惠券"
+                    @click.native.stop="scope.row.type === '4' ? openCouponDrawer(scope.row) : null"
+                    clearable
+                    style="width: 100%;">
+                    <el-option
+                      v-for="item in couponList"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id">
+                    </el-option>
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="商品" align="center">
+                <template slot-scope="scope">
+                  <el-select
+                    :disabled="scope.row.type !== '5'"
+                    :ref="`customSelect_${scope.row.code}`"
+                    v-model="scope.row.goodsId"
+                    placeholder="请选择商品"
+                    @click.native.stop="scope.row.type === '5' ? openGoodsDrawer(scope.row) : null"
+                    clearable
+                    style="width: 100%;">
+                    <el-option
+                      v-for="item in goodsList"
+                      :key="item.id"
+                      :label="item.name"
+                      :value="item.id">
+                    </el-option>
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖励数量" align="center" width="180">
+                <template slot-scope="scope">
+                  <el-input :disabled="scope.row.type === '5'" v-model="scope.row.amount" :controls="false" style="width: 100%"/>
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" align="center" class-name="probability-col" width="120">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.probability" placeholder="例如: 10%"></el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="保底" align="center" width="100">
+                <template slot-scope="scope">
+                  <el-switch
+                    v-model="scope.row.isGuarantee"
+                    active-color="#13ce66">
+                  </el-switch>
+                </template>
+              </el-table-column>
+              <el-table-column v-if="false" label="唯一序列号">
+                <template slot-scope="scope">
+                  <el-input v-model="scope.row.code" placeholder="奖励唯一序列号">
+                    <el-button slot="append" icon="el-icon-refresh" @click="generateUUID(scope.row)"></el-button>
+                  </el-input>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作" align="center" width="80">
+                <template slot-scope="scope">
+                  <el-button type="danger" icon="el-icon-delete" @click="removeRewardItem(scope.$index)"></el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-form-item>
+
+        <el-form-item label="状态" prop="status">
+          <el-radio-group v-model="form.status">
+            <el-radio v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictValue">{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="奖励描述" prop="description">
+          <el-input v-model="form.description" type="textarea" placeholder="请输入奖励描述" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+
+    <el-dialog
+      :title="`奖励详情 - ${currentReward.name || '未知奖励'}`"
+      :visible.sync="detailVisible"
+      width="700px"
+      append-to-body
+    >
+      <div class="reward-detail-container">
+        <!-- 基础信息 -->
+        <el-descriptions :column="2" border class="base-info" :label-style="{ width: '120px' }" :content-style="{ width: '200px' }">
+          <el-descriptions-item label="奖励名称">{{ currentReward.name || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="奖励类型">
+            <dict-tag :options="typeOptions" :value="currentReward.rewardType"/>
+          </el-descriptions-item>
+          <el-descriptions-item label="状态">
+            <dict-tag :options="statusOptions" :value="currentReward.status"/>
+          </el-descriptions-item>
+          <el-descriptions-item label="期望值">{{ currentReward.expectedValue || 0 }}</el-descriptions-item>
+          <el-descriptions-item label="描述" :span="2">{{ currentReward.description || '-' }}</el-descriptions-item>
+        </el-descriptions>
+
+        <!-- 奖励内容详情 -->
+        <div class="reward-content">
+          <h4>奖励内容详情</h4>
+
+          <!-- 宝箱类型奖励 -->
+          <div v-if="currentReward.rewardType === 1" class="chest-reward">
+            <el-table :data="parsedRewardItems" size="small" border stripe>
+              <el-table-column label="奖励模式">
+                <template slot-scope="{row}">
+                  <el-select v-model="row.jltype" placeholder="请选择奖励模式" style="width: 100%" disabled>
+                    <el-option
+                      v-for="item in jltypeOptions"
+                      :key="item.dictValue"
+                      :label="item.dictLabel"
+                      :value="item.dictValue"
+                    />
+                  </el-select>
+                </template>
+              </el-table-column>
+              <el-table-column label="数量" prop="amount"  align="center">
+                <template slot-scope="{row}">
+                  {{ row.amount || 1 }}
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" prop="probability" align="center">
+                <template slot-scope="{row}">
+                  <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
+              暂无奖励配置
+            </div>
+          </div>
+
+          <!-- 红包类型奖励 -->
+          <div v-else-if="currentReward.rewardType === 2" class="redpacket-reward">
+            <div class="reward-amount">
+              <i class="el-icon-money" style="color: #e6a23c; font-size: 24px;"></i>
+              <span class="amount-text">{{ rewardAmount }} 元</span>
+              <el-tag type="warning" size="small">红包奖励</el-tag>
+            </div>
+          </div>
+
+          <!-- 芳华币类型奖励 -->
+          <div v-else-if="currentReward.rewardType === 3" class="points-reward">
+            <div class="reward-amount">
+              <i class="el-icon-coin" style="color: #67c23a; font-size: 24px;"></i>
+              <span class="amount-text">{{ rewardAmount }} 福币</span>
+              <el-tag type="success" size="small">福币奖励</el-tag>
+            </div>
+          </div>
+
+          <!-- 转盘类型奖励 -->
+          <div v-else-if="currentReward.rewardType === 4" class="chest-reward">
+            <el-table :data="parsedRewardItems" size="small" border stripe>
+              <el-table-column label="图标" prop="iconUrl" align="center" width="80">
+                <template slot-scope="{row}">
+                  <el-image
+                    v-if="row.iconUrl"
+                    :src="row.iconUrl"
+                    :preview-src-list="[row.iconUrl]"
+                    fit="cover"
+                    style="width:32px;height:32px;border:1px solid #ebeef5;border-radius:4px;"
+                  />
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖品名称" prop="name" align="center" />
+              <el-table-column label="奖品类型" prop="type" align="center">
+                <template slot-scope="{row}">
+                  <el-tag v-if="row.type" size="small">
+                    {{ getSpinItemTypeLabel(row.type) }}
+                  </el-tag>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="数量" prop="amount" align="center">
+                <template slot-scope="{row}">
+                  <span v-if="row.amount">
+                    {{ row.amount }}
+                  </span>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" prop="probability" align="center">
+                <template slot-scope="{row}">
+                  <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
+              暂无奖励配置
+            </div>
+          </div>
+
+          <!-- 保底转盘类型奖励 -->
+          <div v-else-if="currentReward.rewardType === 5" class="chest-reward">
+            <el-table :data="parsedRewardItems" size="small" border stripe>
+              <el-table-column label="图标" prop="iconUrl" align="center" width="80">
+                <template slot-scope="{row}">
+                  <el-image
+                    v-if="row.iconUrl"
+                    :src="row.iconUrl"
+                    :preview-src-list="[row.iconUrl]"
+                    fit="cover"
+                    style="width:32px;height:32px;border:1px solid #ebeef5;border-radius:4px;"
+                  />
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="奖品名称" prop="name" align="center" />
+              <el-table-column label="奖品类型" prop="type" align="center">
+                <template slot-scope="{row}">
+                  <el-tag v-if="row.type" size="small">
+                    {{ getSpinItemTypeLabel(row.type) }}
+                  </el-tag>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="数量" prop="amount" align="center">
+                <template slot-scope="{row}">
+                  <span v-if="row.amount">
+                    {{ row.amount }}
+                  </span>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="概率" prop="probability" align="center">
+                <template slot-scope="{row}">
+                  <el-tag v-if="row.probability" size="small">{{ row.probability }}</el-tag>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column label="保底" prop="isGuarantee" align="center">
+                <template slot-scope="{row}">
+                  <el-tag size="small" :type="row.isGuarantee ? 'success' : 'info'">{{ row.isGuarantee ? '是' : '否' }}</el-tag>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div v-if="!parsedRewardItems || parsedRewardItems.length === 0" class="empty-tip">
+              暂无奖励配置
+            </div>
+          </div>
+
+          <!-- 未知类型 -->
+          <div v-else class="unknown-reward">
+            <el-alert type="info" title="未知奖励类型" :closable="false"></el-alert>
+          </div>
+        </div>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailVisible = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 选择优惠券 -->
+    <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="couponDrawerOpen">
+      <coupon-component @select-coupon="selectCoupon"></coupon-component>
+    </el-drawer>
+
+<!--    &lt;!&ndash; 选择商品 &ndash;&gt;-->
+<!--    <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="goodsDrawerOpen">-->
+<!--      <RewardGoodsComponent @select-goods="selectGoods"></RewardGoodsComponent>-->
+<!--    </el-drawer>-->
+
+  </div>
+</template>
+
+<script>
+import { listReward, getReward, delReward, addReward, updateReward, exportReward } from "@/api/course/reward";
+import CouponComponent from '@/views/components/his/couponComponent.vue'
+// import RewardGoodsComponent from '@/views/components/course/rewardGoodsComponent.vue'
+import {Loading} from "element-ui";
+import { getByIds } from '@/api/his/coupon'
+// import { getGoodsByIds } from '@/api/reward/rewardGoods'
+
+export default {
+  name: "Reward",
+  components: {
+    CouponComponent
+  },
+  data() {
+    return {
+      finalQuality:1,
+      uploadUrl: process.env.VUE_APP_BASE_API+"/common/uploadOSS",
+      detailVisible: false,
+      currentReward: {},
+      activeCollapse: [],
+      jltypeOptions: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      rewardList: [],
+      typeOptions: [],
+      // 转盘奖励-奖励类型选项
+      spinItemTypeOptions: [],
+      statusOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      width: '1200px',
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        name: null,
+        description: null,
+        rewardType: null,
+        status: null,
+        expectedValue: null,
+        createId: null,
+        actualRewards: null
+      },
+      // 已选中优惠券列表
+      couponList: [],
+      couponDrawerOpen: false,
+      currentRow: null,
+      // 已选中商品列表
+      goodsList: [],
+      goodsDrawerOpen: false,
+      // 表单参数
+      form: {
+        id: null,
+        name: null,
+        description: null,
+        rewardType: null,
+        status: "1",
+        expectedValue: 0,
+        account: 0,
+        rewardItems: [],
+        openChestUrl:"",
+        closeChestUrl:"",
+
+      },
+      // 表单校验
+      rules: {
+        name: [
+          { required: true, message: "奖励名称不能为空", trigger: "blur" }
+        ],
+        rewardType: [
+          { required: true, message: "奖励类型不能为空", trigger: "change" }
+        ],
+        status: [
+          { required: true, message: "状态 不能为空", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  computed: {
+    // 解析奖励项数据
+    parsedRewardItems() {
+      if (!this.currentReward.actualRewards) return null;
+      try {
+        const parsed = JSON.parse(this.currentReward.actualRewards);
+        return Array.isArray(parsed) ? parsed : null;
+      } catch (e) {
+        console.error("解析奖励内容失败", e);
+        return null;
+      }
+    },
+
+    // 获取红包或芳华币金额
+    rewardAmount() {
+      if (!this.currentReward.actualRewards) return 0;
+
+      try {
+        const parsed = JSON.parse(this.currentReward.actualRewards);
+        if (this.currentReward.rewardType === 2) {
+          return parsed.amount || parsed.money || 0;
+        } else if (this.currentReward.rewardType === 3) {
+          return parsed.points || parsed.score || 0;
+        }
+        return 0;
+      } catch (e) {
+        return 0;
+      }
+    }
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_reward_type").then((response) => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_user_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    this.getDicts("sys_reward_spin_type").then((response) => {
+      this.jltypeOptions = response.data;
+    });
+    this.getDicts("spin_reward_type").then((response) => {
+      this.spinItemTypeOptions = response.data;
+    });
+  },
+  methods: {
+    changeType(row) {
+      row.amount = null
+      row.couponId = null
+      row.goodsId = null
+      if (row.type === '5') {
+        row.amount = 1
+      }
+    },
+    openCouponDrawer(row) {
+      this.$nextTick(() => {
+        this.$refs[`customSelect_${row.code}`]?.blur?.()
+      });
+      this.currentRow = row
+      this.couponDrawerOpen = true
+    },
+    selectCoupon(coupon) {
+      this.currentRow.couponId = coupon.id
+      if (!this.couponList.some(item => item.id === coupon.id)) {
+        this.couponList.push(coupon)
+      }
+      this.couponDrawerOpen = false
+    },
+    openGoodsDrawer(row) {
+      this.$nextTick(() => {
+        this.$refs[`customSelect_${row.code}`]?.blur?.()
+      });
+      this.currentRow = row
+      this.goodsDrawerOpen = true
+    },
+    selectGoods(goods) {
+      this.currentRow.goodsId = goods.id
+      if (!this.goodsList.some(item => item.id === goods.id)) {
+        this.goodsList.push(goods)
+      }
+      this.goodsDrawerOpen = false
+    },
+    handleSuccessOpen(res, file) {
+      if(res.code==200){
+        this.form.openChestUrl = res.url;
+      }
+      else{
+        this.msgError(res.msg);
+      }
+    },
+    handleSuccessClose(res, file) {
+      if(res.code==200){
+        this.form.closeChestUrl = res.url;
+      }
+      else{
+        this.msgError(res.msg);
+      }
+      console.log(this.form.closeChestUrl)
+    },
+    beforeUpload(file) {
+      const isPic =
+        file.type === 'image/jpeg' ||
+        file.type === 'image/png' ||
+        file.type === 'image/gif' ||
+        file.type === 'image/jpg'
+      // const isLt2M = file.size / 1024 / 1024 < 2
+      if (!isPic) {
+        this.$message.error('上传图片只能是 JPG、JPEG、PNG、GIF 格式!')
+        return false
+      }
+      return new Promise((resolve, reject) => {
+        if (file.size / 1024 / 1024 > 3) {
+          this.$message.error('上传的图片不能超过3MB');
+          reject();
+          return;
+        }
+        if (file.size / 1024 / 1024 > 1) {
+          const loadingInstance = Loading.service({ text: '图片内存过大正在压缩图片...' });
+          // 文件大于1MB时进行压缩
+          this.compressImage(file).then((compressedFile) => {
+            loadingInstance.close();
+            if (compressedFile.size / 1024 > 500) {
+              this.$message.error('图片压缩后仍大于500KB');
+              reject();
+            } else {
+              // this.$message.success(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
+              console.log(`图片压缩成功,最终质量为: ${this.finalQuality.toFixed(2)}`);
+              console.log(`最终内存大小为: ${(compressedFile.size/1024).toFixed(2)}KB`);
+              resolve(compressedFile);
+            }
+          }).catch((err) => {
+            loadingInstance.close();
+            console.error(err);
+            reject();
+          });
+        } else {
+          resolve(file);
+        }
+      });
+    },
+    compressImage(file) {
+      return new Promise((resolve, reject) => {
+        const reader = new FileReader();
+        reader.readAsDataURL(file);
+        reader.onload = (event) => {
+          const img = new Image();
+          img.src = event.target.result;
+          img.onload = () => {
+            const canvas = document.createElement('canvas');
+            const ctx = canvas.getContext('2d');
+            const width = img.width;
+            const height = img.height;
+            canvas.width = width;
+            canvas.height = height;
+            ctx.drawImage(img, 0, 0, width, height);
+
+            let quality = 1; // 初始压缩质量
+            let dataURL = canvas.toDataURL('image/jpeg', quality);
+
+            // 逐步压缩,直到图片大小小于500KB并且压缩质量不再降低
+            while (dataURL.length / 1024 > 500 && quality > 0.1) {
+              quality -= 0.01;
+              dataURL = canvas.toDataURL('image/jpeg', quality);
+            }
+            this.finalQuality = quality; // 存储最终的压缩质量
+
+            if (dataURL.length / 1024 > 500) {
+              reject(new Error('压缩后图片仍然大于500KB'));
+              return;
+            }
+
+            const arr = dataURL.split(',');
+            const mime = arr[0].match(/:(.*?);/)[1];
+            const bstr = atob(arr[1]);
+            let n = bstr.length;
+            const u8arr = new Uint8Array(n);
+            while (n--) {
+              u8arr[n] = bstr.charCodeAt(n);
+            }
+            const compressedFile = new Blob([u8arr], { type: mime });
+            compressedFile.name = file.name;
+            resolve(compressedFile);
+          };
+          img.onerror = (error) => {
+            reject(error);
+          };
+        };
+        reader.onerror = (error) => {
+          reject(error);
+        };
+      });
+    },
+    // 查看奖励详情
+    handleViewReward(row) {
+      this.currentReward = { ...row };
+      this.detailVisible = true;
+    },
+
+    // 格式化JSON显示
+    formatJson(json) {
+      if (!json) return '无数据';
+      try {
+        const parsed = JSON.parse(json);
+        return JSON.stringify(parsed, null, 2);
+      } catch (e) {
+        return json;
+      }
+    },
+    /** 查询奖励配置列表 */
+    getList() {
+      this.loading = true;
+      listReward(this.queryParams).then(response => {
+        this.rewardList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        name: null,
+        description: null,
+        rewardType: null,
+        status: "1",
+        expectedValue: 0,
+        account: 0,
+        openChestUrl: "",
+        closeChestUrl:"",
+        rewardItems: []
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加奖励配置";
+      // 若选择转盘类型时,默认添加一条配置
+      if (this.form.rewardType === '4') {
+        this.ensureSpinDefaultItem();
+      }
+      if (this.form.rewardType === '5') {
+        this.ensureSpinDefaultItem2();
+      }
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids
+      getReward(id).then(response => {
+        this.form = response.data;
+        this.form.status = response.data.status.toString();
+        this.form.rewardType = response.data.rewardType.toString();
+
+        // 解析actualRewards
+        try {
+          const actualRewards = JSON.parse(response.data.actualRewards || "[]");
+          if (this.form.rewardType === '1' || this.form.rewardType === '4' || this.form.rewardType === '5') {
+            // 宝箱类型
+            this.form.rewardItems = actualRewards.map(item => ({
+              ...item,
+              couponId: item.couponId || null,
+            }));
+
+            this.initCouponList(actualRewards.filter(item => item.couponId).map(item => item.couponId).join(","))
+            this.initGoodsList(actualRewards.filter(item => item.goodsId).map(item => item.goodsId).join(","))
+          } else if (this.form.rewardType === '2') {
+            // 红包类型
+            this.form.account = actualRewards.amount || 0;
+          } else if (this.form.rewardType === '3') {
+            // 芳华币类型
+            this.form.account = actualRewards.points || 0;
+          }
+        } catch (e) {
+          this.form.rewardItems = [];
+          this.form.account = 0;
+        }
+
+        this.open = true;
+        this.title = "修改奖励配置";
+      });
+    },
+    initCouponList(ids) {
+      if (!ids) {
+        return
+      }
+      getByIds({ids}).then(response => {
+        const {data} = response
+        data.forEach(d => {
+          if (!this.couponList.some(item => item.id === d.couponId)) {
+            this.couponList.push({"id": d.couponId, "name": d.title})
+          }
+        })
+      })
+    },
+    initGoodsList(ids) {
+      if (!ids) {
+        return
+      }
+      getGoodsByIds({ids}).then(response => {
+        const {data} = response
+        data.forEach(g => {
+          if (!this.goodsList.some(item => item.id === g.goodsId)) {
+            this.goodsList.push({"id": g.goodsId, "name": g.goodsName})
+          }
+        })
+      })
+    },
+    // 奖励类型变化处理
+    handleRewardTypeChange(value) {
+      // 清空相关数据
+      this.form.account = 0;
+      this.form.rewardItems = [];
+      this.form.expectedValue = 0
+      // 切换为转盘类型时,默认加入一条配置
+      if (value === '4') {
+        this.ensureSpinDefaultItem();
+      }
+      if (value === '5') {
+        this.ensureSpinDefaultItem2();
+      }
+    },
+    // 更新奖励项名称(当表单名称变化时)
+    updateRewardItemsName() {
+      if (this.form.rewardType === '1' && this.form.rewardItems.length > 0) {
+        // 可选:当表单名称变化时,自动更新所有奖励项的名称
+        // this.form.rewardItems.forEach(item => {
+        //   item.name = this.form.name;
+        // });
+      }
+    },
+    // 添加奖励项
+    addRewardItem() {
+      this.form.rewardItems.push({
+        type: '1',
+        jltype: '1',
+        name: this.form.name || '', // 默认使用表单中的奖励名称
+        couponId: null,
+        amount: 1,
+        probability: '',
+        code: this.generateUUID()
+      });
+    },
+    // 转盘奖励:添加奖励项(包含奖励类型选择)
+    addSpinRewardItem() {
+      this.form.rewardItems.push({
+        type: '2',
+        iconUrl: '',
+        name: '',
+        couponId: null,
+        goodsId: null,
+        amount: '',
+        probability: '',
+        code: this.generateUUID()
+      });
+    },
+    addSpinRewardItem2() {
+      this.form.rewardItems.push({
+        type: '2',
+        isGuarantee: 0,
+        iconUrl: '',
+        name: '',
+        couponId: null,
+        amount: '',
+        probability: '',
+        code: this.generateUUID()
+      });
+    },
+    // 转盘奖励:图标上传成功
+    handleSpinIconSuccess(res, row) {
+      if (res && res.code === 200 && res.url) {
+        this.$set(row, 'iconUrl', res.url);
+      } else {
+        this.msgError(res && res.msg ? res.msg : '图片上传失败');
+      }
+    },
+    // 转盘奖励:确保有一条默认项
+    ensureSpinDefaultItem() {
+      if (!Array.isArray(this.form.rewardItems)) this.form.rewardItems = [];
+      if (this.form.rewardItems.length === 0) {
+        this.addSpinRewardItem();
+      }
+    },
+    ensureSpinDefaultItem2() {
+      if (!Array.isArray(this.form.rewardItems)) this.form.rewardItems = [];
+      if (this.form.rewardItems.length === 0) {
+        this.addSpinRewardItem2();
+      }
+    },
+    // 移除奖励项
+    removeRewardItem(index) {
+      this.form.rewardItems.splice(index, 1);
+    },
+    // 生成UUID
+    generateUUID(item) {
+      if (item) {
+        item.code = this.generateUUIDString();
+      } else {
+        return this.generateUUIDString();
+      }
+    },
+    // 生成UUID字符串
+    generateUUIDString() {
+      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+        var r = Math.random() * 16 | 0,
+          v = c == 'x' ? r : (r & 0x3 | 0x8);
+        return v.toString(16);
+      });
+    },
+    // 获取转盘奖励类型标签
+    getSpinItemTypeLabel(type) {
+      const option = this.spinItemTypeOptions.find(item => item.dictValue === type.toString());
+      return option ? option.dictLabel : type;
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          let actualRewards = "";
+          if (this.form.rewardType === '1' || this.form.rewardType === '4' || this.form.rewardType === '5') {
+            // 宝箱类型,将rewardItems转为JSON
+            actualRewards = JSON.stringify(this.form.rewardItems);
+          } else if (this.form.rewardType === '2') {
+            // 红包类型
+            actualRewards = JSON.stringify({ amount: this.form.account });
+          } else if (this.form.rewardType === '3') {
+            // 芳华币类型
+            actualRewards = JSON.stringify({ points: this.form.account });
+          }
+
+          // 设置actualRewards
+          this.form.actualRewards = actualRewards;
+
+          if (this.form.id != null) {
+            updateReward(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addReward(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除奖励配置编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delReward(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有奖励配置数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(() => {
+        this.exportLoading = true;
+        return exportReward(queryParams);
+      }).then(response => {
+        this.download(response.msg);
+        this.exportLoading = false;
+      }).catch(() => {});
+    }
+  }
+};
+</script>
+
+<style scoped>
+.spin-reward .el-table th,
+.spin-reward .el-table td {
+  padding: 6px 8px;
+}
+
+.spin-reward .probability-col .el-input__inner {
+  text-align: center;
+}
+
+.spin-tips {
+  padding: 8px 12px;
+  color: #909399;
+  font-size: 12px;
+  background-color: #fafafa;
+  border-top: 1px solid #ebeef5;
+  border-bottom: 1px solid #ebeef5;
+}
+.avatar-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 6px;
+  cursor: pointer;
+  position: relative;
+  overflow: hidden;
+}
+.avatar-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 178px;
+  height: 178px;
+  line-height: 178px;
+  text-align: center;
+}
+.avatar {
+  width: 120px;
+  height: 120px;
+  display: block;
+}
+.spin-icon-uploader {
+  display: inline-block;
+}
+.spin-icon {
+  width: 32px;
+  height: 32px;
+  object-fit: cover;
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+}
+.spin-icon-uploader .el-upload {
+  border: 1px dashed #d9d9d9;
+  border-radius: 4px;
+  width: 32px;
+  height: 32px;
+  line-height: 32px;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+}
+.spin-icon-uploader .el-upload:hover {
+  border-color: #409EFF;
+}
+/* 覆盖全局 .avatar-uploader-icon 的大尺寸,限定在转盘上传器内为 32x32 */
+.spin-icon-uploader .avatar-uploader-icon {
+  width: 32px !important;
+  height: 32px !important;
+  line-height: 32px !important;
+  font-size: 16px !important;
+  color: #8c939d;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+}
+.reward-table-container {
+  border: 1px solid #ebeef5;
+  border-radius: 4px;
+}
+
+.table-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 15px;
+  background-color: #f5f7fa;
+  border-bottom: 1px solid #ebeef5;
+}
+
+.table-tips {
+  padding: 10px 15px;
+  color: #909399;
+  font-size: 12px;
+  background-color: #f5f7fa;
+}
+.reward-detail-container {
+  max-height: 60vh;
+  overflow-y: auto;
+}
+
+.base-info {
+  margin-bottom: 20px;
+  width: 100%;
+  table-layout: fixed;
+}
+
+.base-info .el-descriptions__label {
+  width: 120px !important;
+  min-width: 120px;
+}
+
+.base-info .el-descriptions__content {
+  width: 200px !important;
+  min-width: 200px;
+}
+
+.reward-content {
+  margin: 20px 0;
+}
+
+.reward-content h4 {
+  margin-bottom: 15px;
+  color: #303133;
+  font-weight: 600;
+}
+
+.chest-reward,
+.redpacket-reward,
+.points-reward {
+  margin: 15px 0;
+}
+
+.reward-amount {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 15px;
+  background: #f8f9fa;
+  border-radius: 4px;
+}
+
+.amount-text {
+  font-size: 18px;
+  font-weight: bold;
+  color: #409EFF;
+}
+
+.code-text {
+  font-family: monospace;
+  font-size: 12px;
+}
+
+.empty-tip {
+  text-align: center;
+  color: #909399;
+  padding: 20px;
+}
+
+.json-pre {
+  background: #f5f7fa;
+  padding: 12px;
+  border-radius: 4px;
+  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+  font-size: 12px;
+  line-height: 1.5;
+  overflow: auto;
+  max-height: 200px;
+  margin: 0;
+}
+
+.raw-data {
+  margin-top: 20px;
+}
+/* 新增样式 */
+.image-upload-group {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20px;
+}
+
+.image-upload-item {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.image-preview {
+  margin-top: 10px;
+  border: 1px dashed #d9d9d9;
+  padding: 10px;
+  border-radius: 4px;
+}
+
+.image-actions {
+  margin-top: 10px;
+  display: flex;
+  gap: 10px;
+}
+
+.chest-images-preview {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.chest-images-section {
+  margin: 20px 0;
+}
+
+.chest-images-display {
+  display: flex;
+  gap: 30px;
+  justify-content: center;
+  margin-top: 15px;
+}
+
+.chest-image-item {
+  text-align: center;
+}
+
+.chest-image-item p {
+  margin-bottom: 8px;
+  font-weight: bold;
+}
+</style>

+ 232 - 0
src/views/course/rewardRound/index.vue

@@ -0,0 +1,232 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="奖励类型" prop="rewardType">
+        <el-select v-model="queryParams.rewardType" placeholder="请选择奖励类型" clearable size="small">
+          <el-option
+            v-for="item in typeOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+
+      <el-form-item label="奖励状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择奖励状态" clearable size="small">
+          <el-option
+            v-for="item in statusOptions"
+            :key="item.dictValue"
+            :label="item.dictLabel"
+            :value="item.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="销售公司" prop="companyId">
+        <el-select v-model="queryParams.companyId"
+                   placeholder="请选择推送公司"
+                   size="small">
+          <!-- 公司选项 -->
+          <el-option
+            v-for="item in companyList"
+            :key="item.companyId"
+            :label="item.companyName"
+            :value="item.companyId"
+          />
+        </el-select>
+      </el-form-item>
+
+      <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="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="warning"
+          plain
+          icon="el-icon-download"
+          size="mini"
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['course:rewardRound:export']"
+        >导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table border v-loading="loading" :data="rewardRoundList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="领取用户" align="center" prop="userIdByName" />
+      <el-table-column label="奖励类型" align="center" prop="rewardType">
+        <template slot-scope="scope">
+          <dict-tag :options="typeOptions" :value="scope.row.rewardType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="公司" align="center" prop="companyName" />
+      <el-table-column label="实际领取到的奖励" align="center" prop="actualRewards" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="奖励状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+<!--      <el-table-column label="看课记录id" align="center" prop="watchId" />-->
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+  </div>
+</template>
+
+<script>
+import { listRewardRound, getRewardRound, delRewardRound, addRewardRound, updateRewardRound, exportRewardRound } from "@/api/course/rewardRound";
+import {getCompanyList} from "@/api/company/company";
+
+export default {
+  name: "RewardRound",
+  data() {
+    return {
+      dateRange:[],
+      companyList:[],
+      statusOptions: [],
+      typeOptions: [],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 奖励领取记录表格数据
+      rewardRoundList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        rewardId: null,
+        rewardConfigId: null,
+        userId: null,
+        rewardType: null,
+        companyId: null,
+        actualRewards: null,
+        receiveTime: null,
+        createId: null,
+        status: null,
+        courseId: null,
+        watchId: null
+      },
+      // 表单参数
+      form: {},
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_reward_type").then((response) => {
+      this.typeOptions = response.data;
+    });
+    this.getDicts("sys_reward_status").then((response) => {
+      this.statusOptions = response.data;
+    });
+    getCompanyList().then(response => {
+      this.companyList = response.data;
+    });
+  },
+  methods: {
+    /** 查询奖励领取记录列表 */
+    getList() {
+      this.loading = true;
+      listRewardRound(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
+        this.rewardRoundList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        rewardId: null,
+        rewardConfigId: null,
+        userId: null,
+        rewardType: null,
+        companyId: null,
+        actualRewards: null,
+        receiveTime: null,
+        createId: null,
+        createTime: null,
+        updateTime: null,
+        status: 0,
+        courseId: null,
+        watchId: 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.id)
+      this.single = selection.length!==1
+      this.multiple = !selection.length
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      const queryParams = this.queryParams;
+      this.$confirm('是否确认导出所有奖励领取记录数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.exportLoading = true;
+          return exportRewardRound(queryParams);
+        }).then(response => {
+          this.download(response.msg);
+          this.exportLoading = false;
+        }).catch(() => {});
+    }
+  }
+};
+</script>

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

@@ -953,6 +953,12 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="舌诊appkey" prop="tongueAppKey">
+            <el-input v-model="form13.tongueAppKey" label="请输入舌诊密钥"></el-input>
+          </el-form-item>
+          <el-form-item   label="App推送通知地址" prop="appPushUrl">
+            <el-input   v-model="form13.appPushUrl"  label="请输入App推送通知地址"></el-input>
+          </el-form-item>
           <el-form-item label="erpAppKey" v-if="form13.erpOpen == 1 && form13.erpType == 1 " prop="erpAppKey">
             <el-input v-model="form13.erpAppKey" label="请输入erpAppKey"></el-input>
           </el-form-item>