Explorar o código

巨量账户级回传以及优化等

pansl %!s(int64=2) %!d(string=hai) anos
pai
achega
776f5c94d2

+ 1 - 0
package.json

@@ -30,6 +30,7 @@
     "tailwindcss": "^3.2.2",
     "terser": "^5.16.5",
     "vue": "^3.2.47",
+    "vue-clipboard3": "^2.0.0",
     "vue-i18n": "9",
     "vue-router": "4.1.6",
     "vuedraggable": "^4.1.0",

+ 32 - 0
src/api/backConfig/index.ts

@@ -0,0 +1,32 @@
+import http from '@/api/http';
+/**
+ * 巨量账户级回传-账号列表
+ */
+export function callbackJuliangAccountList(params: object) {
+  return http.get('/callback/juliangAccount/list', params);
+}
+/**
+ * 巨量账户级回传-回传日志
+ */
+export function callbackJuliangAccountLogList(params: object) {
+  return http.get('/callback/juliangAccount/log/list', params);
+}
+
+/**
+ * 巨量账户级回传-设置回传比例
+ */
+export function callbackJuliangAccountUpdateCallbackConfig(params: object) {
+  return http.post('/callback/juliangAccount/updateCallbackConfig', params);
+}
+/**
+ * 巨量账户级回传-新增账号
+ */
+export function callbackJuliangAccountAddAccount(params: object) {
+  return http.post(`/callback/juliangAccount/addAccount`, params);
+}
+/**
+ * 巨量账户级回传-补传
+ */
+export function callbackJuliangAccountLogCallbackAgain(params: object) {
+  return http.post(`/callback/juliangAccount/log/callbackAgain`, params);
+}

+ 0 - 52
src/api/bookManage/index.ts

@@ -1,52 +0,0 @@
-import http from '@/api/http';
-/**
- * 籍列表接口
- */
-export function bookList(params: object) {
-  return http.post('/contentManage/book/list', params)
-}
-/**
- * 合作结算方式获取
- */
-export function bookSettlementypes() {
-  return http.get('/contentManage/book/settlementypes')
-}
-/**
- * 获取书籍版权分库信息
- */
-export function bookDistribute(bid: number, params?: object) {
-  return http.get(`/contentManage/book/distribute/${bid}`, params)
-}
-/**
- * 提交书籍版权分库信息
- */
-export function bookDistributeSave(bid: number, params: object) {
-  return http
-    .post(`/contentManage/book/distribute/${bid}`, params)
-    
-}
-/**
- * 提交书籍版权分库信息
- */
-export function bookEditAuthor(params: object) {
-  return http.post('contentManage/book/edit_author', params)
-}
-/**
- * 上传书籍
- */
-export function bookImport(params: object) {
-  return http.post('/contentManage/book/import', params)
-}
-/**
- * 创建书籍
- */
-export function bookCreateBook(params: object) {
-  return http.post('/contentManage/book/createBook', params)
-}
-
-/**
- * 书籍分类
- */
-export function bookCategorylist(params?: object) {
-  return http.get(`/contentManage/book/categorylist`, params)
-}

+ 32 - 0
src/api/promotion/index.ts

@@ -0,0 +1,32 @@
+import http from '@/api/http';
+/**
+ * 推广列表-已经配置或未配置
+ */
+export function tuiguangPromotionList(params: object) {
+  return http.get('/tuiguang/promotion/list', params);
+}
+
+/**
+ * 推广列表-删除
+ */
+export function tuiguangPromotionDel(params: object) {
+  return http.post('/tuiguang/promotion/delete', params);
+}
+/**
+ * 推广-创建
+ */
+export function tuiguangPromotionAdd(params: object) {
+  return http.post(`/tuiguang/promotion/add`, params);
+}
+/**
+ * 推广-编辑
+ */
+export function tuiguangPromotionUpdateSeriesSequence(params: object) {
+  return http.post(`/tuiguang/promotion/updateSeriesSequence`, params);
+}
+/**
+ * 推广-绑定回传配置
+ */
+export function tuiguangPromotionUpdateCallbackConfig(params: object) {
+  return http.post(`/tuiguang/promotion/updateCallbackConfig`, params);
+}

+ 29 - 0
src/components/CopyButton/index.vue

@@ -0,0 +1,29 @@
+<template>
+  <button ref="copyButton" @click="copyText">
+    <slot>复制链接</slot>
+  </button>
+</template>
+
+<script lang="ts" setup>
+import useClipboard from 'vue-clipboard3'
+const props = defineProps({
+  text: {
+    type: String,
+    required: true
+  }
+});
+const { toClipboard } = useClipboard()
+const emit = defineEmits(['copy']);
+
+const copyText = async () => {
+  try {
+    await toClipboard(props.text)
+    console.log('Copied to clipboard')
+    ElMessage.success('复制成功')
+    emit('copy');
+  } catch (e) {
+    ElMessage.error('复制失败')
+    console.error(e)
+  }
+}
+</script>

+ 1 - 1
src/components/Upload/VideoUploader.vue

@@ -113,7 +113,7 @@ const beforeUpload = (file: File) => {
   const fileNamePrefix = file.name.split('.')[0]
   console.log(file.name, 'file.namefile.name', fileNamePrefix);
   if (!/^\d+$/.test(fileNamePrefix) && props.isCheckName) {
-    ElMessage.error(`文件命名错误,请以数字命名,例:1;11;333`);
+    ElMessage.error(`文件命名错误,请以数字命名,例:1.mp4; 11.mp4; 333.mp4`);
     return false
   }
   const filetype = file.type;

+ 8 - 3
src/views/appletManage/form/create.vue

@@ -57,7 +57,8 @@ const rules = reactive({
       required: true,
       message: '请输入APP ID',
       trigger: 'blur'
-    }
+    },
+    { max: 32, message: '至多输入32个字符', trigger: 'blur' }
   ],
   remark: [
     {
@@ -71,14 +72,16 @@ const rules = reactive({
       required: true,
       message: '请输入APP 密钥',
       trigger: 'blur'
-    }
+    },
+    { max: 64, message: '至多输入64个字符', trigger: 'blur' }
   ],
   name: [
     {
       required: true,
       message: '请输入小程序名称',
       trigger: 'blur'
-    }
+    },
+    { max: 15, message: '至多输入15个字符', trigger: 'blur' }
   ],
   company: [
     {
@@ -86,6 +89,7 @@ const rules = reactive({
       message: '请输入所属公司',
       trigger: 'blur'
     },
+    { max: 30, message: '至多输入30个字符', trigger: 'blur' }
   ],
   play_name: [
     {
@@ -93,6 +97,7 @@ const rules = reactive({
       message: '请输入对应剧场名称',
       trigger: 'blur'
     },
+    { max: 15, message: '至多输入15个字符', trigger: 'blur' }
   ]
 });
 const loading = ref(false)

+ 4 - 2
src/views/chargeManage/chargeTemplate/form/create.vue

@@ -7,13 +7,13 @@
     </template>
     <el-form :model="dataForm" label-width="120px" ref="form" v-loading="loading" class="pr-6">
       <el-form-item label="模板名称" prop="name"
-        :rules="[{ required: true, message: '模板名称必须填写' }, { max: 20, message: '至多输入20个字符', trigger: 'blur' },]">
+        :rules="[{ required: true, message: '模板名称必须填写' }, { max: 20, message: '至多输入20个字符', trigger: 'blur' }]">
         <el-input :disabled="props.primary" class="item" v-model="dataForm.name" clearable />
       </el-form-item>
       <el-form-item label="充值档位" prop="options" :rules="[{ required: true, message: '请添加充值档位' }]">
         <el-card class="w-full" shadow="never">
           <div class="pt-5 pl-2">
-            <Add @click="openGears(null, null)" />
+            <Add @click="openGears(null, null)" v-if="tableData.length < 6" />
           </div>
           <el-table :data="tableData" class="w-full mt-3" v-loading="loading">
             <el-table-column prop="price" label="价格">
@@ -164,11 +164,13 @@ const submitFormChange = (formEl: FormInstance | undefined) => {
         if (props.primary) {
           channelPaytemplateUpdate(dataForm.value.id, params).then(res => {
             loading.value = false;
+            ElMessage.success(res.message)
             emit('close')
           })
         } else {
           channelPaytemplateStore(params).then(res => {
             loading.value = false;
+            ElMessage.success(res.message)
             emit('close')
           })
         }

+ 54 - 14
src/views/chargeManage/chargeTemplate/form/createGears.vue

@@ -14,29 +14,37 @@
             <el-option v-for="(item, index) in optionTypeList" :key="index" :label="item.type_name" :value="item" />
           </el-select>
         </el-form-item>
-        <el-form-item label="价格" prop="price" class="form-item-price">
+        <el-form-item label="价格" prop="price">
           <div class="items-center w-full">
             <span>充</span>
-            <el-input style="width:30%;margin:0 5px;" v-model="formData.price" placeholder="请输入金额">
+            <el-input style="width:200px;margin:0 5px;" v-model="formData.price" placeholder="请输入金额">
               <template #append>元</template>
             </el-input>
-            <el-form-item prop="given" class="form-item" v-if="!isShowBi">
-              <div class="flex items-center">
-                <span>送</span>
-                <el-input style="width:100%;margin-left:5px" v-model="formData.given" placeholder="请输入充送看币">
-                  <template #append>币</template>
-                </el-input>
-              </div>
-
-            </el-form-item>
           </div>
+        </el-form-item>
+        <el-form-item prop="given" v-if="!isShowBi" style="margin-bottom:0px;">
+          <div class="items-center w-full">
+            <span>送</span>
+            <el-input style="width:200px;margin:0 5px;" v-model="formData.given" placeholder="请输入充送看币">
+              <template #append>币</template>
+            </el-input>
+          </div>
+        </el-form-item>
+        <el-form-item>
           <div class="mt-4 text-sm text-gray-400">1元=100k币,充送看币不能超过当前价格的3倍</div>
         </el-form-item>
         <el-form-item label="默认项" prop="is_default">
-          <el-select class="w-full" v-model="formData.is_default" filterable clearable placeholder="请选择默认项">
+          <el-select class="mr-2 w-300" v-model="formData.is_default" @change="defaultChange" filterable clearable
+            placeholder="请选择默认项">
             <el-option label="默认项" :value="1" />
             <el-option label="非默认项" :value="0" />
           </el-select>
+          <el-tooltip placement="top">
+            <template #content> 默认选中的充值档位 </template>
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+          </el-tooltip>
         </el-form-item>
       </div>
     </div>
@@ -127,13 +135,45 @@ const sequenceChange = (e) => {
   }
 }
 
+
+const defaultChange = (e) => {
+  console.log(e, 'defaultChange', props.data);
+}
+
 const submitForm = (formEl: FormInstance | undefined) => {
   if (!formEl) return;
   formEl
     .validate(valid => {
       if (valid) {
-        emit('success', formData.value)
-        emit('close')
+        if (!formData.value.given) {
+          formData.value.given = 0
+        }
+        const findSequence = props.data.find(el => el.is_default == 1)
+        if (findSequence?.sequence_text && formData.value.is_default == 1) {
+          ElMessageBox.confirm(
+            `当前默认项为${findSequence.sequence_text},确定要更改吗?`,
+            '提示',
+            {
+              confirmButtonText: '确定',
+              cancelButtonText: '取消',
+              type: 'warning',
+            }
+          )
+            .then(() => {
+              findSequence.is_default = 0
+              emit('success', formData.value)
+              emit('close')
+            })
+            .catch(() => {
+
+            })
+        } else {
+          emit('success', formData.value)
+          emit('close')
+        }
+
+
+
       } else {
       }
     })

+ 1 - 1
src/views/chargeManage/chargeTemplate/index.vue

@@ -2,7 +2,7 @@
   <div v-if="!showCreate">
     <Search :search="search" :reset="reset">
       <template v-slot:body>
-        <el-form-item label="标题">
+        <el-form-item label="模板名称">
           <el-input v-model="query.name" clearable />
         </el-form-item>
       </template>

+ 32 - 0
src/views/payBack/juliangAccount/index.vue

@@ -0,0 +1,32 @@
+<template>
+  <div>
+    <el-card shadow="always" :body-style="{ padding: '20px' }">
+      <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handChange">
+        <el-tab-pane label="广告主列表" name="advertiser">
+          <advertiser></advertiser>
+        </el-tab-pane>
+        <el-tab-pane label="回传日志" name="log">
+
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { useRouter, useRoute } from 'vue-router'
+import type { TabsPaneContext } from 'element-plus'
+import advertiser from "./tabs/advertiserList/index.vue"
+const router = useRouter()
+const route = useRoute()
+const handChange = (tab: TabsPaneContext, event: Event) => {
+  router.push({ path: '/payback/juliangAccount', query: { tab } })
+  activeName.value = tab
+}
+const activeName = ref('advertiser')
+onMounted(() => {
+
+});
+</script>
+
+<style scoped lang="scss"></style>

+ 257 - 0
src/views/payBack/juliangAccount/tabs/advertiserList/form/create.vue

@@ -0,0 +1,257 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <!-- <el-form-item label="推广ID" v-if="promotion" prop="account_name" label-width="120px"
+      :rules="[{ required: false, message: '巨量账户名称必须填写' }, { max: 20, message: '至多输入20个字符', trigger: 'blur' }]">
+      <el-input disabled v-model="promotion" auto-complete="off" placeholder="请输入巨量账户名称"></el-input>
+    </el-form-item> -->
+    <el-form-item label="巨量账户ID" prop="account_id" :rules="[{ required: true, message: '巨量账户ID必须填写' }]"
+      label-width="120px">
+      <el-input v-model.number="formCallback.account_id" type="number" auto-complete="off"
+        placeholder="请输入巨量账户ID"></el-input>
+    </el-form-item>
+    <el-form-item label="巨量账户名称" prop="account_name" label-width="120px"
+      :rules="[{ required: true, message: '巨量账户名称必须填写' }, { max: 20, message: '至多输入20个字符', trigger: 'blur' }]">
+      <el-input v-model="formCallback.account_name" auto-complete="off" placeholder="请输入巨量账户名称"></el-input>
+    </el-form-item>
+    <el-form-item label="回传配置" prop="default_rate" :rules="[{ required: true, message: '回传配置必须填写' }]">
+      <div class="flex flex-col">
+        <el-form-item label="输入回传金额" prop="price">
+          <div class="items-center w-full">
+            <el-input style="width:200px;margin:0 5px;" v-model.number="formCallback.min_money">
+              <template #append>元</template>
+            </el-input>
+            <span>—</span>
+            <el-input style="width:200px;margin:0 5px;" v-model.number="formCallback.max_money">
+              <template #append>元</template>
+            </el-input>
+          </div>
+        </el-form-item>
+        <el-form-item class="ml-6" label="" label-width="fit-content">
+          <span class="small-title">设置回传比例及计划保护数</span>
+        </el-form-item>
+        <el-form-item label="计划保护数:" prop="protect_num">
+          <el-input-number :min="0" type="number" v-model.number="formCallback.protect_num"></el-input-number>
+        </el-form-item>
+        <el-form-item label="回传比例:" prop="default_per">
+          <div>
+            <div class="custom-item">
+              <div class="custom-line">
+                <span>默认回传比例:</span>
+                <el-input-number type="number" class="input" v-model.number="formCallback.default_rate" :min="0"
+                  :max="100"></el-input-number>&nbsp;%
+                <div>
+                  未配置的时间段使用默认回传比例
+                </div>
+              </div>
+            </div>
+            <div class="custom-item" v-for="(item, index) in formCallback.rate_time_config" :key="index">
+              <el-icon class="cursor-pointer el-icon-close closeitem" @click="removeItem(index)">
+                <Close />
+              </el-icon>
+              <div class="custom-line">
+                <span>回传比例:</span>
+                <el-input-number type="number" class="input" v-model="item.config_per" :min="0"
+                  :max="100"></el-input-number>&nbsp;%
+              </div>
+              <div class="custom-line">
+                <span>生效时间:</span>
+                <el-time-picker @change="timeChange($event, item)" is-range v-model="item.time" format="HH:mm"
+                  range-separator="至" value-format="HH:mm" start-placeholder="开始时间" end-placeholder="结束时间"
+                  placeholder="选择时间范围">
+                </el-time-picker>
+              </div>
+            </div>
+            <div class="add-btn" style="cursor: pointer;" @click="addBack">
+              添加回传比例
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="回传开关" prop="state" :rules="[{ required: false, message: '回传开关' }]">
+          <el-switch v-model="formCallback.state" :active-value="1" :inactive-value="0"></el-switch>
+        </el-form-item>
+      </div>
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { Close } from '@element-plus/icons-vue';
+import { FormInstance } from 'element-plus';
+import { callbackJuliangAccountAddAccount } from '@/api/backConfig/index'
+import { useRouter, useRoute } from 'vue-router'
+const router = useRouter()
+const route = useRoute()
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const form = ref()
+const promotion = ref('')
+const formCallback = ref({
+  account_id: '',
+  account_name: '',
+  min_money: null,
+  max_money: null,
+  state: 1,
+  protect_num: 0,
+  default_rate: 0,
+  rate_time_config: [],
+})
+
+const timeChange = (e, item) => {
+  console.log(e, item);
+  item.start_time = e[0]
+  item.end_time = e[1]
+}
+
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!ValiteFunc1()) {
+    return
+  }
+  if (!formEl) return;
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        callbackJuliangAccountAddAccount(formCallback.value).then(res => {
+          ElMessage.success(res.message)
+          console.log(res, 'callbackJuliangAccountAddAccountcallbackJuliangAccountAddAccount');
+          emit('close')
+        })
+        console.log(formCallback.value, 'formCallbackformCallback');
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+const ValiteFunc1 = () => {
+  let valite = true;
+  let dateArr = [];
+  formCallback.value.rate_time_config.forEach((item, index) => {
+    if (
+      item.start_time &&
+      item.end_time &&
+      item.start_time != item.end_time
+    ) {
+      dateArr.push({
+        start_time: item.start_time,
+        end_time: item.end_time
+      });
+    } else {
+      valite = false;
+    }
+  });
+  //时间都填写的情况下,在去判断是否重叠时间
+  valite = isHasRepeatTime(dateArr);
+  let configBool = formCallback.value.rate_time_config.every(el => {
+    return el.config_per && el.end_time && el.start_time
+  })
+
+
+  if (formCallback.value.min_money == null || formCallback.value.max_money == null) {
+    ElMessage.error('请输入回传金额')
+    valite = false;
+  } else if (
+    formCallback.value.max_money <=
+    formCallback.value.min_money &&
+    formCallback.value.max_money
+  ) {
+    ElMessage.error("回传金额上限小于或等于回传金额下限,请重新输入");
+    valite = false;
+  } else if (formCallback.value.protect_num == null) {
+    ElMessage.error('请输入计划保护数')
+    valite = false;
+  } else if (formCallback.value.default_rate == null) {
+    ElMessage.error('请输入默认回传比例')
+    valite = false;
+  } else if (!configBool) {
+    ElMessage.error('请检查回传比例输入是否完成')
+    valite = false;
+  } else if (!valite) {
+    ElMessage.error('起止时间未选择或起止时间有重叠,请重新排查')
+  }
+  return valite;
+}
+
+const isHasRepeatTime = (dateArr = []) => {
+  const startTimeArr = [];
+  const endTimeArr = [];
+  dateArr.map(item => {
+    startTimeArr.push(item.start_time), endTimeArr.push(item.end_time);
+  });
+  const allStartTime = startTimeArr.sort();
+  const allEndTime = endTimeArr.sort();
+  let result = 0;
+  for (let k = 1; k < allStartTime.length; k++) {
+    if (allStartTime[k] < allEndTime[k - 1]) {
+      result += 1;
+      break;
+    }
+  }
+  return result <= 0;
+}
+const removeItem = (idx) => {
+  formCallback.value.rate_time_config.splice(idx, 1);
+}
+const addBack = () => {
+  formCallback.value.rate_time_config.push({
+    start_time: "",
+    end_time: "",
+    config_per: 100
+  });
+}
+
+onMounted(() => {
+  promotion.value = route.query?.promotionId
+});
+</script>
+
+<style lang="scss" scoped>
+.custom-item {
+  margin: 10px 0;
+  background: #f7f7f7;
+  padding: 20px;
+  width: 600px;
+  overflow: hidden;
+
+  .closeitem {
+    float: right;
+  }
+
+  .custom-line {
+    >span {
+      padding-right: 15px;
+    }
+
+    margin-bottom: 10px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+}
+
+.add-btn {
+  height: 30px;
+  width: 640px;
+  text-align: center;
+  border: 1px #20a0ff dashed;
+  line-height: 30px;
+  border-radius: 4px;
+  color: #20a0ff;
+  margin-bottom: 10px;
+}
+
+:deep(.small-title) {
+  font-size: 10px;
+  color: grey;
+}
+</style>

+ 265 - 0
src/views/payBack/juliangAccount/tabs/advertiserList/form/paybackConfig.vue

@@ -0,0 +1,265 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <!-- <el-form-item label="推广ID" v-if="promotion" prop="account_name" label-width="120px"
+      :rules="[{ required: false, message: '巨量账户名称必须填写' }, { max: 20, message: '至多输入20个字符', trigger: 'blur' }]">
+      <el-input disabled v-model="promotion" auto-complete="off" placeholder="请输入巨量账户名称"></el-input>
+    </el-form-item> -->
+    <el-form-item label="巨量账户ID" prop="ids" :rules="[{ required: false, message: '巨量账户ID必须填写' }]" label-width="120px">
+      <span v-for="item in formCallback.account_ids" :key="item" class="account-ids">{{ item }}</span>
+    </el-form-item>
+    <el-form-item label="回传配置" prop="default_rate" :rules="[{ required: true, message: '回传配置必须填写' }]">
+      <div class="flex flex-col">
+        <el-form-item label="输入回传金额" prop="price">
+          <div class="items-center w-full">
+            <el-input style="width:200px;margin:0 5px;" v-model.number="formCallback.min_money">
+              <template #append>元</template>
+            </el-input>
+            <span>—</span>
+            <el-input style="width:200px;margin:0 5px;" v-model.number="formCallback.max_money">
+              <template #append>元</template>
+            </el-input>
+          </div>
+        </el-form-item>
+        <el-form-item class="ml-6" label="" label-width="fit-content">
+          <span class="small-title">设置回传比例及计划保护数</span>
+        </el-form-item>
+        <el-form-item label="计划保护数:" prop="protect_num">
+          <el-input-number :min="0" type="number" v-model.number="formCallback.protect_num"></el-input-number>
+        </el-form-item>
+        <el-form-item label="回传比例:" prop="default_per">
+          <div>
+            <div class="custom-item">
+              <div class="custom-line">
+                <span>默认回传比例:</span>
+                <el-input-number type="number" class="input" v-model.number="formCallback.default_rate" :min="0"
+                  :max="100"></el-input-number>&nbsp;%
+                <div>
+                  未配置的时间段使用默认回传比例
+                </div>
+              </div>
+            </div>
+            <div class="custom-item" v-for="(item, index) in formCallback.rate_time_config" :key="index">
+              <el-icon class="cursor-pointer el-icon-close closeitem" @click="removeItem(index)">
+                <Close />
+              </el-icon>
+              <div class="custom-line">
+                <span>回传比例:</span>
+                <el-input-number type="number" class="input" v-model="item.config_per" :min="0"
+                  :max="100"></el-input-number>&nbsp;%
+              </div>
+              <div class="custom-line">
+                <span>生效时间:</span>
+                <el-time-picker @change="timeChange($event, item)" is-range v-model="item.time" format="HH:mm"
+                  range-separator="至" value-format="HH:mm" start-placeholder="开始时间" end-placeholder="结束时间"
+                  placeholder="选择时间范围">
+                </el-time-picker>
+              </div>
+            </div>
+            <div class="add-btn" style="cursor: pointer;" @click="addBack">
+              添加回传比例
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="回传开关" prop="advertiser_id" :rules="[{ required: false, message: '巨量账户ID必须填写' }]">
+          <el-switch v-model="formCallback.state" :active-value="1" :inactive-value="0"></el-switch>
+        </el-form-item>
+      </div>
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { Close } from '@element-plus/icons-vue';
+import { FormInstance } from 'element-plus';
+import { callbackJuliangAccountUpdateCallbackConfig } from '@/api/backConfig/index';
+import { useRouter, useRoute } from 'vue-router'
+const props = defineProps({
+  primary: Object,
+});
+const router = useRouter()
+const route = useRoute()
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const promotion = ref('')
+const form = ref()
+const formCallback = ref({
+  ids: [],
+  min_money: null,
+  max_money: null,
+  state: 1,
+  protect_num: 0,
+  default_rate: 0,
+  rate_time_config: [],
+})
+
+const timeChange = (e, item) => {
+  console.log(e, item);
+  item.start_time = e[0]
+  item.end_time = e[1]
+}
+
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  console.log(formCallback.value, 'formCallbackformCallback');
+  if (!ValiteFunc1()) {
+    return
+  }
+  if (!formEl) return;
+  loading.value = true;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        callbackJuliangAccountUpdateCallbackConfig(formCallback.value).then(res => {
+          ElMessage.success(res.message)
+          console.log(res, 'callbackJuliangAccountAddAccountcallbackJuliangAccountAddAccount');
+          emit('close')
+        })
+        console.log(formCallback.value, 'formCallbackformCallback');
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+const ValiteFunc1 = () => {
+  let valite = true;
+  let dateArr = [];
+  formCallback.value.rate_time_config.forEach((item, index) => {
+    if (
+      item.start_time &&
+      item.end_time &&
+      item.start_time != item.end_time
+    ) {
+      dateArr.push({
+        start_time: item.start_time,
+        end_time: item.end_time
+      });
+    } else {
+      valite = false;
+    }
+  });
+  //时间都填写的情况下,在去判断是否重叠时间
+  valite = isHasRepeatTime(dateArr);
+  let configBool = formCallback.value.rate_time_config.every(el => {
+    return el.config_per && el.end_time && el.start_time
+  })
+  if (formCallback.value.min_money == null || formCallback.value.max_money == null) {
+    ElMessage.error('请输入回传金额')
+    valite = false;
+  } else if (
+    formCallback.value.max_money <=
+    formCallback.value.min_money &&
+    formCallback.value.max_money
+  ) {
+    ElMessage.error("回传金额上限小于或等于回传金额下限,请重新输入");
+    valite = false;
+  } else if (formCallback.value.protect_num == null) {
+    ElMessage.error('请输入计划保护数')
+    valite = false;
+  } else if (formCallback.value.default_rate == null) {
+    ElMessage.error('请输入默认回传比例')
+    valite = false;
+  } else if (!configBool) {
+    ElMessage.error('请检查回传比例输入是否完成')
+    valite = false;
+  } else if (!valite) {
+    ElMessage.error('起止时间未选择或起止时间有重叠,请重新排查')
+  }
+  return valite;
+}
+
+const isHasRepeatTime = (dateArr = []) => {
+  const startTimeArr = [];
+  const endTimeArr = [];
+  dateArr.map(item => {
+    startTimeArr.push(item.start_time), endTimeArr.push(item.end_time);
+  });
+  const allStartTime = startTimeArr.sort();
+  const allEndTime = endTimeArr.sort();
+  let result = 0;
+  for (let k = 1; k < allStartTime.length; k++) {
+    if (allStartTime[k] <= allEndTime[k - 1]) {
+      result += 1;
+      break;
+    }
+  }
+  return result <= 0;
+}
+const removeItem = (idx) => {
+  formCallback.value.rate_time_config.splice(idx, 1);
+}
+const addBack = () => {
+  formCallback.value.rate_time_config.push({
+    start_time: "",
+    end_time: "",
+    config_per: 100
+  });
+}
+const cancel = () => {
+};
+
+if (props.primary) {
+  formCallback.value = JSON.parse(JSON.stringify(props.primary))
+}
+onMounted(() => {
+  promotion.value = route.query?.promotionId
+});
+</script>
+
+<style lang="scss" scoped>
+.custom-item {
+  margin: 10px 0;
+  background: #f7f7f7;
+  padding: 20px;
+  width: 600px;
+  overflow: hidden;
+
+  .closeitem {
+    float: right;
+  }
+
+  .custom-line {
+    >span {
+      padding-right: 15px;
+    }
+
+    margin-bottom: 10px;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+}
+
+.add-btn {
+  height: 30px;
+  width: 640px;
+  text-align: center;
+  border: 1px #20a0ff dashed;
+  line-height: 30px;
+  border-radius: 4px;
+  color: #20a0ff;
+  margin-bottom: 10px;
+}
+
+:deep(.small-title) {
+  font-size: 10px;
+  color: grey;
+}
+
+.account-ids {
+  padding: 0 10px;
+  border: 1px solid #c0c4cc;
+  margin-right: 5px;
+  border-radius: 5px;
+  background-color: #c0c4cc;
+  box-shadow: 0 0 0 1px #c0c4cc inset;
+}
+</style>

+ 175 - 0
src/views/payBack/juliangAccount/tabs/advertiserList/index.vue

@@ -0,0 +1,175 @@
+<template>
+  <Search :search="search" :reset="reset">
+    <template v-slot:body>
+      <el-form-item label="巨量账户ID">
+        <el-input placeholder="请输入巨量账户ID" class="input" icon="search" v-model="query.account_id" clearable></el-input>
+      </el-form-item>
+      <el-form-item label="巨量账户">
+        <el-input placeholder="请输入巨量账户" class="input" icon="search" v-model="query.account_name" clearable></el-input>
+      </el-form-item>
+    </template>
+  </Search>
+  <div class="table-default">
+    <div class="pt-5 pl-2">
+      <el-button type="primary" size="default" @click="openForm(null)">新增账户</el-button>
+      <el-button type="primary" size="default" @click="mulSet">批量设置</el-button>
+
+    </div>
+    <el-table :data="tableData" class="mt-3" v-loading="loading" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="80">
+      </el-table-column>
+      <el-table-column label="巨量账户ID" width="260" prop="adv_account_id"></el-table-column>
+      <el-table-column label="巨量账户" width="260" prop="adv_account_name">
+      </el-table-column>
+      <el-table-column label="回传开关" width="150">
+        <template #default="scope">
+          <div>
+            <el-switch v-model="scope.row.state" @change="switchStatus(scope.row)" :active-value="1" :inactive-value="0">
+            </el-switch>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template #default="scope">
+          <el-button link type="primary" size="default" @click="handleEdit(scope.$index, scope.row)">回传配置</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <Paginate />
+  </div>
+  <!-- 设置回传配置 -->
+  <Dialog v-model="paybackVisible" title="设置回传配置" width="80%" destroy-on-close>
+    <paybackConfig @close="closeType('paybackVisible')" :primary="currentPayback"></paybackConfig>
+  </Dialog>
+
+  <Dialog v-model="addAccountVisible" title="添加账户" width="80%" destroy-on-close>
+    <create @close="closeType('addAccountVisible')"></create>
+  </Dialog>
+</template>
+
+<script lang="ts"  setup>
+import { InfoFilled } from '@element-plus/icons-vue';
+import { computed, onMounted, ref } from 'vue';
+import create from './form/create.vue';
+import paybackConfig from './form/paybackConfig.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { useDestroy } from '@/hook/curd/useDestroy';
+import { noticesTypesList } from '@/api/notice/index';
+
+const api = 'callback/juliangAccount/list';
+const delapi = 'system/notices/notice/del';
+const noticesTypes = ref([]);
+
+const { data, query, search, reset, loading } = useGetList(api, true);
+const { destroy, deleted } = useDestroy('post');
+const tableData = computed(() => data.value?.data);
+const addAccountVisible = ref(false)
+const currentPayback = ref({})
+
+const searchForm = ref({
+  advertiser_id: "",
+  advertiser_name: "",
+  send_order_id: ""
+})
+const multipleSelection = ref([])
+const paybackVisible = ref(false) //设置回传弹窗
+const mutSetdialogVisible = ref(false) //批量设置弹窗
+
+const closeType = (type: string) => {
+  switch (type) {
+    case 'paybackVisible':
+      paybackVisible.value = false
+      break;
+    case 'addAccountVisible':
+      addAccountVisible.value = false
+      break;
+  }
+  search()
+}
+
+
+const delAdvertiser = (data) => {
+  // advertiserDel(data.id).then(res => {
+  //   // 重新请求数据
+  //   this.getData(this.searchForm, (data) => {
+  //     this.tableData = data.list;
+  //     this.meta = data.meta;
+  //   })
+  // })
+}
+// 更改广告主配置状态
+const switchStatus = (data) => {
+
+}
+
+// 批量设置
+const mulSet = () => {
+  if (multipleSelection.value.length <= 0) {
+    return ElMessage.warning('至少选择一条数据');
+  } else {
+    const content = '确定批量设置回传比例吗?'
+
+    ElMessageBox.confirm(
+      content,
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }
+    )
+      .then(() => {
+        paybackVisible.value = true;
+        currentPayback.value = {
+          account_id: '',
+          account_name: '',
+          min_money: null,
+          max_money: null,
+          state: 1,
+          protect_num: 0,
+          default_rate: 0,
+          rate_time_config: [],
+        }
+        currentPayback.value.ids = multipleSelection.value.map(el => el.id)
+        currentPayback.value.account_ids = multipleSelection.value.map(el => el.adv_account_id)
+      })
+      .catch(() => {
+
+      })
+  }
+}
+// 回传配置
+const handleEdit = (index, row) => {
+  console.log(row)
+  currentPayback.value = row
+  if (typeof row.rate_time_config == 'string') {
+    currentPayback.value.rate_time_config = JSON.parse(row.rate_time_config)
+  } else {
+    currentPayback.value.rate_time_config = row.rate_time_config
+  }
+  currentPayback.value.rate_time_config = currentPayback.value.rate_time_config.map(el => {
+    return {
+      ...el,
+      time: [el.start_time, el.end_time]
+    }
+  })
+  currentPayback.value.account_name = currentPayback.value.adv_account_name
+  currentPayback.value.ids = [currentPayback.value.id]
+  currentPayback.value.ids = [currentPayback.value.id]
+  currentPayback.value.account_ids = [currentPayback.value.adv_account_id]
+  paybackVisible.value = true
+}
+// 全选
+const handleSelectionChange = (val) => {
+  multipleSelection.value = val;
+}
+
+const openForm = (data: any) => {
+  addAccountVisible.value = true
+};
+onMounted(() => {
+  search();
+  deleted(search);
+});
+</script>
+<style  lang='scss' scoped></style>

+ 94 - 0
src/views/promotion/promotionList/form/backConfig.vue

@@ -0,0 +1,94 @@
+<template>
+  <el-form :model="formDataForm" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="推广ID" prop="name" :rules="[{ required: true, message: '推广ID必须填写' }]">
+      <el-input disabled v-model="formDataForm.id" name="name" clearable />
+    </el-form-item>
+    <el-form-item label="推广名称" prop="total_episode_num" :rules="[{ required: false, message: '推广名称必须填写' }]">
+      <el-input disabled v-model="props.primary.name" name="author" clearable />
+    </el-form-item>
+    <el-form-item label="回传类型" prop="callback_type" :rules="[{ required: true, message: '请选择回传类型', trigger: 'change' }]">
+      <el-select v-model="formDataForm.callback_type" filterable clearable placeholder="选择回传类型">
+        <el-option v-for="(item, index) in callbackTypeList" :key="index" :label="item.name" :value="item.value" />
+      </el-select>
+    </el-form-item>
+    <el-form-item label="回传配置" prop="callback_config_id"
+      :rules="[{ required: true, message: '请选择回传配置', trigger: 'change' }]">
+      <el-select v-model="formDataForm.callback_config_id" filterable remote clearable :remote-method="remoteMethod"
+        placeholder="选择回传配置">
+        <el-option v-for="(item, index) in callbackConfigList" :key="index" :label="item.adv_account_name"
+          :value="item.id" />
+      </el-select>
+      <el-button type="primary" link size="default" @click="addPayback">新增回传</el-button>
+    </el-form-item>
+    <el-form-item label="备注" prop="name" :rules="[{ required: false, message: '备注必须填写' }]">
+      <el-input v-model="formDataForm.remark" clearable />
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { useRouter, useRoute } from 'vue-router'
+import { FormInstance } from 'element-plus';
+import { InfoFilled } from '@element-plus/icons-vue';
+import { callbackJuliangAccountList } from '@/api/backConfig/index'
+import { tuiguangPromotionUpdateCallbackConfig } from '@/api/promotion/index'
+const props = defineProps({
+  primary: Object,
+});
+const router = useRouter()
+const route = useRoute()
+const callbackConfigList = ref([])
+const form = ref<FormInstance>();
+const loading = ref(false);
+const formDataForm = ref({ videos: [] })
+const callbackTypeList = ref([{ id: 1, value: 1, name: '巨量账户级回传' }])
+const emit = defineEmits(['close']);
+const init = (params?: object) => {
+  callbackJuliangAccountList({ limit: 30, ...params }).then(res => {
+    callbackConfigList.value = res.data
+  })
+}
+const addPayback = () => {
+  router.push({ path: '/payback/juliangAccount', query: { promotionId: props.primary.id } })
+}
+const remoteMethod = (query: string) => {
+  if (query) {
+    init({ account_name: query })
+  } else {
+    init()
+  }
+}
+const submitForm = (formEl: FormInstance | undefined) => {
+  loading.value = true;
+  console.log(formDataForm.value, formEl, 'formEl');
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        tuiguangPromotionUpdateCallbackConfig(formDataForm.value).then(res => {
+          loading.value = false;
+          emit('close')
+        }).catch(e => {
+          loading.value = false;
+        })
+        console.log(formDataForm.value, 'params');
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+if (props.primary) {
+  console.log(props.primary, 'props.primary');
+  formDataForm.value.name = props.primary.name
+  formDataForm.value.id = props.primary.id
+}
+onMounted(async () => {
+  init()
+});
+</script>

+ 70 - 0
src/views/promotion/promotionList/form/create.vue

@@ -0,0 +1,70 @@
+<template>
+  <el-form :model="formDataForm" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="推广名称" prop="name" :rules="[{ required: false, message: '推广名称必须填写' }]">
+      <el-input disabled v-model="props.primary.name" clearable />
+    </el-form-item>
+    <el-form-item label="入口章节" prop="series_sequence"
+      :rules="[{ required: true, message: '入口章节必须填写', trigger: 'change' }]">
+      <el-select v-model="formDataForm.series_sequence" filterable clearable placeholder="选择入口章节">
+        <el-option v-for="(item, index) in sequenceOptions" :key="index" :label="item.series_name"
+          :value="item.series_sequence" />
+      </el-select>
+    </el-form-item>
+    <el-form-item label="备注" prop="name" :rules="[{ required: false, message: '备注必须填写' }]">
+      <el-input v-model="formDataForm.remark" clearable />
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import Cache from '@/support/cache';
+import { FormInstance } from 'element-plus';
+import { videoStockEpisodeList } from '@/api/video/index'
+import { tuiguangPromotionUpdateSeriesSequence } from '@/api/promotion/index'
+const props = defineProps({
+  primary: String | Number,
+});
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const form = ref()
+const sequenceOptions = ref([])
+const formDataForm = ref({})
+if (props.primary) {
+  formDataForm.value.id = props.primary.id
+  formDataForm.value.series_sequence = props.primary.series_sequence
+  console.log(props.primary, 'props.primaryprops.primary');
+}
+
+const submitForm = (formEl: FormInstance | undefined) => {
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        tuiguangPromotionUpdateSeriesSequence(formDataForm.value).then(res => {
+          console.log(res, 'tuiguangPromotionAddtuiguangPromotionAdd');
+          ElMessage.success(res.message)
+          emit('close')
+          loading.value = false;
+        })
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+const initSequence = (params?: object) => {
+  videoStockEpisodeList({ video_id: props.primary.video_id, limit: 300, ...params }).then(res => {
+    sequenceOptions.value = res.data
+  })
+}
+
+onMounted(() => {
+  initSequence()
+});
+</script>

+ 315 - 0
src/views/promotion/promotionList/index.vue

@@ -0,0 +1,315 @@
+<template>
+  <div>
+    <el-card shadow="always" :body-style="{ padding: '20px' }">
+      <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handChange">
+        <el-tab-pane label="已配置回传" :name="1">
+          <Search :search="search" :reset="resetQuery">
+            <template v-slot:body>
+              <el-form-item label="推广名称" prop="name">
+                <el-input v-model="query.name" placeholder="请输入推广名称" clearable />
+              </el-form-item>
+              <el-form-item label="推广短剧名称" prop="video_name">
+                <el-input v-model="query.video_name" placeholder="请输入推广短剧名称" clearable />
+              </el-form-item>
+              <el-form-item label="推广ID" prop="id">
+                <el-input v-model="query.id" placeholder="请输入推广ID" clearable />
+              </el-form-item>
+              <el-form-item label="创建时间">
+                <el-date-picker unlink-panels clearable @change="timeChange" format="YYYY/MM/DD" value-format="YYYY-MM-DD"
+                  v-model="query.time" type="daterange" :shortcuts="shortcuts" range-separator="To"
+                  start-placeholder="开始时间" end-placeholder="结束时间" />
+              </el-form-item>
+            </template>
+          </Search>
+          <div class="table-default">
+            <el-table :data="tableData" class="mt-3" v-loading="loading" @selection-change="handleSelectionChange">
+              <el-table-column type="selection" />
+              <el-table-column prop="id" label="推广ID" />
+              <el-table-column label="推广名称" show-overflow-tooltip>
+                <template #default="scope">
+                  <div class="wrapper">
+                    <span class="text-lg font-bold text-blue-400 content">
+                      {{ scope.row.name }}
+                    </span>
+                  </div>
+                  <div class="wrapper">
+                    <span class="label">创建时间:</span>
+                    <span class="content">{{ scope.row.created_at }}</span>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="total_episode_num" label="入口章节">
+                <template #default="scope">
+                  <div class="wrapper">
+                    <span class="text-lg font-bold text-blue-400 content">
+                      {{ scope.row.video_name }}
+                    </span>
+                  </div>
+                  <div class="wrapper">
+                    <span class="content">{{ scope.row.series_sequence_name }}</span>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="total_episode_num" label="推广链接">
+                <template #header>
+                  <div class="flex items-center">
+                    <span>推广链接</span>
+                    <el-tooltip placement="top">
+                      <template #content>鼠标移动到【复制】文字,进行查看链接,点击移动【复制】文字,进行复制链接</template>
+                      <el-icon>
+                        <InfoFilled />
+                      </el-icon>
+                    </el-tooltip>
+                  </div>
+                </template>
+                <template #default="scope">
+                  <div class="wrapper" v-if="scope.row.promotion_path">
+                    <el-popover placement="top" title="推广链接" :width="500" trigger="hover"
+                      :content="scope.row.promotion_path">
+                      <template #reference>
+                        <copy-button :text="scope.row.promotion_path">
+                          <el-link :underline="false">复制</el-link>
+                        </copy-button>
+                      </template>
+                    </el-popover>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="total_episode_num" label="监测链接">
+                <template #header>
+                  <div class="flex items-center">
+                    <span>监测链接</span>
+                    <el-tooltip placement="top">
+                      <template #content>鼠标移动到【复制】文字,进行查看链接,点击移动【复制】文字,进行复制链接</template>
+                      <el-icon>
+                        <InfoFilled />
+                      </el-icon>
+                    </el-tooltip>
+                  </div>
+                </template>
+                <template #default="scope">
+                  <div class="wrapper" v-if="scope.row.track_url">
+                    <el-popover placement="top" title="监测链接" popper-style="width:80%" trigger="hover"
+                      :content="scope.row.track_url">
+                      <template #reference>
+                        <copy-button :text="scope.row.track_url">
+                          <el-link :underline="false">复制</el-link>
+                        </copy-button>
+                      </template>
+                    </el-popover>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="callback_type_str" label="回传类型" />
+              <el-table-column prop="callback_config_id" label="回传配置ID" />
+              <el-table-column prop="callback_config_id" label="备注">
+                <template #default="scope">
+                  <div class="wrapper">
+                    <span class="content">{{ scope.row.remark }}</span>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作">
+                <template #default="scope">
+                  <el-button @click="openType('promotionVisible', scope.row)" link type="primary"
+                    size="small">编辑</el-button>
+                  <br />
+                  <el-button link type="primary" size="small" @click="deletePromotion(scope.row)">删除</el-button>
+                  <br />
+                </template>
+              </el-table-column>
+            </el-table>
+            <Paginate />
+          </div>
+        </el-tab-pane>
+        <el-tab-pane label="未配置回传" :name="0">
+          <div class="table-default">
+            <el-table :data="tableData" class="mt-3" v-loading="loading">
+              <el-table-column prop="id" label="推广ID" />
+              <el-table-column label="推广名称" show-overflow-tooltip>
+                <template #default="scope">
+                  <div class="wrapper">
+                    <span class="text-lg font-bold text-blue-400 content">
+                      {{ scope.row.name }}
+                    </span>
+                  </div>
+                  <div class="wrapper">
+                    <span class="label">创建时间:</span>
+                    <span class="content">{{ scope.row.created_at }}</span>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="total_episode_num" label="入口章节">
+                <template #default="scope">
+                  <div class="wrapper">
+                    <span class="text-lg font-bold text-blue-400 content">
+                      {{ scope.row.video_name }}
+                    </span>
+                  </div>
+                  <div class="wrapper">
+                    <span class="content">第{{ scope.row.series_sequence }}集</span>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column label="操作">
+                <template #default="scope">
+                  <el-button @click="openType('promotionVisible', scope.row)" link type="primary"
+                    size="small">编辑</el-button>
+                  <br />
+                  <el-button link type="primary" size="small" @click="deletePromotion(scope.row)">删除</el-button>
+                  <br />
+                  <el-button link type="primary" size="small"
+                    @click="openType('backConfigVisible', scope.row)">回传配置</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+            <Paginate />
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </el-card>
+    <Dialog v-model="promotionVisible" title="编辑推广链接" destroy-on-close>
+      <Create @close="closeType('promotionVisible')" :primary="promotionData" />
+    </Dialog>
+    <Dialog v-model="backConfigVisible" width="50%" :title="titleBack" destroy-on-close>
+      <backConfig @close="closeType('backConfigVisible')" :primary="backConfigData"></backConfig>
+    </Dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { shortcuts } from '@/utils/shortcuts'
+import { useRouter, useRoute } from 'vue-router'
+import type { TabsPaneContext } from 'element-plus'
+import { InfoFilled } from '@element-plus/icons-vue';
+import Create from './form/create.vue';
+import backConfig from './form/backConfig.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { useDestroy } from '@/hook/curd/useDestroy';
+import { useOpen } from '@/hook/curd/useOpen';
+import { useUserStore } from '@/stores/modules/user/index'
+import Cache from '@/support/cache';
+const activeName = ref(1)
+import { tuiguangPromotionDel } from '@/api/promotion/index'
+const api = 'tuiguang/promotion/list';
+const multipleSelection = ref([]);
+const router = useRouter()
+const route = useRoute()
+const { data, query, search, reset, loading } = useGetList(api);
+const { deleted } = useDestroy();
+const uploadBooksVisible = ref(false)
+const promotionVisible = ref(false)
+const promotionData = ref({})
+const backConfigVisible = ref(false)
+const backConfigData = ref({})
+const current = ref({})
+const titleBack = computed(() => activeName.value ? '更新回传配置' : '回传配置')
+
+const timeChange = (e) => {
+  if (query.value.time) {
+    const timeArr = toRaw(e);
+    query.value.start_time = timeArr[0]
+    query.value.end_time = timeArr[1]
+  } else {
+    delete query.value.start_time
+    delete query.value.end_time
+  }
+}
+
+const handChange = (tab: TabsPaneContext, event: Event) => {
+  console.log(tab, event)
+  router.push({ path: '/promotion/promotionList', query: { tab } })
+  activeName.value = tab
+  query.value.is_config = tab
+  search()
+}
+
+const deletePromotion = (row: object) => {
+  ElMessageBox.confirm(
+    `确定要删除 ${row.name} 吗?`,
+    '提示',
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+      tuiguangPromotionDel({ id: row.id }).then(res => {
+        ElMessage.success(res.message)
+        search()
+        console.log(res);
+      })
+    })
+    .catch(() => {
+
+    })
+}
+
+const resetQuery = () => {
+  reset()
+}
+const openType = (type: string, data: object) => {
+  current.value = data;
+  switch (type) {
+    case 'promotionVisible':
+      promotionVisible.value = true
+      promotionData.value = data
+      break;
+    case 'backConfigVisible':
+      backConfigVisible.value = true
+      backConfigData.value = data
+      break;
+  }
+}
+const tableData = computed(() => data.value?.data);
+const { open, close, title, visible, id } = useOpen();
+
+const handleSelectionChange = (val: []) => {
+  console.log(toRaw(val));
+  multipleSelection.value = val;
+};
+
+const closeType = (type) => {
+  console.log(type, 'typetype');
+  switch (type) {
+    case 'promotionVisible':
+      promotionVisible.value = false
+      break;
+    case 'backConfigVisible':
+      backConfigVisible.value = false
+      break;
+  }
+  search()
+}
+
+onMounted(() => {
+  console.log(route, 'routerrouter', route.query);
+  activeName.value = Number(route.query.tab || 1)
+  query.value.is_config = activeName.value
+  search();
+});
+</script>
+
+<style scoped lang="scss">
+.table-default {
+  .set-warpper {
+    height: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+  }
+
+  .wrapper {
+    margin: 8px;
+
+    .label {
+      margin-right: 6px;
+    }
+
+    .content {
+      font-size: 15px;
+    }
+  }
+}
+</style>

+ 1 - 1
src/views/user/caster/index.vue

@@ -3,7 +3,7 @@
     <div class="w-full">
       <Search :search="search" :reset="reset">
         <template v-slot:body>
-          <el-form-item label="账号">
+          <el-form-item label="邮箱">
             <el-input v-model="query.email" clearable />
           </el-form-item>
           <el-form-item label="用户名">

+ 19 - 6
src/views/videoManage/detail.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="flex flex-col justify-between w-full sm:flex-row" style="width:100%;">
     <div class="table-default" style="width:100%;">
-      <el-table :data="tableData" class="w-full mt-3" v-loading="loading" style="width:100%;">
+      <el-table :data="tableData" class="w-full mt-3" style="width:100%;">
         <el-table-column prop="series_name" label="章节名称" />
         <el-table-column prop="is_charge" label="是否付费">
           <template #default="scope">
@@ -13,7 +13,7 @@
         <el-table-column label="操作" width="200">
           <template #default="scope">
             <el-button link type="primary" size="small" @click="play(scope.row)">播放</el-button>
-            <!-- <el-button link type="primary" size="small" @click="createLink(scope.row)">创建推广链接</el-button> -->
+            <el-button link type="primary" size="small" @click="createLink(scope.row)">创建推广链接</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -31,8 +31,8 @@
       <el-form-item label="推广名称" prop="name" :rules="[{ required: true, message: '推广名称必须填写' }]">
         <el-input v-model="formDataForm.name" name="name" clearable />
       </el-form-item>
-      <el-form-item label="入口章节" prop="total_episode_num" :rules="[{ required: false, message: '入口章节必须填写' }]">
-        <el-input disabled v-model="formDataForm.total_episode_num" name="author" clearable />
+      <el-form-item label="入口章节" prop="series_sequence" :rules="[{ required: false, message: '入口章节必须填写' }]">
+        <el-input disabled v-model="current.series_name" name="author" clearable />
       </el-form-item>
       <div class="flex justify-end">
         <el-button type="primary" @click="submitForm(form)">{{
@@ -44,14 +44,19 @@
 </template>
 
 <script lang="ts" setup>
+// import router from '@/router'
+import Cache from '@/support/cache';
 import { computed, onMounted, ref } from 'vue';
 import { useGetList } from '@/hook/curd/useGetList';
 import { useOpen } from '@/hook/curd/useOpen';
 import { FormInstance } from 'element-plus';
+import { tuiguangPromotionAdd } from '@/api/promotion/index'
 const api = 'videoStock/episode/list';
 const props = defineProps({
   primary: String | Number,
 });
+const router = useRouter()
+const form = ref()
 const formDataForm = ref({})
 const current = ref({})
 const playVisible = ref(false)
@@ -60,6 +65,8 @@ const { data, query, search, reset, loading } = useGetList(api);
 const { open, close, title, visible, id } = useOpen();
 if (props.primary) {
   query.value.video_id = props.primary.id
+  formDataForm.value.video_id = props.primary.id
+  formDataForm.value.miniprogram_id = JSON.parse(Cache.get('nav_data'))?.advertiser?.miniProgramIds
 }
 const tableData = computed(() => data.value?.data);
 const play = (e: object) => {
@@ -68,6 +75,7 @@ const play = (e: object) => {
 }
 const createLink = (e: object) => {
   current.value = e;
+  formDataForm.value.series_sequence = e.series_sequence
   linkVisible.value = true;
 }
 
@@ -77,7 +85,13 @@ const submitForm = (formEl: FormInstance | undefined) => {
   formEl
     .validate(valid => {
       if (valid) {
-
+        tuiguangPromotionAdd(formDataForm.value).then(res => {
+          console.log(res, 'tuiguangPromotionAddtuiguangPromotionAdd');
+          ElMessage.success(res.message)
+          loading.value = false;
+          linkVisible.value = false;
+          router.push({ path: '/promotion/promotionList', query: { tab: 0 } })
+        })
       } else {
         loading.value = false;
       }
@@ -86,7 +100,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
 }
 
 onMounted(() => {
-  console.log(props.primary, 'props.primaryprops.primary');
   search();
 });
 </script>

+ 1 - 1
src/views/videoManage/form/uploadVideo.vue

@@ -9,7 +9,7 @@
     <el-form-item label="上传" prop="videos" :rules="[{ required: true, message: '请上传文件', trigger: 'change' }]">
       <div class="items-center w-full">
         支持MP4格式
-        <span class="text-red-400">上传文件统一命名格式要求,例:1;11;333</span>
+        <span class="text-red-400">上传文件统一命名格式要求,例:1.mp4; 11.mp4; 333.mp4</span>
       </div>
       <div class="w-full">
 

+ 6 - 5
src/views/videoManage/index.vue

@@ -17,9 +17,9 @@
       </template>
     </Search>
     <div class="table-default">
-      <div class="set-warpper">
+      <!-- <div class="set-warpper">
         <el-button type="primary" @click="mulSet" size="default" v-action="'video.video.mulSet'">批量操作</el-button>
-      </div>
+      </div> -->
       <Operate :show="open" v-action="'video.video.add'" />
       <el-table :data="tableData" class="mt-3" v-loading="loading" @selection-change="handleSelectionChange">
         <el-table-column type="selection" />
@@ -102,8 +102,8 @@
             <el-button link type="primary" size="small" @click="openType('videoDLVisible', scope.row)"
               v-action="'video.episode.downloadList'">视频下载</el-button>
             <br />
-            <!-- <el-button link type="primary" size="small"
-              @click="openType('videoDetailVisible', scope.row)">生成推广链接</el-button> -->
+            <el-button link type="primary" size="small" v-if="isShowCreatePromotion"
+              @click="openType('videoDetailVisible', scope.row)">生成推广链接</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -145,6 +145,7 @@ import moment from 'moment';
 import { videoStockVideoUpdate, videoStockVideoAdd, videoStockVideoCategoryList } from '@/api/video/index'
 import { useUserStore } from '@/stores/modules/user/index'
 import Cache from '@/support/cache';
+import { fa } from 'element-plus/es/locale';
 const ismulSet = ref(false)
 const api = 'videoStock/video/list';
 const addApi = 'contentManage/book/edit_author';
@@ -165,6 +166,7 @@ const selectType = ref([])
 const statusOptions = ref([{ label: '连载中', value: '1' }, { label: '完结', value: '2' }]);
 const cooperations = ref([]);
 const rolesIdentify = ref([]);
+const isShowCreatePromotion = computed(() => JSON.parse(Cache.get('nav_data'))?.advertiser?.miniProgramIds && JSON.parse(Cache.get('nav_data'))?.app)
 
 
 const userStore = useUserStore()
@@ -280,7 +282,6 @@ const mulSet = () => {
     });
   }
 };
-
 onMounted(() => {
   rolesIdentify.value = userStore.getRoles?.map(el => el?.identify)
   console.log(rolesIdentify.value, 'rolesroles');