Преглед изворни кода

更新推广页;新增表格组件;

fxs пре 8 месеци
родитељ
комит
35718340a4

+ 6 - 0
components.d.ts

@@ -7,11 +7,15 @@ export {}
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
+    ElButton: typeof import('element-plus/es')['ElButton']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
+    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElIcon: typeof import('element-plus/es')['ElIcon']
     ElImage: typeof import('element-plus/es')['ElImage']
+    ElInput: typeof import('element-plus/es')['ElInput']
+    ElMenItem: typeof import('element-plus/es')['ElMenItem']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
@@ -26,10 +30,12 @@ declare module 'vue' {
     IconIcBaselineVisibilityOff: typeof import('~icons/ic/baseline-visibility-off')['default']
     IconMaterialSymbolsLightLogout: typeof import('~icons/material-symbols-light/logout')['default']
     IconMdiPassword: typeof import('~icons/mdi/password')['default']
+    Menu: typeof import('./src/components/navigation/Menu.vue')['default']
     MyButton: typeof import('./src/components/form/MyButton.vue')['default']
     MyInput: typeof import('./src/components/form/MyInput.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
+    Table: typeof import('./src/components/table/Table.vue')['default']
     TimeLineChart: typeof import('./src/components/echarts/TimeLineChart.vue')['default']
   }
 }

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/img/logo.svg


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/img/promotion/acc-manage-active.svg


Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/img/promotion/acc-manage-default.svg


+ 1 - 0
public/img/promotion/ad-manage-active.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729041716770" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6002" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M854.528 34.816H172.032c-79.36 0-143.36 64.512-143.36 143.872v441.344c0 22.016 17.92 39.424 39.424 39.424h752.64c22.016 0 39.424-17.92 39.424-39.424 0-22.016-17.92-39.424-39.424-39.424H107.52V178.688c0-35.328 28.672-64.512 64.512-64.512h682.496c35.328 0 64.512 28.672 64.512 64.512v345.6c0 22.016 17.92 39.424 39.424 39.424s39.424-17.92 39.424-39.424V178.688c0.512-79.36-64-143.872-143.36-143.872zM958.976 747.008h-890.88c-22.016 0-39.424 17.92-39.424 39.424s17.92 39.424 39.424 39.424h407.04c-1.024 3.072-1.536 6.656-1.536 9.728v138.752c0 22.016 17.92 39.424 39.424 39.424s39.424-17.92 39.424-39.424v-138.752c0-3.584-0.512-6.656-1.536-9.728h407.04c22.016 0 39.424-17.92 39.424-39.424s-16.896-39.424-38.4-39.424z" fill="#197afb" p-id="6003"></path><path d="M406.016 207.36c-18.944 1.024-32.768 11.776-40.96 31.744L286.72 449.536v3.072c-1.024 4.096-1.536 7.68-1.536 10.752 1.024 16.384 10.24 24.064 27.136 24.064 14.336 0 23.552-6.144 28.672-17.92l12.288-36.352h107.52l10.752 36.352c5.12 12.288 14.848 17.92 28.672 17.92 17.408 0 26.112-7.68 27.136-22.528 0-4.096-1.024-9.216-3.072-15.36L448.512 239.104c-9.216-21.504-23.04-31.744-42.496-31.744z m-36.352 172.544l36.352-112.128 36.352 112.128H369.664zM574.464 211.968c-17.92 1.024-27.648 10.752-28.672 28.672v218.112c1.024 17.92 11.264 27.648 30.208 28.672h66.56c84.992-4.096 129.024-49.152 133.12-134.656-3.072-90.624-48.64-137.728-136.192-140.8h-65.024z m140.8 139.264c-2.048 53.248-26.112 81.408-72.704 83.456h-39.424V264.704h34.816c50.176 1.024 76.288 29.696 77.312 86.528z" fill="#197afb" p-id="6004"></path></svg>

+ 1 - 0
public/img/promotion/ad-manage-default.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729041826578" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="859" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M854.528 34.816H172.032c-79.36 0-143.36 64.512-143.36 143.872v441.344c0 22.016 17.92 39.424 39.424 39.424h752.64c22.016 0 39.424-17.92 39.424-39.424 0-22.016-17.92-39.424-39.424-39.424H107.52V178.688c0-35.328 28.672-64.512 64.512-64.512h682.496c35.328 0 64.512 28.672 64.512 64.512v345.6c0 22.016 17.92 39.424 39.424 39.424s39.424-17.92 39.424-39.424V178.688c0.512-79.36-64-143.872-143.36-143.872zM958.976 747.008h-890.88c-22.016 0-39.424 17.92-39.424 39.424s17.92 39.424 39.424 39.424h407.04c-1.024 3.072-1.536 6.656-1.536 9.728v138.752c0 22.016 17.92 39.424 39.424 39.424s39.424-17.92 39.424-39.424v-138.752c0-3.584-0.512-6.656-1.536-9.728h407.04c22.016 0 39.424-17.92 39.424-39.424s-16.896-39.424-38.4-39.424z" fill="#333333" p-id="860"></path><path d="M406.016 207.36c-18.944 1.024-32.768 11.776-40.96 31.744L286.72 449.536v3.072c-1.024 4.096-1.536 7.68-1.536 10.752 1.024 16.384 10.24 24.064 27.136 24.064 14.336 0 23.552-6.144 28.672-17.92l12.288-36.352h107.52l10.752 36.352c5.12 12.288 14.848 17.92 28.672 17.92 17.408 0 26.112-7.68 27.136-22.528 0-4.096-1.024-9.216-3.072-15.36L448.512 239.104c-9.216-21.504-23.04-31.744-42.496-31.744z m-36.352 172.544l36.352-112.128 36.352 112.128H369.664zM574.464 211.968c-17.92 1.024-27.648 10.752-28.672 28.672v218.112c1.024 17.92 11.264 27.648 30.208 28.672h66.56c84.992-4.096 129.024-49.152 133.12-134.656-3.072-90.624-48.64-137.728-136.192-140.8h-65.024z m140.8 139.264c-2.048 53.248-26.112 81.408-72.704 83.456h-39.424V264.704h34.816c50.176 1.024 76.288 29.696 77.312 86.528z" fill="#333333" p-id="861"></path></svg>

Разлика између датотеке није приказан због своје велике величине
+ 0 - 0
public/img/promotion/ad-tencent.svg


+ 1 - 0
public/img/promotion/ad-tt.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1729042167533" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2214" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M751.84 328.88L611.472 83.04l-166.2 41.144 188.536 322.152z" fill="#01C6D2" p-id="2215"></path><path d="M195.024 212.424l-48.48 158.272h369.88l-47.824-158.272z" fill="#76E4D9" p-id="2216"></path><path d="M231.584 397.92L96 636.48 205.248 754.48l184.208-319.096z" fill="#3468D4" p-id="2217"></path><path d="M385.184 576.192L273.16 695.888l136.896 238.896 160.296-37.856z" fill="#3058B0" p-id="2218"></path><path d="M505.288 651.128l45.712 157.816h275.848l48.12-157.816z" fill="#3568D5" p-id="2219"></path><path d="M814.544 263.76l-183.52 321.968L786.96 623.76 928 385.496z" fill="#3C8BFF" p-id="2220"></path></svg>

+ 153 - 0
src/components/navigation/Menu.vue

@@ -0,0 +1,153 @@
+<script setup lang="ts">
+import type { BaseMenu } from '@/types/Promotion/Menu'
+import { useMenu } from '@/hooks/useMenu'
+import { ref, useAttrs } from 'vue'
+
+const { setRouterDefaultActive } = useMenu()
+
+interface MenuProps {
+  menuStyle?: string
+  menuItemStyle?: string
+  defaultActive?: string
+  menuList: BaseMenu[]
+}
+
+const props = withDefaults(defineProps<MenuProps>(), {})
+const attrs = useAttrs()
+
+// 纵向菜单折叠,仅在mode为vertical时生效
+const isCollapse = ref(false)
+
+// 默认激活
+const defaultActive = ref()
+
+/**
+ * @description: 初始化默认菜单
+ * @return {*}
+ */
+const initDefaultActive = () => {
+  if (!(attrs.router || props.defaultActive))
+    throw new Error('defaultActive or router is required')
+  if (attrs.router) setRouterDefaultActive(defaultActive, props.menuList)
+  else defaultActive.value = props.defaultActive
+}
+
+initDefaultActive()
+</script>
+
+<template>
+  <div
+    class="menuContainer"
+    :class="
+      $attrs.mode === 'vertical' ? 'verticalContainer' : 'horizontalContainer'
+    "
+  >
+    <el-menu
+      v-bind="$attrs"
+      :default-active="defaultActive"
+      class="menu"
+      :collapse="isCollapse"
+      :style="menuStyle"
+      :ellipsis="false"
+    >
+      <template v-for="menuItem in menuList" :key="menuItem.name">
+        <!-- 有子菜单 -->
+        <el-sub-menu v-if="menuItem.children" :index="menuItem.name">
+          <template #title>
+            <el-image
+              v-if="menuItem.iconDefault"
+              style="
+                width: 20px;
+                height: 20px;
+                vertical-align: middle;
+                padding-right: 5px;
+              "
+              :src="menuItem.iconDefault"
+              :fit="'fill'"
+            />
+            <span>{{ menuItem.title }}</span>
+          </template>
+          <el-menu-item
+            v-for="item in menuItem.children"
+            :index="$attrs.router ? item.path : item.name"
+          >
+            <el-image
+              v-if="item.iconDefault"
+              style="
+                width: 20px;
+                height: 20px;
+                vertical-align: middle;
+                padding-right: 5px;
+              "
+              :src="item.iconDefault"
+              :fit="'fill'"
+            />
+            <span>{{ item.title }}</span>
+          </el-menu-item>
+        </el-sub-menu>
+        <!-- 无子菜单 -->
+        <el-menu-item
+          :style="menuItemStyle"
+          :index="$attrs.router ? menuItem.path : menuItem.name"
+          v-else
+        >
+          <el-image
+            v-if="menuItem.iconDefault"
+            style="
+              width: 20px;
+              height: 20px;
+              vertical-align: middle;
+              padding-right: 5px;
+            "
+            :src="menuItem.iconDefault"
+            :fit="'fill'"
+          />
+          <span>{{ menuItem.title }}</span>
+        </el-menu-item>
+      </template>
+    </el-menu>
+    <div
+      v-if="$attrs.mode === 'vertical'"
+      class="sideBarFold"
+      @click="isCollapse = !isCollapse"
+    >
+      <el-icon :size="20"><Fold /></el-icon>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.menuContainer {
+  position: relative;
+}
+
+/* 垂直样式 */
+.verticalContainer {
+  .menu {
+    height: 93vh;
+    min-width: 180px;
+  }
+
+  .sideBarFold {
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+    cursor: pointer;
+  }
+}
+
+/* 水平样式 */
+.horizontalContainer {
+  .menu {
+    /* min-width: 0; */
+    padding: 0 20px;
+  }
+
+  .sideBarFold {
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+    cursor: pointer;
+  }
+}
+</style>

+ 199 - 0
src/components/table/Table.vue

@@ -0,0 +1,199 @@
+<script setup lang="ts">
+import type { TableProps } from '@/types/Tables/table'
+import { TableFilterType } from '@/types/Tables/table'
+
+import { computed, reactive, ref } from 'vue'
+import { Search } from '@element-plus/icons-vue'
+
+const props = withDefaults(defineProps<TableProps>(), {})
+
+// 过滤表单的数据
+const filterFormData = reactive<{
+  [key: string]: {
+    state: boolean
+    value: any
+  }
+}>({})
+
+// 过滤表单字段的状态信息
+const filterFieldsStateList = reactive<
+  Array<{
+    label: string
+    state: boolean
+    value: any
+  }>
+>([])
+
+// 当前已经选中的展示的过滤字段
+const filterFields = ref<Array<any>>([])
+
+/**
+ * @description: 初始化查询表单
+ * @return {*}
+ */
+const initfilterForm = () => {
+  for (let [k, v] of Object.entries(props.filtersInfo)) {
+    filterFormData[k] = {
+      state: true,
+      value: v.value,
+    }
+    // 初始化过滤表单字段的状态信息
+    filterFieldsStateList.push({
+      label: v.label,
+      state: true,
+      value: v.name,
+    })
+  }
+}
+
+/**
+ * @description: 初始化过滤字段,把所有字段展示出来
+ * @return {*}
+ */
+const initFilterFields = () => {
+  filterFieldsStateList.forEach(v => {
+    filterFields.value.push(v.value)
+  })
+}
+
+/**
+ * @description: 当查询字段改变的时候,查询表单的数据也需要同步的增删
+ * @param {*} val
+ * @return {*}
+ */
+const changeFilterFields = (val: Array<any>) => {
+  for (let item in filterFormData) {
+    if (!val.includes(item)) filterFormData[item].state = false
+  }
+  console.log(filterFormData)
+}
+
+initfilterForm()
+initFilterFields()
+</script>
+
+<template>
+  <div class="tableContainer">
+    <div class="filterContainer">
+      <div class="filterContent">
+        <div class="filterFields">
+          <el-input
+            :readonly="true"
+            placeholder="筛选字段"
+            prefix-icon="Filter"
+            style="width: 112px"
+          >
+            <template #append>
+              <el-select
+                v-model="filterFields"
+                multiple
+                collapse-tags
+                collapse-tags-tooltip
+                placeholder="Select"
+                style="width: 180px"
+                @change="changeFilterFields"
+              >
+                <el-option
+                  v-for="item in filterFieldsStateList"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                />
+              </el-select>
+            </template>
+          </el-input>
+        </div>
+
+        <template v-for="item in filtersInfo" :key="item.name">
+          <el-input
+            class="filterItem"
+            v-if="
+              filterFields.includes(item.name) &&
+              item.type === TableFilterType.Search
+            "
+            v-model="filterFormData[item.name].value"
+            style="width: 240px"
+            placeholder="Please Input"
+            :suffix-icon="Search"
+          />
+
+          <el-select
+            class="filterItem"
+            v-if="
+              filterFields.includes(item.name) &&
+              item.type === TableFilterType.Select
+            "
+            v-model="filterFormData[item.name].value"
+            placeholder="Select"
+            style="width: 240px"
+          >
+            <template #label="{ label, value }">
+              <span>{{ item.label }}: </span>
+              <span style="margin-left: 10px">{{ value }}</span>
+            </template>
+            <el-option
+              v-for="option in item.options"
+              :key="option.value"
+              :label="option.label"
+              :value="option.value"
+            />
+          </el-select>
+        </template>
+      </div>
+      <div class="filterButtonContainer">
+        <el-button class="queryBtn" color="#197afb">查询</el-button>
+        <el-button class="queryBtn" color="#626aef" plain>重置</el-button>
+      </div>
+    </div>
+    <div class="operationContainer"></div>
+    <div class="tableContent"></div>
+  </div>
+</template>
+
+<style scoped>
+.tableContainer {
+  width: 100%;
+}
+
+.filterContainer {
+  width: 100%;
+  padding: 10px 20px;
+  display: flex;
+  align-items: center;
+  margin: 0 auto;
+}
+
+.filterContent {
+  width: 90%;
+  display: flex;
+  /* align-items: center; */
+  /* flex-direction: column; */
+  /* display: flex; */
+  /* justify-content: space-between; */
+  /* flex-wrap: wrap; */
+}
+
+.filterButtonContainer {
+  width: 10%;
+  height: 100%;
+  display: flex;
+  /* flex-direction: column; */
+  align-items: center;
+  justify-content: center;
+}
+
+.queryBtn {
+  /* width: 50%; */
+  /* padding-bottom: 8px; */
+  margin-right: 8px;
+}
+
+.filterItem {
+  margin-bottom: 10px;
+  margin-right: 8px;
+}
+
+.filterFields {
+  margin-right: 150px;
+}
+</style>

+ 32 - 0
src/hooks/useDate.ts

@@ -0,0 +1,32 @@
+import { createDateRange } from '@/utils/common'
+export function useDate() {
+  // 快速选择日期
+  const shortcuts = [
+    {
+      text: '上一周',
+      value: () => createDateRange(7),
+    },
+    {
+      text: '上个月',
+      value: () => createDateRange(30),
+    },
+    {
+      text: '近三个月',
+      value: () => createDateRange(90),
+    },
+  ]
+
+  /**
+   * @description: 禁止选取今天之后的日期
+   * @param {*} date
+   * @return {*}
+   */
+  const disableDate = (time: Date) => {
+    return time.getTime() > Date.now()
+  }
+
+  return {
+    shortcuts,
+    disableDate,
+  }
+}

+ 45 - 0
src/hooks/useMenu.ts

@@ -0,0 +1,45 @@
+import type { Ref } from 'vue'
+import type { BaseMenu } from '@/types/Promotion/Menu'
+import router from '@/router'
+
+export function useMenu() {
+  /**
+   * @description: 设置默认激活的菜单,根据当前路由匹配到对应的菜单
+   * @return {*}
+   */
+  const setRouterDefaultActive = (
+    defaultActive: Ref<string>,
+    navBarMenuList: BaseMenu[],
+  ) => {
+    let hasFind = false // 是否找到匹配的菜单
+    let matchedRoutedList = router.currentRoute.value.matched // 当前匹配到的路由的数组
+    matchedRoutedList.forEach(item => {
+      let matchedRouteName = item.name // 把每一个匹配到的路由去再次匹配
+      let matchBar = navBarMenuList.find(item => {
+        let result = item.name === matchedRouteName
+        if (result) return result // 如果匹配到了,则直接返回
+
+        let children = item.children // 没匹配到就去找子菜单
+        if (children) {
+          return children.some(child => {
+            return child.name === matchedRouteName
+          })
+        }
+      })
+      if (matchBar) {
+        // 有就赋值
+        hasFind = true
+        defaultActive.value = matchBar.path || ''
+      }
+    })
+    // 没有就赋值为空且报错
+    if (!hasFind) {
+      defaultActive.value = ''
+      throw new Error('当前路由没有匹配到菜单')
+    }
+  }
+
+  return {
+    setRouterDefaultActive,
+  }
+}

+ 23 - 0
src/router/home.ts

@@ -10,9 +10,32 @@ export default [
   {
     path: '/promotion',
     name: 'Promotion',
+    redirect: '/promotion/ttAd',
     component: () => import('@/views/Promotion/promotion.vue'),
     meta: {
       needKeepAlive: true,
     },
+    children: [
+      {
+        path: 'ttAd',
+        name: 'TtAd',
+        component: () => import('@/views/Promotion/adManage/ttad.vue'),
+      },
+      {
+        path: 'tencentAd',
+        name: 'TencentAd',
+        component: () => import('@/views/Promotion/adManage/tencentAd.vue'),
+      },
+      {
+        path: 'accTtAd',
+        name: 'AccTtAd',
+        component: () => import('@/views/Promotion/accManage/accTtAd.vue'),
+      },
+      {
+        path: 'accTencentAd',
+        name: 'AccTencentAd',
+        component: () => import('@/views/Promotion/accManage/accTencentAd.vue'),
+      },
+    ],
   },
 ]

+ 29 - 0
src/types/Promotion/Menu.ts

@@ -0,0 +1,29 @@
+// interface BaseMenu {
+//   name: string
+//   title: string
+//   children?: AnyMenu[]
+// }
+
+// interface WithIconMeun extends BaseMenu {
+//   iconDefault: string
+//   iconActive?: string
+// }
+
+// interface WithRouterMenu extends BaseMenu {
+//   path: string
+// }
+
+// type FullMenu = WithIconMeun & WithRouterMenu
+
+// type AnyMenu = WithRouterMenu | WithIconMeun | BaseMenu | FullMenu
+
+// export type { BaseMenu, WithIconMeun, WithRouterMenu, FullMenu }
+
+interface BaseMenu {
+  name: string
+  title: string
+  children?: BaseMenu[]
+  iconDefault?: string
+  iconActive?: string
+  path?: string
+}

+ 57 - 0
src/types/Tables/table.ts

@@ -0,0 +1,57 @@
+enum TableFilterType {
+  Search,
+  Select,
+  Date,
+}
+
+// 选择框的选项
+interface SelectFilterOptions {
+  label: string
+  value: any
+}
+
+// 筛选条件的基础信息
+interface BaseFilterItem {
+  type: TableFilterType
+  name: string
+  value: any
+  placeholder: string
+}
+
+// 搜索框
+interface SearchFilterItem extends BaseFilterItem {
+  type: TableFilterType.Search
+}
+
+// 选择框
+interface SelectFilterItem extends BaseFilterItem {
+  type: TableFilterType.Select
+  options: SelectFilterOptions[]
+}
+
+// 日期选择框
+interface DateFilterItem extends BaseFilterItem {
+  type: TableFilterType.Date
+  startDate: Date
+  endDate: Date
+}
+
+// 联合类型
+type TableFilterItem = SearchFilterItem | SelectFilterItem | DateFilterItem
+
+// 表格的props
+interface TableProps {
+  filtersInfo: {
+    [key: string]: TableFilterItem
+  }
+}
+export { TableFilterType }
+export type {
+  SelectFilterOptions,
+  BaseFilterItem,
+  SearchFilterItem,
+  SelectFilterItem,
+  DateFilterItem,
+  TableFilterItem,
+  TableProps,
+}

+ 3 - 3
src/utils/axios/auth.ts

@@ -2,8 +2,8 @@
  * @Author: fxs bjnsfxs@163.com
  * @Date: 2024-08-21 11:12:21
  * @LastEditors: fxs bjnsfxs@163.com
- * @LastEditTime: 2024-10-15 15:38:38
- * @FilePath: \Game-Backstage-Management-Systemc:\Users\NINGMEI\Desktop\Quantity-Creation-Management-System\src\utils\axios\auth.ts
+ * @LastEditTime: 2024-10-16 14:27:56
+ * @FilePath: \Quantity-Creation-Management-System\src\utils\axios\auth.ts
  * @Description:
  *
  */
@@ -14,7 +14,7 @@ import { getLoginState } from '../localStorage/localStorage'
 
 export const authLogin = (): boolean => {
   const state = getLoginState()
-  console.log(state)
+
   if (!state) {
     ElMessage({
       type: MessageType.Warning,

+ 36 - 14
src/views/Index.vue

@@ -1,9 +1,11 @@
 <script setup lang="ts">
+import type { BaseMenu } from '@/types/Promotion/Menu'
 import { initLoadResouce } from '@/utils/resource'
-import { reactive, onMounted } from 'vue'
+import { reactive, onMounted, ref } from 'vue'
 import router from '@/router'
 import { setLoginState } from '@/utils/localStorage/localStorage'
 import { removeAllToeken } from '@/utils/token/token'
+import Menu from '@/components/navigation/Menu.vue'
 
 interface NavBarMenu {
   name: string
@@ -12,7 +14,7 @@ interface NavBarMenu {
 
 // 资源的加载路径
 const resourceInfo: Record<string, string> = {
-  logo: `/img/logo.svg`,
+  logo: `/logo.svg`,
   defaultHead: `/img/default/defaultHead.png`,
 }
 
@@ -20,46 +22,60 @@ const resourceInfo: Record<string, string> = {
 const blobUrlInfo = reactive<Record<string, string>>({})
 
 // 顶部导航栏信息
-const navBarMenuList: Array<NavBarMenu> = [
+const navBarMenuList: Array<BaseMenu> = [
   {
     name: 'Home',
     title: '首页',
+    path: '/home',
   },
   {
     name: 'Promotion',
     title: '推广',
+    path: '/promotion',
   },
   {
     name: 'Report',
     title: '报表',
+    path: '/report',
   },
   {
     name: 'Material',
     title: '素材',
+    path: '/material',
   },
   {
     name: 'Asset',
     title: '资产',
+    path: '/asset',
   },
   {
     name: 'Task',
     title: '任务',
+    path: '/task',
   },
   {
     name: 'Management',
     title: '管理',
+    path: '/management',
   },
 ]
 
+// 菜单默认选中
+const defaultActive = ref<string>(router.currentRoute.value.path)
+
 /**
- * @description: 头部导航栏改变
- * @param {*} val 对应的name
+ * @description: 设置默认激活的菜单,根据当前路由匹配到对应的菜单
  * @return {*}
  */
-const changeNavBar = (val: string) => {
-  // navBarSelect.value = val
-  router.push(`/${val}`)
-}
+// const setDefaultActive = () => {
+//   let curRouteMatched = router.currentRoute.value.matched
+//   let matchBar = navBarMenuList.find(item => {
+//     return curRouteMatched.find(matchRoute => matchRoute.name === item.name)
+//   })
+//   defaultActive.value = matchBar?.path || ''
+// }
+
+// setDefaultActive()
 
 // 登出
 const logOut = () => {
@@ -95,19 +111,25 @@ onMounted(() => {
 
       <!-- 顶部导航栏 -->
       <div class="navBarMenu">
-        <el-menu
-          :default-active="navBarMenuList[0].name"
+        <Menu
+          mode="horizontal"
+          :router="true"
+          :menu-item-style="'padding: 0px 20px;'"
+          :menu-list="navBarMenuList"
+        ></Menu>
+        <!-- <el-menu
+          :default-active="defaultActive"
           class="el-menu-demo"
           mode="horizontal"
-          @select="changeNavBar"
+          router
         >
           <el-menu-item
             v-for="item in navBarMenuList"
             class="navBarMenuItem"
-            :index="item.name"
+            :index="item.path"
             >{{ item.title }}</el-menu-item
           >
-        </el-menu>
+        </el-menu> -->
       </div>
 
       <div class="headPortraitBox">

+ 7 - 0
src/views/Promotion/accManage/accTencentAd.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div></div>
+</template>
+
+<style scoped></style>

+ 7 - 0
src/views/Promotion/accManage/accTtAd.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div></div>
+</template>
+
+<style scoped></style>

+ 7 - 0
src/views/Promotion/adManage/tencentAd.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div></div>
+</template>
+
+<style scoped></style>

+ 156 - 0
src/views/Promotion/adManage/ttad.vue

@@ -0,0 +1,156 @@
+<script setup lang="ts">
+import { TableFilterType } from '@/types/Tables/table'
+
+import type {
+  SearchFilterItem,
+  SelectFilterItem,
+  DateFilterItem,
+  TableProps,
+  TableFilterItem,
+} from '@/types/Tables/table'
+import type { BaseMenu } from '@/types/Promotion/Menu'
+
+import { useDate } from '@/hooks/useDate'
+import { ref } from 'vue'
+
+import Menu from '@/components/navigation/Menu.vue'
+import Table from '@/components/table/Table.vue'
+
+const { disableDate, shortcuts } = useDate()
+
+// 导航
+const ttAdMenu: BaseMenu[] = [
+  {
+    name: 'account',
+    title: '账户',
+  },
+  {
+    name: 'project',
+    title: '项目',
+  },
+  {
+    name: 'advertise',
+    title: '广告',
+  },
+]
+
+const filterInfo: {
+  [key: string]: TableFilterItem
+} = {
+  accMain: {
+    label: '账号主体',
+    name: 'accMain',
+    type: TableFilterType.Select,
+    value: 'all',
+    options: [
+      { label: '全部', value: 'all' },
+      { label: '主体1', value: '1' },
+      { label: '主体2', value: '2' },
+    ],
+  },
+  ttProject: {
+    label: '创量项目',
+    name: 'ttProject',
+    type: TableFilterType.Select,
+    value: '',
+    options: [
+      { label: '项目A', value: 'A' },
+      { label: '项目B', value: 'B' },
+    ],
+  },
+  optimizer: {
+    label: '优化师',
+    name: 'optimizer',
+    type: TableFilterType.Select,
+    value: '',
+    options: [
+      { label: '优化师1', value: 'jack' },
+      { label: '优化师2', value: 'rose' },
+    ],
+  },
+  name: {
+    label: '名称',
+    name: 'name',
+    type: TableFilterType.Search,
+    value: '',
+  },
+  product: {
+    label: '产品',
+    name: 'product',
+    type: TableFilterType.Select,
+    value: '',
+    options: [
+      { label: '产品1', value: '1' },
+      { label: '产品2', value: '2' },
+    ],
+  },
+}
+
+// 选择的日期
+const selectDate = ref(shortcuts[0].value())
+
+/**
+ * @description: 日期改变
+ * @param {*} val
+ * @return {*}
+ */
+const dateChange = (val: Array<Date>) => {
+  //   emits('changeDate', val)
+}
+</script>
+
+<template>
+  <div class="ttAdContainer">
+    <div class="ttAdHeader">
+      <Menu
+        :default-active="'account'"
+        :menu-list="ttAdMenu"
+        :menu-item-style="'margin-right:30px;'"
+        mode="horizontal"
+      ></Menu>
+      <div class="datePickerBox">
+        <el-date-picker
+          class="datePicker"
+          v-model="selectDate"
+          :disabled-date="disableDate"
+          :unlink-panels="false"
+          @change="dateChange"
+          type="daterange"
+          range-separator="至"
+          start-placeholder="Start date"
+          end-placeholder="End date"
+          :shortcuts="shortcuts"
+          :size="'small'"
+        >
+        </el-date-picker>
+      </div>
+    </div>
+    <div class="ttAdContent">
+      <Table :filters-info="filterInfo"></Table>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+.ttAdContainer {
+  width: 100%;
+}
+
+.ttAdHeader {
+  width: 100%;
+  border-bottom: 1px solid #e0e0e0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.ttAdContent {
+  width: 96%;
+}
+
+.datePickerBox {
+  /* width: 100px !important; */
+  margin-left: auto;
+  margin-right: 5%;
+}
+</style>

+ 116 - 32
src/views/Promotion/promotion.vue

@@ -1,45 +1,107 @@
 <script setup lang="ts">
+import type { BaseMenu } from '@/types/Promotion/Menu'
+
+import router from '@/router'
+import Menu from '@/components/navigation/Menu.vue'
+
 import { ref } from 'vue'
 
+// <el-menu
+//         :default-active="defaultActive"
+//         class="promotionMenu"
+//         :collapse="isCollapse"
+//         style="min-width: 180px"
+//         router
+//       >
+//         <template v-for="menuItem in menu" :key="menuItem.name">
+//           <!-- 有子菜单 -->
+//           <el-sub-menu v-if="menuItem.children" :index="menuItem.name">
+//             <template #title>
+//               <el-icon><location /></el-icon>
+//               <span>{{ menuItem.title }}</span>
+//             </template>
+//             <el-menu-item v-for="item in menuItem.children" :index="item.path">
+//               <el-icon><document /></el-icon>
+//               <span>{{ item.title }}</span>
+//             </el-menu-item>
+//           </el-sub-menu>
+//           <!-- 无子菜单 -->
+//           <el-menu-item :index="menuItem.path" v-else>
+//             <el-icon><document /></el-icon>
+//             <span>{{ menuItem.title }}</span>
+//           </el-menu-item>
+//         </template>
+//       </el-menu>
+//       <div class="sideBarFold" @click="isCollapse = !isCollapse">
+//         <el-icon :size="20"><Fold /></el-icon>
+//       </div>
+
+// 侧边菜单的展示控制
 const isCollapse = ref(false)
+
+// 菜单默认选中
+const defaultActive = ref<string>(router.currentRoute.value.path)
+
+const menu: Array<BaseMenu> = [
+  {
+    name: 'adManage',
+    title: '广告管理',
+    iconDefault: '/img/promotion/ad-manage-default.svg',
+    iconActive: '/img/promotion/ad-manage-active.svg',
+    path: '/promotion/ttAd',
+    children: [
+      {
+        name: 'TtAd',
+        title: '巨量广告',
+        iconDefault: '/img/promotion/ad-tt.svg',
+        path: '/promotion/ttAd',
+      },
+      {
+        name: 'TencentAd',
+        title: '腾讯广告',
+        iconDefault: '/img/promotion/ad-tencent.svg',
+        path: '/promotion/tencentAd',
+      },
+    ],
+  },
+  {
+    name: 'accManage',
+    title: '账号管理',
+    iconDefault: '/img/promotion/acc-manage-default.svg',
+    iconActive: '/img/promotion/acc-manage-active.svg',
+    path: '/promotion/accTtAd',
+    children: [
+      {
+        name: 'AccTtAd',
+        title: '巨量广告',
+        iconDefault: '/img/promotion/ad-tt.svg',
+        path: '/promotion/accTtAd',
+      },
+      {
+        name: 'AccTencentAd',
+        title: '腾讯广告',
+        iconDefault: '/img/promotion/ad-tencent.svg',
+        path: '/promotion/accTencentAd',
+      },
+    ],
+  },
+]
 </script>
 
 <template>
   <div class="promotionContainer">
     <div class="promotionSiderBar">
-      <el-menu default-active="2" class="promotionMenu" :collapse="isCollapse">
-        <el-sub-menu index="1">
-          <template #title>
-            <el-icon><location /></el-icon>
-            <span>Navigator One</span>
-          </template>
-          <el-menu-item-group title="Group One">
-            <el-menu-item index="1-1">item one</el-menu-item>
-            <el-menu-item index="1-2">item two</el-menu-item>
-          </el-menu-item-group>
-          <el-menu-item-group title="Group Two">
-            <el-menu-item index="1-3">item three</el-menu-item>
-          </el-menu-item-group>
-          <el-sub-menu index="1-4">
-            <template #title>item four</template>
-            <el-menu-item index="1-4-1">item one</el-menu-item>
-          </el-sub-menu>
-        </el-sub-menu>
-        <el-menu-item index="2">
-          <el-icon><icon-menu /></el-icon>
-          <span>Navigator Two</span>
-        </el-menu-item>
-        <el-menu-item index="3" disabled>
-          <el-icon><document /></el-icon>
-          <span>Navigator Three</span>
-        </el-menu-item>
-        <el-menu-item index="4">
-          <el-icon><setting /></el-icon>
-          <span>Navigator Four</span>
-        </el-menu-item>
-      </el-menu>
+      <Menu
+        mode="vertical"
+        :menu-list="menu"
+        :default-active="defaultActive"
+        :router="true"
+        :menu-style="'min-width: 180px'"
+      ></Menu>
+    </div>
+    <div class="promotionContent">
+      <router-view></router-view>
     </div>
-    <div class="promotionContent"></div>
   </div>
 </template>
 
@@ -48,4 +110,26 @@ const isCollapse = ref(false)
   width: 100%;
   display: flex;
 }
+
+.promotionSiderBar {
+  width: 180px;
+  position: relative;
+}
+
+.promotionMenu {
+  height: 93vh;
+}
+
+.sideBarFold {
+  position: absolute;
+  right: 20px;
+  bottom: 20px;
+  cursor: pointer;
+}
+
+.promotionContent {
+  width: calc(100% - 180px);
+
+  /* padding: 20px; */
+}
 </style>

Неке датотеке нису приказане због велике количине промена