Browse Source

随机红包

xw 18 hours ago
parent
commit
a6311b20db
2 changed files with 355 additions and 92 deletions
  1. 23 0
      src/api/course/courseRandomRedPacket.js
  2. 332 92
      src/views/course/courseRandomRedPacket/index.vue

+ 23 - 0
src/api/course/courseRandomRedPacket.js

@@ -7,6 +7,13 @@ export function getCourseRandomRedPacket(project) {
   })
 }
 
+export function listCourseRandomRedPacket() {
+  return request({
+    url: '/course/courseRandomRedPacket/list',
+    method: 'get'
+  })
+}
+
 export function saveCourseRandomRedPacket(data) {
   return request({
     url: '/course/courseRandomRedPacket',
@@ -14,3 +21,19 @@ export function saveCourseRandomRedPacket(data) {
     data: data
   })
 }
+
+export function batchSaveCourseRandomRedPacket(data) {
+  return request({
+    url: '/course/courseRandomRedPacket/batch',
+    method: 'post',
+    data: data
+  })
+}
+
+export function copyCourseRandomRedPacket(data) {
+  return request({
+    url: '/course/courseRandomRedPacket/copy',
+    method: 'post',
+    data: data
+  })
+}

+ 332 - 92
src/views/course/courseRandomRedPacket/index.vue

@@ -11,15 +11,26 @@
         <el-tag type="info" size="small">红包模块 v2.0</el-tag>
       </div>
 
-      <el-form ref="form" :model="form" :rules="rules" label-width="120px" v-loading="loading">
+      <el-tabs v-model="activeTab" type="card" class="config-tabs">
+        <el-tab-pane label="单项目配置" name="single" />
+        <el-tab-pane label="批量配置" name="batch" />
+      </el-tabs>
+
+      <el-form
+        v-show="activeTab === 'single'"
+        ref="singleFormRef"
+        :model="singleForm"
+        :rules="singleRules"
+        label-width="120px"
+        v-loading="loading"
+      >
         <el-form-item label="课程项目" prop="project">
           <el-select
-            v-model="form.project"
+            v-model="singleForm.project"
             placeholder="请选择课程项目"
             filterable
             clearable
             style="width: 420px"
-            @change="handleProjectChange"
           >
             <el-option
               v-for="item in projectOptions"
@@ -34,14 +45,14 @@
 
         <el-form-item label="开启随机红包">
           <el-switch
-            v-model="form.enableRandom"
+            v-model="singleForm.enableRandom"
             active-color="#13ce66"
             inactive-color="#ff4949"
           />
         </el-form-item>
 
-        <template v-if="form.enableRandom">
-          <div v-for="(tier, index) in form.tiers" :key="index" class="tier-row">
+        <template v-if="singleForm.enableRandom">
+          <div v-for="(tier, index) in singleForm.tiers" :key="'single-' + index" class="tier-row">
             <el-form-item
               label="红包金额"
               :prop="'tiers.' + index + '.amount'"
@@ -77,15 +88,15 @@
               type="text"
               icon="el-icon-delete"
               class="delete-btn"
-              :disabled="form.tiers.length <= 1"
-              @click="removeTier(index)"
+              :disabled="singleForm.tiers.length <= 1"
+              @click="removeTier('singleForm', index)"
             />
           </div>
 
           <el-form-item>
-            <el-button type="primary" plain icon="el-icon-plus" @click="addTier">新增档位</el-button>
-            <span class="weight-total" :class="{ error: totalWeight !== 100 }">
-              权重总和:{{ totalWeight }}%
+            <el-button type="primary" plain icon="el-icon-plus" @click="addTier('singleForm')">新增档位</el-button>
+            <span class="weight-total" :class="{ error: singleTotalWeight !== 100 }">
+              权重总和:{{ singleTotalWeight }}%
             </span>
           </el-form-item>
         </template>
@@ -94,14 +105,16 @@
 
         <el-form-item label="允许企业">
           <el-select
-            v-model="whitelistCompanyIds"
+            v-model="singleWhitelistCompanyIds"
             multiple
             filterable
             clearable
             collapse-tags
             placeholder="不选择代表全部企业走随机红包"
             style="width: 100%"
+            @change="handleWhitelistChange('single')"
           >
+            <el-option label="全部企业 (ALL)" value="ALL" />
             <el-option
               v-for="item in companyOptions"
               :key="item.dictValue"
@@ -113,8 +126,125 @@
         </el-form-item>
 
         <el-form-item>
-          <el-button type="primary" :loading="submitting" @click="submitForm">保存配置</el-button>
-          <el-button @click="resetForm">重置</el-button>
+          <el-button type="primary" :loading="submitting" @click="submitSingleForm">保存配置</el-button>
+          <el-button @click="resetSingleForm">重置</el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-form
+        v-show="activeTab === 'batch'"
+        ref="batchFormRef"
+        :model="batchForm"
+        :rules="batchRules"
+        label-width="120px"
+        v-loading="batchLoading"
+      >
+        <el-form-item label="课程项目" prop="projects">
+          <el-select
+            v-model="batchForm.projects"
+            placeholder="请选择课程项目(可多选)"
+            filterable
+            clearable
+            multiple
+            collapse-tags
+            style="width: 420px"
+          >
+            <el-option
+              v-for="item in projectOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="Number(item.dictValue)"
+            />
+          </el-select>
+        </el-form-item>
+
+        <el-divider content-position="left">红包开关配置</el-divider>
+
+        <el-form-item label="开启随机红包">
+          <el-switch
+            v-model="batchForm.enableRandom"
+            active-color="#13ce66"
+            inactive-color="#ff4949"
+          />
+        </el-form-item>
+
+        <template v-if="batchForm.enableRandom">
+          <div v-for="(tier, index) in batchForm.tiers" :key="'batch-' + index" class="tier-row">
+            <el-form-item
+              label="红包金额"
+              :prop="'tiers.' + index + '.amount'"
+              :rules="[{ required: true, message: '请填写金额', trigger: 'blur' }]"
+            >
+              <el-input-number
+                v-model="tier.amount"
+                :min="0.01"
+                :max="200"
+                :step="0.01"
+                :precision="2"
+                controls-position="right"
+              />
+              <span class="unit">元</span>
+            </el-form-item>
+            <el-form-item
+              label="权重占比"
+              :prop="'tiers.' + index + '.weight'"
+              :rules="[
+                { required: true, message: '请填写权重', trigger: 'blur' },
+                { type: 'number', min: 1, message: '权重不能小于0', trigger: 'blur' }
+              ]"
+            >
+              <el-input-number
+                v-model="tier.weight"
+                :min="1"
+                :max="100"
+                controls-position="right"
+              />
+              <span class="unit">%</span>
+            </el-form-item>
+            <el-button
+              type="text"
+              icon="el-icon-delete"
+              class="delete-btn"
+              :disabled="batchForm.tiers.length <= 1"
+              @click="removeTier('batchForm', index)"
+            />
+          </div>
+
+          <el-form-item>
+            <el-button type="primary" plain icon="el-icon-plus" @click="addTier('batchForm')">新增档位</el-button>
+            <span class="weight-total" :class="{ error: batchTotalWeight !== 100 }">
+              权重总和:{{ batchTotalWeight }}%
+            </span>
+          </el-form-item>
+        </template>
+
+        <el-divider content-position="left">企业白名单</el-divider>
+
+        <el-form-item label="允许企业">
+          <el-select
+            v-model="batchWhitelistCompanyIds"
+            multiple
+            filterable
+            clearable
+            collapse-tags
+            placeholder="不选择代表全部企业走随机红包"
+            style="width: 100%"
+            @change="handleWhitelistChange('batch')"
+          >
+            <el-option label="全部企业 (ALL)" value="ALL" />
+            <el-option
+              v-for="item in companyOptions"
+              :key="item.dictValue"
+              :label="item.dictLabel"
+              :value="String(item.dictValue)"
+            />
+          </el-select>
+          <div class="tip">多个企业ID逗号分隔,内部存储ALL代表全部企业可参与随机红包</div>
+        </el-form-item>
+
+        <el-form-item>
+          <el-button type="primary" :loading="submitting" @click="submitBatchForm">批量保存</el-button>
+          <el-button @click="resetBatchForm">重置</el-button>
         </el-form-item>
       </el-form>
     </el-card>
@@ -122,34 +252,81 @@
 </template>
 
 <script>
-import { getCourseRandomRedPacket, saveCourseRandomRedPacket } from '@/api/course/courseRandomRedPacket'
+import {
+  getCourseRandomRedPacket,
+  saveCourseRandomRedPacket,
+  batchSaveCourseRandomRedPacket
+} from '@/api/course/courseRandomRedPacket'
 import { allList } from '@/api/company/company'
 
 const defaultTier = () => ({ amount: 1.88, weight: 50 })
 
+const defaultConfig = () => ({
+  enableRandom: false,
+  companyWhitelist: '',
+  tiers: [defaultTier()]
+})
+
 export default {
   name: 'CourseRandomRedPacket',
   data() {
     return {
+      activeTab: 'single',
       loading: false,
+      batchLoading: false,
       submitting: false,
       projectOptions: [],
       companyOptions: [],
-      whitelistCompanyIds: [],
-      form: {
+      singleWhitelistCompanyIds: [],
+      batchWhitelistCompanyIds: [],
+      batchReferenceProject: null,
+      singleForm: {
         project: null,
-        enableRandom: false,
-        companyWhitelist: '',
-        tiers: [defaultTier()]
+        ...defaultConfig()
+      },
+      batchForm: {
+        projects: [],
+        ...defaultConfig()
       },
-      rules: {
+      singleRules: {
         project: [{ required: true, message: '请选择课程项目', trigger: 'change' }]
+      },
+      batchRules: {
+        projects: [{ type: 'array', required: true, min: 1, message: '请至少选择一个课程项目', trigger: 'change' }]
       }
     }
   },
   computed: {
-    totalWeight() {
-      return (this.form.tiers || []).reduce((sum, item) => sum + (Number(item.weight) || 0), 0)
+    singleTotalWeight() {
+      return this.calcTotalWeight(this.singleForm.tiers)
+    },
+    batchTotalWeight() {
+      return this.calcTotalWeight(this.batchForm.tiers)
+    }
+  },
+  watch: {
+    'singleForm.project'(project) {
+      if (project) {
+        this.loadSingleConfig(project)
+      } else {
+        this.applyDefaultConfig(this.singleForm)
+      }
+    },
+    'batchForm.projects': {
+      handler(projects) {
+        if (!projects || !projects.length) {
+          this.batchReferenceProject = null
+          this.applyDefaultConfig(this.batchForm)
+          return
+        }
+        const referenceProject = projects[0]
+        if (referenceProject === this.batchReferenceProject) {
+          return
+        }
+        this.batchReferenceProject = referenceProject
+        this.loadBatchConfig(referenceProject)
+      },
+      deep: true
     }
   },
   created() {
@@ -160,107 +337,166 @@ export default {
       this.companyOptions = res.rows || []
     })
     if (this.$route.query.project) {
-      this.form.project = Number(this.$route.query.project)
-      this.loadConfig()
+      this.singleForm.project = Number(this.$route.query.project)
     }
   },
   methods: {
-    handleProjectChange() {
-      if (this.form.project) {
-        this.loadConfig()
+    calcTotalWeight(tiers) {
+      return (tiers || []).reduce((sum, item) => sum + (Number(item.weight) || 0), 0)
+    },
+    applyDefaultConfig(form) {
+      const defaults = defaultConfig()
+      form.enableRandom = defaults.enableRandom
+      form.companyWhitelist = defaults.companyWhitelist
+      form.tiers = JSON.parse(JSON.stringify(defaults.tiers))
+      this.parseWhitelistToUi(form === this.singleForm ? 'single' : 'batch')
+    },
+    applyConfigData(form, data) {
+      if (data) {
+        form.enableRandom = !!data.enableRandom
+        form.companyWhitelist = data.companyWhitelist || ''
+        form.tiers = (data.tiers && data.tiers.length)
+          ? JSON.parse(JSON.stringify(data.tiers.map(item => ({
+            amount: Number(item.amount),
+            weight: Number(item.weight)
+          }))))
+          : JSON.parse(JSON.stringify(defaultConfig().tiers))
       } else {
-        this.resetForm(false)
+        this.applyDefaultConfig(form)
+        return
+      }
+      this.parseWhitelistToUi(form === this.singleForm ? 'single' : 'batch')
+    },
+    parseWhitelistToUi(mode) {
+      const form = mode === 'single' ? this.singleForm : this.batchForm
+      const whitelist = (form.companyWhitelist || '').trim()
+      const idsKey = mode === 'single' ? 'singleWhitelistCompanyIds' : 'batchWhitelistCompanyIds'
+      if (!whitelist || whitelist.toUpperCase() === 'ALL') {
+        this[idsKey] = whitelist.toUpperCase() === 'ALL' ? ['ALL'] : []
+        return
+      }
+      this[idsKey] = whitelist.split(',').map(item => item.trim()).filter(Boolean)
+    },
+    buildWhitelistFromUi(mode) {
+      const companyIds = mode === 'single' ? this.singleWhitelistCompanyIds : this.batchWhitelistCompanyIds
+      if (companyIds.includes('ALL')) {
+        return 'ALL'
       }
+      if (companyIds && companyIds.length) {
+        return companyIds.join(',')
+      }
+      return ''
     },
-    loadConfig() {
-      if (!this.form.project) {
+    handleWhitelistChange(mode) {
+      const idsKey = mode === 'single' ? 'singleWhitelistCompanyIds' : 'batchWhitelistCompanyIds'
+      const companyIds = this[idsKey]
+      if (!companyIds.length) {
+        return
+      }
+      if (companyIds.includes('ALL')) {
+        this[idsKey] = ['ALL']
         return
       }
+      this[idsKey] = companyIds.filter(id => id !== 'ALL')
+    },
+    loadSingleConfig(project) {
       this.loading = true
-      getCourseRandomRedPacket(this.form.project).then(res => {
-        const data = res.data
-        if (data) {
-          this.form.enableRandom = !!data.enableRandom
-          this.form.companyWhitelist = data.companyWhitelist || ''
-          this.form.tiers = (data.tiers && data.tiers.length)
-            ? data.tiers.map(item => ({
-              amount: Number(item.amount),
-              weight: Number(item.weight)
-            }))
-            : [defaultTier()]
-          this.parseWhitelist()
-        } else {
-          this.form.enableRandom = false
-          this.form.companyWhitelist = ''
-          this.form.tiers = [defaultTier()]
-          this.whitelistCompanyIds = []
-        }
+      getCourseRandomRedPacket(project).then(res => {
+        this.applyConfigData(this.singleForm, res.data)
       }).finally(() => {
         this.loading = false
       })
     },
-    parseWhitelist() {
-      const whitelist = (this.form.companyWhitelist || '').trim()
-      if (!whitelist || whitelist.toUpperCase() === 'ALL') {
-        this.whitelistCompanyIds = []
-        return
+    loadBatchConfig(project) {
+      this.batchLoading = true
+      getCourseRandomRedPacket(project).then(res => {
+        this.applyConfigData(this.batchForm, res.data)
+      }).finally(() => {
+        this.batchLoading = false
+      })
+    },
+    validateConfig(form, totalWeight) {
+      if (form.enableRandom) {
+        if (totalWeight !== 100) {
+          this.$message.warning('权重总和必须为 100%')
+          return false
+        }
+        if (!form.tiers.length) {
+          this.$message.warning('至少保留一个红包档位')
+          return false
+        }
       }
-      this.whitelistCompanyIds = whitelist.split(',').map(item => item.trim()).filter(Boolean)
+      return true
     },
-    buildWhitelist() {
-      if (!this.whitelistCompanyIds || this.whitelistCompanyIds.length === 0) {
-        return ''
+    buildPayload(form, mode) {
+      return {
+        enableRandom: form.enableRandom,
+        companyWhitelist: this.buildWhitelistFromUi(mode),
+        tiers: form.enableRandom ? form.tiers : []
       }
-      return this.whitelistCompanyIds.join(',')
     },
-    addTier() {
-      this.form.tiers.push({ amount: 1.88, weight: 10 })
+    addTier(formName) {
+      this[formName].tiers.push({ amount: 1.88, weight: 10 })
     },
-    removeTier(index) {
-      this.form.tiers.splice(index, 1)
+    removeTier(formName, index) {
+      this[formName].tiers.splice(index, 1)
     },
-    resetForm(clearProject = true) {
-      if (clearProject) {
-        this.form.project = null
-      }
-      this.form.enableRandom = false
-      this.form.companyWhitelist = ''
-      this.form.tiers = [defaultTier()]
-      this.whitelistCompanyIds = []
+    resetSingleForm() {
+      this.singleForm.project = null
+      this.applyDefaultConfig(this.singleForm)
       this.$nextTick(() => {
-        if (this.$refs.form) {
-          this.$refs.form.clearValidate()
+        if (this.$refs.singleFormRef) {
+          this.$refs.singleFormRef.clearValidate()
         }
       })
     },
-    submitForm() {
-      this.$refs.form.validate(valid => {
+    resetBatchForm() {
+      this.batchForm.projects = []
+      this.batchReferenceProject = null
+      this.applyDefaultConfig(this.batchForm)
+      this.$nextTick(() => {
+        if (this.$refs.batchFormRef) {
+          this.$refs.batchFormRef.clearValidate()
+        }
+      })
+    },
+    submitSingleForm() {
+      this.$refs.singleFormRef.validate(valid => {
         if (!valid) {
           return
         }
-        if (this.form.enableRandom) {
-          if (this.totalWeight !== 100) {
-            this.$message.warning('权重总和必须为 100%')
-            return
-          }
-          if (!this.form.tiers.length) {
-            this.$message.warning('至少保留一个红包档位')
-            return
-          }
+        if (!this.validateConfig(this.singleForm, this.singleTotalWeight)) {
+          return
         }
         this.submitting = true
-        const payload = {
-          project: this.form.project,
-          enableRandom: this.form.enableRandom,
-          companyWhitelist: this.buildWhitelist(),
-          tiers: this.form.enableRandom ? this.form.tiers : []
-        }
-        saveCourseRandomRedPacket(payload).then(() => {
+        saveCourseRandomRedPacket({
+          project: this.singleForm.project,
+          ...this.buildPayload(this.singleForm, 'single')
+        }).then(() => {
           this.$message.success('保存成功')
         }).finally(() => {
           this.submitting = false
         })
       })
+    },
+    submitBatchForm() {
+      this.$refs.batchFormRef.validate(valid => {
+        if (!valid) {
+          return
+        }
+        if (!this.validateConfig(this.batchForm, this.batchTotalWeight)) {
+          return
+        }
+        this.submitting = true
+        batchSaveCourseRandomRedPacket({
+          projects: this.batchForm.projects,
+          ...this.buildPayload(this.batchForm, 'batch')
+        }).then(() => {
+          this.$message.success(`已成功配置 ${this.batchForm.projects.length} 个项目`)
+        }).finally(() => {
+          this.submitting = false
+        })
+      })
     }
   }
 }
@@ -286,6 +522,10 @@ export default {
   line-height: 1.6;
 }
 
+.course-random-red-packet .config-tabs {
+  margin-bottom: 16px;
+}
+
 .course-random-red-packet .tier-row {
   display: flex;
   align-items: flex-start;
@@ -313,4 +553,4 @@ export default {
 .course-random-red-packet .weight-total.error {
   color: #f56c6c;
 }
-</style>
+</style>