Browse Source

Merge remote-tracking branch 'origin/master'

yfh 2 months ago
parent
commit
d6cc695c96

+ 1 - 1
.env.prod-kyt

@@ -34,7 +34,7 @@ ENV = 'development'
 VUE_APP_BASE_API = '/prod-api'
 
 #默认 1、会员 2、企微
-VUE_APP_COURSE_DEFAULT = 1
+VUE_APP_COURSE_DEFAULT = 2
 
 # 路由懒加载
 VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 13 - 2
src/api/company/statistics.js

@@ -100,7 +100,7 @@ export function exportCustomer(query) {
   })
 }
 
- 
+
 export function customerVisit(query) {
   return request({
     url: '/company/statistics/customerVisit',
@@ -116,6 +116,17 @@ export function exportCustomerVisit(query) {
   })
 }
 
-
+export function ipadStaticTotal(dateTime) {
+  return request({
+    url: '/company/statistics/ipadStaticTotal/' + dateTime,
+    method: 'get'
+  })
+}
+export function exportIpadStaticByTime(dateTime) {
+  return request({
+    url: '/company/statistics/exportIpadStaticByTime/' + dateTime,
+    method: 'get'
+  })
+}
 
 

+ 8 - 0
src/api/course/videoResource.js

@@ -52,3 +52,11 @@ export function batchAddVideoResource(data) {
   })
 }
 
+// 批量修改视频资源
+export function batchUpdateVideoResource(data) {
+  return request({
+    url: '/course/videoResource/batchUpdateClass',
+    method: 'post',
+    params: data
+  })
+}

+ 69 - 0
src/api/recharge/template.js

@@ -0,0 +1,69 @@
+import request from '@/utils/request'
+
+// 查询充值模板列表
+export function listRechargeTemplate(query) {
+  return request({
+    url: '/recharge-templates/list',
+    method: 'post',
+    data: query
+  })
+}
+
+// 查询充值模板详细
+export function getRechargeTemplate(id) {
+  return request({
+    url: '/recharge-templates/' + id,
+    method: 'get'
+  })
+}
+
+// 新增充值模板
+export function addRechargeTemplate(data) {
+  return request({
+    url: '/recharge-templates',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改充值模板
+export function updateRechargeTemplate(data) {
+  return request({
+    url: '/recharge-templates/' + data.id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除充值模板
+export function delRechargeTemplate(id) {
+  return request({
+    url: '/recharge-templates/' + id,
+    method: 'delete'
+  })
+}
+
+// 修改充值模板状态
+export function updateRechargeTemplateStatus(id, status) {
+  return request({
+    url: '/recharge-templates/' + id + '/status',
+    method: 'put',
+    params: {
+      status: status
+    }
+  })
+}
+
+
+/**
+ * 获取优惠券列表
+ * @param {Object} query 查询参数
+ * @returns {Promise} 请求结果
+ */
+export function getCouponList(query) {
+  return request({
+    url: '/recharge-templates/getCouponList',
+    method: 'get',
+    params: query
+  })
+}

BIN
src/assets/logo/hcl.png


+ 1 - 1
src/views/components/his/storeOrderDetails.vue

@@ -303,7 +303,6 @@
                          <el-option key="JD"  label="京东" value="JD" />
                          <el-option key="DBL"  label="德邦" value="DBL" />
 						              <el-option key="YD"  label="韵达" value="YD" />
-						              <el-option key="STO"  label="申通" value="STO" />
                        </el-select>
                      </el-form-item>
                     <el-form-item label="物流单号" prop="deliverySn"  >
@@ -470,6 +469,7 @@ import {getCitys} from "@/api/store/city";
           orderId:null,
           deliveryId:null,
           deliveryCode:null,
+          deliverySn: null
         },
         showList:true,
         edit:{

+ 5 - 5
src/views/course/userCoursePeriod/index.vue

@@ -275,7 +275,7 @@
             start-placeholder="开始日期"
             end-placeholder="结束日期"
             value-format="yyyy-MM-dd"
-            :picker-options="{disabledDate}"
+            :picker-options="{}"
             :clearable="false"
           >
           </el-date-picker>
@@ -287,7 +287,7 @@
             type="date"
             value-format="yyyy-MM-dd"
             placeholder="选择日期"
-            :picker-options="{disabledDate}"
+            :picker-options="{}"
             :clearable="false"
           >
           </el-date-picker>
@@ -1712,9 +1712,9 @@ export default {
       this.updatePeriodDate.form = {id: row.id, dayDate: row.dayDate};
       this.updatePeriodDate.open = true;
     },
-    disabledDate(time) {
-      return time.getTime() < new Date(new Date().setHours(0,0,0,0));
-    },
+    // disabledDate(time) {
+    //   return time.getTime() < new Date(new Date().setHours(0,0,0,0));
+    // },
     getPickerOptions() {
       // 如果已选择看课时间范围,则领取红包时间应在看课时间范围内
       if (this.updateCourse.form.timeRange &&

+ 92 - 1
src/views/course/videoResource/index.vue

@@ -66,6 +66,16 @@
           v-hasPermi="['course:videoResource:batchAdd']"
         >批量新增</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          icon="el-icon-plus"
+          size="mini"
+          :disabled="multiple"
+          @click="handleBatchUpdate"
+          v-hasPermi="['course:videoResource:batchUpdateClass']"
+        >批量修改分类</el-button>
+      </el-col>
       <el-col :span="1.5">
         <el-button
           type="danger"
@@ -340,6 +350,37 @@
       <video ref="up-video" id="video" width="100%" height="400px" controls :src="videoPreviewUrl" />
     </el-dialog>
 
+    <!--批量修改弹框-->
+    <minimizable-dialog :title="'批量修改'" :visible.sync="batchUpdateVisible" width="700px" append-to-body :before-close="cancel"
+                        @minimize="hasMinimizableDialog = true" @restore="hasMinimizableDialog = false">
+      <el-form ref="form" :model="batchUpdateForm" :rules="rules" label-width="80px">
+      <el-form-item label="分类" prop="typeId">
+          <el-select v-model="batchUpdateForm.typeId" placeholder="请选择分类" style="width: 100%" @change="val => changeCateType(val, 2)">
+            <el-option
+              v-for="item in rootTypeList"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue">
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="子分类" prop="typeSubId">
+          <el-select v-model="batchUpdateForm.typeSubId" clearable placeholder="请选择子分类" style="width: 100%">
+            <el-option
+              v-for="item in subTypeList"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="item.dictValue">
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div class="dialog-footer">
+        <el-button @click="cancelBatch">取 消</el-button>
+        <el-button type="primary" @click="submitBatchUpdate">保 存</el-button>
+      </div>
+    </minimizable-dialog>
     <!-- 批量选择视频弹窗 -->
     <minimizable-dialog :title="'选择视频'" :visible.sync="batchAddVisible" width="1200px" append-to-body class="batch-dialog" :close-on-click-modal="false" :before-close="cancelBeforeBatch"
                         @minimize="hasMinimizableDialog = true" @restore="hasMinimizableDialog = false">
@@ -720,7 +761,8 @@ import {
   getVideoResource,
   listVideoResource,
   updateVideoResource,
-  batchAddVideoResource
+  batchAddVideoResource,
+  batchUpdateVideoResource
 } from '@/api/course/videoResource'
 import {listUserCourseCategory,getCatePidList,getCateListByPid} from '@/api/course/userCourseCategory'
 import {getByIds, listCourseQuestionBank} from '@/api/course/courseQuestionBank'
@@ -828,6 +870,15 @@ export default {
       batchLoading: false,
       videoList: [],
 
+      // 批量修改相关
+      batchUpdateVisible: false,
+      batchUpdateLoading: false,
+      batchUpdateForm: {
+        typeId: null,
+        typeSubId: null,
+        ids: [],
+      },
+
       // 批量上传相关
       showUpload: false,
       batchUploadForm: {
@@ -1383,6 +1434,17 @@ export default {
       this.batchAddVisible = true;
       this.videoList = []; // 清空之前的视频列表
     },
+    /** 批量修改 */
+    handleBatchUpdate(){
+      if (this.ids.length === 0) {
+        this.$message.warning("请至少选择一条数据");
+        return;
+      }
+      this.batchUpdateForm.typeId = null;
+      this.batchUpdateForm.typeSubId = null;
+      this.batchUpdateForm.ids = this.ids; // 将选中的ID传递给批量修改表单
+      this.batchUpdateVisible = true;
+    },
     cancelBeforeBatch(done, cancel) {
       if (!this.videoList || this.videoList.length === 0) {
         done()
@@ -1414,6 +1476,35 @@ export default {
       }).catch(() => {
       });
     },
+    /** 批量修改 */
+    submitBatchUpdate() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.batchUpdateForm.ids.length === 0) {
+            this.$message.warning("未选择任何数据");
+            return;
+          }
+
+          this.$confirm('是否确认修改视频素材库编号为"' + this.batchUpdateForm.ids.join(',') + '"的数据项?', "警告", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning"
+          }).then(() => {
+            // 构造正确的参数格式
+            const params = new URLSearchParams();
+            params.append('typeId', this.batchUpdateForm.typeId || '');
+            params.append('typeSubId', this.batchUpdateForm.typeSubId || '');
+            params.append('ids', this.batchUpdateForm.ids.join(','));
+
+            return batchUpdateVideoResource(params);
+          }).then(() => {
+            this.getList();
+            this.batchUpdateVisible = false;
+            this.msgSuccess("修改成功");
+          }).catch(() => {});
+        }
+      });
+    },
 
     /** 提交批量添加 */
     submitBatchAdd() {

+ 252 - 0
src/views/his/statistics/ipadStatic.vue

@@ -0,0 +1,252 @@
+<template>
+    <div class="app-container">
+      <el-card>
+        <div slot="header" class="clearfix">
+          <span>设备绑定统计</span>
+        </div>
+
+        <el-tabs v-model="activeTab" @tab-click="handleTabClick">
+          <el-tab-pane label="按天统计" name="daily">
+            <el-form :inline="true" class="demo-form-inline">
+              <el-form-item label="日期">
+                <el-date-picker
+                  v-model="dailyDate"
+                  type="date"
+                  placeholder="选择日期"
+                  value-format="yyyy-MM-dd"
+                  @change="handleDailyDateChange"
+                ></el-date-picker>
+              </el-form-item>
+
+              <el-form-item>
+                <el-button type="primary" @click="searchDaily">查询</el-button>
+                <el-button @click="resetDaily">重置</el-button>
+                <el-button type="success" @click="exportDaily">导出</el-button>
+              </el-form-item>
+            </el-form>
+
+            <el-table
+              v-loading="dailyLoading"
+              border
+              :data="companyList"
+              style="width: 100%"
+            >
+              <el-table-column prop="date" label="日期" width="180" />
+              <el-table-column prop="companyName" label="公司名称" />
+              <el-table-column prop="bindCount" label="绑定台数" />
+            </el-table>
+
+            <div class="chart-container" style="margin-top: 20px; height: 400px">
+              <div ref="dailyChart" style="height: 100%; width: 100%"></div>
+            </div>
+          </el-tab-pane>
+
+          <el-tab-pane label="按月统计" name="monthly">
+            <el-form :inline="true" class="demo-form-inline">
+              <el-form-item label="月份">
+                <el-date-picker
+                  v-model="monthlyDate"
+                  type="month"
+                  placeholder="选择月份"
+                  value-format="yyyy-MM"
+                  @change="handleMonthlyDateChange"
+                ></el-date-picker>
+              </el-form-item>
+
+              <el-form-item>
+                <el-button type="primary" @click="searchMonthly">查询</el-button>
+                <el-button @click="resetMonthly">重置</el-button>
+                <el-button type="success" @click="exportMonthly">导出</el-button>
+              </el-form-item>
+            </el-form>
+
+            <el-table
+              v-loading="monthlyLoading"
+              border
+              :data="companyList"
+              style="width: 100%"
+            >
+              <el-table-column prop="date" label="月份" width="180" />
+              <el-table-column prop="companyName" label="公司名称" />
+              <el-table-column prop="bindCount" label="绑定台数" />
+            </el-table>
+
+            <div class="chart-container" style="margin-top: 20px; height: 400px">
+              <div ref="monthlyChart" style="height: 100%; width: 100%"></div>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </el-card>
+    </div>
+  </template>
+
+  <script>
+  import { ipadStaticTotal,exportIpadStaticByTime} from "@/api/company/statistics";
+
+  export default {
+    name: 'ipadStatic',
+    data() {
+      const today = this.parseTime(new Date(), '{y}-{m}-{d}')
+      const yesterday = this.parseTime(new Date(new Date().getTime() - 24 * 60 * 60 * 1000), '{y}-{m}-{d}')
+      return {
+        // 激活的标签页
+        activeTab: 'daily',
+        // 按天统计数据
+        dailyLoading: false,
+        dailyDate: yesterday, // 默认当天
+        // 按月统计数据
+        monthlyLoading: false,
+        monthlyDate: this.parseTime(new Date(), '{y}-{m}'), // 默认当月
+        companyList: [],
+      }
+    },
+    created() {
+      // 获取当天数据
+      this.getList()
+    },
+    methods: {
+      // 标签页切换
+      handleTabClick(tab) {
+        if (tab.name === 'daily') {
+          this.getList()
+        } else if (tab.name === 'monthly') {
+          this.getMonthlyList()
+        }
+      },
+
+      // 按天统计相关方法
+      handleDailyDateChange(val) {
+        if (val) {
+          this.dailyDate = val
+        }
+      },
+      searchDaily() {
+        this.getList()
+      },
+      resetDaily() {
+        const today = this.parseTime(new Date(), '{y}-{m}-{d}')
+        const yesterday = this.parseTime(new Date(new Date().getTime() - 24 * 60 * 60 * 1000), '{y}-{m}-{d}')
+        this.dailyDate = yesterday
+        this.getList()
+      },
+
+      // 导出
+      exportDaily() {
+        this.$confirm("是否确认导出数据项?", "警告", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning",
+        })
+            .then(() => {
+            this.exportLoading = true;
+            return exportIpadStaticByTime(this.dailyDate);
+            })
+            .then((response) => {
+            this.download(response.msg);
+            this.exportLoading = false;
+            })
+            .catch(() => { });
+      },
+      getList() {
+        this.dailyLoading = true
+        ipadStaticTotal(this.dailyDate).then(response => {
+          // 处理返回数据,添加日期字段用于表格和图表显示
+          this.companyList = response.list;
+          this.dailyLoading = false;
+        }).catch(() => {
+          this.dailyLoading = false;
+        });
+      },
+
+      // 按月统计相关方法
+      handleMonthlyDateChange(val) {
+        if (val) {
+          this.monthlyDate = val
+        }
+      },
+      searchMonthly() {
+        this.getMonthlyList()
+      },
+      resetMonthly() {
+        this.monthlyDate = this.parseTime(new Date(), '{y}-{m}')
+        this.getMonthlyList()
+      },
+      exportMonthly() {
+        this.$confirm("是否确认导出数据项?", "警告", {
+            confirmButtonText: "确定",
+            cancelButtonText: "取消",
+            type: "warning",
+        })
+            .then(() => {
+            this.exportLoading = true;
+            return exportIpadStaticByTime(this.monthlyDate);
+            })
+            .then((response) => {
+            this.download(response.msg);
+            this.exportLoading = false;
+            })
+            .catch(() => { });
+      },
+      getMonthlyList() {
+        this.monthlyLoading = true
+        ipadStaticTotal(this.monthlyDate).then(response => {
+          // 处理返回数据,添加月份字段用于表格和图表显示
+          this.companyList = response.list;
+          this.monthlyLoading = false;
+        }).catch(() => {
+          this.monthlyLoading = false;
+          this.monthlyTotal = 0;
+        });
+      },
+
+      // 日期格式化
+      parseTime(time, pattern) {
+        let date;
+        if (arguments.length === 0 || !time) {
+          return null
+        }
+        const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+
+        if (typeof time === 'object') {
+          date = time
+        } else {
+          if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
+            time = parseInt(time)
+          } else if (typeof time === 'string') {
+            time = time.replace(new RegExp(/-/gm), '/')
+          }
+          if ((typeof time === 'number') && (time.toString().length === 10)) {
+            time = time * 1000
+          }
+          date = new Date(time)
+        }
+        const formatObj = {
+          y: date.getFullYear(),
+          m: date.getMonth() + 1,
+          d: date.getDate(),
+          h: date.getHours(),
+          i: date.getMinutes(),
+          s: date.getSeconds(),
+          a: date.getDay()
+        }
+        const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+          let value = formatObj[key]
+          // Note: getDay() returns 0 on Sunday
+          if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
+          if (result.length > 0 && value < 10) {
+            value = '0' + value
+          }
+          return value || 0
+        })
+        return time_str
+      }
+    }
+  }
+  </script>
+
+  <style scoped>
+  .chart-container {
+    position: relative;
+    padding: 20px 0;
+  }
+  </style>

+ 755 - 0
src/views/user/rechargeTemplate/index.vue

@@ -0,0 +1,755 @@
+<template>
+  <div class="app-container">
+    <!-- 查询条件 -->
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="模板名称" prop="templateName">
+        <el-input
+          v-model="queryParams.templateName"
+          placeholder="请输入模板名称"
+          clearable
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
+          <el-option label="启用" value="1" />
+          <el-option label="禁用" value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标签" prop="tag">
+        <el-input
+          v-model="queryParams.tag"
+          placeholder="请输入标签"
+          clearable
+          @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="['recharge:template:add']"
+        >新增</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="['recharge:template:remove']"
+        >删除</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 表格数据 -->
+    <el-table v-loading="loading" :data="templateList" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="55" align="center" />
+      <el-table-column label="模板ID" align="center" prop="id" width="80" />
+      <el-table-column label="模板名称" align="center" prop="templateName" :show-overflow-tooltip="true" />
+      <el-table-column label="储值金额" align="center" prop="rechargeAmount" width="100">
+        <template slot-scope="scope">
+          <span>¥{{ scope.row.rechargeAmount }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="赠送金额" align="center" prop="bonusAmount" width="100">
+        <template slot-scope="scope">
+          <span>¥{{ scope.row.bonusAmount || 0 }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="标签" align="center" prop="tag" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.tag" size="mini">{{ scope.row.tag }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="限购次数" align="center" prop="purchaseLimit" width="100" />
+      <el-table-column label="排序" align="center" prop="sortOrder" width="80" />
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-switch
+            v-model="scope.row.status"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleStatusChange(scope.row)"
+          ></el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="默认选中" align="center" prop="isDefault" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.isDefault === 1" type="success" size="mini">是</el-tag>
+          <el-tag v-else type="info" size="mini">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="绑定优惠券" align="center" width="100">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.couponCount" type="primary" size="mini">{{ scope.row.couponCount }}张</el-tag>
+          <el-tag v-else type="info" size="mini">无</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="有效期" align="center" width="180">
+        <template slot-scope="scope">
+          <span v-if="scope.row.startTime && scope.row.endTime">
+            {{ parseTime(scope.row.startTime, '{y}-{m}-{d}') }} 至 {{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else>永久有效</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-view"
+            @click="handleDetail(scope.row)"
+          >详情</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['recharge:template:edit']"
+          >修改</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            @click="handleDelete(scope.row)"
+            v-hasPermi="['recharge:template:remove']"
+          >删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改充值模板对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="800px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="模板名称" prop="templateName">
+              <el-input v-model="form.templateName" placeholder="请输入模板名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="储值金额" prop="rechargeAmount">
+              <el-input-number v-model="form.rechargeAmount" :precision="2" :min="0" placeholder="请输入储值金额" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="赠送金额" prop="bonusAmount">
+              <el-input-number v-model="form.bonusAmount" :precision="2" :min="0" placeholder="请输入赠送金额" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="限购次数" prop="purchaseLimit">
+              <el-input-number v-model="form.purchaseLimit" :min="0" placeholder="请输入限购次数,0为不限制" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="排序序号" prop="sortOrder">
+              <el-input-number v-model="form.sortOrder" :min="0" placeholder="请输入排序序号" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="标签" prop="tag">
+              <el-input v-model="form.tag" placeholder="请输入标签" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="12">
+            <el-form-item label="状态" prop="status">
+              <el-radio-group v-model="form.status">
+                <el-radio :label="1">启用</el-radio>
+                <el-radio :label="0">禁用</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="默认选中" prop="isDefault">
+              <el-radio-group v-model="form.isDefault">
+                <el-radio :label="1">是</el-radio>
+                <el-radio :label="0">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="有效期" prop="validTime">
+              <el-date-picker
+                v-model="validTime"
+                type="datetimerange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                value-format="yyyy-MM-dd HH:mm:ss"
+                format="yyyy-MM-dd HH:mm:ss"
+              ></el-date-picker>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="图标" prop="iconUrl">
+              <el-upload
+                class="avatar-uploader"
+                :action="uploadFileUrl"
+                :headers="headers"
+                :show-file-list="false"
+                :on-success="handleIconSuccess"
+                :before-upload="beforeIconUpload"
+                :on-error="handleIconError"
+              >
+                <img v-if="form.iconUrl" :src="form.iconUrl" class="avatar" />
+                <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+              </el-upload>
+              <div class="el-upload__tip">
+                <div v-if="form.iconUrl" style="margin-top: 10px;">
+                  <el-button size="mini" type="danger" icon="el-icon-delete" @click="handleIconRemove">移除</el-button>
+                </div>
+                <div style="color: #999; font-size: 12px; margin-top: 5px;">
+                  只能上传jpg/png文件,且不超过2MB
+                </div>
+              </div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="详情链接" prop="detailUrl">
+              <el-input v-model="form.detailUrl" placeholder="请输入详情链接" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="描述文案" prop="shortDesc">
+              <el-input v-model="form.shortDesc" type="textarea" placeholder="请输入描述文案" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="权益详情" prop="benefitDetails">
+              <el-input v-model="form.benefitDetails" type="textarea" placeholder="请输入权益详情" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="绑定优惠券" prop="couponIdList">
+              <el-select
+                v-model="form.couponIdList"
+                multiple
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入优惠券名称搜索"
+                :remote-method="remoteCouponSearch"
+                :loading="couponLoading"
+                style="width: 100%"
+                @focus="handleCouponFocus"
+                @clear="handleCouponClear"
+                clearable
+              >
+                <el-option
+                  v-for="item in couponOptions"
+                  :key="item.couponId"
+                  :label="`${item.title}(¥${item.limitTime})`"
+                  :value="item.couponId"
+                >
+                  <span>{{ item.title }}</span>
+                  <span style="float: right; color: #8492a6; font-size: 13px">
+                    ¥{{ item.price }}
+                  </span>
+                </el-option>
+              </el-select>
+              <div class="el-form-item__tip" style="margin-top: 5px; color: #999; font-size: 12px;">
+                已选择 {{ form.couponIdList ? form.couponIdList.length : 0 }} 张优惠券
+              </div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 详情对话框 -->
+    <el-dialog title="充值模板详情" :visible.sync="detailOpen" width="800px" append-to-body>
+      <el-descriptions :column="2" border>
+        <el-descriptions-item label="模板名称">{{ detailData.templateName }}</el-descriptions-item>
+        <el-descriptions-item label="储值金额">¥{{ detailData.rechargeAmount }}</el-descriptions-item>
+        ```vue
+        <el-descriptions-item label="赠送金额">¥{{ detailData.bonusAmount || 0 }}</el-descriptions-item>
+        <el-descriptions-item label="限购次数">{{ detailData.purchaseLimit || '不限制' }}</el-descriptions-item>
+        <el-descriptions-item label="排序序号">{{ detailData.sortOrder }}</el-descriptions-item>
+        <el-descriptions-item label="标签">{{ detailData.tag || '无' }}</el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <el-tag :type="detailData.status === 1 ? 'success' : 'danger'">
+            {{ detailData.status === 1 ? '启用' : '禁用' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="默认选中">
+          <el-tag :type="detailData.isDefault === 1 ? 'success' : 'info'">
+            {{ detailData.isDefault === 1 ? '是' : '否' }}
+          </el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="有效期" :span="2">
+          <span v-if="detailData.startTime && detailData.endTime">
+            {{ parseTime(detailData.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }} 至 {{ parseTime(detailData.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
+          </span>
+          <span v-else>永久有效</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="图标" :span="2">
+          <img v-if="detailData.iconUrl" :src="getImageUrl(detailData.iconUrl)" style="max-height: 100px;" />
+          <span v-else>无</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="详情链接" :span="2">{{ detailData.detailUrl || '无' }}</el-descriptions-item>
+        <el-descriptions-item label="描述文案" :span="2">{{ detailData.shortDesc || '无' }}</el-descriptions-item>
+        <el-descriptions-item label="权益详情" :span="2">{{ detailData.benefitDetails || '无' }}</el-descriptions-item>
+        <el-descriptions-item label="绑定优惠券" :span="2">
+          <div v-if="detailData.coupons && detailData.coupons.length > 0">
+            <el-tag v-for="(coupon, index) in detailData.coupons" :key="index" type="success" style="margin-right: 5px; margin-bottom: 5px;">
+              {{ coupon.title }} (¥{{ coupon.price }})
+            </el-tag>
+          </div>
+          <span v-else>无</span>
+        </el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  listRechargeTemplate,
+  getRechargeTemplate,
+  delRechargeTemplate,
+  addRechargeTemplate,
+  updateRechargeTemplate,
+  updateRechargeTemplateStatus,
+  getCouponList
+} from "@/api/recharge/template";
+import { getToken } from "@/utils/auth";
+import { parseTime } from "@/utils/common";
+
+export default {
+  name: "RechargeTemplate",
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 充值模板表格数据
+      templateList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 是否显示详情弹出层
+      detailOpen: false,
+      // 详情数据
+      detailData: {},
+      // 有效期时间范围
+      validTime: [],
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        templateName: null,
+        status: null,
+        tag: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        templateName: [
+          { required: true, message: "模板名称不能为空", trigger: "blur" }
+        ],
+        rechargeAmount: [
+          { required: true, message: "储值金额不能为空", trigger: "blur" }
+        ],
+        status: [
+          { required: true, message: "状态不能为空", trigger: "change" }
+        ],
+        sortOrder: [
+          { required: true, message: "排序序号不能为空", trigger: "blur" }
+        ]
+      },
+      // 上传参数
+      uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
+      headers: {
+        Authorization: "Bearer " + getToken()
+      },
+      // 优惠券相关
+      couponOptions: [],
+      couponLoading: false,
+      couponSearchTimer: null // 搜索防抖定时器
+    };
+  },
+  created() {
+    this.getList();
+  },
+  beforeDestroy() {
+    // 组件销毁时清理定时器
+    if (this.couponSearchTimer) {
+      clearTimeout(this.couponSearchTimer);
+    }
+  },
+  methods: {
+    parseTime,
+    handleIconRemove(){
+      this.form.iconUrl = null;
+    },
+    /** 查询充值模板列表 */
+    getList() {
+      this.loading = true;
+      listRechargeTemplate(this.queryParams).then(response => {
+        this.templateList = 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;
+    },
+
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.loadDefaultCoupons(); // 新增时加载默认选项
+      this.open = true;
+      this.title = "添加充值模板";
+    },
+
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id || this.ids;
+      getRechargeTemplate(id).then(response => {
+        this.form = response.data;
+
+        // 设置有效期时间范围
+        if (this.form.startTime && this.form.endTime) {
+          this.validTime = [this.form.startTime, this.form.endTime];
+        }
+
+        // 如果已有绑定的优惠券,加载到选项中
+        if (this.form.couponIdList && this.form.couponIdList.length > 0) {
+          this.loadSelectedCoupons(this.form.couponIdList);
+        } else {
+          // 没有已选择的优惠券时,加载默认选项
+          this.loadDefaultCoupons();
+        }
+
+        this.open = true;
+        this.title = "修改充值模板";
+      });
+    },
+
+    /** 详情按钮操作 */
+    handleDetail(row) {
+      const id = row.id;
+      getRechargeTemplate(id).then(response => {
+        this.detailData = response.data;
+        this.detailOpen = true;
+      });
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          // 处理有效期时间
+          if (this.validTime && this.validTime.length === 2) {
+            this.form.startTime = this.validTime[0];
+            this.form.endTime = this.validTime[1];
+          } else {
+            this.form.startTime = null;
+            this.form.endTime = null;
+          }
+
+          if (this.form.id != null) {
+            updateRechargeTemplate(this.form).then(response => {
+              this.$message.success("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addRechargeTemplate(this.form).then(response => {
+              this.$message.success("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const ids = row.id || this.ids;
+      this.$confirm('是否确认删除充值模板编号为"' + ids + '"的数据项?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        return delRechargeTemplate(ids);
+      }).then(() => {
+        this.getList();
+        this.$message.success("删除成功");
+      }).catch(() => {});
+    },
+
+    /** 状态修改 */
+    handleStatusChange(row) {
+      let text = row.status === 1 ? "启用" : "停用";
+      this.$confirm('确认要"' + text + '""' + row.templateName + '"模板吗?').then(() => {
+        return updateRechargeTemplateStatus(row.id, row.status);
+      }).then(() => {
+        this.$message.success(text + "成功");
+      }).catch(() => {
+        row.status = row.status === 0 ? 1 : 0;
+      });
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    // 表单重置
+    reset() {
+      // 清理搜索定时器
+      if (this.couponSearchTimer) {
+        clearTimeout(this.couponSearchTimer);
+        this.couponSearchTimer = null;
+      }
+
+      this.form = {
+        id: null,
+        templateName: null,
+        rechargeAmount: null,
+        bonusAmount: null,
+        benefitDetails: null,
+        status: 1,
+        sortOrder: 0,
+        startTime: null,
+        endTime: null,
+        userType: null,
+        purchaseLimit: null,
+        tag: null,
+        shortDesc: null,
+        detailUrl: null,
+        iconUrl: null,
+        isDefault: 0,
+        couponIdList: []
+      };
+      this.validTime = [];
+      this.resetForm("form");
+      this.couponOptions = [];
+    },
+
+    // 优惠券下拉框获得焦点时
+    handleCouponFocus() {
+      // 如果没有选项且没有已选择的优惠券,加载默认选项
+      if (this.couponOptions.length === 0 && (!this.form.couponIdList || this.form.couponIdList.length === 0)) {
+        this.loadDefaultCoupons();
+      }
+    },
+
+    // 清空优惠券选择
+    handleCouponClear() {
+      this.form.couponIdList = [];
+    },
+
+    // 加载默认优惠券列表
+    loadDefaultCoupons() {
+      this.couponLoading = true;
+      getCouponList({
+        pageSize: 20,
+        pageNum: 1,
+        status: 1 // 只查询有效的优惠券
+      }).then(response => {
+        this.couponOptions = response.data.list || [];
+        this.couponLoading = false;
+      }).catch(() => {
+        this.couponLoading = false;
+        this.couponOptions = [];
+      });
+    },
+
+    // 远程搜索优惠券(添加防抖)
+    remoteCouponSearch(query) {
+      // 清除之前的定时器
+      if (this.couponSearchTimer) {
+        clearTimeout(this.couponSearchTimer);
+      }
+
+      // 设置新的定时器,300ms后执行搜索
+      this.couponSearchTimer = setTimeout(() => {
+        if (query && query.trim() !== '') {
+          this.couponLoading = true;
+          getCouponList({
+            couponName: query.trim(),
+            pageSize: 50, // 增加搜索结果数量
+            pageNum: 1,
+            status: 1 // 只查询有效的优惠券
+          }).then(response => {
+            this.couponOptions = response.data.list || [];
+            this.couponLoading = false;
+          }).catch(() => {
+            this.couponLoading = false;
+            this.couponOptions = [];
+            this.$message.error('搜索优惠券失败');
+          });
+        } else {
+          // 如果搜索为空,加载默认选项
+          this.loadDefaultCoupons();
+        }
+      }, 300);
+    },
+
+    // 加载已选择的优惠券
+    loadSelectedCoupons(couponIds) {
+      if (couponIds && couponIds.length > 0) {
+        this.couponLoading = true;
+        getCouponList({
+          couponIds: couponIds.join(',')
+        }).then(response => {
+          // 合并已选择的优惠券到选项中,避免重复
+          const selectedCoupons = response.data.list || [];
+          const existingIds = this.couponOptions.map(item => item.couponId);
+
+          selectedCoupons.forEach(coupon => {
+            if (!existingIds.includes(coupon.couponId)) {
+              this.couponOptions.push(coupon);
+            }
+          });
+
+          this.couponLoading = false;
+        }).catch(() => {
+          this.couponLoading = false;
+        });
+      }
+    },
+
+    // 获取完整的图片URL
+    getImageUrl(url) {
+      if (!url) return '';
+      // 如果已经是完整URL,直接返回
+      if (url.startsWith('http://') || url.startsWith('https://')) {
+        return url;
+      }
+      // 如果是相对路径,拼接基础URL
+      return process.env.VUE_APP_BASE_API + url;
+    },
+
+    // 图标上传前校验
+    beforeIconUpload(file) {
+      const isJPG = file.type === 'image/jpeg';
+      const isPNG = file.type === 'image/png';
+      const isGIF = file.type === 'image/gif';
+      const isWebP = file.type === 'image/webp';
+      const isLt2M = file.size / 1024 /1024 < 2; // 限制大小为2MB
+      if (!isJPG && !isPNG && !isGIF && !isWebP) {
+        this.$message.error('上传头像图片只能是 JPG、PNG、GIF 或 WebP 格式!');
+      }
+      if (!isLt2M) {
+        this.$message.error('上传头像图片大小不能超过 2MB!');
+      }
+      return (isJPG || isPNG || isGIF || isWebP) && isLt2M;
+    },
+
+    // 图标上传成功后处理
+    handleIconUploadSuccess(response, file) {
+      if (response && response.url) {
+        this.form.iconUrl = response.url; // 赋值上传后的图片URL
+      } else {
+        this.$message.error('图标上传失败,请重试');
+      }
+    },
+
+    // 图标上传失败处理
+    handleIconUploadError() {
+      this.$message.error('图标上传失败,请重试');
+    },
+    handleIconSuccess(response, file) {
+      // 处理图标上传成功的逻辑
+      this.form.iconUrl = response.url; // 假设返回的数据结构有URL
+      this.$message.success('图标上传成功!');
+    },
+    handleIconError(err, file) {
+      // 处理图标上传失败的逻辑
+      this.$message.error('图标上传失败,请重试.');
+    },
+  }
+};
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: right;
+}
+</style>
+