Browse Source

完成广告组详情的推广商品和搜索词

guojing_wu 1 year ago
parent
commit
2f0cf7cf10

+ 1 - 1
.prettierrc.js

@@ -4,7 +4,7 @@ module.exports = {
 	// 指定每个缩进级别的空格数
 	tabWidth: 2,
 	// 使用制表符而不是空格缩进行
-	useTabs: true,
+	useTabs: false,
 	// 在语句末尾打印分号
 	semi: false,
 	// 使用单引号而不是双引号

+ 1 - 7
src/components/MetricsCards/mCard.vue

@@ -9,7 +9,7 @@
         <Top :class="colorClass" v-if="isBoost"/>
         <Bottom :class="colorClass" v-else/>
       </el-icon>
-      <span :class="colorClass">{{ formatGapVal(selectedData?.gapVal) }}</span>
+      <span :class="colorClass">{{ selectedData?.gapVal ? selectedData?.gapVal + '%' : '' }}</span>
     </div>
   </el-card>
 </template>
@@ -49,12 +49,6 @@ const isBoost = computed(():boolean => {
 })
 const colorClass = computed((): "green"|"red" => isBoost.value ? "green": "red")
 
-const formatGapVal = (gapVal: number | undefined) => {
-  if (gapVal) {
-    return (gapVal * 100).toFixed(2) + '%'
-  }
-}
-
 </script>
 
 <style scoped>

+ 1 - 1
src/components/dataCompare/index.vue

@@ -11,7 +11,7 @@
             <Bottom v-if="props.gapVal < 0"/>
           </el-icon>
         </template>
-        <span>{{ props.gapVal ? (props.gapVal * 100).toFixed(2) + '%' : ''}}</span>
+        <span>{{ props.gapVal ? props.gapVal.toFixed(2) + '%' : ''}}</span>
       </p>
     </template>
     <p>对比周期:{{ compareDate[0] }} ~ {{ compareDate[1] }}</p>

+ 193 - 0
src/components/ruleCalendar/index.vue

@@ -0,0 +1,193 @@
+<template>
+	<div class="calendar">
+		<table class="calendar-table calendar-table-hour">
+			<thead class="calendar-head">
+				<tr>
+					<th rowspan="8" class="week-td">星期 / 时间</th>
+					<th colspan="12">00:00 - 12:00</th>
+					<th colspan="12">12:00 - 24:00</th>
+					<th colspan="4" rowspan="2" class="week-td" style="display: none">小时</th>
+				</tr>
+				<tr>
+					<th colspan="1" v-for="(_, i) in 24" :key="i">{{ i }}</th>
+				</tr>
+			</thead>
+			<tbody class="calendar-body">
+				<tr>
+					<th class="td-normal">星期一</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期二</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期三</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期四</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期五</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期六</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th class="td-normal">星期日</th>
+					<td data-week="0" :data-time="i" class="un-selected" v-for="(_, i) in 24" :key="i">
+						<span class=""></span>
+					</td>
+					<!-- <td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">ON</span>
+					</td>
+					<td colspan="2" class="td-normal" style="display: none">
+						<span class="hover-link td-normal">OFF</span>
+					</td> -->
+				</tr>
+				<tr>
+					<th colspan="28" class="clear-bar td-normal">
+						<span class="pull-left td-normal">可拖动鼠标选择时间段</span>
+						<span class="hover-link fr td-normal">全部重置</span>
+					</th>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue'
+</script>
+
+<style lang="scss" scoped>
+.calendar {
+	background-color: #fff;
+	-webkit-user-select: none;
+	position: relative;
+	display: inline-block;
+
+	.calendar-table {
+		border-collapse: collapse;
+	}
+
+	.week-td {
+		width: 90px;
+	}
+}
+
+table {
+	display: table;
+	border-collapse: separate;
+	box-sizing: border-box;
+	text-indent: initial;
+	border-spacing: 2px;
+	border-color: gray;
+
+	thead {
+		display: table-header-group;
+		vertical-align: middle;
+		border-color: inherit;
+	}
+
+	tr {
+		border: 1px solid #e0e5f4;
+		font-size: 12px;
+		text-align: center;
+		line-height: 32px;
+		color: rgba(0, 0, 0, 0.5);
+
+		th {
+			min-width: 40px;
+			border: 1px solid #e0e5f4;
+			font-size: 12px;
+			text-align: center;
+			line-height: 32px;
+			background: #f7f8fa;
+		}
+
+		td {
+			border: 1px solid #e0e5f4;
+			font-size: 12px;
+			text-align: center;
+			line-height: 32px;
+			min-width: 40px;
+			cursor: pointer;
+
+			&:hover {
+				background: #ccdbff;
+			}
+
+			.active {
+				background: #ccdbff;
+			}
+		}
+	}
+}
+
+.clear-bar {
+	line-height: 32px;
+	padding: 0 12px;
+
+	.hover-link {
+		color: #1c6bde;
+		cursor: pointer;
+		font-size: 13px;
+	}
+	.fr {
+		float: right;
+	}
+}
+</style>

+ 7 - 0
src/settings.ts

@@ -52,6 +52,13 @@ export default {
 							}
 						},
 					},
+					toolbar: {
+						buttons: {
+							compact: {
+								show: false
+							}
+						}
+					},
 					/* search: {
 						layout: 'multi-line',
 						collapse: true,

+ 4 - 3
src/theme/app.scss

@@ -332,8 +332,8 @@ body,
 	position: initial !important;
 
 	.fs-search-col >* {
-		margin-left: 0 !important;
-	}
+	margin-left: 0 !important;
+}
 }
 
 .asj-tabs {
@@ -373,7 +373,7 @@ body,
   z-index: 10;
   top: 0;
   height: 80px;
-  margin-bottom: 3px;
+  margin-bottom: 2px;
   display: flex;
   flex-direction: column;
 }
@@ -387,6 +387,7 @@ body,
   position: sticky;
   top: 80px;
   z-index: 10;
+	border-top: 1px solid #aeafb0;
 }
 
 // 顶层公共搜索样式

+ 7 - 3
src/types/views.d.ts

@@ -349,8 +349,8 @@ declare interface ShowMetric {
 
 declare interface MetricData extends MetricOptions {
   metricVal: string,
-  preVal?: number,
-  gapVal?: number
+  preVal?: number | null,
+  gapVal?: number | null
 }
 
 declare interface Portfolio {
@@ -375,7 +375,11 @@ declare interface SpCampaign {
 
 declare type SpAdGroup = {
 	id?: number,
+	adGroupId?: string,
 	adGroupName?: string,
 	state?: string,
-	defaultBid?: string
+	defaultBid?: string,
+	startDate?: string,
+	endDate?: string,
+	targetingType?: string
 }

+ 22 - 5
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/ads/api.ts

@@ -1,11 +1,10 @@
 import { request } from '/@/utils/service';
-import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
-import XEUtils from 'xe-utils';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
 
-export const apiPrefix = '/api/ad_manage/spads/';
-export function GetList(query: PageQuery) {
+export const apiPrefix = '/api/ad_manage/spgroupdetail/';
+export function GetList(query: UserPageQuery) {
     return request({
-        url: apiPrefix,
+        url: apiPrefix + 'ads/list/',
         method: 'get',
         params: query,
     })
@@ -40,3 +39,21 @@ export function DelObj(id: DelReq) {
         data: { id },
     });
 }
+
+
+export function getCardData(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + "total/",
+        method: 'GET',
+        params: query
+    })
+  }
+  
+  export function getLineData(query: UserPageQuery) {
+    query["dateRangeType"] = "D"
+    return request({
+      url: apiPrefix + "daily/",
+      method: 'GET',
+      params: query
+    })
+  }

+ 29 - 30
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/ads/crud.tsx

@@ -1,11 +1,14 @@
 import * as api from './api'
 import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
-import { inject, nextTick, ref } from 'vue'
+import { inject } from 'vue'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
 import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
 
 export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
 	const pageRequest = async (query: UserPageQuery) => {
-		query["adGroup"] = context["adGroupId"]
+		const params = parseQueryParams(context.value)
+		XEUtils.assign(query, params)
 		return await api.GetList(query);
 	};
 	const editRequest = async ({ form, row }: EditReq) => {
@@ -29,23 +32,23 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
         fixedHeight: false
       },
 			actionbar: {
-				show: true,
+				show: false,
 				buttons: {
 					add: {
 						show: false
-					},
-					create: {
-						text: '添加推广商品',
-						type: "primary",
-						show: true,
-						click() {
-
-						}
 					}
 				}
 			},
 			search: {
-				show: false
+				show: true,
+				buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
 			},
 			toolbar: {
         buttons: {
@@ -86,28 +89,24 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 			},
 			columns: {
         asin: {
-          title: 'ASIN',
-          column: {
-            width: '200px'
-          },
-					search: {
-						show: true,
-						component: {
-							props: {
-								clearable: true
-							}
-						}
-					},
-					form: {
-						rules: [{required: true, message:'必填项'}]
+					title: 'asin',
+					column: {
+						width: "120px"
 					}
-        },
+				},
 				sku: {
-					title: 'SKU'
+					title:'sku',
+					column: {
+						width: "180px"
+					}
 				},
 				state: {
-					title: '状态'
-				}
+					title: '状态',
+					column: {
+						width: "100px"
+					}
+				},
+				...BaseColumn
 			}
 		}
 	}

+ 57 - 53
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/ads/index.vue

@@ -1,69 +1,73 @@
 <template>
-    <fs-page class="fs-page-custom">
-        <fs-crud ref="crudRef" v-bind="crudBinding">
-            <template #header-middle>
-                <el-tabs v-model="tabActiveName" class="chart-tabs" type="border-card" @tab-change="changeTab">
-                    <el-tab-pane label="数据趋势" name="dataTendency">
-                        <!-- <DataTendencyChart ref="dataTendencyRef"/> -->
-                    </el-tab-pane>
-                    <el-tab-pane label="广告结构" name="adStruct" :lazy="true">
-                        <!-- <AdStructChart ref="adStructChartRef"/> -->
-                    </el-tab-pane>
-                    <el-tab-pane label="散点视图" name="scatterView" :lazy="true"></el-tab-pane>
-                </el-tabs>
-            </template>
-        </fs-crud>
-    </fs-page>
+	<fs-page class="fs-page-custom">
+		<fs-crud ref="crudRef" v-bind="crudBinding">
+			<template #search-left>
+				<DateRangePicker v-model="dateRange"></DateRangePicker>
+			</template>
+			<template #header-middle>
+				<DataTendencyChart :query="queryParams" :fetch-card="getCardData" :fetch-line="getLineData"> </DataTendencyChart>
+			</template>
+			<template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <DataCompare
+          :field="field" 
+          :value="scope.row[field]"
+          :prev-val="scope.row[`prev${field}`]"
+          :gap-val="scope.row[`gap${field}`]" 
+          :date-range="dateRange"
+          :show-compare="showCompare"/>
+      </template>
+      <template #toolbar-left>
+        <div>
+          <span>数据对比 </span>
+          <el-switch v-model="showCompare" size="small" />
+        </div>
+      </template>
+		</fs-crud>
+	</fs-page>
 </template>
 
 <script lang="ts" setup>
-import {ref, onMounted, nextTick, inject} from 'vue'
-import {useFs, FsPage} from '@fast-crud/fast-crud'
-import {createCrudOptions} from './crud'
-import {useRoute, useRouter} from 'vue-router'
-// import DataTendencyChart from './chartComponents/dataTendency.vue'
-import {useShopInfo} from '/@/stores/shopInfo'
-// import AdStructChart from './chartComponents/adStruct.vue'
+import { ref, onMounted, watch } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+import { LocationQueryValue } from 'vue-router'
+import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
+// import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { getCardData, getLineData } from './api'
+import { storeToRefs } from 'pinia'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import DataCompare from '/@/components/dataCompare/index.vue'
 
-const tabActiveName = ref('dataTendency')
-const shopInfo = useShopInfo()
-const dateRange = inject('dateRange')
-// const start = dateRange.value[0]
-// const end = dateRange.value[1]
-const {crudBinding, crudRef, crudExpose} = useFs({createCrudOptions, context: {}})
-
-const route = useRoute()
-const router = useRouter()
-const adStructChartRef = ref()
-const dataTendencyRef = ref()
 
+interface Props {
+	adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+const showCompare = ref(false)
+const queryParams = ref({
+	adGroupId: props.adGroupId,
+	dateRange,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
 
 onMounted(() => {
-    crudExpose.doRefresh()
+	crudExpose.doRefresh()
 })
-
-const resizeTabChart = () => {
-    if (tabActiveName.value === 'dataTendency') {
-        dataTendencyRef.value.resizeChart()
-    } else if (tabActiveName.value === 'adStruct') {
-        adStructChartRef.value.resizeChart()
-    }
-}
-const changeTab = () => {
-    nextTick(() => {
-        resizeTabChart()
-    })
-}
-defineExpose({resizeTabChart})
+watch(queryParams, async () => {
+	crudExpose.doRefresh()
+}, { deep: true })
 </script>
 
 <style lang="scss">
 .chart-tabs {
-    margin: 5px 0;
+	margin: 5px 0;
 
-    .el-tabs__nav {
-        padding-left: 0 !important;
-    }
+	.el-tabs__nav {
+		padding-left: 0 !important;
+	}
 }
-
 </style>

+ 12 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/api.ts

@@ -0,0 +1,12 @@
+import { request } from '/@/utils/service'
+import { LocationQueryValue } from 'vue-router'
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/'
+
+export function GetObj(adGroupId: LocationQueryValue | LocationQueryValue[]) {
+	return request({
+		url: apiPrefix + 'head/',
+		method: 'get',
+		params: { adGroupId },
+	})
+}

+ 45 - 17
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/index.vue

@@ -6,36 +6,64 @@
       </span>
       <div class="asj-detail-info">
         <span>状态:{{ adGroupInfo.state }}</span>
-        <span>投放类型:</span>
-        <span>默认竞价:{{ adGroupInfo.defaultBid }}</span>
-        <span>投放日期:</span>
+        <span>投放类型:{{ adGroupInfo.targetingType }}</span>
+        <span>默认竞价:{{ profile.currency_symbol + adGroupInfo.defaultBid }}</span>
+        <span>投放日期:{{ adGroupInfo.startDate }} ~ {{ adGroupInfo.endDate ?? '无结束日期' }}</span>
       </div>
     </div>
-    <el-tabs type="border-card" class="asj-detail-tabs">
-      <el-tab-pane label="商品推广">
-        <!-- <Ads /> -->
+    <el-tabs type="border-card" class="asj-detail-tabs" v-model="tabActiveName">
+      <el-tab-pane label="推广商品" name="adProducts">
+        <Ads v-if="tabActiveName==='adProducts'" :adGroupId="route.query.adGroupId"></Ads>
+      </el-tab-pane>
+      <template v-if="route.query.targetingType === 'automatic'">
+        <el-tab-pane label="定向" name="tab2">
+          <div v-if="tabActiveName === 'tab2'">定向</div>
+        </el-tab-pane>
+        <el-tab-pane label="否定投放" name="tab3">
+          <div v-if="tabActiveName === 'tab3'">否定商品</div>
+        </el-tab-pane>
+      </template>
+      <template v-else-if="route.query.targetingType ==='product'">
+        <el-tab-pane label="商品投放" name="tab2">
+          <div v-if="tabActiveName === 'tab2'">商品投放</div>
+        </el-tab-pane>
+        <el-tab-pane label="否定商品" name="tab3">
+          <div v-if="tabActiveName === 'tab3'">否定商品</div>
+        </el-tab-pane>
+      </template>
+      <template v-else>
+        <el-tab-pane label="关键词" name="tab2">
+          <div v-if="tabActiveName === 'tab2'">关键词</div>
+        </el-tab-pane>
+        <el-tab-pane label="否定词" name="tab3">
+          <div v-if="tabActiveName === 'tab3'">否定词</div>
+        </el-tab-pane>
+      </template>
+      <el-tab-pane label="搜索关键词" name="searchTerm">
+        <SearchTerm v-if="tabActiveName === 'searchTerm'" :adGroupId="route.query.adGroupId" />
       </el-tab-pane>
-      <el-tab-pane label="定向"></el-tab-pane>
-      <el-tab-pane label="否定投放"></el-tab-pane>
-      <el-tab-pane label="搜索关键词"></el-tab-pane>
     </el-tabs>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { Ref, ref, onMounted } from 'vue'
+import { Ref, ref, onMounted, computed } from 'vue'
 import { useRoute, useRouter } from 'vue-router'
-import { GetObj } from '../api'
-// import Ads from '/@/views/adManage/sp/ads/index.vue'
+import { GetObj } from './api'
+import { useShopInfo } from '/@/stores/shopInfo'
+// import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
+import Ads from './ads/index.vue'
+import SearchTerm from './searchTerm/index.vue'
 
-const router = useRouter()
+const tabActiveName = ref('adProducts')
+const shopInfo = useShopInfo()
+const { profile } = storeToRefs(shopInfo)
 const route = useRoute()
-const adGroupInfo = ref({
-  id: 0, adGroupName: '', state: '', defaultBid: ''
-})
+const adGroupInfo: Ref<SpAdGroup> = ref({})
 
 onMounted(async () => {
-  const resp = await GetObj(route.query.id)
+  const resp = await GetObj(route.query.adGroupId)
   adGroupInfo.value = resp.data
 })
 

+ 11 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/searchTerm/api.ts

@@ -0,0 +1,11 @@
+import { request } from '/@/utils/service';
+import { UserPageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+
+export const apiPrefix = '/api/ad_manage/spgroupdetail/searchterm/';
+export function GetList(query: UserPageQuery) {
+    return request({
+        url: apiPrefix + 'list/',
+        method: 'get',
+        params: query,
+    })
+}

+ 79 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/searchTerm/crud.tsx

@@ -0,0 +1,79 @@
+import * as api from './api'
+import { dict, UserPageQuery, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'
+import { inject } from 'vue'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import { parseQueryParams } from '/@/views/adManage/utils/tools.js'
+import XEUtils from 'xe-utils'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const pageRequest = async (query: UserPageQuery) => {
+    const params = parseQueryParams(context.value)
+    XEUtils.assign(query, params)
+    return await api.GetList(query)
+  }
+
+  //权限判定
+  const hasPermissions = inject('$hasPermissions')
+
+  return {
+    crudOptions: {
+      table: {},
+      container: {
+        fixedHeight: false,
+      },
+      actionbar: {
+        show: false,
+      },
+      search: {
+        show: true,
+        buttons: {
+					search: {
+						show: false
+					},
+					reset: {
+						show: false
+					}
+				}
+      },
+      // toolbar: {
+      //   buttons: {
+      //     search: {
+      //       show: true,
+      //     },
+      //     compact: {
+      //       show: false,
+      //     },
+      //   },
+      // },
+      request: {
+        pageRequest,
+      },
+      rowHandle: {
+        show: false,
+      },
+      columns: {
+        searchTerm: {
+          title: '搜索词',
+          column: {
+            width: '200px',
+            fixed: 'left',
+          },
+        },
+        targeting: {
+          title: '定向',
+          column: {
+            width: '200px'
+          },
+        },
+        matchType: {
+          title: '匹配类型',
+          column: {
+            width: '100px',
+            align: 'center'
+          },
+        },
+        ...BaseColumn,
+      },
+    },
+  }
+}

+ 67 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/adGroupDetail/searchTerm/index.vue

@@ -0,0 +1,67 @@
+<template>
+  <fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+      <template #search-left>
+        <DateRangePicker v-model="dateRange"></DateRangePicker>
+      </template>
+
+      <template v-for="field of Object.keys(BaseColumn)" #[`cell_${field}`]="scope">
+        <DataCompare
+          :field="field"
+          :value="scope.row[field]"
+          :prev-val="scope.row[`prev${field}`]"
+          :gap-val="scope.row[`gap${field}`]"
+          :date-range="dateRange"
+          :show-compare="showCompare"
+        />
+      </template>
+      <template #toolbar-left>
+        <div class="campare-switch">
+          <span>数据对比 </span>
+          <el-switch v-model="showCompare" size="small" />
+        </div>
+      </template>
+    </fs-crud>
+  </fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, computed, watch, onBeforeMount } from 'vue'
+import { useFs, FsPage } from '@fast-crud/fast-crud'
+import { createCrudOptions } from './crud'
+// import { useShopInfo } from '/@/stores/shopInfo'
+import { usePublicData } from '/@/stores/publicData'
+import { storeToRefs } from 'pinia'
+import { LocationQueryValue } from 'vue-router'
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+import DataCompare from '/@/components/dataCompare/index.vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+
+interface Props {
+  adGroupId: LocationQueryValue | LocationQueryValue[]
+}
+const props = defineProps<Props>()
+// const shopInfo = useShopInfo()
+const publicData = usePublicData()
+const { dateRange } = storeToRefs(publicData)
+// const { profile } = storeToRefs(shopInfo)
+const queryParams = ref({
+  adGroupId: props.adGroupId,
+  dateRange,
+})
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: queryParams })
+const showCompare = ref(false)
+
+onMounted(async () => {
+  crudExpose.doRefresh()
+})
+watch(
+  queryParams,
+  async () => {
+    crudExpose.doRefresh()
+  },
+  { deep: true }
+)
+</script>
+
+<style scoped></style>

+ 15 - 0
src/views/adManage/sp/campaigns/campaignDetail/adGroups/crud.tsx

@@ -88,6 +88,21 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
 						rules: [{required: true, message:'必填项'}]
 					}
         },
+				targetingType: {
+					title: '投放类型',
+					type: 'dict-select',
+					column: {
+						align: 'center',
+						width: '110px'
+					},
+					dict: dict({
+						data: [
+							{ label: '自动定向', value: 'automatic' },
+							{ label: '商品定向', value: 'product' },
+							{ label: '关键词投放', value: 'keyword' }
+						]
+					})
+				},
 				state: {
 					title: '状态'
 				},

+ 1 - 2
src/views/adManage/sp/campaigns/campaignDetail/adGroups/index.vue

@@ -24,7 +24,6 @@ import { useFs, FsPage } from '@fast-crud/fast-crud';
 import { createCrudOptions } from './crud'
 import { useRoute, useRouter, LocationQueryValue } from 'vue-router'
 import DateRangePicker from '/@/components/DateRangePicker/index.vue'
-// import AdGroupChart from './chartComponents/adGroupChart.vue'
 import { usePublicData } from '/@/stores/publicData'
 import { storeToRefs } from 'pinia'
 import DataTendencyChart from '/@/views/adManage/sp/chartComponents/dataTendency.vue'
@@ -49,7 +48,7 @@ onMounted(() => {
 const jumpAds = (row: any) => {
   router.push({
     name: 'AdGroupDetail',
-    query: { id: row.id, adGroupId: row.adGroupId, tagsViewName: row.adGroupName }
+    query: { adGroupId: row.adGroupId, targetingType: row.targetingType, tagsViewName: row.adGroupName }
   })
 }
 watch(

+ 1 - 1
src/views/adManage/sp/chartComponents/dataTendency.vue

@@ -228,7 +228,7 @@ const initLine = async () => {
     info.axisLine.lineStyle.color = metrics.value[index].color
   })
 
-  XEUtils.arrayEach(metricsItems.value, info => {
+  XEUtils.arrayEach(props.metricEnum, info => {
     option.legend.selected[info.label] = false
   })
   for(const info of metrics.value) {

+ 54 - 51
src/views/adManage/utils/tools.ts

@@ -2,68 +2,71 @@ import dayjs, { Dayjs } from 'dayjs'
 import { unref } from 'vue'
 import XEUtils from 'xe-utils'
 
-export function recentDaysRange(timezone: string, days: number):string[] {
-    const now_tz = dayjs(new Date()).tz(timezone)
-    const start = now_tz.subtract(days, 'day')
-    const end = now_tz.subtract(1, 'day')
-    return [start.format("YYYY-MM-DD"), end.format("YYYY-MM-DD")]
+export function recentDaysRange(timezone: string, days: number): string[] {
+  const now_tz = dayjs(new Date()).tz(timezone)
+  const start = now_tz.subtract(days, 'day')
+  const end = now_tz.subtract(1, 'day')
+  return [start.format('YYYY-MM-DD'), end.format('YYYY-MM-DD')]
 }
 
-export function buildChartOpt(option:any, metrics: any[]) {
-    const tmp: any = {}
-    const opt:any = {
-        legend: {selected: {}},
-        yAxis: [],
-        series: []
+export function buildChartOpt(option: any, metrics: any[]) {
+  const tmp: any = {}
+  const opt: any = {
+    legend: { selected: {} },
+    yAxis: [],
+    series: [],
+  }
+  for (const info of metrics) {
+    tmp[info.color] = info
+  }
+  for (const info of option.series) {
+    const color = info.itemStyle.color
+    const metricInfo = tmp[color]
+    if (metricInfo) {
+      opt.series.push({
+        id: info.id,
+        name: metricInfo.label,
+        encode: { y: metricInfo.metric },
+      })
+      opt.yAxis.push({ id: info.id, name: metricInfo.label, show: true })
+    } else {
+      opt.yAxis.push({ id: info.id, show: false })
     }
-    for (const info of metrics) { tmp[info.color] = info }
-    for (const info of option.series) {
-        const color = info.itemStyle.color
-        const metricInfo = tmp[color]
-        if (metricInfo) {
-        opt.series.push({
-            id: info.id,
-            name: metricInfo.label, 
-            encode: {y: metricInfo.metric},
-        })
-        opt.yAxis.push({id: info.id, name: metricInfo.label, show: true})
-        } else {
-        opt.yAxis.push({id: info.id, show: false})
-        }
+  }
+  for (const label of Object.keys(option.legend.selected)) {
+    if (XEUtils.findIndexOf(metrics, (info) => info.label === label) === -1) {
+      opt.legend.selected[label] = false
+    } else {
+      opt.legend.selected[label] = true
     }
-    for (const label of Object.keys(option.legend.selected)) {
-        if (XEUtils.findIndexOf(metrics, info => info.label === label) === -1) {
-        opt.legend.selected[label] = false
-        } else {
-        opt.legend.selected[label] = true
-        }
-    }
-    return opt
+  }
+  console.log(opt)
+  return opt
 }
 
 export function getCompareDate(dateRange: string[]) {
-    const start = dayjs(dateRange[0])
-    const end = dayjs(dateRange[1])
-    const days = end.diff(start, 'day')
-    const preEnd = start.subtract(1, 'day')
-    const preStart = preEnd.subtract(days, 'day')
-    return [preStart.format("YYYY-MM-DD"), preEnd.format("YYYY-MM-DD")]
+  const start = dayjs(dateRange[0])
+  const end = dayjs(dateRange[1])
+  const days = end.diff(start, 'day')
+  const preEnd = start.subtract(1, 'day')
+  const preStart = preEnd.subtract(days, 'day')
+  return [preStart.format('YYYY-MM-DD'), preEnd.format('YYYY-MM-DD')]
 }
 
 export function parseQueryParams(body: any) {
-    const ret:any = {}
-    for (const key in body) {
-      const val = unref(body[key])
-      if (key === 'dateRange') {
-        ret["startDate"] = val[0]
-        ret["endDate"] = val[1]
-      } else {
-        ret[key] = val
-      }
+  const ret: any = {}
+  for (const key in body) {
+    const val = unref(body[key])
+    if (key === 'dateRange') {
+      ret['startDate'] = val[0]
+      ret['endDate'] = val[1]
+    } else {
+      ret[key] = val
     }
-    return ret
+  }
+  return ret
 }
 
-export function getEnumLabel(enumObj: {label:string, value:string}[], value: any) {
-    return enumObj.find(item => item.value === value)?.label
+export function getEnumLabel(enumObj: { label: string; value: string }[], value: any) {
+  return enumObj.find((item) => item.value === value)?.label
 }

+ 2 - 16
src/views/demo/index.vue

@@ -1,27 +1,13 @@
 <template>
   <div class="test">
-    <MetricsCards v-model="selectedVal" :metric-items="options"></MetricsCards>
-    <p>{{ selectedVal }}</p>
+    <RuleCalendar></RuleCalendar>
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue'
-import MetricsCards from '/@/components/MetricsCards/index.vue'
-import MCard from '/@/components/MetricsCards/mCard.vue'
+import RuleCalendar from '/@/components/RuleCalendar/index.vue'
 
-const selectedVal = ref([{metric: 'ACOS', color: 'blue'}])
-const options = ref([
-  {label: 'ACOS', value: 'ACOS', metricVal: "18.00%", preVal: '20.15%', gapVal: '-2.00%', disabled:true},
-  {label: '点击量', value: 'clicks', metricVal: "19.00%", preVal: '20.15%', gapVal: '-1.00%', disabled:true},
-  {label: '曝光量', value: 'impression', metricVal: "20.00%", preVal: '15.00%', gapVal: '5.00%', disabled:true},
-  {label: '转化率1', value: 'rate1', metricVal: "1.00%", preVal: '15.00%', gapVal: '5.00%', disabled:true},
-  {label: '转化率2', value: 'rate2', metricVal: "2.00%", preVal: '15.00%', gapVal: '5.00%', disabled:true},
-  {label: '转化率3', value: 'rate3', metricVal: "3.00%", preVal: '15.00%', gapVal: '5.00%', disabled:true},
-  {label: '转化率4', value: 'rate4', metricVal: "4.00%", preVal: '15.00%', gapVal: '5.00%'},
-  {label: '转化率5', value: 'rate5', metricVal: "5.00%", preVal: '15.00%', gapVal: '5.00%'},
-  {label: '转化率6', value: 'rate6', metricVal: "6.00%", preVal: '15.00%', gapVal: '5.00%'},
-])
 
 </script>