Jelajahi Sumber

Merge branch 'wgj' of ASJ_ADS/ads_web into master

guojing_wu 1 tahun lalu
induk
melakukan
e37bbd2100

+ 2 - 1
.env.development

@@ -2,7 +2,8 @@
 ENV = 'development'
 
 # 本地环境接口地址
-VITE_API_URL = 'http://127.0.0.1:8000'
+# VITE_API_URL = 'http://127.0.0.1:8000'
+VITE_API_URL = 'http://192.168.1.225/'
 
 # 是否启用按钮权限
 VITE_PM_ENABLED = true

+ 19 - 2
package-lock.json

@@ -21,6 +21,7 @@
 				"axios": "^1.2.1",
 				"countup.js": "^2.3.2",
 				"cropperjs": "^1.5.13",
+				"dayjs": "^1.11.10",
 				"e-icon-picker": "^2.1.1",
 				"echarts": "^5.4.1",
 				"echarts-gl": "^2.0.9",
@@ -54,6 +55,7 @@
 				"xe-utils": "^3.5.7"
 			},
 			"devDependencies": {
+				"@types/luxon": "^3.3.3",
 				"@types/node": "^18.11.13",
 				"@types/nprogress": "^0.2.0",
 				"@types/sortablejs": "^1.15.0",
@@ -3540,6 +3542,12 @@
 				"@types/lodash": "*"
 			}
 		},
+		"node_modules/@types/luxon": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.3.tgz",
+			"integrity": "sha512-/BJF3NT0pRMuxrenr42emRUF67sXwcZCd+S1ksG/Fcf9O7C3kKCY4uJSbKBE4KDUIYr3WMsvfmWD8hRjXExBJQ==",
+			"dev": true
+		},
 		"node_modules/@types/node": {
 			"version": "18.18.4",
 			"license": "MIT"
@@ -4927,7 +4935,8 @@
 		},
 		"node_modules/dayjs": {
 			"version": "1.11.10",
-			"license": "MIT"
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+			"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
 		},
 		"node_modules/debounce": {
 			"version": "1.2.1",
@@ -11359,6 +11368,12 @@
 				"@types/lodash": "*"
 			}
 		},
+		"@types/luxon": {
+			"version": "3.3.3",
+			"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.3.tgz",
+			"integrity": "sha512-/BJF3NT0pRMuxrenr42emRUF67sXwcZCd+S1ksG/Fcf9O7C3kKCY4uJSbKBE4KDUIYr3WMsvfmWD8hRjXExBJQ==",
+			"dev": true
+		},
 		"@types/node": {
 			"version": "18.18.4"
 		},
@@ -12178,7 +12193,9 @@
 			"version": "2.2.0"
 		},
 		"dayjs": {
-			"version": "1.11.10"
+			"version": "1.11.10",
+			"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
+			"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
 		},
 		"debounce": {
 			"version": "1.2.1"

+ 2 - 0
package.json

@@ -21,6 +21,7 @@
 		"axios": "^1.2.1",
 		"countup.js": "^2.3.2",
 		"cropperjs": "^1.5.13",
+		"dayjs": "^1.11.10",
 		"e-icon-picker": "^2.1.1",
 		"echarts": "^5.4.1",
 		"echarts-gl": "^2.0.9",
@@ -54,6 +55,7 @@
 		"xe-utils": "^3.5.7"
 	},
 	"devDependencies": {
+		"@types/luxon": "^3.3.3",
 		"@types/node": "^18.11.13",
 		"@types/nprogress": "^0.2.0",
 		"@types/sortablejs": "^1.15.0",

+ 111 - 0
src/components/DateRangePicker/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div>
+    <el-date-picker
+      v-model="dateRangeValue"
+      type="daterange"
+      unlink-panels
+      range-separator="To"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :shortcuts="shortcuts"
+      size="default"
+      value-format="YYYY-MM-DD"
+      :disabled-date="disabledDate"
+      :clearable="false"
+      :popper-options="{placement: props.popperPlacement}"
+      @change="$emit('change', dateRangeValue)"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import dayjs, { Dayjs } from 'dayjs'
+
+
+const props = defineProps({
+  timezone: { type: String, required: true },
+  popperPlacement: { type: String, default: 'bottom-start' }
+})
+
+const dateRangeValue = ref(['2023-10-01', '2023-10-10'])
+
+
+function disabledDate(datetime: Date) {
+  const now = dayjs(new Date()).tz(props.timezone)
+  const now_tz = now.startOf("day")
+  return now_tz.isBefore(dayjs(datetime).tz(props.timezone))
+}
+
+function tzFormat(dt_tz: Dayjs) {
+  return dt_tz.format("YYYY-MM-DD HH:mm:ss")
+}
+
+function recentDays(days: number):string[] {
+  const now_tz = dayjs(new Date()).tz(props.timezone)
+  const start = now_tz.subtract(days, 'day')
+  const end = now_tz.subtract(1, 'day')
+  return [tzFormat(start), tzFormat(end)]
+}
+
+function currentWeekMonthYear(dim: 'month'|'week'|'year'): string[] {
+  const end = dayjs(new Date()).tz(props.timezone)
+  const start = end.startOf(dim)
+  return [tzFormat(start), tzFormat(end)]
+}
+
+function lastWeekMonthYear(dim: 'month'|'week'|'year'): string[] {
+  const last = dayjs(new Date()).tz(props.timezone).subtract(1, dim)
+  return [tzFormat(last.startOf(dim)), tzFormat(last.endOf(dim))]
+}
+
+const shortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const now_tz = dayjs(new Date()).tz(props.timezone)
+      return [tzFormat(now_tz), tzFormat(now_tz)]
+    },
+  },
+  {
+    text: '昨天',
+    value: () => recentDays(1)
+  },
+  {
+    text: '最近7天',
+    value: () => recentDays(7)
+  },
+  {
+    text: '最近15天',
+    value: () => recentDays(15)
+  },
+  {
+    text: '最近30天',
+    value: () => recentDays(30)
+  },
+  {
+    text: '本周',
+    value: () => currentWeekMonthYear('week')
+  },
+  {
+    text: '上周',
+    value: () => lastWeekMonthYear('week')
+  },
+  {
+    text: '本月',
+    value: () => currentWeekMonthYear('month')
+  },
+  {
+    text: '上月',
+    value: () => lastWeekMonthYear('month')
+  },
+  {
+    text: '本年',
+    value: () => currentWeekMonthYear('year')
+  }
+]
+</script>
+
+<style scoped>
+
+</style>

+ 9 - 0
src/main.ts

@@ -33,6 +33,15 @@ import 'vxe-table/lib/style.css'
 import '/@/assets/style/reset.scss';
 import 'element-tree-line/dist/style.css'
 
+import dayjs from 'dayjs'
+import UTC from 'dayjs/plugin/utc'
+import Timezon from 'dayjs/plugin/timezone'
+import IsSameOrBefore from 'dayjs/plugin/isSameOrBefore'
+
+dayjs.extend(UTC)
+dayjs.extend(Timezon)
+dayjs.extend(IsSameOrBefore)
+
 let forIconfont = analyzingIconForIconfont(iconfont); //解析class
 iconList.addIcon(forIconfont.list); // 添加iconfont dvadmin3的icon
 iconList.addIcon(elementPlus); // 添加element plus的图标

+ 9 - 0
src/theme/app.scss

@@ -322,3 +322,12 @@ body,
 		padding-left: #{$i}px !important;
 	}
 }
+
+// 自定义全局样式
+.ads-container {
+  padding: 5px;
+}
+
+.fs-page-custom {
+	position: initial !important;
+}

+ 16 - 0
src/theme/element.scss

@@ -319,3 +319,19 @@
 		overflow: auto;
 	}
 }
+
+
+.fs-crud-header {
+	padding: 0 0 5px 0 !important;
+}
+
+.el-tabs__header {
+	margin-bottom: 0px;
+}
+.fs-crud-search {
+	height: auto;
+	padding: auto;
+}
+.fs-search-box {
+	height: 45px;
+}

+ 42 - 0
src/views/adManage/portfolios/api.ts

@@ -0,0 +1,42 @@
+import { request } from '/@/utils/service';
+import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+import XEUtils from 'xe-utils';
+
+export const apiPrefix = '/api/ad_manage/portfolios/';
+export function GetList(query: PageQuery) {
+    return request({
+        url: apiPrefix,
+        method: 'get',
+        params: query,
+    })
+}
+export function GetObj(id: InfoReq) {
+    return request({
+        url: apiPrefix + id,
+        method: 'get',
+    });
+}
+
+export function AddObj(obj: AddReq) {
+    return request({
+        url: apiPrefix,
+        method: 'post',
+        data: obj,
+    });
+}
+
+export function UpdateObj(obj: EditReq) {
+    return request({
+        url: apiPrefix + obj.id + '/',
+        method: 'put',
+        data: obj,
+    });
+}
+
+export function DelObj(id: DelReq) {
+    return request({
+        url: apiPrefix + id + '/',
+        method: 'delete',
+        data: { id },
+    });
+}

+ 179 - 0
src/views/adManage/portfolios/crud.tsx

@@ -0,0 +1,179 @@
+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 { successMessage } from '/@/utils/message';
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js';
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+	const pageRequest = async (query: UserPageQuery) => {
+		return await api.GetList(query);
+	};
+	const editRequest = async ({ form, row }: EditReq) => {
+		form.id = row.id;
+		return await api.UpdateObj(form);
+	};
+	const delRequest = async ({ row }: DelReq) => {
+		return await api.DelObj(row.id);
+	};
+	const addRequest = async ({ form }: AddReq) => {
+		return await api.AddObj(form);
+	};
+
+	//权限判定
+	const hasPermissions = inject('$hasPermissions');
+
+	return {
+		crudOptions: {
+			table: {
+				height: 800,
+			},
+			container: {
+        fixedHeight: false
+      },
+			request: {
+				pageRequest,
+				addRequest,
+				editRequest,
+				delRequest,
+			},
+			rowHandle: {
+				fixed: 'right',
+				width: 80,
+				buttons: {
+					view: {
+						show: false,
+					},
+					edit: {
+						iconRight: 'Edit',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Update'),
+					},
+					remove: {
+						iconRight: 'Delete',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Delete'),
+					},
+					// custom: {
+					// 	text: '字典配置',
+					// 	type: 'text',
+					// 	// show: hasPermissions('dictionary:Update'),
+					// 	tooltip: {
+					// 		placement: 'top',
+					// 		content: '字典配置',
+					// 	},
+					// 	//@ts-ignore
+					// 	click: (ctx: any) => {
+					// 		const { row } = ctx;
+					// 		context!.subDictRef.value.drawer = true;
+					// 		nextTick(() => {
+					// 			context!.subDictRef.value.setSearchFormData({ form: { parent: row.id } });
+					// 			context!.subDictRef.value.doRefresh();
+					// 		});
+					// 	},
+					// },
+				},
+			},
+			columns: {
+				// _index: {
+				// 	title: '序号',
+				// 	form: { show: false },
+				// 	column: {
+				// 		//type: 'index',
+				// 		align: 'center',
+				// 		width: '70px',
+				// 		columnSetDisabled: true, //禁止在列设置中选择
+				// 		formatter: (context) => {
+				// 			//计算序号,你可以自定义计算规则,此处为翻页累加
+				// 			let index = context.index ?? 1;
+				// 			let pagination = crudExpose!.crudBinding.value.pagination;
+				// 			// @ts-ignore
+				// 			return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
+				// 		},
+				// 	},
+				// },
+        name: {
+          title: '广告组合',
+          column: {
+            width: '150px'
+          },
+					search: {
+						show: true,
+						component: {
+							props: {
+								clearable: true
+							}
+						}
+					},
+					form: {
+						rules: [{required: true, message:'必填项'}]
+					}
+        },
+        state: {
+          title: '状态',
+          type: 'dict-select',
+          dict: dict({
+            data:[
+              {value:'enabled', label:'投放中'},
+              {value:'disable', label:'禁用'},
+            ] 
+          }),
+					form: {
+						show: false
+					}
+        },
+				budget_policy: {
+					title: '预算类型',
+					type: 'dict-select',
+					dict: dict({
+						data: [
+							{ value: '', label: '无预算上限' },
+							{ value: 'dateRange', label: '日期范围' },
+							{ value: 'monthlyRecurring', label: '按月' },
+						]
+					}),
+					form: {
+						value: ''
+					}
+				},
+        budget_startDate: {
+          title: '开始日期',
+					type: 'date',
+					form: {
+						show: compute(context => context.form.budget_policy === "dateRange"),
+						rules: [{required: true, message:'必填项'}]
+					}
+        },
+        budget_endDate: {
+          title: '结束日期',
+					type: 'date',
+					form: {
+						show: compute(context => context.form.budget_policy !== '')
+					}
+        },
+        budget_amount: {
+          title: '预算',
+					type: 'number',
+					form: {
+						value: 0,
+						show: compute(context => context.form.budget_policy !== ''),
+						rules: [{required: true, message:'必填项'}],
+						component: {
+							min: 0,
+							precision: 2,
+							controlsPosition: "right"
+						}
+					}
+        },
+        inBudget: {
+          title: '是否预算内',
+					form: {
+						show: false
+					}
+        },
+        ...BaseColumn
+			},
+		},
+	};
+};

+ 44 - 0
src/views/adManage/portfolios/index.vue

@@ -0,0 +1,44 @@
+<template>
+	<div class="ads-container">
+		<div class="public-search">
+			<DateRangePicker timezone="America/Los_Angeles"></DateRangePicker>
+		</div>
+		<fs-page class="fs-page-custom">
+			<fs-crud ref="crudRef" v-bind="crudBinding">
+				<template #header-middle>
+					<el-card style="height: 500px;">此处用于显示可视化图形</el-card>
+				</template>
+			</fs-crud>
+		</fs-page>
+	</div>
+</template>
+
+<script lang="ts" setup name="Portfolios">
+import { ref, onMounted, defineAsyncComponent } from 'vue';
+import { useFs } from '@fast-crud/fast-crud';
+import { createCrudOptions } from './crud';
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
+
+// 页面打开后获取列表数据
+onMounted(() => {
+	crudExpose.doRefresh();
+});
+</script>
+
+<style scoped>
+
+.public-search {
+  display: flex;
+  gap: 3px;
+  padding-bottom: 3px;
+  position: sticky;
+  top: 0;
+  z-index: 2;
+  width: 100%;
+  background-color: #f8f8f8;
+  box-shadow: 0px 0px 0px rgba(51,89,181,0.16);
+}
+
+</style>

+ 9 - 0
src/views/adManage/sp/campaigns/CreateCampaigns/index.vue

@@ -0,0 +1,9 @@
+<template>
+  <p>新建广告页面</p>
+</template>
+
+<script lang="ts" setup>
+</script>
+
+<style scoped>
+</style>

+ 42 - 0
src/views/adManage/sp/campaigns/api.ts

@@ -0,0 +1,42 @@
+import { request } from '/@/utils/service';
+import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+import XEUtils from 'xe-utils';
+
+export const apiPrefix = '/api/ad_manage/spCampaigns/';
+export function GetList(query: PageQuery) {
+    return request({
+        url: apiPrefix,
+        method: 'get',
+        params: query,
+    })
+}
+export function GetObj(id: InfoReq) {
+    return request({
+        url: apiPrefix + id,
+        method: 'get',
+    });
+}
+
+export function AddObj(obj: AddReq) {
+    return request({
+        url: apiPrefix,
+        method: 'post',
+        data: obj,
+    });
+}
+
+export function UpdateObj(obj: EditReq) {
+    return request({
+        url: apiPrefix + obj.id + '/',
+        method: 'put',
+        data: obj,
+    });
+}
+
+export function DelObj(id: DelReq) {
+    return request({
+        url: apiPrefix + id + '/',
+        method: 'delete',
+        data: { id },
+    });
+}

+ 138 - 0
src/views/adManage/sp/campaigns/crud.tsx

@@ -0,0 +1,138 @@
+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 { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js'
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+	const pageRequest = async (query: UserPageQuery) => {
+		return await api.GetList(query);
+	};
+	const editRequest = async ({ form, row }: EditReq) => {
+		form.id = row.id;
+		return await api.UpdateObj(form);
+	};
+	const delRequest = async ({ row }: DelReq) => {
+		return await api.DelObj(row.id);
+	};
+	const addRequest = async ({ form }: AddReq) => {
+		return await api.AddObj(form);
+	};
+
+	//权限判定
+	const hasPermissions = inject('$hasPermissions');
+
+	return {
+		crudOptions: {
+			table: {
+				height: 800
+			},
+			container: {
+        fixedHeight: false
+      },
+			actionbar: {
+				show: true,
+				buttons: {
+					add: {
+						show: false
+					},
+					create: {
+						text: '新建广告活动',
+						type: "primary",
+						show: true,
+						click() {
+
+						}
+					}
+				}
+			},
+			search: {
+				show: false
+			},
+			toolbar: {
+        buttons: {
+					search: {
+						show: true
+					},
+					compact: {
+						show: false
+					}
+				}
+			},
+			request: {
+				pageRequest,
+				addRequest,
+				editRequest,
+				delRequest,
+			},
+			rowHandle: {
+				fixed: 'right',
+				width: 80,
+				buttons: {
+					view: {
+						show: false,
+					},
+					edit: {
+						iconRight: 'Edit',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Update'),
+					},
+					remove: {
+						iconRight: 'Delete',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Delete'),
+					},
+				},
+			},
+			columns: {
+        name: {
+          title: '广告活动',
+          column: {
+            width: '150px'
+          },
+					search: {
+						show: true,
+						component: {
+							props: {
+								clearable: true
+							}
+						}
+					},
+					form: {
+						rules: [{required: true, message:'必填项'}]
+					}
+        },
+				targetingType: {
+					title: '投放类型',
+					type: 'dict-select',
+					search: {
+						show: true
+					},
+					dict: dict({
+						data: [
+							{ value: 'AUTO', label: '自动' },
+							{ value: 'MANUAL', label: '手动' },
+						]
+					})
+				},
+				state: {
+					title: '状态'
+				},
+				startDate: {
+					title: '开始日期'
+				},
+				endDate: {
+					title: '结束日期'
+				},
+				budget: {
+					title: '预算'
+				},
+				portfolio: {
+					title: '广告组合'
+				},
+        ...BaseColumn
+			}
+		}
+	}
+}

+ 24 - 0
src/views/adManage/sp/campaigns/index.vue

@@ -0,0 +1,24 @@
+<template>
+	<fs-page class="fs-page-custom">
+    <fs-crud ref="crudRef" v-bind="crudBinding">
+			<template #header-middle>
+				<el-card style="height: 500px;margin-bottom: 5px;" shadow="hover">图形</el-card>
+			</template>
+		</fs-crud>
+	</fs-page>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, defineAsyncComponent } from 'vue';
+import { useFs, FsPage } from '@fast-crud/fast-crud';
+import { createCrudOptions } from './crud';
+
+const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
+
+onMounted(() => {
+	crudExpose.doRefresh();
+});
+</script>
+
+<style scoped>
+</style>

+ 54 - 0
src/views/adManage/sp/index.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="ads-container">
+    <div class="public-search">
+      <DateRangePicker timezone="America/Los_Angeles" @change="changeDateRange"></DateRangePicker>
+      <el-select v-model="portfolios" placeholder="广告组合"></el-select>
+    </div>
+    <el-tabs>
+      <el-tab-pane label="广告活动">
+        <campaigns></campaigns>
+      </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-tab-pane label="广告位"></el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+import campaigns from './campaigns/index.vue'
+import { ref } from 'vue'
+
+const portfolios = ref([])
+
+function changeDateRange(val: string[]) {
+  console.log(val)
+}
+
+</script>
+
+<style scoped>
+.public-search {
+  display: flex;
+  gap: 3px;
+  padding-bottom: 3px;
+  position: sticky;
+  top: 0;
+  z-index: 2;
+  width: 100%;
+  background-color: #f8f8f8;
+  box-shadow: 0px 0px 0px rgba(51,89,181,0.16);
+}
+:deep(.el-tabs__header.is-top) {
+  background-color: #fff;
+  position: sticky;
+  top: 32px;
+  z-index: 1;
+  box-shadow: 0px 0px 12px rgba(51,89,181,0.16);
+}
+:deep(.el-tabs__nav) {
+  padding-left: 10px;
+}
+</style>

+ 69 - 0
src/views/adManage/utils/commonTabColumn.js

@@ -0,0 +1,69 @@
+export const BaseColumn = {
+  impressions: {
+    title: '曝光量',
+    form: {
+      show: false
+    }
+  },
+  clicks: {
+    title: '点击量',
+    form: {
+      show: false
+    }
+  },
+  ctr: {
+    title: '点击率',
+    form: {
+      show: false
+    }
+  },
+  spend: {
+    title: '花费',
+    form: {
+      show: false
+    }
+  },
+  cpc: {
+    title: '点击成本',
+    form: {
+      show: false
+    }
+  },
+  purchases: {
+    title: '订单量',
+    form: {
+      show: false
+    }
+  },
+  unitsOrdered: {
+    title: '销量',
+    form: {
+      show: false
+    }
+  },
+  sales: {
+    title: '销售额',
+    form: {
+      show: false
+    }
+  },
+  acos: {
+    title: 'ACOS',
+    form: {
+      show: false
+    }
+  },
+  roas: {
+    title: 'ROAS',
+    form: {
+      show: false
+    }
+  },
+  cpa: {
+    title: '订单成本',
+    form: {
+      show: false
+    }
+  }
+}
+

+ 42 - 0
src/views/demo/api.ts

@@ -0,0 +1,42 @@
+import { request } from '/@/utils/service';
+import { PageQuery, AddReq, DelReq, EditReq, InfoReq } from '@fast-crud/fast-crud';
+import XEUtils from 'xe-utils';
+
+export const apiPrefix = '/api/ad_manage/';
+export function GetList(query: PageQuery) {
+    return request({
+        url: apiPrefix,
+        method: 'get',
+        params: query,
+    })
+}
+export function GetObj(id: InfoReq) {
+    return request({
+        url: apiPrefix + id,
+        method: 'get',
+    });
+}
+
+export function AddObj(obj: AddReq) {
+    return request({
+        url: apiPrefix,
+        method: 'post',
+        data: obj,
+    });
+}
+
+export function UpdateObj(obj: EditReq) {
+    return request({
+        url: apiPrefix + obj.id + '/',
+        method: 'put',
+        data: obj,
+    });
+}
+
+export function DelObj(id: DelReq) {
+    return request({
+        url: apiPrefix + id + '/',
+        method: 'delete',
+        data: { id },
+    });
+}

+ 117 - 0
src/views/demo/crud.tsx

@@ -0,0 +1,117 @@
+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 { successMessage } from '/@/utils/message';
+import { BaseColumn } from '/@/views/adManage/utils/commonTabColumn.js';
+
+export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+	const pageRequest = async (query: UserPageQuery) => {
+		return await api.GetList(query);
+	};
+	const editRequest = async ({ form, row }: EditReq) => {
+		form.id = row.id;
+		return await api.UpdateObj(form);
+	};
+	const delRequest = async ({ row }: DelReq) => {
+		return await api.DelObj(row.id);
+	};
+	const addRequest = async ({ form }: AddReq) => {
+		return await api.AddObj(form);
+	};
+
+	//权限判定
+	const hasPermissions = inject('$hasPermissions');
+
+	return {
+		crudOptions: {
+			table: {
+				height: 800,
+				size: "small",
+			},
+			container: {
+        fixedHeight: false
+      },
+			request: {
+				pageRequest,
+				addRequest,
+				editRequest,
+				delRequest,
+			},
+			rowHandle: {
+				fixed: 'right',
+				width: 80,
+				buttons: {
+					view: {
+						show: false,
+					},
+					edit: {
+						iconRight: 'Edit',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Update'),
+					},
+					remove: {
+						iconRight: 'Delete',
+						type: 'text',
+            text: null
+						// show: hasPermissions('dictionary:Delete'),
+					},
+					// custom: {
+					// 	text: '字典配置',
+					// 	type: 'text',
+					// 	// show: hasPermissions('dictionary:Update'),
+					// 	tooltip: {
+					// 		placement: 'top',
+					// 		content: '字典配置',
+					// 	},
+					// 	//@ts-ignore
+					// 	click: (ctx: any) => {
+					// 		const { row } = ctx;
+					// 		context!.subDictRef.value.drawer = true;
+					// 		nextTick(() => {
+					// 			context!.subDictRef.value.setSearchFormData({ form: { parent: row.id } });
+					// 			context!.subDictRef.value.doRefresh();
+					// 		});
+					// 	},
+					// },
+				},
+			},
+			columns: {
+				// _index: {
+				// 	title: '序号',
+				// 	form: { show: false },
+				// 	column: {
+				// 		//type: 'index',
+				// 		align: 'center',
+				// 		width: '70px',
+				// 		columnSetDisabled: true, //禁止在列设置中选择
+				// 		formatter: (context) => {
+				// 			//计算序号,你可以自定义计算规则,此处为翻页累加
+				// 			let index = context.index ?? 1;
+				// 			let pagination = crudExpose!.crudBinding.value.pagination;
+				// 			// @ts-ignore
+				// 			return ((pagination.currentPage ?? 1) - 1) * pagination.pageSize + index + 1;
+				// 		},
+				// 	},
+				// },
+        name: {
+          title: '广告活动',
+          column: {
+            width: '150px'
+          },
+					search: {
+						show: true,
+						component: {
+							props: {
+								clearable: true
+							}
+						}
+					},
+					form: {
+						rules: [{required: true, message:'必填项'}]
+					}
+        }
+			}
+		}
+	}
+}

+ 28 - 0
src/views/demo/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <div>
+    <DateRangePicker
+      timezone="America/Los_Angeles"
+      @change="changedValue">
+    </DateRangePicker>
+     {{ startDate }} To {{ endDate }}
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from 'vue'
+import DateRangePicker from '/@/components/DateRangePicker/index.vue'
+
+
+// let dateRangeVal = reactive([])
+const startDate = ref('')
+const endDate = ref('')
+
+function changedValue(value: string[]) {
+  // dateRangeVal = value
+  console.log(value[0], value[1])
+  startDate.value = value[0]
+  endDate.value = value[1]
+
+}
+
+</script>