Sfoglia il codice sorgente

红德堂-打卡活动转盘抽奖及积分商品免单券相关代码

Long 1 giorno fa
parent
commit
f55d8b0b94

+ 2 - 2
.env.development

@@ -19,11 +19,11 @@ VUE_APP_OBS_SERVER = https://obs.cn-north-4.myhuaweicloud.com
 # 存储桶配置
 VUE_APP_OBS_BUCKET = xfk-hw079058881
 # 存储桶配置
-VUE_APP_COS_BUCKET = xfk-1323137866
+VUE_APP_COS_BUCKET = hylj-1323137866
 # 存储桶配置
 VUE_APP_COS_REGION = ap-chongqing
 # 线路一地址
-VUE_APP_VIDEO_LINE_1 = https://xfktcpv.ylrzcloud.com
+VUE_APP_VIDEO_LINE_1 = https://tcpv.ylrzcloud.com
 # 线路二地址
 VUE_APP_VIDEO_LINE_2 = https://xfkobs.ylrztop.com
 #火山云视频地址域名

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

@@ -0,0 +1,66 @@
+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
+  })
+}
+export function listByIds(ids) {
+  return request({
+    url: '/course/reward/listByIds/' + ids,
+    method: 'get',
+  })
+}
+//获取客服列表
+export function getAppCustomerList() {
+  return request({
+    url: '/course/reward/getAppCustomerList' ,
+    method: 'get',
+  })
+}

+ 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
+  })
+}

+ 62 - 0
src/api/his/checkinActivity.js

@@ -0,0 +1,62 @@
+import request from '@/utils/request'
+
+// 查询看课打卡活动列表
+export function listCheckinActivity(query) {
+  return request({
+    url: '/course/checkinActivity/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询看课打卡活动详细
+export function getCheckinActivity(activityId) {
+  return request({
+    url: '/course/checkinActivity/' + activityId,
+    method: 'get'
+  })
+}
+
+// 新增看课打卡活动
+export function addCheckinActivity(data) {
+  return request({
+    url: '/course/checkinActivity/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改看课打卡活动
+export function updateCheckinActivity(data) {
+  return request({
+    url: '/course/checkinActivity/edit',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除看课打卡活动
+export function delCheckinActivity(activityId) {
+  return request({
+    url: '/course/checkinActivity/' + activityId,
+    method: 'delete'
+  })
+}
+
+// 复制活动
+export function copyCheckinActivity(data) {
+  return request({
+    url: '/course/checkinActivity/copy',
+    method: 'post',
+    data: data
+  })
+}
+
+// 分页查询活动的领取记录
+export function getPrizeRecord(query) {
+  return request({
+    url: '/course/checkinActivity/prizeRecord',
+    method: 'get',
+    params: query
+  })
+}

+ 9 - 0
src/api/his/coupon.js

@@ -57,3 +57,12 @@ export function exportCoupon(query) {
     params: query
   })
 }
+
+// 根据ids查询优惠券列表
+export function getByIds(query) {
+  return request({
+    url: '/his/coupon/getByIds',
+    method: 'get',
+    params: query
+  })
+}

+ 44 - 0
src/api/reward/rewardGoods.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询奖励商品列表
+export const listRewardGoods = (query) => request({
+  url: '/reward/rewardGoods/list',
+  method: 'get',
+  params: query
+})
+
+// 查询奖励商品详情
+export const getRewardGoods = (id) => request({
+  url: '/reward/rewardGoods/' + id,
+  method: 'get'
+})
+
+// 新增奖励商品
+export const addRewardGoods = (data) => request({
+  url: '/reward/rewardGoods',
+  method: 'post',
+  data: data
+})
+
+// 修改奖励商品
+export const updateRewardGoods = (data) => request({
+  url: '/reward/rewardGoods',
+  method: 'put',
+  data: data
+})
+
+// 删除奖励商品
+export const delRewardGoods = (id) => request({
+  url: '/reward/rewardGoods/' + id,
+  method: 'delete'
+})
+
+// 根据ids查询列表
+export function getGoodsByIds(query) {
+  return request({
+    url: '/reward/rewardGoods/getByIds',
+    method: 'get',
+    params: query
+  })
+}
+

+ 44 - 0
src/api/reward/rewardGoodsOrder.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 查询奖励商品订单列表
+export const listRewardGoodsOrder = (query) => request({
+  url: '/reward/rewardGoodsOrder/list',
+  method: 'get',
+  params: query
+})
+
+// 查询奖励商品订单详情
+export const getRewardGoodsOrder = (id) => request({
+  url: '/reward/rewardGoodsOrder/' + id,
+  method: 'get'
+})
+
+// 查询支付信息
+export const getRewardGoodsOrderPayInfo = (orderSn) => request({
+  url: '/reward/rewardGoodsOrder/payment/' + orderSn,
+  method: 'get'
+})
+
+// 查询物流信息
+export const getExpress = (orderId) => request({
+  url: '/reward/rewardGoodsOrder/getExpress/' + orderId,
+  method: 'get'
+})
+
+// 取消订单
+export const cancelOrder = (orderId) => request({
+  url: '/reward/rewardGoodsOrder/cancelOrder/' + orderId,
+  method: 'put'
+})
+
+// 查询收货地址
+export const getAddress = (orderId) => request({
+  url: '/reward/rewardGoodsOrder/queryAddress/' + orderId,
+  method: 'get'
+})
+
+// 查询收货电话
+export const getPhone = (orderId) => request({
+  url: '/reward/rewardGoodsOrder/queryPhone/' + orderId,
+  method: 'get'
+})

+ 3 - 0
src/components/Editor/wang.vue

@@ -16,6 +16,9 @@ export default {
       uploadedImageCount: 0  // 记录已上传的图片数量
     }
   },
+  mounted() {
+    this.initEditor();
+  },
   beforeDestroy() {
     if (this.editor != null) {
       this.editor.destroy()

+ 625 - 0
src/views/components/course/rewardComponent.vue

@@ -0,0 +1,625 @@
+<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>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table 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"
+            @click="selectReward(scope.row)"
+          >选择
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <el-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 || currentReward.rewardType === 6" 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>
+  </div>
+</template>
+
+<script>
+import { listReward } from "@/api/course/reward";
+
+export default {
+  name: "Reward",
+  props: {
+    rewardType: null
+  },
+  data() {
+    return {
+      finalQuality:1,
+      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: this.rewardType,
+        status: 1,
+      },
+    };
+  },
+  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: {
+    selectReward(row) {
+      this.$emit("select-reward", {"id": row.id, "name": row.name });
+    },
+    changeType(row) {
+      row.amount = null
+      row.couponId = null
+      row.goodsId = null
+      if (row.type === '5') {
+        row.amount = 1
+      }
+    },
+    // 查看奖励详情
+    handleViewReward(row) {
+      this.currentReward = { ...row };
+      this.detailVisible = true;
+    },
+    /** 查询奖励配置列表 */
+    getList() {
+      this.loading = true;
+      listReward(this.queryParams).then(response => {
+        this.rewardList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    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
+    },
+    // 获取转盘奖励类型标签
+    getSpinItemTypeLabel(type) {
+      const option = this.spinItemTypeOptions.find(item => item.dictValue === type.toString());
+      return option ? option.dictLabel : type;
+    },
+  }
+};
+</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>

+ 205 - 0
src/views/components/course/rewardGoodsComponent.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="商品ID" prop="goodsId">
+        <el-input
+          v-model="queryParams.goodsId"
+          placeholder="请输入商品ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="商品名称" prop="goodsName">
+        <el-input
+          v-model="queryParams.goodsName"
+          placeholder="请输入商品名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="所属店铺" prop="storeId">
+        <el-select v-model="queryParams.storeId" placeholder="请选择店铺" clearable size="small">
+          <el-option
+            v-for="dict in storeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="商品编码" prop="storeGoodsSn">
+        <el-input
+          v-model="queryParams.storeGoodsSn"
+          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-table v-loading="loading" border :data="goodsList">
+      <el-table-column label="商品ID" align="center" prop="goodsId" />
+      <el-table-column label="商品图片" align="center" prop="goodsImg" width="120">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.goodsImg" width="100">
+            <img :src="scope.row.goodsImg" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="商品名称" align="center" prop="goodsName" show-overflow-tooltip />
+      <el-table-column label="所属店铺" align="center" prop="storeName" />
+      <el-table-column label="商品编码" align="center" prop="storeGoodsSn" />
+      <el-table-column label="原价" align="center" prop="opPrice" />
+      <el-table-column label="单价" align="center" prop="price" />
+      <el-table-column label="库存" align="center" prop="stock" />
+      <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="sort" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="selectGoods(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 { listRewardGoods } from "@/api/reward/rewardGoods";
+import { listStore } from '@/api/his/storeProduct'
+
+export default {
+  name: "RewardGoods",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 奖励商品表格数据
+      goodsList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 图片数组
+      imageArr: [],
+      // 轮播图
+      photoArr: [],
+      // 店铺选项
+      storeOptions: [],
+      // 状态选项
+      statusOptions: [],
+      activeName: '1',
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        goodsId: null,
+        goodsName: null,
+        storeId: null,
+        storeGoodsSn: null,
+        status: 1
+      },
+    };
+  },
+  created() {
+    this.getStoreList();
+    this.getList();
+    this.getDicts("sys_spec_show").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    selectGoods(row) {
+      this.$emit("select-goods", {"id": row.goodsId, "name": row.goodsName });
+    },
+    getStoreList() {
+      listStore().then(response => {
+        const {rows} = response
+        this.storeOptions = rows
+      });
+    },
+    /** 查询奖励商品列表 */
+    getList() {
+      this.loading = true;
+      listRewardGoods(this.queryParams).then(response => {
+        this.goodsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+  }
+};
+</script>
+
+<style scoped>
+.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: 150px;
+  height: 150px;
+  line-height: 150px;
+  text-align: center;
+}
+</style>

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

@@ -0,0 +1,197 @@
+<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",
+  props: {
+    couponType: null
+  },
+  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: this.couponType,
+        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;
+    });
+  },
+  watch: {
+    couponType(newVal) {
+      this.queryParams.couponType = newVal;
+      this.queryParams.pageNum = 1;
+      this.getList();
+    }
+  },
+  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>

+ 79 - 0
src/views/components/his/couponDetails.vue

@@ -15,8 +15,64 @@
           <el-descriptions-item label="有效期"><span v-if="item!=null">{{item.limitTime}}</span></el-descriptions-item>
           <el-descriptions-item label="状态"><span v-if="item!=null"> <dict-tag :options="statusOptions" :value="item.status"/></span></el-descriptions-item>
           <el-descriptions-item label="卷类型"><span v-if="item!=null"> <dict-tag :options="couponTypeOptions" :value="item.couponType"/></span></el-descriptions-item>
+          <el-descriptions-item label="logo" v-if="item.logo">
+            <template>
+              <el-popover
+                placement="right"
+                title=""
+                trigger="hover">
+                <img slot="reference" :src="item.logo" width="50">
+                <img :src="item.logo" style="max-width: 150px;">
+              </el-popover>
+            </template>
+          </el-descriptions-item>
+          <el-descriptions-item label="项目归属" v-if="item.projectId && (item.couponType === 5 || item.couponType === 6)">
+            <dict-tag :options="projects" :value="item.projectId"/>
+          </el-descriptions-item>
         </el-descriptions>
     </div>
+
+    <!-- 积分商品展示 - 仅当优惠券类型为7时显示 -->
+    <div class="contentx" v-if="item!=null && item.couponType === 7 && integralGoods">
+        <div class="desct">免单积分商品</div>
+        <el-table border width="100%" style="margin-top:5px;" :data="[integralGoods]">
+            <el-table-column label="商品ID" align="center" prop="goodsId" />
+            <el-table-column label="商品图片" align="center" width="100">
+                <template slot-scope="scope">
+                    <el-popover
+                        placement="right"
+                        title=""
+                        trigger="hover">
+                        <img slot="reference" :src="scope.row.imgUrl" width="50" v-if="scope.row.imgUrl">
+                        <img :src="scope.row.imgUrl" style="max-width: 100px;" v-if="scope.row.imgUrl">
+                        <div v-else style="width: 50px; height: 50px; background-color: #f5f5f5; display: flex; align-items: center; justify-content: center;">
+                            <span style="color: #999; font-size: 12px;">无图片</span>
+                        </div>
+                    </el-popover>
+                </template>
+            </el-table-column>
+            <el-table-column label="商品名称" show-overflow-tooltip align="center" prop="goodsName" />
+            <el-table-column label="商品编号" align="center" prop="barCode" />
+            <el-table-column label="所需积分" align="center" prop="integral" />
+            <el-table-column label="商品价格" align="center" prop="cash">
+                <template slot-scope="scope">
+                    <div v-if="scope.row.cash != null">
+                        {{scope.row.cash.toFixed(2)}}
+                    </div>
+                    <div v-else>-</div>
+                </template>
+            </el-table-column>
+            <el-table-column label="库存数量" align="center" prop="stock" />
+            <el-table-column label="商品状态" align="center" prop="status">
+                <template slot-scope="scope">
+                    <el-tag :type="scope.row.status === 1 ? 'success' : 'danger'">
+                        {{scope.row.status === 1 ? '上架' : '下架'}}
+                    </el-tag>
+                </template>
+            </el-table-column>
+        </el-table>
+    </div>
+
     <div class="contentx" v-if="item!=null">
             <div class="desct"> 优惠劵领取信息</div>
             <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
@@ -57,6 +113,7 @@
 <script>
 import { listCoupon, getCoupon, delCoupon, addCoupon, updateCoupon, exportCoupon } from "@/api/his/coupon";
 import { getListUserCoupon } from "@/api/his/userCoupon";
+import { getIntegralGoods } from "@/api/his/integralGoods";
   export default {
     name: "coupon",
     props:["data"],
@@ -78,6 +135,10 @@ import { getListUserCoupon } from "@/api/his/userCoupon";
           pageSize: 10,
           couponId: null,
         },
+        // 积分商品数据
+        integralGoods: null,
+        // 项目数据
+        projects: [],
       }
     },
     created() {
@@ -93,6 +154,9 @@ import { getListUserCoupon } from "@/api/his/userCoupon";
       this.getDicts("sys_coupon_status").then(response => {
         this.couponStatusOptions = response.data;
       });
+      this.getDicts("sys_course_project").then(response => {
+        this.projects = response.data || [];
+      });
     },
     methods: {
       getList() {
@@ -117,10 +181,25 @@ import { getListUserCoupon } from "@/api/his/userCoupon";
       getDetails(orderId) {
         this.item=null;
         this.couponId = orderId;
+        this.integralGoods = null;
         getCoupon(orderId).then(response => {
             this.item = response.data;
             this.queryParams.couponId = orderId;
             this.getList();
+
+            // 如果优惠券类型为7(积分商品免单券),获取积分商品详情
+            if (this.item.couponType === 7 && this.item.freeGoodsId) {
+              this.getIntegralGoodsDetails(this.item.freeGoodsId);
+            }
+        });
+      },
+      /** 获取积分商品详情 */
+      getIntegralGoodsDetails(goodsId) {
+        getIntegralGoods(goodsId).then(response => {
+          this.integralGoods = response.data;
+        }).catch(error => {
+          console.error("获取积分商品详情失败:", error);
+          this.integralGoods = null;
         });
       },
     }

+ 159 - 0
src/views/components/his/integralGoodsComponent.vue

@@ -0,0 +1,159 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="商品名称" prop="goodsName">
+        <el-input
+          v-model="queryParams.goodsName"
+          placeholder="请输入商品名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="商品分类" prop="goodsType">
+        <el-select v-model="queryParams.goodsType" placeholder="请选择商品分类" clearable size="small">
+          <el-option
+            v-for="dict in goodsTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" border :data="integralGoodsList">
+      <el-table-column label="封面图" align="center" prop="imgUrl" >
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.imgUrl" width="100">
+            <img :src="scope.row.imgUrl" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="商品名称" align="center" prop="goodsName" />
+      <el-table-column label="原价" align="center" prop="otPrice" />
+      <el-table-column label="商品分类" align="center" prop="goodsType">
+        <template slot-scope="scope">
+          <dict-tag :options="goodsTypeOptions" :value="scope.row.goodsType"/>
+        </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="integral" />
+      <el-table-column label="需支付金额" align="center" prop="cash" />
+      <el-table-column label="排序" align="center" prop="sort" />
+      <el-table-column label="库存" align="center" prop="stock" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="150">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="selectGoods(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 { listIntegralGoods } from "@/api/his/integralGoods";
+export default {
+  name: "IntegralGoodsComponent",
+  data() {
+    return {
+      photoArr:[],
+      imageArr:[],
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 积分商品表格数据
+      integralGoodsList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 商品分类字典
+      goodsTypeOptions: [],
+      // 状态字典
+      statusOptions: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        goodsName: null,
+        goodsType: null,
+        status: 1,
+        integral: null,
+        createTime: null
+      },
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_integral_goods_type").then(response => {
+      this.goodsTypeOptions = response.data;
+    });
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    selectGoods(row) {
+      this.$emit("select-goods", {"id": row.goodsId, "name": row.goodsName });
+    },
+    /** 查询积分商品列表 */
+    getList() {
+      this.loading = true;
+      listIntegralGoods(this.queryParams).then(response => {
+        this.integralGoodsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+  }
+};
+</script>

+ 54 - 0
src/views/components/his/packageDetails.vue

@@ -47,6 +47,14 @@
     <el-descriptions-item label="套餐医生" >
          <span v-if="item!=null">{{item.doctors}}</span>
     </el-descriptions-item>
+    <el-descriptions-item label="项目" >
+         <span v-if="selectedProject">{{selectedProject.dictLabel}}</span>
+         <span v-else>-</span>
+    </el-descriptions-item>
+    <el-descriptions-item label="课程" >
+         <span v-if="selectedCourse">{{selectedCourse.courseName}}</span>
+         <span v-else>-</span>
+    </el-descriptions-item>
     <el-descriptions-item label="创建时间" >
         <span v-if="item!=null">{{item.createTime}}</span>
     </el-descriptions-item>
@@ -126,6 +134,7 @@
 <script>
 import { listPackage, getPackage, delPackage, addPackage, updatePackage, exportPackage } from "@/api/his/package";
 import {list as getAppMallOptions} from '@/api/course/coursePlaySourceConfig';
+import { getCourseByProject } from "@/api/course/userCourse";
   export default {
     name: "patientdetails",
     props:["data"],
@@ -145,6 +154,11 @@ import {list as getAppMallOptions} from '@/api/course/coursePlaySourceConfig';
        packageSubTypeOptions:[],
         item:null,
         productJson:[],
+        // 项目和课程相关
+        projectOptions: [],
+        courseOptions: [],
+        selectedProject: null,
+        selectedCourse: null,
       }
     },
     computed: {
@@ -179,6 +193,9 @@ import {list as getAppMallOptions} from '@/api/course/coursePlaySourceConfig';
      this.getDicts("sys_prescribe_disease_type").then(response => {
        this.diseaseTypeOptions = response.data;
      });
+     this.getDicts("sys_course_project").then(response => {
+       this.projectOptions = response.data;
+     });
      this.getAppMallOptions();
 
     },
@@ -190,16 +207,53 @@ import {list as getAppMallOptions} from '@/api/course/coursePlaySourceConfig';
       },
       getDetails(orderId) {
           this.item=null;
+          this.selectedProject = null;
+          this.selectedCourse = null;
+          this.courseOptions = [];
           getPackage(orderId).then(response => {
               this.item = response.data;
               this.productJson=JSON.parse(response.data.productJson);
                this.item.imgUrl= (this.item.imgUrl).split(",");
+
+               // 处理项目显示
+               if (this.item.projectId) {
+                 this.selectedProject = this.projectOptions.find(project =>
+                   project.dictValue === this.item.projectId.toString()
+                 );
+
+                 // 获取课程列表
+                 if (this.item.projectId) {
+                   this.getCourseByProject(this.item.projectId).then(() => {
+                     // 设置选中的课程
+                     if (this.item.courseId) {
+                       this.selectedCourse = this.courseOptions.find(course =>
+                         course.courseId.toString() === this.item.courseId.toString()
+                       );
+                     }
+                   });
+                 }
+               }
           });
       },
       drug(row){
         this.drugList=row.row.drug
         this.drugOpen=true;
       },
+      /** 根据项目获取课程列表 */
+      getCourseByProject(projectId) {
+        return getCourseByProject(projectId).then(response => {
+          if (response && response.data && Array.isArray(response.data)) {
+            this.courseOptions = response.data;
+          } else {
+            this.courseOptions = [];
+          }
+          return response;
+        }).catch(error => {
+          console.error("获取课程列表失败:", error);
+          this.courseOptions = [];
+          throw error;
+        });
+      },
     }
   }
 </script>

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

@@ -0,0 +1,1606 @@
+<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" v-if="false" />
+      <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" v-show="false">
+          <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 v-if="form.rewardType === '6'" 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 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 || currentReward.rewardType === 6" 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 :coupon-type="6" @select-coupon="selectCoupon"></coupon-component>
+    </el-drawer>
+
+    <!-- 选择商品 -->
+    <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, RewardGoodsComponent
+  },
+  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.rewardType === '6') {
+            // 宝箱类型
+            this.form.rewardItems = actualRewards.map(item => ({
+              ...item,
+              couponId: item.couponId || null,
+              goodsId: item.goodsId || 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();
+      }
+      if (value === '6') {
+        this.ensureSpinDefaultItem();
+      }
+    },
+    // 更新奖励项名称(当表单名称变化时)
+    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'
+            || this.form.rewardType === '6') {
+            // 宝箱类型,将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>

+ 114 - 0
src/views/course/rewardGoods/goodsDetail.vue

@@ -0,0 +1,114 @@
+<template>
+  <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%; " >
+    <div style="padding: 20px; background-color: #fff;">
+      商品详情
+    </div>
+
+    <div class="content" v-if="item!=null">
+      <div class="desct">
+        商品信息
+      </div>
+      <el-descriptions title="" :column="3" border>
+        <el-descriptions-item label="店铺名称"  >
+          <span>{{item.storeName}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品编码" >
+          <span>{{item.storeGoodsSn}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品图片" >
+          <el-image v-if="item.goodsImg != null"
+                    style="width: 100px"
+                    :src="item.goodsImg"
+                    :preview-src-list="[item.goodsImg]">
+          </el-image>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品名称" >
+          <span>{{item.goodsName}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品介绍"  >
+          <span>{{item.goodsIntroduce}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品价格" >
+          <span>{{item.price}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品原价" >
+          <span>{{item.opPrice}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="库存" >
+          <span>{{item.stock}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="排序" >
+          <span>{{item.sort}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="状态" >
+          <dict-tag :options="statusOptions" :value="item.status"/>
+        </el-descriptions-item>
+        <el-descriptions-item label="创建时间" >
+          <span>{{item.createTime}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="更新时间" >
+          <span>{{item.updateTime}}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="备注" >
+          <span>{{item.remark}}</span>
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+
+    <div class="content" v-if="item!=null">
+      <div class="desct">
+        商品描述
+      </div>
+      <span v-html="item.goodsDesc"></span>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getRewardGoods } from '@/api/reward/rewardGoods'
+
+export default {
+  name: "GoodsDetail",
+  data() {
+    return {
+      item: {},
+      statusOptions: null
+    }
+  },
+  created() {
+    this.getDicts("sys_spec_show").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    getDetails(id) {
+      getRewardGoods(id).then(response => {
+        this.item = response.data;
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.el-descriptions-item__content {
+  max-width: 150px;
+  min-width: 100px;
+}
+.content{
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px 20px;
+
+  margin: 20px;
+}
+.el-descriptions-item__label.is-bordered-label{
+  font-weight: normal;
+}
+.desct {
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #524b4a;
+  font-weight: bold;
+}
+</style>

+ 523 - 0
src/views/course/rewardGoods/index.vue

@@ -0,0 +1,523 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="商品ID" prop="goodsId">
+        <el-input
+          v-model="queryParams.goodsId"
+          placeholder="请输入商品ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="商品名称" prop="goodsName">
+        <el-input
+          v-model="queryParams.goodsName"
+          placeholder="请输入商品名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="所属店铺" prop="storeId">
+        <el-select v-model="queryParams.storeId" placeholder="请选择店铺" clearable size="small">
+          <el-option
+            v-for="dict in storeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="商品编码" prop="storeGoodsSn">
+        <el-input
+          v-model="queryParams.storeGoodsSn"
+          placeholder="请输入商品编码"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleAdd"
+          v-hasPermi="['reward:rewardGoods: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="['reward:rewardGoods: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="['reward:rewardGoods:remove']"
+        >删除</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">
+      <el-tab-pane label="已上架" name="1"></el-tab-pane>
+      <el-tab-pane label="待上架" name="0"></el-tab-pane>
+    </el-tabs>
+
+    <el-table v-loading="loading" border :data="goodsList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="商品ID" align="center" prop="goodsId" />
+      <el-table-column label="商品图片" align="center" prop="goodsImg" width="120">
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.goodsImg" width="100">
+            <img :src="scope.row.goodsImg" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+
+      <el-table-column label="商品名称" align="center" prop="goodsName" show-overflow-tooltip />
+      <el-table-column label="所属店铺" align="center" prop="storeName" />
+      <el-table-column label="商品编码" align="center" prop="storeGoodsSn" />
+      <el-table-column label="原价" align="center" prop="opPrice" />
+      <el-table-column label="单价" align="center" prop="price" />
+      <el-table-column label="库存" align="center" prop="stock" />
+      <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="sort" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            v-hasPermi="['reward:rewardGoods:query']"
+            @click="handleDetails(scope.row)"
+          >详情
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['reward:rewardGoods:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['reward:rewardGoods: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="900px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="商品名称" prop="goodsName">
+          <el-input v-model="form.goodsName" placeholder="请输入奖励名称" />
+        </el-form-item>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="所属店铺" prop="storeId">
+              <el-select v-model="form.storeId" placeholder="请选择店铺">
+                <el-option
+                  v-for="dict in storeOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictLabel"
+                  :value="dict.dictValue"
+                />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="商品编码" prop="storeGoodsSn">
+              <el-input v-model="form.storeGoodsSn" placeholder="请输入商品编码" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-form-item label="商品封面" prop="goodsImg">
+          <Material v-model="imageArr" type="image" :num="1" :width="150" :height="150" />
+        </el-form-item>
+
+        <el-form-item label="轮播图" prop="goodsImages">
+          <Material v-model="photoArr" type="image" :num="10" :width="150" :height="150" />
+        </el-form-item>
+
+        <el-form-item label="商品介绍" prop="goodsIntroduce">
+          <el-input v-model="form.goodsIntroduce" type="textarea" placeholder="请输入商品介绍" />
+        </el-form-item>
+
+        <el-form-item label="商品描述" prop="images">
+          <Editor ref="myeditor"  @on-text-change="updateText"/>
+        </el-form-item>
+
+        <el-row>
+          <el-col :span="6">
+            <el-form-item label="库存" prop="stock">
+              <el-input v-model="form.stock"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="原价" prop="opPrice">
+              <el-input v-model="form.opPrice"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="单价" prop="price">
+              <el-input v-model="form.price"/>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="序号" prop="sort">
+              <el-input v-model="form.sort"/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <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="remark">
+          <el-input v-model="form.remark" 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-drawer
+      :with-header="false"
+      size="75%"
+      :title="show.title" :visible.sync="show.open">
+      <GoodsDetail  ref="goodsDetail" />
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { listRewardGoods, getRewardGoods, delRewardGoods, addRewardGoods, updateRewardGoods } from "@/api/reward/rewardGoods";
+import ImageUpload from '@/components/ImageUpload/index.vue';
+import { listStore } from '@/api/his/storeProduct'
+import Material from '@/components/Material/index.vue'
+import Editor from '@/components/Editor/wang.vue'
+import GoodsDetail from './goodsDetail.vue'
+
+export default {
+  name: "RewardGoods",
+  components: { GoodsDetail, Material, ImageUpload, Editor },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 奖励商品表格数据
+      goodsList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 图片数组
+      imageArr: [],
+      // 轮播图
+      photoArr: [],
+      // 店铺选项
+      storeOptions: [],
+      // 状态选项
+      statusOptions: [],
+      activeName: '1',
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        goodsId: null,
+        goodsName: null,
+        storeId: null,
+        storeGoodsSn: null,
+        status: 1
+      },
+      // 表单参数
+      form: {
+        goodsName: null,
+        goodsId: null,
+        storeId: null,
+        storeGoodsSn: null,
+        goodsImg: null,
+        goodsImages: null,
+        goodsIntroduce: null,
+        goodsDesc: null,
+        opPrice: 0,
+        price: 0,
+        stock: 0,
+        status: "1",
+        sort: 1,
+        remark: null
+      },
+      // 表单校验
+      rules: {
+        goodsName: [
+          { required: true, message: "商品名称不能为空", trigger: "blur" }
+        ],
+        storeId: [
+          { required: true, message: "店铺不能为空", trigger: "change" }
+        ],
+        storeGoodsSn: [
+          { required: true, message: "商品编码不能为空", trigger: "blur" }
+        ],
+        goodsImg: [
+          { required: true, message: "商品封面不能为空", trigger: "blur" }
+        ],
+        goodsImages: [
+          { required: true, message: "商品轮播图不能为空", trigger: "blur" }
+        ],
+        goodsIntroduce: [
+          { required: true, message: "商品介绍不能为空", trigger: "blur" }
+        ],
+        stock: [
+          { required: true, message: "库存数量不能为空", trigger: "blur" }
+        ],
+        opPrice: [
+          { required: true, message: "原价不能为空", trigger: "blur" }
+        ],
+        price: [
+          { required: true, message: "单价不能为空", trigger: "blur" }
+        ],
+        sort: [
+          { required: true, message: "序号不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "状态不能为空", trigger: "blur" }
+        ]
+      },
+      show: {
+        title: '商品详情',
+        open: false
+      }
+    };
+  },
+  created() {
+    this.getStoreList();
+    this.getList();
+    this.getDicts("sys_spec_show").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  watch: {
+    imageArr: function(val) {
+      this.form.goodsImg = val.join(',');
+    },
+    photoArr: function(val) {
+      this.form.goodsImages = val.join(',')
+    }
+  },
+  methods: {
+    handleClick(tab) {
+      this.queryParams.status = tab.name
+      this.getList()
+    },
+    getStoreList() {
+      listStore().then(response => {
+        const {rows} = response
+        this.storeOptions = rows
+      });
+    },
+    /** 查询奖励商品列表 */
+    getList() {
+      this.loading = true;
+      listRewardGoods(this.queryParams).then(response => {
+        this.goodsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        goodsName: null,
+        goodsId: null,
+        storeId: null,
+        storeGoodsSn: null,
+        goodsImg: null,
+        goodsImages: null,
+        goodsIntroduce: null,
+        goodsDesc: null,
+        opPrice: 0,
+        price: 0,
+        stock: 0,
+        status: "1",
+        sort: 1,
+        remark: 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.goodsId)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加奖励商品";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.goodsId || this.ids
+      getRewardGoods(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改奖励商品";
+        if (this.form.goodsImg) {
+          this.imageArr = this.form.goodsImg.split(",");
+        }
+        if (this.form.goodsImages) {
+          this.photoArr = this.form.goodsImages.split(",");
+        }
+        this.form.status = this.form.status + ''
+      });
+    },
+    /** 详情 */
+    handleDetails(row) {
+      this.show.open = true
+      setTimeout(() => {
+        this.$refs.goodsDetail.getDetails(row.goodsId)
+      }, 1)
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.goodsId != null) {
+            updateRewardGoods(this.form).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addRewardGoods(this.form).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.goodsId || this.ids;
+      this.$confirm('是否确认删除奖励商品编号为"' + ids + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return delRewardGoods(ids);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+    updateText(text) {
+      this.form.goodsDesc = text
+    },
+  }
+};
+</script>
+
+<style scoped>
+.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: 150px;
+  height: 150px;
+  line-height: 150px;
+  text-align: center;
+}
+</style>

+ 347 - 0
src/views/course/rewardGoodsOrder/goodsOrderDetail.vue

@@ -0,0 +1,347 @@
+<template>
+  <div style="background-color: #f0f2f5; padding-bottom: 20px; min-height: 100%;">
+    <div style="padding: 20px; background-color: #fff;">
+      订单详情
+    </div>
+
+    <div class="contentx" v-if="item != null">
+      <div class="desct"></div>
+      <div class="order-status">
+        <el-steps :active="getActiveStep(item.status)" align-center finish-status="success">
+          <el-step title="待支付"></el-step>
+          <el-step title="已支付"></el-step>
+          <el-step title="已发货"></el-step>
+          <el-step title="已完成"></el-step>
+          <el-step title="已取消" v-if="item.status === -1"></el-step>
+        </el-steps>
+      </div>
+
+      <el-card shadow="never" style="margin-top: 15px">
+        <div class="operate-container">
+          <span style="margin-left: 20px">订单状态:
+            <el-tag prop="status" v-for="(ite, index) in orderStatusOptions" v-if="item.status == ite.dictValue">{{ite.dictLabel}}</el-tag>
+          </span>
+        </div>
+        <div class="desct">
+          基本信息
+        </div>
+        <el-descriptions title="" :column="3" border>
+          <el-descriptions-item label="订单ID">
+            <span>{{ item.orderId }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="订单编号">
+            <span>{{ item.orderSn }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="订单状态">
+            <dict-tag :options="orderStatusOptions" :value="item.status"/>
+          </el-descriptions-item>
+          <el-descriptions-item label="所属店铺">
+            <span>{{ item.storeName }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="下单用户">
+            <span>{{ item.nickName }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="收货人">
+            <span>{{ item.userName }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="订单金额">
+            <span class="color-danger">¥{{ item.orderMoney }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="支付金额">
+            <span class="color-danger">¥{{ item.payMoney }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="快递单号">
+            <span>{{ item.deliverySn || '-' }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="下单时间">
+            <span>{{ item.createTime }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="支付时间">
+            <span v-if="item.payTime">{{ item.payTime }}</span>
+            <span v-else>-</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="备注">
+            <span>{{ item.remark || '-' }}</span>
+          </el-descriptions-item>
+          <el-descriptions-item label="收货人电话">
+            <span>{{ item.mobile || '-' }}</span>
+            <el-button icon="el-icon-search"
+                       size="mini"
+                       v-if="item.mobile && item.mobile.indexOf('*') !== -1"
+                       @click="handlePhone()"
+                       style="margin-left: 20px;"
+                       circle
+                       v-hasPermi="['reward:rewardGoodsOrder:queryPhone']"></el-button>
+          </el-descriptions-item>
+          <el-descriptions-item label="收货地址">
+            <span>{{ item.address || '-' }}</span>
+            <el-button icon="el-icon-search"
+                       size="mini"
+                       v-if="item.address && item.address.indexOf('*') !== -1"
+                       @click="handleAddress()"
+                       style="margin-left: 20px;"
+                       circle
+                       v-hasPermi="['reward:rewardGoodsOrder:queryAddress']"></el-button>
+          </el-descriptions-item>
+        </el-descriptions>
+      </el-card>
+    </div>
+
+    <div class="contentx" style="padding-bottom: 70px;" v-if="item != null">
+      <div class="desct">
+        商品信息
+      </div>
+      <el-table border v-if="item.goodsJson" :data="[JSON.parse(item.goodsJson)]" size="small" style="width: 100%;margin-top: 20px">
+        <el-table-column label="商品图片" min-width="150" align="center">
+          <template slot-scope="scope">
+            <el-image
+              v-if="scope.row.goodsImg"
+              style="height: 80px"
+              :src="scope.row.goodsImg"
+              :preview-src-list="[scope.row.goodsImg]"
+              fit="cover">
+            </el-image>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品编码" min-width="200" align="center">
+          <template slot-scope="scope">
+            <span>{{ scope.row.storeGoodsSn || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" min-width="300" align="center">
+          <template slot-scope="scope">
+            <span>{{ scope.row.goodsName || '-' }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品原价" min-width="150" align="center">
+          <template slot-scope="scope">
+            <span>¥{{ (scope.row.opPrice || 0).toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品单价" min-width="150" align="center">
+          <template slot-scope="scope">
+            <span>¥{{ (scope.row.price || 0).toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="购买数量" min-width="120" align="center">
+          <template slot-scope="scope">
+            <span>1</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品总价" min-width="150" align="center">
+          <template slot-scope="scope">
+            <span class="color-danger">¥{{ scope.row.price.toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <div style="float: right;margin: 20px" v-if="item.payMoney != null">
+        合计:<span class="color-danger">¥{{ (item.payMoney || 0).toFixed(2) }}</span>
+      </div>
+    </div>
+
+    <div class="contentx" v-if="item != null">
+      <div class="desct">
+        支付信息
+      </div>
+      <el-table
+        border
+        :data="payList"
+        size="small"
+        style="width: 100%;margin-top: 20px">
+        <el-table-column label="支付单号" align="center" prop="payCode" />
+        <el-table-column label="支付金额" align="center" prop="payMoney">
+          <template slot-scope="scope">
+            <span>¥{{ scope.row.payMoney.toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="支付方式" align="center" prop="payTypeCode" />
+        <el-table-column label="外部订单号" align="center" prop="tradeNo" />
+        <el-table-column label="创建时间" align="center" prop="createTime" />
+        <el-table-column label="支付时间" align="center" prop="payTime" />
+      </el-table>
+    </div>
+
+    <div class="contentx" v-if="item != null">
+      <div class="desct">
+        物流信息
+      </div>
+      <el-table
+        border
+        :data="expressList"
+        size="small"
+        style="width: 100%;margin-top: 20px">
+        <el-table-column label="操作时间" align="center" prop="AcceptTime" />
+        <el-table-column label="位置" align="center" prop="Location" />
+        <el-table-column label="描述" align="center" prop="AcceptStation" />
+      </el-table>
+    </div>
+
+    <div class="contentx" v-if="item != null">
+      <div class="desct">
+        操作记录
+      </div>
+      <el-timeline>
+        <el-timeline-item
+          v-for="(activity, index) in orderLogs"
+          :key="index"
+          :timestamp="parseTime(activity.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
+        >
+          {{ activity.content }}
+          <el-tag size="mini" style="margin-left: 10px;">{{ activity.operator }}</el-tag>
+        </el-timeline-item>
+      </el-timeline>
+    </div>
+  </div>
+</template>
+
+<script>
+import {
+  getAddress,
+  getExpress,
+  getPhone,
+  getRewardGoodsOrder,
+  getRewardGoodsOrderPayInfo
+} from '@/api/reward/rewardGoodsOrder'
+import Template from '@/views/his/complaint/template.vue'
+
+export default {
+  name: "GoodsOrderDetail",
+  components: { Template },
+  data() {
+    return {
+      item: null,
+      orderStatusOptions: [],
+      payStatusOptions: [],
+      payList: [],
+      expressList: [],
+      orderLogs: []
+    }
+  },
+  created() {
+    this.getDicts("sys_reward_order_status").then(response => {
+      this.orderStatusOptions = response.data;
+    });
+    this.getDicts("sys_reward_pay_status").then(response => {
+      this.payStatusOptions = response.data;
+    });
+  },
+  methods: {
+    handlePhone() {
+      const orderId = this.item.orderId
+      getPhone(orderId).then(response => {
+        this.item.mobile = response.userPhone
+      })
+    },
+    handleAddress() {
+      const orderId = this.item.orderId
+      getAddress(orderId).then(response => {
+        this.item.address = response.userAddress
+      })
+    },
+    getDetails(id) {
+      getRewardGoodsOrder(id).then(response => {
+        this.item = response.data;
+
+        // 初始化支付信息
+        this.payList = [];
+        if (this.item.payMoney && this.item.payMoney > 0) {
+          getRewardGoodsOrderPayInfo(this.item.orderSn).then(res => {
+            this.payList = res.data;
+          })
+        }
+
+        // 物流信息
+        this.expressList = []
+        if (this.item.deliverySn) {
+          getExpress(this.item.orderId).then(response => {
+            const data = response.data
+            this.expressList = data?.Traces || []
+          });
+        }
+
+        // 模拟操作记录,实际应该从后端获取
+        this.orderLogs = [
+          {
+            content: '订单创建',
+            operator: '系统',
+            createTime: this.item.createTime
+          }
+        ];
+        if (this.item.payTime) {
+          this.orderLogs.push({
+            content: '订单支付成功',
+            operator: '系统',
+            createTime: this.item.payTime
+          });
+        }
+        if (this.item.deliveryTime) {
+          this.orderLogs.push({
+            content: '订单已发货',
+            operator: '系统',
+            createTime: this.item.deliveryTime
+          });
+        }
+        if (this.item.finishTime) {
+          this.orderLogs.push({
+            content: '订单已完成',
+            operator: '系统',
+            createTime: this.item.finishTime
+          });
+        }
+        if (this.item.cancelTime) {
+          this.orderLogs.push({
+            content: '订单已取消',
+            operator: '系统',
+            createTime: this.item.cancelTime
+          });
+        }
+      })
+    },
+    getActiveStep(status) {
+      // 根据订单状态返回当前步骤
+      const statusMap = {
+        '1': 0, // 待支付
+        '2': 1, // 已支付
+        '3': 2, // 已发货
+        '4': 3, // 已完成
+        '-1': 4  // 已取消
+      };
+      return statusMap[status] || 0;
+    }
+  }
+}
+</script>
+
+<style scoped>
+.contentx {
+  height: 100%;
+  background-color: #fff;
+  padding: 0px 20px 20px;
+  margin: 20px;
+}
+
+.desct {
+  padding-top: 20px;
+  padding-bottom: 20px;
+  color: #524b4a;
+  font-weight: bold;
+}
+
+.color-warning {
+  color: #e6a23c;
+  font-weight: bold;
+}
+
+.el-descriptions-item__content {
+  max-width: 150px;
+  min-width: 100px;
+}
+
+.operate-container {
+  background: #F2F6FC;
+  height: 60px;
+  margin: -20px -20px 0;
+  line-height: 60px;
+}
+</style>

+ 318 - 0
src/views/course/rewardGoodsOrder/index.vue

@@ -0,0 +1,318 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="所属店铺" prop="storeId">
+        <el-select v-model="queryParams.storeId" placeholder="请选择店铺" clearable size="small">
+          <el-option
+            v-for="dict in storeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="会员ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          placeholder="请输入会员ID"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="收货人" prop="receiveUserName">
+        <el-input
+          v-model="queryParams.receiveUserName"
+          placeholder="请输入收货人"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="收货电话" prop="receiveUserPhone">
+        <el-input
+          v-model="queryParams.receiveUserPhone"
+          placeholder="请输入收货人电话"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="订单编号" prop="orderSn">
+        <el-input
+          v-model="queryParams.orderSn"
+          placeholder="请输入订单编号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="快递单号" prop="deliverySn">
+        <el-input
+          v-model="queryParams.deliverySn"
+          placeholder="请输入快递单号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="下单时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item label="支付时间" prop="payTime">
+        <el-date-picker
+          v-model="queryParams.payTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item label="发货时间" prop="deliveryTime">
+        <el-date-picker
+          v-model="queryParams.deliveryTime"
+          size="small"
+          style="width: 240px"
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          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-tabs type="card" v-model="activeName" @tab-click="handleClick">
+      <el-tab-pane label="全部订单" name=""></el-tab-pane>
+      <el-tab-pane
+        v-for="dict in orderStatusOptions"
+        :key="dict.dictValue"
+        :label="dict.dictLabel"
+        :name="dict.dictValue"
+      ></el-tab-pane>
+    </el-tabs>
+
+    <el-table v-loading="loading" border :data="orderList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="订单ID" align="center" prop="orderId"/>
+      <el-table-column label="订单编号" align="center" prop="orderSn" width="180"/>
+      <el-table-column label="所属店铺" align="center" prop="storeName" show-overflow-tooltip />
+      <el-table-column label="下单用户" align="center" prop="nickName" />
+      <el-table-column label="收货人" align="center" prop="userName" />
+      <el-table-column label="订单金额" align="center" prop="orderMoney" />
+      <el-table-column label="支付金额" align="center" prop="payMoney" />
+      <el-table-column label="订单状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="orderStatusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="下单时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="支付时间" align="center" prop="payTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150" fixed="right">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleDetails(scope.row)"
+            v-hasPermi="['reward:rewardGoodsOrder:query']"
+          >详情</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleCancel(scope.row)"
+            v-hasPermi="['reward:rewardGoodsOrder:cancelOrder']"
+            v-if="scope.row.status === 1 || scope.row.status === 2 || scope.row.status === 3"
+          >取消</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <el-drawer
+      :with-header="false"
+      size="75%"
+      :title="show.title"
+      :visible.sync="show.open"
+    >
+      <GoodsOrderDetail ref="goodsOrderDetail" />
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { listRewardGoodsOrder, cancelOrder } from '@/api/reward/rewardGoodsOrder'
+import GoodsOrderDetail from './goodsOrderDetail.vue'
+import { listStore } from '@/api/his/storeProduct'
+
+export default {
+  name: "RewardGoodsOrder",
+  components: { GoodsOrderDetail },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 订单表格数据
+      orderList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 店铺选项
+      storeOptions: [],
+      // 当前激活的tab
+      activeName: "",
+      // 订单状态选项
+      orderStatusOptions: [],
+      // 详情弹窗
+      show: {
+        title: '订单详情',
+        open: false
+      },
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        storeId: null,
+        userId: null,
+        receiveUserName: null,
+        receiveUserPhone: null,
+        orderSn: null,
+        deliverySn: null,
+        status: null,
+        createTime: null,
+        payTime: null,
+        deliveryTime: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {}
+    };
+  },
+  created() {
+    this.getStoreList();
+    this.getList();
+    this.getDicts("sys_reward_order_status").then(response => {
+      this.orderStatusOptions = response.data;
+    });
+  },
+  methods: {
+    getStoreList() {
+      listStore().then(response => {
+        const {rows} = response
+        this.storeOptions = rows
+      });
+    },
+    /** 查询奖励商品订单列表 */
+    getList() {
+      this.loading = true;
+      const { createTime, payTime, deliveryTime, ...restParams } = this.queryParams;
+
+      const params = {
+        ...restParams,
+        ...this.parseDateRanges({ createTime, payTime, deliveryTime })
+      };
+      listRewardGoodsOrder(params).then(response => {
+        this.orderList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    parseDateRanges(dateFields) {
+      const result = {};
+      Object.keys(dateFields).forEach(field => {
+        const dateArray = dateFields[field];
+        const capitalizedField = field.charAt(0).toUpperCase() + field.slice(1);
+
+        if (dateArray && dateArray.length === 2) {
+          result[`s${capitalizedField}`] = dateArray[0] + ' 00:00:00'
+          result[`e${capitalizedField}`] = dateArray[1] + ' 00:00:00'
+        } else {
+          result[`s${capitalizedField}`] = null;
+          result[`e${capitalizedField}`] = null;
+        }
+      });
+      return result;
+    },
+    /** 搜索按钮操作 */
+    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
+    },
+    /** Tab切换 */
+    handleClick(tab) {
+      this.queryParams.status = tab.name;
+      this.getList();
+    },
+    /** 详情操作 */
+    handleDetails(row) {
+      this.show.open = true;
+      setTimeout(() => {
+        this.$refs.goodsOrderDetail.getDetails(row.orderId);
+      }, 100);
+    },
+    /** 取消订单操作 */
+    handleCancel(row) {
+      this.$confirm('确认要取消ID为' + row.orderId + '的订单吗?', '取消订单', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        cancelOrder(row.orderId).then(() => {
+          this.$message.success('订单取消成功');
+          this.getList();
+        })
+      }).catch(() => {});
+    },
+  }
+};
+</script>
+
+<style scoped>
+</style>

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

@@ -0,0 +1,233 @@
+<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.dateRange = []
+      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>

+ 95 - 3
src/views/his/coupon/index.vue

@@ -88,6 +88,18 @@
     <el-table v-loading="loading" border :data="couponList" @selection-change="handleSelectionChange">
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="优惠券名称" align="center" prop="title" width="120px"/>
+      <el-table-column label="logo" align="center" prop="logo" >
+        <template slot-scope="scope">
+          <el-popover
+            placement="right"
+            title=""
+            trigger="hover"
+          >
+            <img slot="reference" :src="scope.row.logo" width="50">
+            <img :src="scope.row.logo" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
       <el-table-column label="面额" align="center" prop="price" />
       <el-table-column label="折扣" align="center" prop="rate" />
       <el-table-column label="数量" align="center" prop="number" />
@@ -186,6 +198,10 @@
           <el-input v-model="form.title" placeholder="请输入优惠券名称" />
         </el-form-item>
 
+        <el-form-item label="logo图片" prop="logo">
+          <Material v-model="imageArr" type="image" :num="1" :width="150" :height="150" />
+        </el-form-item>
+
         <el-form-item label="过期时间" prop="limitTime">
           <el-date-picker clearable size="small"
             v-model="form.limitTime"
@@ -240,6 +256,33 @@
           </el-select>
         </el-form-item>
 
+        <el-form-item label="项目归属" prop="projectId" v-if="form.couponType === 5 || form.couponType === 6">
+          <el-select v-model="form.projectId" placeholder="请选择项目归属" clearable>
+            <el-option
+              v-for="item in projects"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="parseInt(item.dictValue)">
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="免单商品" prop="freeGoodsId" v-if="form.couponType === 7">
+          <el-select
+            ref="customSelect"
+            v-model="form.freeGoodsId"
+            placeholder="请选择积分商品"
+            @click.native.stop="openIntegralGoodsDialog()"
+            clearable>
+            <el-option
+              v-for="item in integralGoodsList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
+        </el-form-item>
+
         <el-form-item label="领取后有效期(天)" prop="limitDay" >
           <el-input-number v-model="form.limitDay"  :min="1"  label="请输入领取后有效期"></el-input-number>
         </el-form-item>
@@ -263,6 +306,11 @@
              :title="show.title" :visible.sync="show.open">
          <couponDetails  ref="Details" />
        </el-drawer>
+
+    <!-- 选择积分商品 -->
+    <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="integralGoodsDialog.open">
+      <integralGoodsComponent @select-goods="selectGoods"></integralGoodsComponent>
+    </el-drawer>
   </div>
 </template>
 
@@ -270,12 +318,14 @@
 import { listCoupon, getCoupon, delCoupon, addCoupon, updateCoupon, exportCoupon } from "@/api/his/coupon";
 import { sendCoupon } from "@/api/his/userCoupon";
 import couponDetails from '../../components/his/couponDetails.vue';
-import {getUserList} from "@/api/his/doctor";
+import integralGoodsComponent from '@/views/components/his/integralGoodsComponent.vue'
 import {selectUser} from "@/api/his/user";
 import {getAllCateList} from "@/api/his/packageCate";
+import { getIntegralGoods } from '@/api/his/integralGoods'
+import Material from '@/components/Material/index.vue'
 export default {
   name: "Coupon",
-  components: { couponDetails },
+  components: { Material, couponDetails, integralGoodsComponent },
   data() {
     return {
       show:{
@@ -348,9 +398,23 @@ export default {
         couponType: [
           { required: true, message: "券类型不能为空", trigger: "blur" }
         ],
-      }
+        freeGoodsId: [
+          { required: true, message: "免单商品不能为空", trigger: "blur" }
+        ],
+      },
+      integralGoodsList: [],
+      integralGoodsDialog: {
+        open: false
+      },
+      imageArr: [],
+      projects: []
     };
   },
+  watch: {
+    imageArr: function(val) {
+      this.form.logo = val.join(',')
+    }
+  },
   created() {
     this.getList();
     getAllCateList().then(response => {
@@ -363,6 +427,9 @@ export default {
     this.getDicts("sys_company_status").then(response => {
       this.statusOptions = response.data;
     });
+    this.getDicts("sys_course_project").then(response => {
+      this.projects = response.data || [];
+    });
     this.getDicts("sys_coupon_limit_type").then(response => {
       this.limitTypeOptions = response.data;
     });
@@ -434,6 +501,8 @@ export default {
         limitCount:1,
         limitType:null,
         cateIds:null,
+        freeGoodsId: null,
+        projectId: null,
       };
       this.resetForm("form");
     },
@@ -497,7 +566,17 @@ export default {
           if(this.form.cateIds!=null){
            this.form.cateIds= JSON.parse(this.form.cateIds)
           }
+        // 积分商品免单券
+        if (this.form.couponType === 7) {
+          getIntegralGoods(this.form.freeGoodsId).then(response => {
+            const integralGoods = response.data
+            if (!this.integralGoodsList.some(item => item.id === integralGoods.goodsId)){
+              this.integralGoodsList.push({"id": integralGoods.goodsId, "name": integralGoods.goodsName})
+            }
+          })
+        }
 
+        this.imageArr = this.form.logo?.split(",");
       });
     },
     /** 提交按钮 */
@@ -563,6 +642,19 @@ export default {
           this.download(response.msg);
           this.exportLoading = false;
         }).catch(() => {});
+    },
+    openIntegralGoodsDialog() {
+      this.$nextTick(() => {
+        this.$refs["customSelect"]?.blur?.()
+      });
+      this.integralGoodsDialog.open = true
+    },
+    selectGoods(goods) {
+      this.form.freeGoodsId = goods.id
+      if (!this.integralGoodsList.some(item => item.id === goods.id)) {
+        this.integralGoodsList.push(goods);
+      }
+      this.integralGoodsDialog.open = false
     }
   }
 };

+ 193 - 78
src/views/his/courseCheckIn/index.vue

@@ -16,6 +16,13 @@
           <el-option label="已停用" :value="3" />
         </el-select>
       </el-form-item>
+      <el-form-item label="奖品类型" prop="prizeType">
+        <el-select v-model="queryParams.prizeType" placeholder="请选择奖品类型" clearable size="small">
+          <el-option label="红包" :value="1" />
+          <el-option label="积分商品免单券" :value="2" />
+          <el-option label="大礼品" :value="3" />
+        </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>
@@ -44,6 +51,14 @@
       <el-table-column label="需要打卡天数" align="center" prop="checkinDays" min-width="100" />
       <el-table-column label="参与公司" align="center" prop="companyNames" min-width="150" show-overflow-tooltip />
       <el-table-column label="参与项目" align="center" prop="projectNames" min-width="150" show-overflow-tooltip />
+      <el-table-column label="奖品类型" align="center" prop="prizeType" min-width="120">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.prizeType === 1" type="warning">红包</el-tag>
+          <el-tag v-else-if="scope.row.prizeType === 2" type="success">积分商品免单券</el-tag>
+          <el-tag v-else-if="scope.row.prizeType === 3" type="primary">大礼品</el-tag>
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
       <el-table-column label="活动状态" align="center" prop="status" min-width="100">
         <template slot-scope="scope">
           <el-tag v-if="scope.row.status === 0" type="info">未开始</el-tag>
@@ -158,7 +173,8 @@
         <el-form-item label="奖品类型" prop="prizeType">
           <el-select v-model="form.prizeType" placeholder="请选择奖品类型" size="small" style="width: 200px">
             <el-option :label="'红包'" :value="1" />
-            <el-option :label="'积分商品'" :value="2" />
+            <el-option :label="'积分商品免单券'" :value="2" />
+            <el-option label="大礼品" :value="3" />
           </el-select>
         </el-form-item>
 
@@ -168,20 +184,40 @@
           <span style="margin-left: 10px; color: #999;">元</span>
         </el-form-item>
 
-        <el-form-item v-if="form.prizeType === 2" label="积分商品" prop="prizeGoodsId"
-          :rules="{ required: true, message: '积分商品不能为空', trigger: 'change' }">
-          <el-select filterable v-model="form.prizeGoodsId" placeholder="请选择积分商品" clearable size="small" style="width: 100%" @change="handlePrizeGoodsChange">
+        <el-form-item v-if="form.prizeType === 2" label="免单券" prop="freeCouponId"
+                      :rules="{ required: true, message: '免单券不能为空', trigger: 'change' }">
+          <el-select
+            ref="customSelectCoupon"
+            v-model="form.freeCouponId"
+            placeholder="请选择优惠券"
+            @click.native.stop="openCouponDrawer"
+            clearable
+            style="width: 100%;">
             <el-option
-              v-for="item in integralGoodsList"
-              :key="item.goodsId"
-              :label="item.goodsName"
-              :value="item.goodsId"
-            />
+              v-for="item in couponList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
           </el-select>
         </el-form-item>
 
-        <el-form-item v-if="form.prizeType === 2 && form.prizeGoodsName" label="已选商品">
-          <el-tag type="success">{{ form.prizeGoodsName }}</el-tag>
+        <el-form-item v-if="form.prizeType === 3" label="大礼品配置" prop="grandGiftId"
+                      :rules="{ required: true, message: '大礼品配置不能为空', trigger: 'change' }">
+          <el-select
+            ref="customSelectReward"
+            v-model="form.grandGiftId"
+            placeholder="请选择大礼品奖励"
+            @click.native.stop="openRewardDrawer"
+            clearable
+            style="width: 100%;">
+            <el-option
+              v-for="item in rewardList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id">
+            </el-option>
+          </el-select>
         </el-form-item>
 
         <el-form-item label="备注" prop="remark">
@@ -219,8 +255,8 @@
 
     <!-- 奖品领取记录对话框 -->
     <el-dialog :title="'奖品领取记录 - ' + prizeRecordTitle" :visible.sync="prizeRecordOpen" width="900px" append-to-body>
-      <el-tabs v-model="prizeRecordType" @tab-click="handlePrizeRecordTabChange">
-        <el-tab-pane label="红包记录" name="redpacket">
+      <el-tabs v-model="prizeRecordType">
+        <el-tab-pane label="红包记录" name="1" v-if="prizeRecordType === '1'">
           <el-table v-loading="prizeRecordLoading" :data="prizeRecordList" border style="width: 100%">
             <el-table-column label="ID" align="center" prop="receiveId" min-width="80" />
             <el-table-column label="用户昵称" align="center" prop="userNickName" min-width="120" show-overflow-tooltip />
@@ -240,7 +276,7 @@
             <el-table-column label="领取时间" align="center" prop="receiveTime" min-width="160" />
           </el-table>
         </el-tab-pane>
-        <el-tab-pane label="积分商品记录" name="goods">
+        <el-tab-pane label="积分商品免单券领取记录" name="2" v-if="prizeRecordType === '2'">
           <el-table v-loading="prizeRecordLoading" :data="prizeRecordList" border style="width: 100%">
             <el-table-column label="ID" align="center" prop="receiveId" min-width="80" />
             <el-table-column label="用户昵称" align="center" prop="userNickName" min-width="120" show-overflow-tooltip />
@@ -256,6 +292,22 @@
             <el-table-column label="领取时间" align="center" prop="receiveTime" min-width="160" />
           </el-table>
         </el-tab-pane>
+        <el-tab-pane label="大礼品转盘领取记录" name="3" v-if="prizeRecordType === '3'">
+          <el-table v-loading="prizeRecordLoading" :data="prizeRecordList" border style="width: 100%">
+            <el-table-column label="ID" align="center" prop="receiveId" min-width="80" />
+            <el-table-column label="用户昵称" align="center" prop="userNickName" min-width="120" show-overflow-tooltip />
+            <el-table-column label="企业名" align="center" prop="companyName" min-width="150" show-overflow-tooltip />
+            <el-table-column label="奖励名称" align="center" prop="goodsName" min-width="150" show-overflow-tooltip />
+            <el-table-column label="领取状态" align="center" prop="receiveStatus" min-width="100">
+              <template slot-scope="scope">
+                <el-tag v-if="scope.row.receiveStatus === 0" type="info">未领取</el-tag>
+                <el-tag v-else-if="scope.row.receiveStatus === 1" type="success">已领取</el-tag>
+                <el-tag v-else-if="scope.row.receiveStatus === 2" type="danger">领取失败</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="领取时间" align="center" prop="receiveTime" min-width="160" />
+          </el-table>
+        </el-tab-pane>
       </el-tabs>
       <pagination
         v-show="prizeRecordTotal>0"
@@ -265,15 +317,39 @@
         @pagination="getPrizeRecordList"
       />
     </el-dialog>
+
+    <!-- 选择大礼品 -->
+    <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="rewardDrawerOpen">
+      <reward-component :reward-type="6" @select-reward="selectReward"></reward-component>
+    </el-drawer>
+
+    <!-- 选择公域疗法券优惠券 -->
+    <el-drawer :append-to-body="true" :with-header="false" size="75%" :visible.sync="couponDrawerOpen">
+      <coupon-component :coupon-type="7" @select-coupon="selectCoupon"></coupon-component>
+    </el-drawer>
   </div>
 </template>
 
 <script>
 import { getCompanyList } from "@/api/company/company";
-import request from '@/utils/request'
+import { listByIds, listReward } from '@/api/course/reward'
+import { listIntegralGoods } from "@/api/his/integralGoods";
+import {
+  listCheckinActivity,
+  getCheckinActivity,
+  addCheckinActivity,
+  updateCheckinActivity,
+  delCheckinActivity,
+  copyCheckinActivity,
+  getPrizeRecord
+} from "@/api/his/checkinActivity";
+import CouponComponent from '@/views/components/his/couponComponent.vue'
+import RewardComponent from '@/views/components/course/rewardComponent.vue'
+import { getByIds } from '@/api/his/coupon'
 
 export default {
   name: "CourseCheckIn",
+  components: { CouponComponent, RewardComponent },
   data() {
     return {
       loading: true,
@@ -283,13 +359,15 @@ export default {
       companys: [],
       projects: [],
       integralGoodsList: [],
+      grandGiftList: [],
       open: false,
       title: "",
       queryParams: {
         pageNum: 1,
         pageSize: 10,
         activityName: null,
-        status: null
+        status: null,
+        prizeType: null
       },
       form: {
         activityId: null,
@@ -307,7 +385,9 @@ export default {
         prizeGoodsName: null,
         prizeList: [],
         remark: null,
-        checkinDays: null
+        checkinDays: null,
+        grandGiftId: null,
+        freeCouponId: null,
       },
       rules: {
         activityName: [
@@ -341,7 +421,7 @@ export default {
       // 奖品领取记录
       prizeRecordOpen: false,
       prizeRecordTitle: "",
-      prizeRecordType: "redpacket",
+      prizeRecordType: null,
       prizeRecordLoading: false,
       prizeRecordList: [],
       prizeRecordTotal: 0,
@@ -350,7 +430,11 @@ export default {
         pageSize: 10,
         activityId: null,
         prizeType: 1
-      }
+      },
+      couponDrawerOpen: false,
+      couponList: [],
+      rewardDrawerOpen: false,
+      rewardList: [],
     };
   },
   created() {
@@ -358,15 +442,12 @@ export default {
     this.getProjectList();
     this.getIntegralGoodsList();
     this.getList();
+    this.initGiftList()
   },
   methods: {
     getList() {
       this.loading = true;
-      request({
-        url: '/course/checkinActivity/list',
-        method: 'get',
-        params: this.queryParams
-      }).then(response => {
+      listCheckinActivity(this.queryParams).then(response => {
         this.list = response.rows || [];
         this.total = response.total || 0;
         this.loading = false;
@@ -385,14 +466,16 @@ export default {
       });
     },
     getIntegralGoodsList() {
-      request({
-        url: '/his/integralGoods/list',
-        method: 'get',
-        params: { pageNum: 1, pageSize: 1000 }
-      }).then(response => {
+      listIntegralGoods({ pageNum: 1, pageSize: 1000 }).then(response => {
         this.integralGoodsList = response.rows || [];
       });
     },
+    initGiftList() {
+      listReward({"rewardType": 6}).then(response => {
+        const {rows} = response
+        this.grandGiftList = rows
+      })
+    },
     handleQuery() {
       this.queryParams.pageNum = 1;
       this.getList();
@@ -403,7 +486,8 @@ export default {
         pageNum: 1,
         pageSize: 10,
         activityName: null,
-        status: null
+        status: null,
+        prizeType: null
       };
       this.handleQuery();
     },
@@ -424,7 +508,8 @@ export default {
         prizeGoodsName: null,
         prizeList: [],
         remark: null,
-        checkinDays: null
+        checkinDays: null,
+        grandGiftId: null,
       };
     },
     // 校验打卡天数不能超过活动时长
@@ -451,12 +536,6 @@ export default {
         this.form.prizeGoodsName = null;
       }
     },
-    handlePrizeTypeChange(prizeType) {
-      // 切换类型时清空相关字段
-      this.form.redpacketAmount = null;
-      this.form.prizeGoodsId = null;
-      this.form.prizeGoodsName = null;
-    },
     handleAdd() {
       this.reset();
       this.open = true;
@@ -468,10 +547,7 @@ export default {
     },
     handleUpdate(row) {
       const activityId = row.activityId;
-      request({
-        url: '/course/checkinActivity/' + activityId,
-        method: 'get'
-      }).then(response => {
+      getCheckinActivity(activityId).then(response => {
         // 先重置表单
         this.reset();
         // 使用 Object.assign 合并数据,保持响应式
@@ -495,8 +571,12 @@ export default {
           if (this.form.prizeType === 1) {
             this.$set(this.form, 'redpacketAmount', prize.redpacketAmount);
           } else if (this.form.prizeType === 2) {
-            this.$set(this.form, 'prizeGoodsId', prize.goodsId);
-            this.handlePrizeGoodsChange(prize.goodsId);
+            this.$set(this.form, 'freeCouponId', prize.freeCouponId)
+            this.initCouponList(prize.freeCouponId)
+          } else if (this.form.prizeType === 3) {
+            this.$set(this.form, 'grandGiftId', prize.grandGiftId);
+            console.log(prize.grandGiftId);
+            this.initRewardList(prize.grandGiftId)
           }
         }
         this.open = true;
@@ -509,6 +589,33 @@ export default {
         });
       });
     },
+    initRewardList(id) {
+      if (!id) {
+        return;
+      }
+
+      listByIds(id).then(response => {
+        const {data} = response
+        data.forEach(d => {
+          if (!this.rewardList.some(item => item.id === d.id)) {
+            this.rewardList.push({"id": d.id, "name": d.name})
+          }
+        })
+      })
+    },
+    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})
+          }
+        })
+      })
+    },
     handleDelete(row) {
       const activityIds = row.activityId;
       this.$confirm('是否确认删除该打卡活动?', "警告", {
@@ -516,10 +623,7 @@ export default {
         cancelButtonText: "取消",
         type: "warning"
       }).then(() => {
-        return request({
-          url: '/course/checkinActivity/' + activityIds,
-          method: 'delete'
-        });
+        return delCheckinActivity(activityIds);
       }).then(() => {
         this.getList();
         this.msgSuccess("删除成功");
@@ -543,11 +647,7 @@ export default {
             startTime: this.copyForm.timeRange[0],
             endTime: this.copyForm.timeRange[1]
           };
-          request({
-            url: '/course/checkinActivity/copy',
-            method: 'post',
-            data: params
-          }).then(() => {
+          copyCheckinActivity(params).then(() => {
             this.msgSuccess("复制成功");
             this.copyOpen = false;
             this.getList();
@@ -559,23 +659,14 @@ export default {
       this.prizeRecordTitle = row.activityName;
       this.prizeRecordQuery.activityId = row.activityId;
       this.prizeRecordQuery.pageNum = 1;
-      this.prizeRecordType = "redpacket";
-      this.prizeRecordQuery.prizeType = 1;
+      this.prizeRecordQuery.prizeType = row.prizeType;
+      this.prizeRecordType = row.prizeType.toString()
       this.prizeRecordOpen = true;
       this.getPrizeRecordList();
     },
-    handlePrizeRecordTabChange() {
-      this.prizeRecordQuery.prizeType = this.prizeRecordType === "redpacket" ? 1 : 2;
-      this.prizeRecordQuery.pageNum = 1;
-      this.getPrizeRecordList();
-    },
     getPrizeRecordList() {
       this.prizeRecordLoading = true;
-      request({
-        url: '/course/checkinActivity/prizeRecord',
-        method: 'get',
-        params: this.prizeRecordQuery
-      }).then(response => {
+      getPrizeRecord(this.prizeRecordQuery).then(response => {
         this.prizeRecordList = response.rows || [];
         this.prizeRecordTotal = response.total || 0;
         this.prizeRecordLoading = false;
@@ -599,8 +690,13 @@ export default {
           } else if (this.form.prizeType === 2) {
             prizeList.push({
               prizeType: 2,
-              goodsId: this.form.prizeGoodsId
-            });
+              freeCouponId: this.form.freeCouponId
+            })
+          } else if (this.form.prizeType === 3) {
+            prizeList.push({
+              prizeType: 3,
+              grandGiftId: this.form.grandGiftId
+            })
           }
 
           const submitData = {
@@ -614,25 +710,18 @@ export default {
             notifyTemplate: this.form.notifyTemplate,
             status: this.form.status,
             prizeList: prizeList,
-            remark: this.form.remark
+            remark: this.form.remark,
+            prizeType: this.form.prizeType
           };
 
           if (this.form.activityId != null) {
-            request({
-              url: '/course/checkinActivity/edit',
-              method: 'put',
-              data: submitData
-            }).then(() => {
+            updateCheckinActivity(submitData).then(() => {
               this.msgSuccess("修改成功");
               this.open = false;
               this.getList();
             });
           } else {
-            request({
-              url: '/course/checkinActivity/add',
-              method: 'post',
-              data: submitData
-            }).then(() => {
+            addCheckinActivity(submitData).then(() => {
               this.msgSuccess("新增成功");
               this.open = false;
               this.getList();
@@ -640,7 +729,33 @@ export default {
           }
         }
       });
-    }
+    },
+    openCouponDrawer() {
+      this.$nextTick(() => {
+        this.$refs.customSelectCoupon?.blur?.()
+      });
+      this.couponDrawerOpen = true
+    },
+    selectCoupon(coupon) {
+      this.form.freeCouponId = coupon.id
+      if (!this.couponList.some(item => item.id === coupon.id)) {
+        this.couponList.push(coupon)
+      }
+      this.couponDrawerOpen = false
+    },
+    openRewardDrawer() {
+      this.$nextTick(() => {
+        this.$refs.customSelectReward?.blur?.()
+      });
+      this.rewardDrawerOpen = true
+    },
+    selectReward(reward) {
+      this.form.grandGiftId = reward.id
+      if (!this.rewardList.some(item => item.id === reward.id)) {
+        this.rewardList.push(reward)
+      }
+      this.rewardDrawerOpen = false
+    },
   }
 };
 </script>

+ 23 - 5
src/views/his/package/index.vue

@@ -621,7 +621,7 @@
     </el-drawer>
 
     <el-dialog :title="product.title" v-if="product.open"  :visible.sync="product.open" width="1000px" append-to-body>
-      <product-attr-value-select  ref="Details"  @selectProduct="selectProduct" />
+      <product-attr-value-select  ref="productDetails"  @selectProduct="selectProduct" />
     </el-dialog>
 
 
@@ -909,11 +909,19 @@ export default {
       // 如果有选择项目,则获取对应的课程列表
       if (projectId) {
         console.log("项目ID:", projectId)
+        // 确保projectId是字符串类型,与下拉框选项匹配
+        this.form.projectId = projectId.toString();
+
         // 使用 Promise 确保课程列表加载完成
-        this.fetchCourseByProject(projectId).then(() => {
+        this.fetchCourseByProject(this.form.projectId).then(() => {
           // 强制更新视图,确保下拉框显示正确
-          this.$forceUpdate();
+          this.$nextTick(() => {
+            this.$forceUpdate();
+          });
         });
+      } else {
+        // 如果清空了项目,也清空项目ID
+        this.form.projectId = null;
       }
     },
     /** 根据项目获取课程列表 */
@@ -946,6 +954,13 @@ export default {
       console.log("当前表单课程ID:", this.form.courseId);
       console.log("可用课程列表:", this.courseOptions);
 
+      // 确保courseId是字符串类型,与下拉框选项匹配
+      if (courseId) {
+        this.form.courseId = courseId.toString();
+      } else {
+        this.form.courseId = null;
+      }
+
       // 强制更新视图,确保选择正确显示
       this.$nextTick(() => {
         this.$forceUpdate();
@@ -1101,7 +1116,7 @@ export default {
         });
       }else {
         setTimeout(() => {
-          this.$refs.Details.getDetails(this.form.storeId);
+          this.$refs.productDetails.getDetails(this.form.storeId);
         }, 1);
         this.product.open=true;
       }
@@ -1215,7 +1230,10 @@ export default {
         this.open = true;
         this.title = "修改套餐包";
         if (this.form.projectId) {
-          console.log("this.form.projectId",this.form.projectId)
+          console.log("this.form.projectId", this.form.projectId);
+          // 确保projectId是字符串类型,与下拉框选项匹配
+          this.form.projectId = this.form.projectId.toString();
+
           // 立即获取课程列表
           this.fetchCourseByProject(this.form.projectId).then(() => {
             // 课程列表加载完成后,再设置课程ID