Bladeren bron

充值模板管理等

pansl 1 jaar geleden
bovenliggende
commit
b1a2b5b256

+ 47 - 0
src/api/charge/index.ts

@@ -0,0 +1,47 @@
+import http from '@/api/http';
+/**
+ * 充值选项类型
+ */
+export function channelPaytemplateOptionTypeList(params?: object) {
+  return http.get('/channel/paytemplate/optionTypeList', params);
+}
+/**
+ * 位置列表
+ */
+export function channelPaytemplateOptionOptionSequence(params?: object) {
+  return http.get('/channel/paytemplate/optionSequence', params);
+}
+/**
+ * 充值模板列表
+ */
+export function channelPaytemplateList(params: object) {
+  return http.get('/channel/paytemplate/list', params);
+}
+/**
+ * 模板详情
+ */
+export function channelPaytemplateShow(id: number | string, params?: object) {
+  return http.get(`/channel/paytemplate/show/${id}`, params);
+}
+
+/**
+ * 添加充值模板
+ */
+export function channelPaytemplateStore(params: object) {
+  return http.post('/channel/paytemplate/store', params);
+}
+/**
+ * 更新模板
+ */
+export function channelPaytemplateUpdate(id: number | string, params: object) {
+  return http.post(`/channel/paytemplate/update/${id}`, params);
+}
+/**
+ * 更新模板开关
+ */
+export function channelPaytemplateUpdateStatus(
+  id: number | string,
+  params: object
+) {
+  return http.post(`/channel/paytemplate/updateStatus/${id}`, params);
+}

+ 66 - 57
src/api/http.ts

@@ -1,9 +1,9 @@
-import { Code } from '@/enum/app'
-import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
-import { env, getAuthToken, removeAuthToken } from '@/support/helper'
-import Message from '@/support/message'
-import router from '@/router'
-import ResponseData from '@/types/responseData'
+import { Code } from '@/enum/app';
+import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
+import { env, getAuthToken, removeAuthToken } from '@/support/helper';
+import Message from '@/support/message';
+import router from '@/router';
+import ResponseData from '@/types/responseData';
 
 /**
  * http util
@@ -13,40 +13,40 @@ class Http {
    * axios config
    * @protected
    */
-  protected config: AxiosRequestConfig = {}
+  protected config: AxiosRequestConfig = {};
 
   /**
    * base url
    * @protected
    */
-  protected baseURL: string = ''
+  protected baseURL: string = '';
 
   /**
    * http request timeout
    *
    * @protected
    */
-  protected timeout: number = 0
+  protected timeout: number = 0;
 
   /**
    * http request headers
    *
    * @protected
    */
-  protected headers: { [k: string]: string } = {}
+  protected headers: { [k: string]: string } = {};
 
   /**
    * axios instance
    *
    * @protected
    */
-  protected request: AxiosInstance
+  protected request: AxiosInstance;
 
   /**
    * instance
    */
   constructor() {
-    this.request = axios.create(this.getConfig())
+    this.request = axios.create(this.getConfig());
   }
 
   /**
@@ -57,8 +57,8 @@ class Http {
    */
   public get(path: string, params: object = {}) {
     return this.request.get(this.baseURL + path, {
-      params,
-    })
+      params
+    });
   }
 
   /**
@@ -68,7 +68,7 @@ class Http {
    * @param data
    */
   public post(path: string, data: object = {}) {
-    return this.request.post(this.baseURL + path, data)
+    return this.request.post(this.baseURL + path, data);
   }
 
   /**
@@ -78,7 +78,7 @@ class Http {
    * @param data
    */
   public put(path: string, data: object = {}) {
-    return this.request.put(this.baseURL + path, data)
+    return this.request.put(this.baseURL + path, data);
   }
 
   /**
@@ -87,7 +87,7 @@ class Http {
    * @param path
    */
   public delete(path: string) {
-    return this.request.delete(this.baseURL + path)
+    return this.request.delete(this.baseURL + path);
   }
 
   /**
@@ -97,9 +97,9 @@ class Http {
    * @returns
    */
   public setTimeout(timeout: number): Http {
-    this.timeout = timeout
+    this.timeout = timeout;
 
-    return this
+    return this;
   }
 
   /**
@@ -109,9 +109,9 @@ class Http {
    * @returns
    */
   public setBaseUrl(url: string): Http {
-    this.baseURL = url
+    this.baseURL = url;
 
-    return this
+    return this;
   }
 
   /**
@@ -122,9 +122,9 @@ class Http {
    * @returns
    */
   public setHeader(key: string, value: string): Http {
-    this.headers.key = value
+    this.headers.key = value;
 
-    return this
+    return this;
   }
 
   /**
@@ -134,18 +134,18 @@ class Http {
    */
   protected getConfig(): AxiosRequestConfig {
     // set base url
-    this.config.baseURL = this.baseURL ? this.baseURL : env('VITE_BASE_URL')
+    this.config.baseURL = this.baseURL ? this.baseURL : env('VITE_BASE_URL');
 
     // set timeout
-    this.config.timeout = this.timeout ? this.timeout : 60000
+    this.config.timeout = this.timeout ? this.timeout : 60000;
 
     // set ajax request
-    this.headers['X-Requested-With'] = 'XMLHttpRequest'
+    this.headers['X-Requested-With'] = 'XMLHttpRequest';
     // set dashboard request
-    this.headers['Request-from'] = 'Dashboard'
-    this.config.headers = this.headers
+    this.headers['Request-from'] = 'Dashboard';
+    this.config.headers = this.headers;
 
-    return this.config
+    return this.config;
   }
 
   /**
@@ -153,18 +153,20 @@ class Http {
    *
    */
   public interceptorsOfRequest(): void {
-    this.request.interceptors.request.use(function (config: AxiosRequestConfig) {
-      const token = getAuthToken()
+    this.request.interceptors.request.use(function (
+      config: AxiosRequestConfig
+    ) {
+      const token = getAuthToken();
       if (token) {
         if (!config.headers) {
-          config.headers = {}
+          config.headers = {};
         }
 
-        config.headers.authorization = 'Bearer ' + token
+        config.headers.authorization = 'Bearer ' + token;
       }
 
-      return config
-    })
+      return config;
+    });
   }
 
   /**
@@ -174,42 +176,49 @@ class Http {
   public interceptorsOfResponse(): void {
     this.request.interceptors.response.use(
       response => {
-        const r: ResponseData = response.data
-        const code = r.code
-        const message = r.message
+        const r: ResponseData = response.data;
+        const code = r.code;
+        const message = r.message;
         if (code === 1e4) {
-          return r
+          return r;
         }
 
         if (code === 10004) {
-          Message.error(message || 'Error')
+          Message.error(message || 'Error');
         } else if (code === Code.LOST_LOGIN || code === Code.LOGIN_EXPIRED) {
           // to re-login
           Message.confirm(message + ',需要重新登陆', function () {
-            removeAuthToken()
-            router.push('/login')
-          })
-        } else if (code === Code.LOGIN_BLACKLIST || code === Code.USER_FORBIDDEN) {
-          Message.error(message || 'Error')
-          removeAuthToken()
+            removeAuthToken();
+            router.push('/login');
+          });
+        } else if (
+          code === Code.LOGIN_BLACKLIST ||
+          code === Code.USER_FORBIDDEN
+        ) {
+          Message.error(message || 'Error');
+          removeAuthToken();
           // to login page
-          router.push('/login')
+          router.push('/login');
+        } else if (code === -2333) {//对视频上传错误特殊处理的状态码
+          ElMessageBox.alert(
+            `上传失败,《${message}》文件名已存在,请修改后重新上传`
+          );
         } else {
-          Message.error(message || 'Error')
+          Message.error(message || 'Error');
         }
 
-        return Promise.reject(new Error(message || 'Error'))
+        return Promise.reject(new Error(message || 'Error'));
       },
       error => {
-        Message.error(error.message)
-        return Promise.reject(error)
-      },
-    )
+        Message.error(error.message);
+        return Promise.reject(error);
+      }
+    );
   }
 }
 
-const http = new Http()
-http.interceptorsOfRequest()
+const http = new Http();
+http.interceptorsOfRequest();
 
-http.interceptorsOfResponse()
-export default http
+http.interceptorsOfResponse();
+export default http;

+ 12 - 6
src/components/Upload/VideoUploader.vue

@@ -63,6 +63,7 @@ const progress = ref(0)
 const uploading = ref(false)
 const video = inject('video')
 const loadingInstance = ref({})
+const uploadedFiles = ref([]); // 用于保存已上传完成的文件
 // 校验上传文件格式
 const getType = (acceptType: string) => {
   let val = "";
@@ -109,8 +110,10 @@ const getToken = async () => {
 }
 
 const beforeUpload = (file: File) => {
-  if (!/^[0-9]+_/.test(file.name) && props.isCheckName) {
-    ElMessage.error(`文件上传格式错误,请以数字和下划线开头,例如:01_xxx`);
+  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`);
     return false
   }
   const filetype = file.type;
@@ -163,13 +166,16 @@ const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
   }
 }
 
-const onSuccess = (response: any) => {
-  uploading.value = false
-  ElMessage.success('上传成功')
-  loadingInstance.value.close();
+const onSuccess = (response: any, uploadFile?: any, uploadFiles?: any) => {
   emits('success', response)
   console.log(props.public_video_url);
+  uploadedFiles.value.push(response)
   props.fileList.push({ name: response.fname, url: props.public_video_url, ...response })
+  ElMessage.success('上传成功')
+  if (uploadFiles.length == uploadedFiles.value.length) {
+    loadingInstance.value.close();
+    uploading.value = false
+  }
 }
 
 const onError = (error: any) => {

+ 1 - 1
src/layout/components/header/index.vue

@@ -77,7 +77,7 @@ const isShowNavData = computed(() => {
     miniPrograms.value = navDataArr.value[0]?.miniPrograms
     isAdverDisabled.value = true;
   }
-  return userStore.getRoles?.some(el => (el?.identify == 'optimizer' || el?.identify == 'company'))
+  return userStore.getRoles?.some(el => (el?.identify == 'optimizer'))
 })
 const appChange = (e) => {
   Cache.set('nav_data', JSON.stringify(navData.value));

+ 20 - 0
src/router/modules/charge.ts

@@ -0,0 +1,20 @@
+// import { RouteRecordRaw } from 'vue-router';
+// // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// // @ts-ignore
+// const router: RouteRecordRaw[] = [
+//   {
+//     path: '/charge',
+//     component: () => import('@/layout/index.vue'),
+//     meta: { title: '充值管理', icon: 'user' },
+//     children: [
+//       {
+//         path: 'index',
+//         name: 'user-account',
+//         meta: { title: '充值模板管理', icon: 'home' },
+//         component: () => import('@/views/chargeManage/chargeTemplate/index.vue')
+//       }
+//     ]
+//   }
+// ];
+
+// export default router;

+ 209 - 0
src/views/chargeManage/chargeTemplate/form/create.vue

@@ -0,0 +1,209 @@
+<template>
+  <el-card shadow="always" :body-style="{ padding: '20px' }">
+    <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' },]">
+        <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)" />
+          </div>
+          <el-table :data="tableData" class="w-full mt-3" v-loading="loading">
+            <el-table-column prop="price" label="价格">
+              <template #default="scope">
+                <span>{{ scope.row.price }}元</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="sequence_text" label="位置" />
+            <el-table-column prop="type_name" label="挡位类型" />
+            <el-table-column prop="charge_coin" label="充看币">
+              <template #default="scope">
+                <span v-if="!isShowKb(scope.row)">{{ Math.round(Number(scope.row.price) * 100) }}k币</span>
+                <span v-else>{{ scope.row.type_name }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="given" label="充看送币">
+              <template #default="scope">
+                <span>{{ scope.row.given }}k币</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="default_text" label="默认项">
+              <template #default="scope">
+                <span>{{ scope.row.is_default ? '默认项' : '非默认项' }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="200">
+              <template #default="scope">
+                <el-button link type="primary" size="small" @click="openGears(scope.$index, scope.row)">编辑</el-button>
+                <el-button link type="danger" size="small" @click.prevent="deleteRow(scope.$index)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </el-form-item>
+      <el-form-item label="启用状态" prop="status" :rules="[{ required: true, message: '请设置启用状态' }]">
+        <el-switch v-model="dataForm.status" :active-value="1" :inactive-value="0" />
+      </el-form-item>
+      <div class="flex justify-start ml-9">
+        <el-button type="primary" size="default" @click="cancel">取消</el-button>
+        <el-button type="primary" @click="submitFormChange(form)">确认</el-button>
+      </div>
+    </el-form>
+    <Dialog v-model="visibleGears" :title="title" width="40%" destroy-on-close>
+      <createGears @close="gearsClose" @success="success" :data="tableData" :primary="gearsData" :ismulSet="ismulSet" />
+    </Dialog>
+  </el-card>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, ref } from 'vue';
+import {
+  channelPaytemplateShow,
+  channelPaytemplateStore,
+  channelPaytemplateUpdate
+} from '@/api/charge/index'
+import { FormInstance } from 'element-plus';
+import createGears from './createGears.vue'
+const visibleGears = ref(false)
+const loading = ref(false)
+const gearsData = ref()
+const tableData = ref([])
+const form = ref()
+const emit = defineEmits(['close']);
+const dataForm = ref({})
+import {
+  channelPaytemplateOptionTypeList,
+  channelPaytemplateOptionOptionSequence
+} from '@/api/charge/index'
+const optionTypeList = ref([]);
+const props = defineProps({
+  primary: String | Number | Object,
+  api: String,
+});
+const filtersObj = ['MONTH', 'QUARTER', 'YEAR']
+
+const isShowKb = (e) => {
+  return filtersObj.includes(e.type)
+}
+const gearsClose = () => {
+  visibleGears.value = false
+}
+const openGears = (index: number | null, row: object) => {
+  if (row) {
+    gearsData.value = row
+    gearsData.value.index = index
+  } else {
+    gearsData.value = null
+  }
+  visibleGears.value = true
+}
+const deleteRow = (index: number) => {
+  ElMessageBox.confirm(
+    '确定要删除此条充值档位吗?',
+    '提示',
+    {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+      tableData.value.splice(index, 1)
+    })
+    .catch(() => {
+
+    })
+}
+const success = (e) => {
+  console.log(e, 'successsuccesssuccess');
+  const data = {
+    ...e,
+    ...e.typeObj,
+    ...e.sequenceObj
+  };
+  if (e.index != undefined) {
+    tableData.value[e.index] = data
+  } else {
+    tableData.value.push(data)
+  }
+
+  // emit('close')
+}
+const cancel = () => {
+  emit('close')
+}
+const submitFormChange = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  dataForm.value.options = tableData.value.map(el => {
+    return {
+      price: Number(el.price),
+      type: el.type,
+      given: Number(el.given),
+      sequence: el.sequence,
+      is_default: el.is_default
+    }
+  })
+  loading.value = true;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        const params = {
+          name: dataForm.value.name,
+          status: dataForm.value.status,
+          options: JSON.stringify(dataForm.value.options),
+        }
+        console.log(params, 'paramsparams');
+        if (props.primary) {
+          channelPaytemplateUpdate(dataForm.value.id, params).then(res => {
+            console.log(res, 'channelPaytemplateUpdatechannelPaytemplateUpdatechannelPaytemplateUpdate');
+            loading.value = false;
+            emit('close')
+          })
+
+        } else {
+          channelPaytemplateStore(params).then(res => {
+            console.log(res, 'channelPaytemplateStorechannelPaytemplateStorechannelPaytemplateStore');
+            loading.value = false;
+            emit('close')
+          })
+        }
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+if (props.primary) {
+  console.log(props.primary, 'props.primary');
+  channelPaytemplateShow(props.primary).then(res => {
+    dataForm.value = res.data.template_info
+    console.log(res, 'channelPaytemplateShowchannelPaytemplateShow');
+    tableData.value = res.data.template_item_list
+  })
+}
+
+onMounted(() => {
+  channelPaytemplateOptionTypeList().then(res => {
+    console.log(res);
+    optionTypeList.value = res.data
+
+  })
+});
+</script>
+<style scoped lang="scss">
+.item {
+  width: 300px;
+}
+
+.type-wrapper {
+  width: 80%;
+  display: flex;
+
+  .btn {
+    margin: 0 6px;
+  }
+}
+</style>

+ 170 - 0
src/views/chargeManage/chargeTemplate/form/createGears.vue

@@ -0,0 +1,170 @@
+<template>
+  <el-form :model="formData" label-width="120px" ref="ruleForm" :rules="rules" class="pr-4">
+    <div class="flex flex-row justify-between">
+      <div class="w-full">
+        <el-form-item label="位置" prop="sequenceObj">
+          <el-select class="w-full" v-model="formData.sequenceObj" @change="sequenceChange" value-key="sequence"
+            filterable clearable placeholder="请选择位置">
+            <el-option v-for="(item, index) in optionSequence" :key="index" :label="item.sequence_text" :value="item" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="档位类型" prop="typeObj">
+          <el-select class="w-full" v-model="formData.typeObj" value-key="type" filterable clearable
+            placeholder="请选择档位类型">
+            <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">
+          <div class="items-center w-full">
+            <span>充</span>
+            <el-input style="width:30%;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>
+          <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-option label="默认项" :value="1" />
+            <el-option label="非默认项" :value="0" />
+          </el-select>
+        </el-form-item>
+      </div>
+    </div>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(ruleForm)">{{ $t('system.confirm') }}</el-button>
+    </div>
+  </el-form>
+</template>
+
+<script lang="ts" setup>
+import { InfoFilled } from '@element-plus/icons-vue';
+import type { FormInstance, FormRules } from 'element-plus'
+import {
+  channelPaytemplateOptionTypeList,
+  channelPaytemplateOptionOptionSequence
+} from '@/api/charge/index'
+const ruleForm = ref<FormInstance>()
+const props = defineProps({
+  primary: String | Number,
+  data: Array
+});
+
+const filtersObj = ['MONTH', 'QUARTER', 'YEAR']
+
+const isShowBi = computed(() => filtersObj.includes(formData.value?.typeObj?.type))
+const optionTypeList = ref([]);
+const optionSequence = ref([])
+const formData = ref({})
+const rules = reactive({
+  price: [
+    {
+      required: true,
+      message: '请输入金额',
+      trigger: 'blur'
+    },
+    { pattern: /^\d+(\.\d{1,2})?$/, message: '金额最多只能有两位小数', trigger: 'blur' },
+    { pattern: /^(?!0\d+)\d+(\.\d+)?$/, message: '请输入有效的金额', trigger: 'blur' },
+  ],
+  given: [
+    {
+      required: true,
+      message: '请输入充送看币',
+      trigger: 'blur'
+    },
+    { pattern: /^[1-9]\d*$/, message: '请输入正整数充送看币', trigger: 'blur' },
+  ],
+  sequenceObj: [
+    {
+      required: true,
+      message: '请选择位置',
+      trigger: 'change'
+    }
+  ],
+  typeObj: [
+    {
+      required: true,
+      message: '请选择档位类型',
+      trigger: 'change'
+    }
+  ],
+
+  is_default: [
+    {
+      required: true,
+      message: '请选择默认项',
+      trigger: 'change'
+    }
+  ],
+});
+const init = () => {
+  channelPaytemplateOptionTypeList().then(res => {
+    console.log(res);
+    optionTypeList.value = res.data
+
+  })
+  channelPaytemplateOptionOptionSequence().then(res => {
+    console.log(res);
+    optionSequence.value = res.data
+  })
+}
+
+const sequenceChange = (e) => {
+  console.log(e, 'sequenceChangesequenceChange');
+  console.log(props.data, 'props.data');
+  const findSequence = props.data.find(el => el.sequence == e.sequence)
+  console.log(findSequence, 'findSequencefindSequence');
+  if (findSequence?.sequence_text) {
+    ElMessage.error(`${findSequence.sequence_text},已存在请重新选择`)
+    const timer = setTimeout(() => {
+      formData.value.sequenceObj = ''
+      clearTimeout(timer)
+    }, 30)
+  }
+}
+
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        emit('success', formData.value)
+        emit('close')
+      } else {
+      }
+    })
+    .then(() => { });
+}
+
+if (props.primary) {
+  console.log(props.primary, '88888888888');
+  formData.value = JSON.parse(JSON.stringify(props.primary))
+  formData.value.sequenceObj = { sequence_text: props.primary.sequence_text, sequence: props.primary.sequence }
+  formData.value.typeObj = { type_name: props.primary.type_name, type: props.primary.type }
+}
+
+const emit = defineEmits(['close', 'success']);
+onMounted(() => {
+  init()
+});
+</script>
+
+<style lang="scss" scoped>
+:deep(.form-item) {
+  display: inline-table;
+  width: 30%;
+}
+
+:deep(.form-item-price) {
+  flex-wrap: nowrap;
+}
+</style>

+ 70 - 0
src/views/chargeManage/chargeTemplate/index.vue

@@ -0,0 +1,70 @@
+<template>
+  <div v-if="!showCreate">
+    <Search :search="search" :reset="reset">
+      <template v-slot:body>
+        <el-form-item label="标题">
+          <el-input v-model="query.name" clearable />
+        </el-form-item>
+      </template>
+    </Search>
+    <div class="table-default">
+      <div class="pt-5 pl-2">
+        <Add @click="openForm(null)" v-action="'manage.payTemplate.store'" />
+      </div>
+      <el-table :data="tableData" class="mt-3" v-loading="loading">
+        <el-table-column prop="name" label="模板名称" />
+        <el-table-column label="状态" v-action="'manage.payTemplate.updateStatus'">
+          <template #default="scope">
+            <el-switch v-model="scope.row.status" @change="statusChange($event, scope.row)" :active-value="1"
+              :inactive-value="0" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="200">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="openForm(scope.row)"
+              v-action="'manage.payTemplate.update'">编辑</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <Paginate />
+    </div>
+  </div>
+  <Create v-else @close="cancel(search)" :primary="primary" :api="editapi" />
+</template>
+
+<script lang="ts" setup>
+import { computed, onMounted, ref } from 'vue';
+import Create from './form/create.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { channelPaytemplateUpdateStatus } from '@/api/charge/index'
+
+const api = 'channel/paytemplate/list';
+let editapi = 'system/notices/notice/edit';
+let showCreate = ref(false)
+
+const { data, query, search, reset, loading } = useGetList(api, true);
+const tableData = computed(() => data.value?.data);
+const primary = ref({})
+
+const openForm = (data: any) => {
+  showCreate.value = true
+  if (data) {
+    primary.value = data.id
+  } else {
+    primary.value = data
+  }
+};
+const cancel = () => {
+  showCreate.value = false
+  search()
+};
+const statusChange = (e, row) => {
+  channelPaytemplateUpdateStatus(row.id, { status: e }).then(res => {
+    ElMessage.success(res.message)
+    search()
+  })
+};
+onMounted(() => {
+  search();
+});
+</script>

+ 1 - 1
src/views/system/notice/form/create.vue

@@ -10,7 +10,7 @@
             <el-option v-for="item in noticesTypes" :key="item.id" :label="item.name" :value="item.id" />
           </el-select>
           <el-button type="text" size="default" class="btn" @click="addType"
-            v-action="'contentManage.System\\NoticeTypes.add'">添加类型</el-button>
+            v-action="'system.NoticeTypes.add'">添加类型</el-button>
         </div>
       </el-form-item>
       <el-form-item label="通知人群类型" prop="type" :rules="[{ required: true, message: '请选择通知人群类型' }]">

+ 3 - 3
src/views/system/notice/index.vue

@@ -14,7 +14,7 @@
     </Search>
     <div class="table-default">
       <div class="pt-5 pl-2">
-        <Add @click="openForm(null)" v-action="'contentManage.System\\Notices.addNotice'" />
+        <Add @click="openForm(null)" v-action="'system.Notices.addNotice'" />
       </div>
       <el-table :data="tableData" class="mt-3" v-loading="loading">
         <el-table-column prop="sort" label="排序" />
@@ -30,8 +30,8 @@
         <el-table-column prop="created_at" label="发布时间" />
         <el-table-column label="操作" width="200">
           <template #default="scope">
-            <Update @click="openForm(scope.row)" v-action="'contentManage.System\\Notices.edit'" />
-            <Destroy @click="destroy(delapi, scope.row.id)" v-action="'contentManage.System\\Notices.delete'" />
+            <Update @click="openForm(scope.row)" v-action="'system.Notices.edit'" />
+            <Destroy @click="destroy(delapi, scope.row.id)" v-action="'system.Notices.delete'" />
           </template>
         </el-table-column>
       </el-table>

+ 39 - 0
src/views/videoManage/detail.vue

@@ -13,6 +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> -->
           </template>
         </el-table-column>
       </el-table>
@@ -24,18 +25,37 @@
       <source :src="current.public_video_url" type="video/mp4">
     </video>
   </Dialog>
+
+  <Dialog v-model="linkVisible" title="创建推广链接" destroy-on-close>
+    <el-form :model="formDataForm" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+      <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>
+      <div class="flex justify-end">
+        <el-button type="primary" @click="submitForm(form)">{{
+          $t('system.confirm')
+        }}</el-button>
+      </div>
+    </el-form>
+  </Dialog>
 </template>
 
 <script lang="ts" setup>
 import { computed, onMounted, ref } from 'vue';
 import { useGetList } from '@/hook/curd/useGetList';
 import { useOpen } from '@/hook/curd/useOpen';
+import { FormInstance } from 'element-plus';
 const api = 'videoStock/episode/list';
 const props = defineProps({
   primary: String | Number,
 });
+const formDataForm = ref({})
 const current = ref({})
 const playVisible = ref(false)
+const linkVisible = ref(false)
 const { data, query, search, reset, loading } = useGetList(api);
 const { open, close, title, visible, id } = useOpen();
 if (props.primary) {
@@ -46,6 +66,25 @@ const play = (e: object) => {
   current.value = e;
   playVisible.value = true;
 }
+const createLink = (e: object) => {
+  current.value = e;
+  linkVisible.value = true;
+}
+
+const submitForm = (formEl: FormInstance | undefined) => {
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
 onMounted(() => {
   console.log(props.primary, 'props.primaryprops.primary');
   search();

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

@@ -35,7 +35,7 @@
             <el-input-number style="width:200px;" v-model="formData.d_charge_coin" :precision="2" :step="1" :min="1"
               placeholder="请输入看剧币" />
             <el-tooltip placement="top">
-              <template #content> multiple lines<br />second line </template>
+              <template #content> 默认定价为默认所需看剧币<br />1元=100币 </template>
               <el-icon>
                 <InfoFilled />
               </el-icon>

+ 29 - 6
src/views/videoManage/form/subscribeSet.vue

@@ -8,14 +8,14 @@
           </el-radio-group>
         </el-form-item>
         <el-form-item label="起始集" prop="chargeSequence">
-          <el-input-number style="width:200px;" v-model="formData.chargeSequence" :step="1" step-strictly :min="1"
-            :max="30" @change="handleChange" placeholder="请输入起始集" />
+          <el-input-number style="width:200px;" v-model.number="formData.chargeSequence" :step="1" step-strictly
+            placeholder="请输入起始集" />
           <span class="ml-3 text-gray-400">范围:1-30</span>
         </el-form-item>
         <el-form-item label="单集定价" prop="chargeCoin">
           <div class="flex items-center w-full">
-            <el-input-number style="width:200px;" v-model="formData.chargeCoin" :step="1" step-strictly :min="50"
-              :max="300" @change="handleChange" placeholder="请输入单集定价" />
+            <el-input-number style="width:200px;" v-model.number="formData.chargeCoin" :step="1" step-strictly
+              placeholder="请输入单集定价" />
             <span class="ml-3 text-gray-400">范围:50-300</span>
           </div>
         </el-form-item>
@@ -37,6 +37,27 @@ const props = defineProps({
   primary: String | Number,
 });
 const formData = ref({ status: 2, miniProgramIds: [] })
+const validateNumber = (rule, value, callback) => {
+  const min = 1;
+  const max = 30;
+
+  if (value < min || value > max) {
+    callback(new Error(`范围必须在 ${min} 到 ${max} 之间`));
+  } else {
+    callback();
+  }
+};
+
+const validateNumberCoin = (rule, value, callback) => {
+  const min = 50;
+  const max = 300;
+
+  if (value < min || value > max) {
+    callback(new Error(`范围必须在 ${min} 到 ${max} 之间`));
+  } else {
+    callback();
+  }
+};
 const rules = reactive({
   chargeType: [{ required: true, message: '请选择收费模式', trigger: 'change' }],
   chargeSequence: [
@@ -44,14 +65,16 @@ const rules = reactive({
       required: true,
       message: '请输入起始集',
       trigger: 'blur'
-    }
+    },
+    { validator: validateNumber, trigger: 'blur' },
   ],
   chargeCoin: [
     {
       required: true,
       message: '请输入单集定价',
       trigger: 'blur'
-    }
+    },
+    { validator: validateNumberCoin, trigger: 'blur' },
   ]
 
 });

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

@@ -7,8 +7,12 @@
       <el-input disabled v-model="formDataForm.total_episode_num" name="author" clearable />
     </el-form-item>
     <el-form-item label="上传" prop="videos" :rules="[{ required: true, message: '请上传文件', trigger: 'change' }]">
-      <div class="w-full">支持MP4格式</div>
+      <div class="items-center w-full">
+        支持MP4格式
+        <span class="text-red-400">上传文件统一命名格式要求,例:1;11;333</span>
+      </div>
       <div class="w-full">
+
         <video-uploader :fileList="videofileList" :public_video_url='formDataForm.cover_image' @fileRemove="fileRemove"
           @success="fileSuccess"></video-uploader>
       </div>
@@ -58,6 +62,8 @@ const submitForm = (formEl: FormInstance | undefined) => {
           loading.value = false;
           videofileList.value = []
           emit('close')
+        }).catch(e => {
+          loading.value = false;
         })
         console.log(formDataForm.value, 'params', params);
       } else {

+ 4 - 5
src/views/videoManage/form/videoDL.vue

@@ -1,7 +1,9 @@
 <template>
+  <el-alert title="注:如需批量下载,则多选目标集数,点击下载按钮即可" type="warning" show-icon :closable="false" />
   <div class="wrapper">
     <!-- :poster="videoImg"视频封面 -->
-    <video id="my-player" style="width:500px;" :key="current.public_video_url" class="video-js" controls autoplay ref="myVideo">
+    <video id="my-player" style="width:500px;" :key="current.public_video_url" class="video-js" controls autoplay
+      ref="myVideo">
       <source :src="current.public_video_url" type="video/mp4">
     </video>
     <!-- 视频名称列表布局 -->
@@ -9,10 +11,7 @@
       <div class="flex items-center justify-between h-10">
         <div class="w-full text-base font-medium">剧名: <span class="ml-2 text-lg font-bold text-dark-600">{{
           props.primary.name }}</span> <span v-if="current.series_name">(当前播放{{ current.series_name }})</span></div>
-        <el-tooltip placement="top">
-          <template #content> 如需批量下载,则多选目标集数,点击下载按钮即可<br /> </template>
-          <download v-if="videoChoose.length > 0" :urls="videoChooseUrls" :fileName="props.primary.name"></download>
-        </el-tooltip>
+        <download v-if="videoChoose.length > 0" :urls="videoChooseUrls" :fileName="props.primary.name"></download>
       </div>
       <mytabs :meta="meta" v-if="meta.total" @change="handChange">
         <template v-slot:content>

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

@@ -27,7 +27,7 @@
         <el-table-column label="封面" show-overflow-tooltip>
           <template #default="scope">
             <div class="flex flex-col items-start justify-center wrapper">
-              <el-popover placement="top" :width="200" trigger="hover">
+              <el-popover placement="top" :width="200" trigger="click">
                 <template #reference>
                   <el-image :src="scope.row.cover_image" class="cursor-pointer" style="width:48px;height:48px;"
                     fit="contain" :lazy="true"></el-image>
@@ -45,7 +45,7 @@
                 @click="openType('videoDetailVisible', scope.row)">
                 {{ scope.row.name }}
               </span>
-              <span v-if="!rolesIdentify.includes('optimizer')">
+              <span>
                 【 {{ scope.row.update_type_str }} 】
               </span>
             </div>
@@ -72,7 +72,7 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column prop="category_str" label="频道" v-if="!rolesIdentify.includes('company')" />
+        <el-table-column prop="category_str" label="频道" />
         <el-table-column prop="shelf_type_str" label="上架状态" v-if="!rolesIdentify.includes('company')" />
         <el-table-column prop="charge_sequence" label="起始集" v-if="!rolesIdentify.includes('company')" />
         <el-table-column prop="charge_coin" label="定价" v-if="!rolesIdentify.includes('company')">
@@ -101,6 +101,9 @@
               v-action="'video.video.setChargeConfig'">订阅设置</el-button><br />
             <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> -->
           </template>
         </el-table-column>
       </el-table>