Browse Source

页面配置:首页列表管理

pansl 1 năm trước cách đây
mục cha
commit
5553005927

+ 26 - 0
src/api/pageLayout/homePage/index.ts

@@ -0,0 +1,26 @@
+import http from '@/api/http';
+/**
+ * 首页列表-列表
+ */
+export function operationManageFirstPageList(params?: object) {
+  return http.get('/operationManage/firstPage/list', params);
+}
+
+/**
+ * 首页列表-开启状态
+ */
+export function operationManageFirstPageEnableStatus(params: object) {
+  return http.post('/operationManage/firstPage/enableStatus', params);
+}
+/**
+ * 首页列表-添加
+ */
+export function operationManageFirstPageAdd(params: object) {
+  return http.post(`/operationManage/firstPage/add`, params);
+}
+/**
+ * 首页列表-配置
+ */
+export function operationManageFirstPageSetConfig(params: object) {
+  return http.post(`/operationManage/firstPage/setConfig`, params);
+}

+ 98 - 0
src/views/pageLayout/homePageManage/form/config.vue

@@ -0,0 +1,98 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="列表名称" prop="ids" :rules="[{ required: false, message: '巨量账户ID必须填写' }]" label-width="120px">
+      <el-input v-model="props.primary.type_str" disabled size="default" clearable></el-input>
+    </el-form-item>
+    <el-form-item label="短剧" prop="duanjus" :rules="[{ required: true, message: '选择短剧' }]">
+      <div>
+        <div class="flex flex-wrap mb-5" v-if="formCallback.duanjus.length > 0">
+          <div v-for="item in formCallback.duanjus" :key="item.id" class="mt-3 mr-3">
+            <el-input v-model="item.name" disabled placeholder="短剧名称" style="width:300px;" class="input-with-select">
+              <template #prepend>
+                <el-input type="number" min="1" disabled v-model.number="item.sort" placeholder="排序" size="default"
+                  clearable style="width: 115px"></el-input>
+              </template>
+            </el-input>
+          </div>
+        </div>
+        <div>
+          <el-button type="primary" size="default" @click="chooseShort">选择短剧</el-button>
+        </div>
+      </div>
+    </el-form-item>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="submitForm(form)">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </el-form>
+  <Dialog v-model="videoVisible" title="选择短剧" width="50%" destroy-on-close>
+    <videoList @confirm="confirm" :primary="formCallback.duanjus"></videoList>
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import videoList from './videoList.vue';
+import { Close } from '@element-plus/icons-vue';
+import { FormInstance } from 'element-plus';
+import { operationManageFirstPageSetConfig } from '@/api/pageLayout/homePage/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 videoVisible = ref(false)
+const promotion = ref('')
+const form = ref()
+const formCallback = ref({
+  duanjus: []
+})
+
+
+const chooseShort = () => {
+  videoVisible.value = true
+}
+
+const confirm = (e) => {
+  formCallback.value.duanjus = e
+  videoVisible.value = false
+}
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  console.log(formCallback.value, 'formCallbackformCallback');
+  if (!formEl) return;
+  loading.value = true;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        operationManageFirstPageSetConfig(formCallback.value).then(res => {
+          ElMessage.success(res.message)
+          emit('close')
+        })
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+if (props.primary) {
+  console.log(props.primary, 'props.primaryprops.primary');
+  formCallback.value.id = props.primary.id
+  formCallback.value.duanjus = props.primary.duanjus
+}
+onMounted(() => {
+});
+</script>
+
+<style lang="scss" scoped>
+:deep(.input-with-select .el-input-group__prepend) {
+  background-color: #FFFFFF;
+  padding: 0;
+  border: 1px solid #dcdfe6;
+}
+</style>

+ 59 - 0
src/views/pageLayout/homePageManage/form/create.vue

@@ -0,0 +1,59 @@
+<template>
+  <el-form :model="formCallback" label-width="120px" ref="form" v-loading="loading" class="pr-4">
+    <el-form-item label="列表名称" prop="type" :rules="[{ required: true, message: '选择列表名称' }]" label-width="120px">
+      <el-select v-model="formCallback.type" clearable filterable placeholder="请选择支付方式">
+        <el-option v-for="(item, index) in typeList" :key="index" :label="item.name" :value="item.value" />
+      </el-select>
+    </el-form-item>
+    <el-form-item label="状态" prop="status" label-width="120px" :rules="[{ required: true, message: '选择状态' }]">
+      <el-switch v-model="formCallback.status" :active-value="1" :inactive-value="0"></el-switch>
+    </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 { operationManageFirstPageAdd } from '@/api/pageLayout/homePage/index'
+const emit = defineEmits(['close']);
+const loading = ref(false)
+const form = ref()
+const promotion = ref('')
+const formCallback = ref({
+  status: 0,
+})
+
+const typeList = ref([{ name: '本周精选', value: 1 }, { name: '优选好剧', value: 2 }])
+
+// 提交回传配置
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  loading.value = true;
+  if (!formEl) return;
+  formEl
+    .validate(valid => {
+      if (valid) {
+        operationManageFirstPageAdd(formCallback.value).then(res => {
+          ElMessage.success(res.message)
+          emit('close')
+        })
+        loading.value = false;
+      } else {
+        loading.value = false;
+      }
+    })
+    .then(() => { });
+}
+
+
+onMounted(() => {
+
+});
+</script>
+
+<style lang="scss" scoped></style>

+ 256 - 0
src/views/pageLayout/homePageManage/form/videoList.vue

@@ -0,0 +1,256 @@
+<template>
+  <div>
+    <div class="flex flex-wrap ml-6">
+      <el-form-item label="所选短剧:" class="font-bold">
+        <div v-for="(item, index) in multipleSelection" :key="item.id" class="mb-3 mr-3">
+          <el-input type="number" :disabled="false" min="1" v-model.number="sortValues[index]" placeholder="排序"
+            size="default" clearable style="width: 115px;"></el-input>
+          <el-input v-model="item.name" :disabled="true" placeholder="短剧名称" style="width:300px;"
+            class="input-with-select">
+          </el-input>
+        </div>
+      </el-form-item>
+    </div>
+    <Search :search="search" :reset="resetQuery">
+      <template v-slot:body>
+        <el-form-item label="短剧" prop="name">
+          <el-input v-model="query.videoName" placeholder="请输入短剧名称" clearable />
+        </el-form-item>
+        <el-form-item label="状态" prop="updateType">
+          <el-select v-model="query.updateType" filterable clearable remote placeholder="请选择状态">
+            <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="频道" prop="categoryId">
+          <el-cascader v-model="selectType" :options="cooperations" filterable clearable :change-on-select="true"
+            @change="handleCascaderChange" />
+        </el-form-item>
+      </template>
+    </Search>
+    <div class="table-default">
+      <el-table :data="tableData" ref="multipleTableRef" class="mt-3" v-loading="loading" row-key="id"
+        @selection-change="handleSelectionChange">
+        <el-table-column type="selection" reserve-selection />
+        <el-table-column prop="id" label="剧号" />
+        <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="click">
+                <template #reference>
+                  <el-image :src="scope.row.cover_image" class="cursor-pointer" style="width:48px;height:48px;"
+                    fit="contain" :lazy="true"></el-image>
+                </template>
+                <el-image :src="scope.row.cover_image" style="width:100%;" fit="contain"></el-image>
+              </el-popover>
+              <el-button type="primary" link size="default" @click="dowload(scope.row)">下载</el-button>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="短剧名称" show-overflow-tooltip min-width="250">
+          <template #default="scope">
+            <div class="wrapper">
+              <span class="text-lg font-bold text-blue-400 cursor-pointer content">
+                {{ scope.row.name }}
+              </span>
+              <span>
+                【 {{ scope.row.update_type_str }} 】
+              </span>
+            </div>
+            <div class="wrapper">
+              <span class="label">上架时间:</span>
+              <span class="content">{{ scope.row.shelf_at }}</span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="total_episode_num" label="集数">
+          <template #default="scope">
+            <div class="flex wrapper">
+              <div v-if="scope.row.update_type == 1">
+                <span class="content">
+                  {{ scope.row.updated_episode_num }}
+                </span>
+                <span>/</span>
+              </div>
+              <div>
+                <span class="content">
+                  {{ scope.row.total_episode_num }}
+                </span>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="category_str" label="频道" />
+        <el-table-column prop="shelf_type_str" label="上架状态" />
+        <el-table-column prop="charge_sequence" label="起始集" />
+        <el-table-column prop="charge_coin" label="定价">
+          <template #header>
+            <div class="flex items-center">
+              <span>定价</span>
+              <el-tooltip placement="top">
+                <template #content> 定价为所需看剧币<br />1元等于100币 </template>
+                <el-icon>
+                  <InfoFilled />
+                </el-icon>
+              </el-tooltip>
+            </div>
+          </template>
+          <template #default="scope">
+            <span>{{ scope.row.charge_coin }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <Paginate />
+    </div>
+    <div class="flex justify-end">
+      <el-button type="primary" @click="confirm">{{
+        $t('system.confirm')
+      }}</el-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { downloadImage } from '@/utils/index'
+import { InfoFilled } from '@element-plus/icons-vue';
+import { provide } from 'vue'
+import { useGetList } from '@/hook/curd/useGetList';
+import { useDestroy } from '@/hook/curd/useDestroy';
+import { useOpen } from '@/hook/curd/useOpen';
+import { videoStockVideoCategoryList, videoStockVideoList } from '@/api/video/index'
+const api = 'videoStock/video/list';
+const multipleTableRef = ref()
+const multipleSelection = ref([]);
+const { data, query, search, reset, loading } = useGetList(api);
+const selectType = ref([])
+const statusOptions = ref([{ label: '连载中', value: '1' }, { label: '完结', value: '2' }]);
+const cooperations = ref([]);
+const emit = defineEmits(['confirm']);
+const props = defineProps({
+  primary: Object,
+});
+const sortValues = ref([])
+const rowSelectFlag = ref(false)// 禁止toggleRowSelection默认触发handleSelectionChange
+const handleCascaderChange = (val: any) => {
+  console.log(val, 'valval');
+  if (val) {
+    // 获取最后一级选项的值
+    const lastOptionValue = val[val.length - 1];
+    // 更新 selectedOptions 的值
+    query.value.categoryId = lastOptionValue;
+  } else {
+    delete query.value.categoryId
+  }
+}
+
+const resetQuery = () => {
+  query.value = Object.assign({ page: query.value.page, limit: query.value.limit, shelfType: 2 });
+  search()
+  selectType.value = []
+}
+const initType = () => {
+  videoStockVideoCategoryList().then(res => {
+    console.log(res);
+    cooperations.value = res.data
+  })
+}
+// 下载封面
+const dowload = (e: object) => {
+  downloadImage(e.cover_image, e.name)
+}
+
+const validateData = () => {
+  const sortSet = new Set();
+  const nameSet = new Set();
+
+  for (const item of multipleSelection.value) {
+    if (item.sort === null || item.sort === undefined) {
+      ElMessage.error(`${item.name}的排序不能为空`)
+      return false;
+    }
+    if (sortSet.has(item.sort)) {
+      ElMessage.error(`${item.name}的排序重复`)
+      return false;
+    }
+    sortSet.add(item.sort);
+    if (nameSet.has(item.name)) {
+      ElMessage.error(`${item.name}的排序重复`)
+      return false;
+    }
+
+    nameSet.add(item.name);
+  }
+
+  return true;
+};
+
+const tableData = computed(() => data.value?.data);
+
+const handleSelectionChange = (val: []) => {
+  if (rowSelectFlag.value) return
+  multipleSelection.value = val;
+};
+const confirm = () => {
+  const params = multipleSelection.value.map((el, index) => {
+    multipleSelection.value[index].sort = sortValues.value[index]
+    return {
+      id: el.id,
+      sort: sortValues.value[index],
+      name: el.name
+    }
+  })
+  console.log(sortValues.value, params, 'sortValues[index]');
+  if (validateData()) {
+    // 执行提交逻辑
+    console.log('数据验证通过');
+    emit('confirm', params)
+  }
+}
+
+if (props.primary) {
+  // console.log(props.primary, 'props.primaryprops.primary');
+  multipleSelection.value = props.primary || []
+  if (multipleSelection.value.length > 0) {
+    sortValues.value = multipleSelection.value.map(el => el.sort)
+    console.log(sortValues.value, 'sortValues.valuesortValues.value');
+  }
+}
+
+const setRowSelected = () => {
+  rowSelectFlag.value = true
+  Object.keys(multipleSelection.value).forEach(key => {
+    multipleSelection.value[key] && multipleTableRef.value.toggleRowSelection(multipleSelection.value[key], true)
+  })
+  rowSelectFlag.value = false
+}
+onMounted(() => {
+  // 初始化 sortValues 数组长度,保持与 multipleSelection 数组一致
+  // sortValues.value = new Array(multipleSelection.value.length);
+  query.value = Object.assign({ page: query.value.page, limit: query.value.limit, shelfType: 2 });
+  setRowSelected()
+  initType()
+  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>

+ 86 - 0
src/views/pageLayout/homePageManage/index.vue

@@ -0,0 +1,86 @@
+<template>
+  <div class="table-default">
+    <div class="pt-5 pl-2" v-action="'operation.FirstPage.add'">
+      <el-button type="primary" size="default" @click="openForm(null)">新增</el-button>
+    </div>
+    <el-table :data="tableData" class="mt-3" v-loading="loading">
+      <el-table-column label="ID" prop="id"></el-table-column>
+      <el-table-column label="列表名称" prop="type_str">
+      </el-table-column>
+      <el-table-column label="添加时间" prop="created_at">
+      </el-table-column>
+      <el-table-column label="回传开关" v-action="'operation.FirstPage.enableStatus'">
+        <template #default="scope">
+          <div>
+            <el-switch v-model="scope.row.status" :disabled="Boolean(scope.row.status)" @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="handleConfigure(scope.row)"
+            v-action="'operation.FirstPage.setConfig'">配置</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <Paginate />
+  </div>
+  <Dialog v-model="configVisible" title="配置" width="50%" destroy-on-close>
+    <config @close="closeType('configVisible')" :primary="currentConfig"></config>
+  </Dialog>
+
+  <Dialog v-model="addVisible" title="新增" width="30%" destroy-on-close>
+    <create @close="closeType('addVisible')"></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 config from './form/config.vue';
+import { useGetList } from '@/hook/curd/useGetList';
+import { operationManageFirstPageEnableStatus } from '@/api/pageLayout/homePage/index'
+
+const api = 'operationManage/firstPage/list';
+
+const { data, query, search, reset, loading } = useGetList(api, true);
+const tableData = computed(() => data.value?.data);
+const addVisible = ref(false)
+const currentConfig = ref({})
+const configVisible = ref(false) //设置回传弹窗
+
+const closeType = (type: string) => {
+  switch (type) {
+    case 'configVisible':
+      configVisible.value = false
+      break;
+    case 'addVisible':
+      addVisible.value = false
+      break;
+  }
+  search()
+}
+
+const switchStatus = (data: object) => {
+  operationManageFirstPageEnableStatus({ id: data.id }).then(res => {
+    ElMessage.success(res.message)
+    search()
+  })
+}
+
+
+const openForm = (data: any) => {
+  addVisible.value = true
+};
+const handleConfigure = (data: object) => {
+  currentConfig.value = data
+  configVisible.value = true
+};
+onMounted(() => {
+  search();
+});
+</script>
+<style  lang='scss' scoped></style>