Forráskód Böngészése

积分产品区分位置

yuhongqi 22 órája
szülő
commit
ecf63ac35d

+ 18 - 2
src/views/his/integralGoods/index.vue

@@ -237,6 +237,12 @@
             >{{dict.dictLabel}}</el-radio>
           </el-radio-group>
         </el-form-item>
+        <el-form-item label="可见范围" prop="visibleScopeArr">
+          <el-select style="width: 240px" v-model="visibleScopeArr" placeholder="请选择可见范围" size="small" multiple>
+            <el-option label="APP" value="app" />
+            <el-option label="小程序" value="mini" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="所属小程序" prop="appIds">
           <el-select style="width: 240px" v-model="appIds" placeholder="请选择所属小程序" clearable size="small" multiple>
             <el-option
@@ -336,6 +342,7 @@ export default {
     return {
 
       appIds:[], // 选择的小程序
+      visibleScopeArr: ['app', 'mini'],
       // 小程序列表
       appMallOptions:[],
 
@@ -529,8 +536,11 @@ export default {
         sort: null,
         stock: null,
         descs: null,
-        createTime: null
+        createTime: null,
+        visibleScope: 'app,mini'
       };
+      this.appIds = [];
+      this.visibleScopeArr = ['app', 'mini'];
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -575,6 +585,7 @@ export default {
              this.photoArr=this.form.images.split(",");
            }
         this.appIds = this.form.appIds ? this.form.appIds.split(',') : [];
+        this.visibleScopeArr = this.form.visibleScope ? this.form.visibleScope.split(',') : ['app', 'mini'];
 
         //修改按钮
         setTimeout(() => {
@@ -591,11 +602,16 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
+          if (!this.visibleScopeArr || this.visibleScopeArr.length === 0) {
+            this.msgError("请选择可见范围");
+            return;
+          }
 
           // 小程序
           const params = {
             ...this.form,
-            appIds: this.appIds.join(',') // 数组转字符串
+            appIds: this.appIds.join(','),
+            visibleScope: this.visibleScopeArr.join(',')
           };
 
           if (this.form.goodsId != null) {

+ 30 - 3
src/views/hisStore/integralGoods/index.vue

@@ -145,6 +145,11 @@
 <!--      <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="visibleScope" min-width="100">
+        <template slot-scope="scope">
+          {{ formatVisibleScope(scope.row.visibleScope) }}
+        </template>
+      </el-table-column>
       <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">
@@ -226,6 +231,12 @@
         <el-form-item label="库存" prop="stock">
           <el-input-number v-model="form.stock"  :min="0"  label="库存"></el-input-number>
         </el-form-item>
+        <el-form-item label="可见范围" prop="visibleScopeArr">
+          <el-select style="width: 240px" v-model="visibleScopeArr" placeholder="请选择可见范围" size="small" multiple>
+            <el-option label="APP" value="app" />
+            <el-option label="小程序" value="mini" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="状态">
           <el-radio-group v-model="form.status">
             <el-radio
@@ -312,6 +323,7 @@ export default {
     return {
 
       appIds:[], // 选择的小程序
+      visibleScopeArr: ['app', 'mini'],
       // 小程序列表
       appMallOptions:[],
 
@@ -396,6 +408,13 @@ export default {
     });
   },
   methods: {
+    formatVisibleScope(visibleScope) {
+      if (!visibleScope) {
+        return 'APP、小程序';
+      }
+      const labelMap = { app: 'APP', mini: '小程序' };
+      return visibleScope.split(',').map(item => labelMap[item] || item).join('、');
+    },
     getAppMallOptions() {
       getAppMallOptions({pageNum:1,pageSize:100}).then(response => {
         this.appMallOptions = response.rows;
@@ -466,10 +485,13 @@ export default {
         sort: null,
         stock: null,
         descs: null,
-        createTime: null
+        createTime: null,
+        visibleScope: 'app,mini'
       };
       this.imageArr = [];
       this.photoArr = [];
+      this.appIds = [];
+      this.visibleScopeArr = ['app', 'mini'];
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -514,6 +536,7 @@ export default {
              this.photoArr=this.form.images.split(",");
            }
         this.appIds = this.form.appIds ? this.form.appIds.split(',') : [];
+        this.visibleScopeArr = this.form.visibleScope ? this.form.visibleScope.split(',') : ['app', 'mini'];
 
         //修改按钮
         setTimeout(() => {
@@ -530,11 +553,15 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
+          if (!this.visibleScopeArr || this.visibleScopeArr.length === 0) {
+            this.msgError("请选择可见范围");
+            return;
+          }
 
-          // 小程序
           const params = {
             ...this.form,
-            appIds: this.appIds.join(',') // 数组转字符串
+            appIds: this.appIds.join(','),
+            visibleScope: this.visibleScopeArr.join(',')
           };
 
           if (this.form.goodsId != null) {

+ 233 - 3
src/views/system/config/integralConfig.vue

@@ -92,7 +92,7 @@
         </el-col>
         <el-col :span="12">
           <el-form-item  label="浏览商品获得积分" prop="integralProduct">
-            <el-tooltip class="item" effect="dark" content="浏览商品每30秒获得多少积分" placement="top-end">
+            <el-tooltip class="item" effect="dark" content="浏览商城达到指定秒数获得多少积分" placement="top-end">
               <el-input-number  v-model="form11.integralProduct"   ></el-input-number>
             </el-tooltip>
           </el-form-item>
@@ -138,6 +138,59 @@
             </el-tooltip>
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item  label="首次完成购物积分" prop="integralFirstPurchase">
+            <el-input-number v-model="form11.integralFirstPurchase" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item  label="点播课留言积分" prop="integralComment">
+            <el-input-number v-model="form11.integralComment" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item  label="积分有效天数" prop="integralExpireDays">
+            <el-input-number v-model="form11.integralExpireDays" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item  label="积分有效期上线日" prop="integralExpireLaunchDate">
+            <el-input v-model="form11.integralExpireLaunchDate" placeholder="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item  label="邀请单日上限次数" prop="integralInviteDailyLimit">
+            <el-input-number v-model="form11.integralInviteDailyLimit" :min="1" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item  label="分享单日上限次数" prop="integralShareDailyLimit">
+            <el-input-number v-model="form11.integralShareDailyLimit" :min="1" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item  label="看课单日上限次数" prop="integralCourseDailyLimit">
+            <el-input-number v-model="form11.integralCourseDailyLimit" :min="1" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="12">
+          <el-form-item  label="浏览商城单日上限" prop="integralProductDailyLimit">
+            <el-input-number v-model="form11.integralProductDailyLimit" :min="1" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item  label="浏览商城所需秒数" prop="integralProductBrowseSeconds">
+            <el-input-number v-model="form11.integralProductBrowseSeconds" :min="1" />
+          </el-form-item>
+        </el-col>
       </el-row>
       <el-row>
         <el-col :span="12">
@@ -151,13 +204,65 @@
       <el-form-item label="单日可获总积分类型" prop="integralTypeByOneDay">
         <el-select v-model="form11.integralTypeByOneDay" multiple placeholder="请选择类型" filterable clearable size="small">
           <el-option
-            v-for="dict in integralLogTypeOptions"
+            v-for="dict in filteredIntegralLogTypeOptions"
             :key="dict.dictValue"
             :label="dict.dictLabel"
             :value="dict.dictValue"
           />
         </el-select>
+        <div style="margin-top: 8px;">
+          <span style="margin-right: 8px; color: #606266;">配置预览端:</span>
+          <el-radio-group v-model="logTypePreviewPlatform" size="small" @change="handleLogTypePreviewChange">
+            <el-radio-button label="all">全部</el-radio-button>
+            <el-radio-button label="app">APP</el-radio-button>
+            <el-radio-button label="mini">小程序</el-radio-button>
+          </el-radio-group>
+        </div>
       </el-form-item>
+
+      <el-divider content-position="left">积分类型展示端配置</el-divider>
+      <el-table :data="logTypeDisplayRows" border size="small" style="margin-bottom: 20px;">
+        <el-table-column label="类型值" prop="code" width="80" />
+        <el-table-column label="类型名称" prop="name" min-width="180" />
+        <el-table-column label="展示端" min-width="220">
+          <template slot-scope="scope">
+            <el-select
+              v-model="scope.row.scopes"
+              multiple
+              placeholder="请选择展示端"
+              size="small"
+              style="width: 200px"
+              @change="handleLogTypeScopeChange(scope.row)"
+            >
+              <el-option label="APP" value="app" />
+              <el-option label="小程序" value="mini" />
+            </el-select>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <el-divider content-position="left">任务展示端配置</el-divider>
+      <el-table :data="taskDisplayRows" border size="small" style="margin-bottom: 20px;">
+        <el-table-column label="任务名称" prop="name" min-width="160" />
+        <el-table-column label="任务类型" prop="typeLabel" width="100" />
+        <el-table-column label="展示端" min-width="220">
+          <template slot-scope="scope">
+            <el-select
+              v-model="scope.row.scopes"
+              multiple
+              placeholder="请选择展示端"
+              size="small"
+              style="width: 200px"
+              @change="handleTaskScopeChange(scope.row)"
+            >
+              <el-option label="APP" value="app" />
+              <el-option label="小程序" value="mini" />
+            </el-select>
+          </template>
+        </el-table-column>
+        <el-table-column label="说明" prop="tip" min-width="180" show-overflow-tooltip />
+      </el-table>
+
       <div style="display: flex; justify-content: flex-end;">
         <el-button type="primary" :disabled="saveLoading" :loading="saveLoading" @click="submitForm11">提  交</el-button>
       </div>
@@ -168,11 +273,39 @@
 <script>
 import { getConfigByKey, updateConfigByKey } from '@/api/system/config'
 
+const DEFAULT_TASK_DISPLAY_ROWS = [
+  { code: 'SIGN', name: '签到', typeLabel: '签到', defaultScopes: ['app', 'mini'], tip: '任务中心签到模块' },
+  { code: 'REGISTER', name: '首次注册登录', typeLabel: '新手', defaultScopes: ['app', 'mini'], tip: '完成手机号绑定注册' },
+  { code: 'DOWNLOAD_APP', name: '首次下载APP', typeLabel: '新手', defaultScopes: ['app'], tip: '仅 APP 端可完成' },
+  { code: 'FIRST_PURCHASE', name: '首次完成购物', typeLabel: '新手', defaultScopes: ['app', 'mini'], tip: '全渠道首购奖励' },
+  { code: 'INVITED', name: '填写朋友邀请码', typeLabel: '新手', defaultScopes: ['app', 'mini'], tip: '被邀请奖励' },
+  { code: 'SHARE', name: '分享得积分', typeLabel: '每日', defaultScopes: ['app', 'mini'], tip: 'APP+小程序合计日限' },
+  { code: 'COURSE', name: '学习首页课程', typeLabel: '每日', defaultScopes: ['app'], tip: '默认仅 APP 展示' },
+  { code: 'BROWSE_MALL', name: '浏览商城60秒', typeLabel: '每日', defaultScopes: ['app', 'mini'], tip: '浏览商城任务' },
+  { code: 'INVITE', name: '邀好友享积分', typeLabel: '每日', defaultScopes: ['app', 'mini'], tip: '邀请新用户' }
+]
+
+const LOG_TYPE_SCOPE_OVERRIDES = {
+  '10': ['app'],
+  '28': ['app']
+}
+
 export default {
   name: "IntegralConfig",
+  computed: {
+    filteredIntegralLogTypeOptions() {
+      if (this.logTypePreviewPlatform === 'all') {
+        return this.integralLogTypeOptions
+      }
+      return this.integralLogTypeOptions.filter(dict => this.isLogTypeVisible(dict.dictValue, this.logTypePreviewPlatform))
+    }
+  },
   data() {
     return {
       integralLogTypeOptions: [],
+      taskDisplayRows: [],
+      logTypeDisplayRows: [],
+      logTypePreviewPlatform: 'all',
       form11: {
         integralRegister: null,
         integralFinishConsultation: null,
@@ -193,26 +326,123 @@ export default {
         integralTypeByOneDay: null,
         integralSubscriptCourse: null,
         downloadAppIntegral: null,
+        integralFirstPurchase: 20,
+        integralInviteDailyLimit: 3,
+        integralShareDailyLimit: 5,
+        integralCourseDailyLimit: 5,
+        integralProductDailyLimit: 1,
+        integralProductBrowseSeconds: 60,
+        integralComment: 100,
+        integralExpireDays: 90,
+        integralExpireLaunchDate: null,
+        taskDisplayScope: {},
+        logTypeDisplayScope: {},
       },
       saveLoading: false,
     }
   },
   created() {
+    this.initTaskDisplayRows()
     this.getConfigByKey("his.integral");
     this.getDicts("sys_integral_log_type").then(response => {
       this.integralLogTypeOptions = response.data;
+      this.initLogTypeDisplayRows(this.form11.logTypeDisplayScope || {}, response.data)
     });
   },
   methods: {
+    getDefaultLogTypeScopes(code) {
+      return LOG_TYPE_SCOPE_OVERRIDES[code] ? [...LOG_TYPE_SCOPE_OVERRIDES[code]] : ['app', 'mini']
+    },
+    isLogTypeVisible(code, platform) {
+      if (platform === 'all') {
+        return true
+      }
+      const row = this.logTypeDisplayRows.find(item => item.code === String(code))
+      const scopes = row ? row.scopes : this.getDefaultLogTypeScopes(String(code))
+      return scopes.includes(platform)
+    },
+    initLogTypeDisplayRows(logTypeDisplayScope = {}, dictOptions = []) {
+      const rows = (dictOptions || []).map(dict => {
+        const code = String(dict.dictValue)
+        const savedScope = logTypeDisplayScope[code]
+        const scopes = savedScope
+          ? savedScope.split(',').filter(Boolean)
+          : this.getDefaultLogTypeScopes(code)
+        return {
+          code,
+          name: dict.dictLabel,
+          scopes
+        }
+      })
+      rows.sort((a, b) => Number(a.code) - Number(b.code))
+      this.logTypeDisplayRows = rows
+    },
+    handleLogTypeScopeChange(row) {
+      if (!row.scopes || row.scopes.length === 0) {
+        row.scopes = this.getDefaultLogTypeScopes(row.code)
+        this.msgWarning('至少选择一个展示端,已恢复默认配置')
+      }
+    },
+    handleLogTypePreviewChange() {
+      // 仅影响上方「单日可获总积分类型」下拉预览,不改已保存值
+    },
+    buildLogTypeDisplayScope() {
+      const logTypeDisplayScope = {}
+      this.logTypeDisplayRows.forEach(row => {
+        logTypeDisplayScope[row.code] = (row.scopes || this.getDefaultLogTypeScopes(row.code)).join(',')
+      })
+      return logTypeDisplayScope
+    },
+    initTaskDisplayRows(taskDisplayScope = {}) {
+      this.taskDisplayRows = DEFAULT_TASK_DISPLAY_ROWS.map(item => {
+        const savedScope = taskDisplayScope[item.code]
+        const scopes = savedScope
+          ? savedScope.split(',').filter(Boolean)
+          : [...item.defaultScopes]
+        return {
+          ...item,
+          scopes
+        }
+      })
+    },
+    handleTaskScopeChange(row) {
+      if (!row.scopes || row.scopes.length === 0) {
+        row.scopes = [...row.defaultScopes]
+        this.msgWarning('至少选择一个展示端,已恢复默认配置')
+      }
+    },
+    buildTaskDisplayScope() {
+      const taskDisplayScope = {}
+      this.taskDisplayRows.forEach(row => {
+        taskDisplayScope[row.code] = (row.scopes || row.defaultScopes).join(',')
+      })
+      return taskDisplayScope
+    },
     getConfigByKey(key){
       getConfigByKey(key).then(response => {
         this.configId=response.data.configId;
         this.configKey=response.data.configKey;
-        this.form11 =JSON.parse(response.data.configValue);
+        const configValue = Object.assign({
+          integralFirstPurchase: 20,
+          integralInviteDailyLimit: 3,
+          integralShareDailyLimit: 5,
+          integralCourseDailyLimit: 5,
+          integralProductDailyLimit: 1,
+          integralProductBrowseSeconds: 60,
+          integralComment: 100,
+          integralExpireDays: 90,
+          taskDisplayScope: {},
+          logTypeDisplayScope: {}
+        }, JSON.parse(response.data.configValue));
+        this.form11 = configValue
+        this.initTaskDisplayRows(configValue.taskDisplayScope || {})
+        this.initLogTypeDisplayRows(configValue.logTypeDisplayScope || {}, this.integralLogTypeOptions)
       });
     },
     submitForm11(){
       this.saveLoading = true
+      this.form11.taskDisplayScope = this.buildTaskDisplayScope()
+      this.form11.logTypeDisplayScope = this.buildLogTypeDisplayScope()
       const param={configId:this.configId,configValue:JSON.stringify(this.form11)}
       updateConfigByKey(param).then(response => {
         const {code} = response