Переглянути джерело

1、调整直播弹窗位置
2、调整会员管理转移直播会员信息

yys 3 тижнів тому
батько
коміт
0470bc7951

+ 9 - 0
src/api/company/companyUser.js

@@ -120,3 +120,12 @@ export function getCompanyUserListPage(query) {
     params: query
   })
 }
+
+// 批量转移直播归属销售
+export function batchBindCompanyUserId(data) {
+  return request({
+    url: '/company/companyUser/batchBindCompanyUserId',
+    method: 'post',
+    data: data
+  })
+}

+ 90 - 1
src/views/his/user/index.vue

@@ -120,6 +120,16 @@
           v-hasPermi="['his:user:export']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-d-arrow-right"
+          size="mini"
+          :disabled="multiple"
+          @click="handleTransfer"
+        >转移直播归属销售</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
@@ -310,12 +320,42 @@
         <el-button type="danger" @click="confirmDisable">确认拉黑</el-button>
       </div>
     </el-dialog>
+
+    <!-- 转移直播归属销售 -->
+    <el-dialog title="转移直播归属销售" :visible.sync="openTransferDialog" width="500px" append-to-body>
+      <el-form ref="transferForm" :model="transferForm" :rules="transferRules" label-width="120px">
+        <el-form-item label="转移至销售" prop="companyUserId">
+          <el-select
+            v-model="transferForm.companyUserId"
+            placeholder="请选择销售"
+            filterable
+            remote
+            clearable
+            style="width: 100%"
+            :remote-method="loadBindCompanyUserOptions"
+            :loading="bindCompanyUserLoading"
+          >
+            <el-option
+              v-for="item in bindCompanyUserList"
+              :key="item.userId"
+              :label="`${item.nickName}-${item.userId}`"
+              :value="item.userId"
+            />
+          </el-select>
+        </el-form-item>
+        <p>确定将选中的 <strong>{{ ids.length }}</strong> 个用户的直播归属销售进行转移吗?</p>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitTransfer">确 定</el-button>
+        <el-button @click="cancelTransfer">取 消</el-button>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script>
 import { listUser, getUser, delUser, addUser, updateUser, exportUser, addPoint,checkIsSales } from "@/api/his/user";
-import { getCompanyUserList, changeCompanyUser, getCompanyList } from '@/api/company/companyUser';
+import { getCompanyUserList, changeCompanyUser, getCompanyList, batchBindCompanyUserId } from '@/api/company/companyUser';
 import { getAllUserListLimit } from "@/api/store/user";
 import userDetails from '../../components/his/userDetails.vue';
 import userDetailsByNew from './userDetails.vue';
@@ -345,6 +385,15 @@ export default {
       companyUserOptions: [],
       bindCompanyUserList: [],
       bindCompanyUserLoading: false,
+      openTransferDialog: false,
+      transferForm: {
+        companyUserId: null
+      },
+      transferRules: {
+        companyUserId: [
+          { required: true, message: "请选择转移至销售", trigger: "change" }
+        ]
+      },
       show:{
               title:"用户详情",
               open:false,
@@ -628,6 +677,46 @@ export default {
       this.single = selection.length!==1
       this.multiple = !selection.length
     },
+    /** 转移直播归属销售 */
+    handleTransfer() {
+      if (this.ids.length === 0) {
+        this.$message.warning("请至少选择一个用户进行转移");
+        return;
+      }
+      this.transferForm = {
+        companyUserId: null
+      };
+      this.openTransferDialog = true;
+      this.loadBindCompanyUserOptions();
+      this.$nextTick(() => {
+        if (this.$refs.transferForm) {
+          this.$refs.transferForm.clearValidate();
+        }
+      });
+    },
+    cancelTransfer() {
+      this.openTransferDialog = false;
+      this.transferForm = {
+        companyUserId: null
+      };
+    },
+    submitTransfer() {
+      this.$refs["transferForm"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        batchBindCompanyUserId({
+          companyUserId: this.transferForm.companyUserId,
+          fsUserIds: this.ids
+        }).then(response => {
+          if (response.code === 200) {
+            this.$message.success("转移成功");
+            this.openTransferDialog = false;
+            this.getList();
+          }
+        });
+      });
+    },
     /** 新增按钮操作 */
     handleAdd() {
       this.reset();

+ 11 - 0
src/views/live/live/index.vue

@@ -397,6 +397,12 @@
             <el-radio :label="2">竖屏</el-radio>
           </el-radio-group>
         </el-form-item>
+        <el-form-item label="弹窗位置" prop="popupPosition">
+          <el-radio-group v-model="form.popupPosition">
+            <el-radio :label="1">中间</el-radio>
+            <el-radio :label="2">右下角</el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="直播类型" prop="liveType">
           <el-radio-group v-model="form.liveType">
             <el-radio
@@ -823,6 +829,7 @@ export default {
       rules: {
         liveName: [{ required: true, message: "不能为空", trigger: "burl" }],
         showType: [{ required: true, message: "不能为空", trigger: "burl" }],
+        popupPosition: [{ required: true, message: "不能为空", trigger: "change" }],
         liveType: [{ required: true, message: "不能为空", trigger: "burl" }],
         startTime: [{ required: true, message: "不能为空", trigger: "burl" }],
         liveImgUrl: [{ required: true, message: "不能为空", trigger: "burl" }],
@@ -1159,6 +1166,7 @@ export default {
     reset() {
       this.form = {
         showType: 1,
+        popupPosition: 1,
         liveType: 2,
         isShow: 1,
         uploadType: 1,
@@ -1199,6 +1207,9 @@ export default {
       const liveId = row.liveId || this.ids;
       getLive(liveId).then((response) => {
         this.form = response.data;
+        if (this.form.popupPosition == null) {
+          this.form.popupPosition = 1;
+        }
         this.videoUrl = this.form.videoUrl;
         if (this.form.duration) {
           this.form.durationTime = this.secondsToTime(this.form.duration);

+ 220 - 20
src/views/live/liveConfig/task.vue

@@ -4,7 +4,7 @@
       <div class="cfg-banner-icon teal"><i class="el-icon-s-operation"></i></div>
       <div>
         <div class="cfg-banner-title">运营自动化</div>
-        <div class="cfg-banner-desc">配置定时推送商品、红包、抽奖与优惠券等自动化任务</div>
+        <div class="cfg-banner-desc">配置定时推送商品、红包、抽奖、优惠券与签到等自动化任务</div>
       </div>
     </div>
     <div class="cfg-panel">
@@ -103,7 +103,7 @@
       <el-table-column label="任务类型" align="center" prop="taskType" :formatter="taskTypeFormatter" />
       <el-table-column label="触发类型" align="center" prop="triggerType" :formatter="triggerTypeFormatter" />
       <el-table-column label="触发时间" align="center" prop="triggerValue" :formatter="triggerValueFormatter" />
-      <el-table-column label="产品名称" align="center" prop="productName" :formatter="productNameFormatter" />
+      <el-table-column label="关联内容" align="center" prop="productName" :formatter="productNameFormatter" />
       <el-table-column label="状态" align="center" prop="status" :formatter="statusFormatter" />
       <el-table-column label="创建时间" align="center" prop="createdTime" width="180">
         <template slot-scope="scope">
@@ -144,8 +144,8 @@
     />
 
     <!-- 添加或修改直播间自动化任务配置对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+    <el-dialog :title="title" :visible.sync="open" width="620px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="90px">
         <el-form-item label="直播间ID" prop="liveId">
           <el-input :disabled="liveAbled" v-model="form.liveId" placeholder="请输入直播间ID" />
         </el-form-item>
@@ -237,9 +237,28 @@
             <el-option v-for="i in goodsStatusOptions" :key="i.value" :label="i.label" :value="i.value"></el-option>
           </el-select>
         </el-form-item>
-<!--        <el-form-item label="序号" prop="signNo" v-if="form.taskType == 7">-->
-<!--          <el-input v-model.number="form.signNo" placeholder="请输入签到序号" oninput="value=value.replace(/[^\d]/g,'')" />-->
-<!--        </el-form-item>-->
+        <el-form-item label="签到奖励" v-if="form.taskType == 7">
+          <div class="sign-reward-list">
+            <div class="sign-reward-item" v-for="(item, index) in form.rewards" :key="index">
+              <el-select v-model="item.rewardType" placeholder="奖励类型" style="width: 120px; margin-right: 8px;" @change="onSignRewardTypeChange(item, index)">
+                <el-option v-for="i in rewardTypeOptions" :key="i.value" :label="i.label" :value="i.value"></el-option>
+              </el-select>
+              <el-select v-if="item.rewardType == 2" v-model="item.rewardId" placeholder="请选择积分红包" style="width: 180px; margin-right: 8px;">
+                <el-option v-for="i in redOptions" :key="i.value" :label="i.label" :value="i.value"></el-option>
+              </el-select>
+              <template v-else-if="item.rewardType == 5">
+                <el-select v-model="item.rewardId" placeholder="请选择优惠券" style="width: 160px; margin-right: 8px;">
+                  <el-option v-for="i in couponOptions" :key="i.value" :label="i.label" :value="i.value"></el-option>
+                </el-select>
+                <el-input-number v-model="item.couponCount" :min="1" :max="99" controls-position="right" placeholder="数量" style="width: 110px; margin-right: 8px;"></el-input-number>
+                <span style="margin-right: 8px; color: #909399;">张</span>
+              </template>
+              <el-input v-else-if="item.rewardType == 6" v-model="item.amount" placeholder="金额精确到0.1元" style="width: 180px; margin-right: 8px;" @input="onCashAmountInput(item)"></el-input>
+              <el-button type="text" icon="el-icon-delete" @click="removeSignReward(index)" v-if="form.rewards.length > 1">删除</el-button>
+            </div>
+            <el-button type="text" icon="el-icon-plus" @click="addSignReward">添加奖励</el-button>
+          </div>
+        </el-form-item>
         <el-form-item label="触发时间" prop="content">
           <el-time-picker
             default-value="2025-01-01 00:00:00"
@@ -309,11 +328,17 @@ export default {
         {
           value: 6,
           label: "定时商品上下架"
-        },{
+        },
+        {
           value: 7,
-          label: "签到"
+          label: "签到任务"
         }
       ],
+      rewardTypeOptions: [
+        { value: 2, label: "积分红包" },
+        { value: 5, label: "优惠券" },
+        { value: 6, label: "金额红包" }
+      ],
       statusOptions:[{
         value: "0",
         label: "禁用"
@@ -470,6 +495,8 @@ export default {
           return "定时优惠券";
         case 6:
           return "定时商品上下架";
+        case 7:
+          return "签到任务";
         default:
           return "--";
       }
@@ -490,6 +517,29 @@ export default {
       if(content.productName) {
         return content.productName
       }
+      if (row.taskType == 7) {
+        const rewards = Array.isArray(content.rewards) ? content.rewards : null;
+        if (rewards && rewards.length) {
+          return rewards.map(r => {
+            if (!r) return '';
+            if (r.rewardType == 2) return (r.reward && r.reward.desc) || '积分红包';
+            if (r.rewardType == 5) {
+              const title = (r.reward && r.reward.title) || '优惠券';
+              const count = r.couponCount > 0 ? r.couponCount : 1;
+              return title + '×' + count;
+            }
+            if (r.rewardType == 6) return '金额红包¥' + (r.amount != null ? r.amount : '--');
+            return '';
+          }).filter(Boolean).join('、') || '--';
+        }
+        if (content.reward) {
+          if (content.rewardType == 2) return content.reward.desc || '--';
+          if (content.rewardType == 5) return content.reward.title || '--';
+        }
+      }
+      if (row.taskType == 2 && content.desc) return content.desc;
+      if (row.taskType == 4 && content.desc) return content.desc;
+      if (row.taskType == 5 && content.title) return content.title;
       return "--";
     },
     triggerValueFormatter(row, column, value) {
@@ -525,6 +575,12 @@ export default {
         pageSize: 10,
         liveId: null
       }
+      this.form.content = null;
+      this.form.goodsId = null;
+      this.form.goodsStatus = null;
+      this.form.rewardType = null;
+      this.form.rewardId = null;
+      this.form.rewards = this.form.taskType == 7 ? [{ rewardType: null, rewardId: null, couponCount: 1, amount: null }] : [];
       if (this.isLoading || !this.hasMore) return;
       this.isLoading = true;
       this.listParams.liveId = this.liveId;
@@ -544,6 +600,8 @@ export default {
           await this.addCouponList();
         }else if(this.form.taskType == 6){
           await this.addGoodsList();
+        }else if(this.form.taskType == 7){
+          await this.preloadAllSignRewardOptions();
         }
       }catch ( err){
         console.error('加载数据失败:', err);
@@ -551,6 +609,66 @@ export default {
         this.isLoading = false;
       }
     },
+    async preloadAllSignRewardOptions() {
+      await this.addRedList();
+      this.listParams.pageNum = 1;
+      this.hasMore = true;
+      await this.addCouponList();
+    },
+    async onSignRewardTypeChange(item) {
+      item.rewardId = null;
+      item.couponCount = 1;
+      item.amount = null;
+      if (!item.rewardType) return;
+      const others = (this.form.rewards || []).filter(r => r !== item);
+      if (item.rewardType == 2 && others.some(r => r.rewardType == 2)) {
+        this.msgError("积分红包只能配置一个");
+        item.rewardType = null;
+        return;
+      }
+      if (item.rewardType == 6 && others.some(r => r.rewardType == 6)) {
+        this.msgError("金额红包只能配置一个");
+        item.rewardType = null;
+        return;
+      }
+      this.listParams = { pageNum: 1, pageSize: 10, liveId: this.liveId };
+      this.hasMore = true;
+      if (item.rewardType == 2) await this.addRedList();
+      else if (item.rewardType == 5) await this.addCouponList();
+    },
+    onCashAmountInput(item) {
+      if (item.amount == null || item.amount === '') return;
+      let val = String(item.amount).replace(/[^\d.]/g, '');
+      const parts = val.split('.');
+      if (parts.length > 2) {
+        val = parts[0] + '.' + parts.slice(1).join('');
+      }
+      // 金额红包精确到 0.1 元(一位小数)
+      if (parts.length === 2) {
+        val = parts[0] + '.' + parts[1].slice(0, 1);
+      }
+      item.amount = val;
+    },
+    addSignReward() {
+      if (!this.form.rewards) this.form.rewards = [];
+      this.form.rewards.push({ rewardType: null, rewardId: null, couponCount: 1, amount: null });
+    },
+    removeSignReward(index) {
+      this.form.rewards.splice(index, 1);
+    },
+    async loadSignRewardOptions(rewardType) {
+      this.hasMore = true;
+      this.listParams = {
+        pageNum: 1,
+        pageSize: 10,
+        liveId: this.liveId
+      }
+      if (rewardType == 2) {
+        await this.addRedList();
+      } else if (rewardType == 5) {
+        await this.addCouponList();
+      }
+    },
     addGoodsList() {
       if(this.haveData.goods) return
       listLiveGoods(this.listParams).then(res => {
@@ -683,7 +801,9 @@ export default {
         content: null,
         goodsId: null,
         goodsStatus: null,
-        // signNo:null,
+        rewardType: null,
+        rewardId: null,
+        rewards: [],
         status: 1,
         createdTime: null,
         updatedTime: null
@@ -733,7 +853,7 @@ export default {
         await this.addGoodsList();
       }
       const id = row.id || this.ids
-      getTask(id).then(response => {
+      getTask(id).then(async response => {
         this.form = response.data;
         let content = JSON.parse( response.data.content)
         if (this.form.taskType == 1) {
@@ -748,7 +868,37 @@ export default {
           this.form.goodsId = content.goodsId;
           this.form.goodsStatus = content.status;
         }else if(this.form.taskType == 7){
-          // this.form.signNo = content.signNo;
+          await this.preloadAllSignRewardOptions();
+          const rewards = [];
+          if (Array.isArray(content.rewards) && content.rewards.length) {
+            content.rewards.forEach(r => {
+              let rewardId = r.rewardId;
+              if (rewardId == null && r.reward) {
+                if (r.rewardType == 2) rewardId = r.reward.redId;
+                else if (r.rewardType == 5) rewardId = r.reward.couponId;
+              }
+              rewards.push({
+                rewardType: r.rewardType != null ? Number(r.rewardType) : null,
+                rewardId: rewardId,
+                couponCount: r.couponCount > 0 ? Number(r.couponCount) : 1,
+                amount: r.amount != null ? r.amount : null
+              });
+            });
+          } else if (content.rewardType != null) {
+            // 兼容旧单奖励结构
+            let rewardId = content.rewardId;
+            if (rewardId == null && content.reward) {
+              if (content.rewardType == 2) rewardId = content.reward.redId;
+              else if (content.rewardType == 5) rewardId = content.reward.couponId;
+            }
+            rewards.push({
+              rewardType: Number(content.rewardType),
+              rewardId: rewardId,
+              couponCount: content.couponCount > 0 ? Number(content.couponCount) : 1,
+              amount: content.amount != null ? content.amount : null
+            });
+          }
+          this.form.rewards = rewards.length ? rewards : [{ rewardType: null, rewardId: null, couponCount: 1, amount: null }];
         }
         this.open = true;
         this.title = "修改直播间自动化任务配置";
@@ -773,14 +923,54 @@ export default {
 
       }
       if(this.form.taskType == 7){
-        // if(this.form.signNo == null) {
-        //   this.msgError("必须填写签到序号");
-        //   return;
-        // }
-        // 序号后端自己生成,
-        this.form.content = JSON.stringify({
-          signNo: 1
-        })
+        const rewards = [];
+        let hasPointRed = false;
+        let hasCashRed = false;
+        for (const r of (this.form.rewards || [])) {
+          if (!r || r.rewardType == null) continue;
+          if (r.rewardType == 2) {
+            if (r.rewardId == null) {
+              this.msgError("请选择积分红包");
+              return;
+            }
+            if (hasPointRed) {
+              this.msgError("积分红包只能配置一个");
+              return;
+            }
+            hasPointRed = true;
+            rewards.push({ rewardType: 2, rewardId: r.rewardId });
+          } else if (r.rewardType == 5) {
+            if (r.rewardId == null) {
+              this.msgError("请选择优惠券");
+              return;
+            }
+            const couponCount = r.couponCount > 0 ? Number(r.couponCount) : 1;
+            rewards.push({ rewardType: 5, rewardId: r.rewardId, couponCount });
+          } else if (r.rewardType == 6) {
+            if (hasCashRed) {
+              this.msgError("金额红包只能配置一个");
+              return;
+            }
+            const amount = Number(r.amount);
+            if (!amount || amount <= 0) {
+              this.msgError("请输入正确的金额红包金额");
+              return;
+            }
+            // 精确到 0.1 元
+            const normalized = Math.round(amount * 10) / 10;
+            if (Math.abs(amount - normalized) > 1e-8) {
+              this.msgError("金额红包需精确到0.1元");
+              return;
+            }
+            hasCashRed = true;
+            rewards.push({ rewardType: 6, amount: normalized.toFixed(1) });
+          }
+        }
+        if (!rewards.length) {
+          this.msgError("请至少配置一个签到奖励");
+          return;
+        }
+        this.form.content = JSON.stringify({ rewards })
       }
 
       this.$refs["form"].validate(valid => {
@@ -874,4 +1064,14 @@ export default {
   from { transform: rotate(0deg); }
   to { transform: rotate(360deg); }
 }
+
+.sign-reward-list {
+  width: 100%;
+}
+.sign-reward-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 8px;
+  flex-wrap: wrap;
+}
 </style>