Просмотр исходного кода

控制列表和控制列表历史

ssvfdn 3 месяцев назад
Родитель
Сommit
a5e01ce385

+ 40 - 0
apps/web-antd/src/api/game_control/feed_user_list.ts

@@ -0,0 +1,40 @@
+import {requestBodyClient, requestClient} from "#/api/request";
+
+interface ApiResultListData {
+    data: Object;
+    status: number;
+    total: number;
+    list: Array<any>;
+}
+
+/**
+ * 获取点控记录列表
+ */
+export async function getFeedUserList(data:any) {
+    const params = new URLSearchParams(data); // 创建一个新的URLSearchParams对象
+    const queryString = params.toString(); // 转换为查询字符串
+    return requestClient.get<ApiResultListData>('/improve_user_rtp/list?' + queryString);
+}
+
+/**
+ * 增加和更新点控
+ * @param data
+ */
+export async function updateFeedUserInfo(data:any) {
+    return requestBodyClient.post<ApiResultData>('/improve_user_rtp/update', data);
+}
+
+/**
+ * 取消点控
+ * @param data
+ */
+export async function cancelFeedUser(data:any) {
+    return requestClient.post<ApiResultData>('/improve_user_rtp/cancel', data);
+}
+
+/**
+ * 取消所有点控
+ */
+export async function cancelAllFeedUser() {
+    return requestClient.post<ApiResultData>('/improve_user_rtp/all_cancel');
+}

+ 16 - 0
apps/web-antd/src/locales/langs/zh-CN/feed_user.json

@@ -0,0 +1,16 @@
+{
+    "feed_user_title": "养客功能",
+    "feed_user_list_title": "控制列表",
+    "feed_user_record_title": "历史控制记录",
+    "record": {
+        "create_time": "控制时间",
+        "status": "养客状态",
+        "evaluation_period": "养客类型",
+        "net_income": "设定玩家盈利值",
+        "total_win_amount": "玩家养客期间盈利",
+        "control_balance": "开始养客时携带的分数",
+        "balance": "玩家当前剩余分数",
+        "start_end_time": "生效周期",
+        "bet_amount_effective_count": "生效次数/设定次数"
+    }
+}

+ 4 - 1
apps/web-antd/src/locales/langs/zh-CN/game_control.json

@@ -44,9 +44,12 @@
         "admin_name": "操作账号",
         "cancel_control": "取消点控",
         "confirm_cancel_control": "请确认取消【{title}】该点控?",
+        "confirm_cancel_control2": "请确认取消该控制?",
         "confirm_all_cancel_control": "确认取消全部点控?",
+        "confirm_all_cancel_control2": "确认取消全部控制?",
         "end_time": "结束时间",
-        "desc":"(玩家在调控期间每局赢奖倍数限制,此倍数与总押注相乘为每局最高可赢取额度)"
+        "desc":"(玩家在调控期间每局赢奖倍数限制,此倍数与总押注相乘为每局最高可赢取额度)",
+        "view_info": "查看配置"
     },
     "auto_rtp": {
         "new_user_number": "定义新手玩家的游戏局数",

+ 29 - 0
apps/web-antd/src/router/routes/modules/game_control.ts

@@ -72,6 +72,35 @@ const routes: RouteRecordRaw[] = [
                     },
                 ]
             },
+            {
+                meta: {
+                    title: $t('feed_user.feed_user_title'),
+                    icon:'solar:bomb-emoji-broken',
+                    keepAlive: true
+                },
+                name: 'FeedUser',
+                path: '/game-control/feed-user',
+                children:[
+                    {
+                        meta: {
+                            title: $t('feed_user.feed_user_list_title'),
+                            keepAlive: true
+                        },
+                        name: 'FeedUserList',
+                        path: '/game-control/feed-user/list',
+                        component: () => import('#/views/game_control/feed_user/list/index.vue'),
+                    },
+                    {
+                        meta: {
+                            title: $t('feed_user.feed_user_record_title'),
+                            keepAlive: true
+                        },
+                        name: 'FeedUserRecord',
+                        path: '/game-control/feed-user/record',
+                        component: () => import('#/views/game_control/feed_user/record/index.vue'),
+                    },
+                ]
+            }
         ],
     },
 ];

+ 280 - 0
apps/web-antd/src/views/game_control/feed_user/list/create.vue

@@ -0,0 +1,280 @@
+<script setup>
+import {InputNumber, InputGroup, message, Select, SelectOption, RangePicker, DescriptionsItem, Row, Space, Col} from "ant-design-vue";
+
+import {useVbenForm} from "#/adapter/form.js";
+import {$t} from "@vben/locales";
+import {h, reactive, ref, toRaw, toRefs} from "vue";
+import {Page, useVbenModal, confirm} from "@vben/common-ui";
+import {updateFeedUserInfo} from "#/api/game_control/feed_user_list.js";
+import dayjs from "dayjs";
+const title = ref('查看');
+
+let netIncomeList = [10, 20, 30, 50, 70, 100, 200, 500, 1000, 1500, 2000, 3000, 5000, 10000, 50000, 100000, 500000, 1000000];
+let turnoverMultipleList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 50, 100, 500, 1000, 5000];
+let evaluationPeriodList = [
+	{'label':'每日','value':1},
+	{'label':'每周','value':2},
+	{'label':'每月','value':3},
+	{'label':'终生','value':4},
+	{'label':'自选时间段','value':5},
+];
+
+let effectiveCountList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 50, 100, 1000];
+let triggerIntervalRoundsList = [5, 10, 15, 20, 30, 40, 50, 60, 80, 100, 150, 200, 300];
+
+// 添加或更新 {"uname":"115","net_income":100,"turnover_multiple":5,"evaluation_period":4,"effective_count":6,"trigger_interval_rounds":300}
+
+let form = reactive({
+	'net_income': 100,
+	'turnover_multiple': 5,
+	'evaluation_period': 4,
+	'effective_count': 1,
+	'trigger_interval_rounds': 300,
+	'uname':'',
+	'day_time': [],
+	'day_time_error': false,
+	'custom_time_start':"",
+	'custom_time_end':"",
+})
+let is_view = ref(false);
+const [Modal, modalApi] = useVbenModal({
+	draggable: true,
+	async onOpenChange(isOpen) {
+		if (isOpen) {
+			form = reactive({
+				'net_income': 100,
+				'turnover_multiple': 5,
+				'evaluation_period': 4,
+				'effective_count': 1,
+				'trigger_interval_rounds': 300,
+				'uname':'',
+				'day_time': [],
+				'day_time_error' : false,
+			})
+			let data = await modalApi.getData();
+
+			if(data.data) {
+				let _data = data.data;
+				form.net_income = _data.net_income;
+				form.turnover_multiple = _data.turnover_multiple;
+				form.evaluation_period = _data.evaluation_period;
+				form.effective_count = _data.effective_count;
+				form.trigger_interval_rounds = _data.trigger_interval_rounds;
+				form.uname = _data.uname;
+				formApi.setValues({
+					'uname': form.uname,
+				})
+			}
+
+			if(data.is_edit) {
+				is_view.value = false
+				title.value = "新增点控";
+				modalApi.setState({
+					footer:true
+				})
+
+			}else {
+				is_view.value = true;
+				title.value = "查看";
+				modalApi.setState({
+					footer:false
+				})
+			}
+
+			formApi.setState((prev) => {
+				const currentSchema = prev?.schema ?? [];
+				currentSchema[0]['componentProps']['style'] = is_view.value ? 'display:none' : 'width:70%;height:100px;';
+				currentSchema[0]['hideLabel'] = is_view.value;
+				return {
+					schema: currentSchema,
+				};
+			});
+		}
+	},
+	onConfirm:async function () {
+		const status = await formApi.validate();
+		if(!status.valid) {
+			// 验证不通过
+			return false;
+		}
+		const _form = await formApi.getValues();
+		form.day_time_error = false;
+		form.custom_time_start = "";
+		form.custom_time_end = "";
+		if(form.evaluation_period == 5) {
+			if(form.day_time.length == 0) {
+				form.day_time_error = true;
+			}else {
+				form.custom_time_start = form.day_time[0].format('YYYY-MM-DD');
+				form.custom_time_end = form.day_time[1].format('YYYY-MM-DD');
+			}
+		}
+		form.uname = _form.uname;
+		modalApi.lock();
+		const res = await updateFeedUserInfo(toRaw(form));
+		modalApi.unlock();
+		if(res.state) {
+			modalApi.setData({
+				'is_reload': true
+			});
+			message.success(res.message);
+			await modalApi.close();
+		}else {
+			message.error(res.message);
+		}
+	},
+});
+const [Form, formApi] = useVbenForm({
+	// 所有表单项共用,可单独在表单内覆盖
+	commonConfig: {
+		// 所有表单项
+		componentProps: {
+			class: 'w-full',
+		},
+		labelWidth: 130,
+		colon: true,
+	},
+	submitButtonOptions: {
+		show: false
+	},
+	resetButtonOptions: {
+		show: false
+	},
+	// 提交函数
+	// handleSubmit: onSubmit,
+	// 垂直布局,label和input在不同行,值为vertical
+	// 水平布局,label和input在同一行
+	scrollToFirstError: true,
+	layout: 'horizontal',
+	schema: [
+		{
+			// 组件需要在 #/adapter.ts内注册,并加上类型
+			component: 'Textarea',
+			// 对应组件的参数
+			componentProps: {
+				placeholder: $t('game_control.player_control.uname_placeholder'),
+				style:"width:70%;height:100px;",
+			},
+			// 字段名
+			fieldName: 'uname',
+			// 界面显示的label
+			label: '输入玩家的平台ID',
+			rules: 'required',
+		},
+		{
+			component: 'Select',
+			defaultValue: undefined,
+			fieldName: 'net_income',
+			hideLabel: true,
+		},
+	],
+	wrapperClass: 'grid-cols-1',
+});
+</script>
+
+<template>
+	<Modal :title="title">
+		<Form>
+			<template #net_income>
+				<Space class="border w-full" style="display:block;border-bottom:0;">
+					<Row class="border-b h-[60px]" align="middle">
+						<Col :span="7" class="required label"><label>设定玩家在游戏内盈利值达到后关闭功能</label></Col>
+						<Col :span="5" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<Select class="w-full" v-model:value="form.net_income" name="net_income" :disabled="is_view">
+									<SelectOption v-for="value in netIncomeList" :value="value">{{value}}</SelectOption>
+								</Select>
+							</div>
+						</Col>
+						<Col :span="1" class="label warning"><label>或</label></Col>
+						<Col :span="6" class="required label"><label>达到设定流水倍数关闭提升状态</label></Col>
+						<Col :span="5" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<Select class="w-full" v-model:value="form.turnover_multiple" name="turnover_multiple" :disabled="is_view">
+									<SelectOption v-for="value in turnoverMultipleList" :value="value">{{value}}倍</SelectOption>
+								</Select>
+							</div>
+						</Col>
+					</Row>
+					<Row  class="border-b h-[60px]" align="middle">
+						<Col :span="24">
+							<div style="text-align:center;">(设定玩家自动取消系统控制的条件,以上条件 <span style="color:red;">其二达到任一即取消</span> 系统控制,并消耗一次控制次数)</div>
+						</Col>
+					</Row>
+					<Row  class="border-b h-[60px]" align="middle">
+						<Col :span="7" class="required label"><label>系统控制的触发周期选择</label></Col>
+						<Col :span="5" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<Select class="w-full" v-model:value="form.evaluation_period" name="evaluation_period" :disabled="is_view">
+									<SelectOption v-for="item in evaluationPeriodList" :value="item.value">{{item.label}}</SelectOption>
+								</Select>
+							</div>
+						</Col>
+						<Col :span="7" class="required label"><label>周期内可被系统控制的次数</label></Col>
+						<Col :span="5" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<Select class="w-full" v-model:value="form.effective_count" name="effective_count" :disabled="is_view">
+									<SelectOption v-for="value in effectiveCountList" :value="value">{{value}}次</SelectOption>
+								</Select>
+							</div>
+						</Col>
+					</Row>
+					<Row  class="border-b h-[60px]" align="middle">
+						<Col :span="24">
+							<div style="text-align:center;">(设定玩家被系统控制的周期及控制次数, <span style="color:red;">控制次数消耗完,在此周期内玩家不再被控制。</span> 自选时间段为自由选择日期区间)</div>
+						</Col>
+					</Row>
+
+					<Row  class="border-b h-[60px]" align="middle" v-if="form.evaluation_period == 5">
+						<Col :span="7" class="required label"><label>自选周期时间段</label></Col>
+						<Col :span="17" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<RangePicker class="w-full" v-model:value="form.day_time" name="day_time" show-time :status="form.day_time_error ? 'error' : ''" />
+								<p class="text-destructive text-[0.8rem] bottom-1" v-if="form.day_time_error">请选择时间段</p>
+							</div>
+						</Col>
+					</Row>
+
+					<Row  class="border-b h-[60px]" align="middle">
+						<Col :span="7" class="required label"><label>每次系统控制间隔局数</label></Col>
+						<Col :span="5" class="content">
+							<div class="mb-0 w-full pl-4 pr-4">
+								<Select class="w-full" v-model:value="form.trigger_interval_rounds" name="trigger_interval_rounds" :disabled="is_view">
+									<SelectOption v-for="value in triggerIntervalRoundsList" :value="value">每{{value}}局</SelectOption>
+								</Select>
+							</div>
+						</Col>
+						<Col :span="12" class="flex-start">周期内可被系统控制的次数</Col>
+					</Row>
+				</Space>
+			</template>
+		</Form>
+	</Modal>
+</template>
+
+<style scoped>
+.label.required {
+	position: relative;
+}
+.label.required:before {
+	color: var(--vxe-ui-status-danger-color);
+	content: "*";
+}
+.label {
+	align-items: center;
+	background-color: hsl(var(--border));
+	box-shadow: 0 0 2px 0 var(--vxe-ui-layout-background-color);
+	display: flex;
+	font-weight: 550;
+	height: 60px;
+	justify-content: center;
+}
+.content {
+	align-items: center;
+	display: flex;
+	justify-content: center;
+}
+.label.warning {
+	background-color: hsl(var(--warning));
+}
+</style>

+ 233 - 0
apps/web-antd/src/views/game_control/feed_user/list/index.vue

@@ -0,0 +1,233 @@
+<script setup>
+import {$t} from "@vben/locales";
+import {Avatar, Button, Card, Input, InputGroup, message, Select, SelectOption, Tag} from "ant-design-vue";
+import {Page, useVbenModal, confirm} from "@vben/common-ui";
+import {reactive, ref} from "vue";
+import dayjs from "dayjs";
+import {useVbenVxeGrid} from "#/adapter/vxe-table.js";
+
+// 列表筛选
+const filterData = reactive({
+	'search_type':"uname",
+});
+const disabledDate = (current) => {
+	// Can not select days before today and today
+	return current && current > dayjs().endOf('day');
+};
+const formOptions = {
+	// 默认展开
+	collapsed: false,
+	// 所有表单项共用,可单独在表单内覆盖
+	commonConfig: {
+		// 所有表单项
+		componentProps: {
+			class: 'w-full',
+		},
+		labelWidth: 80
+	},
+	// 提交函数
+	// handleSubmit: onSubmit,
+	handleReset: onReset,
+	// 垂直布局,label和input在不同行,值为vertical
+	// 水平布局,label和input在同一行
+	layout: 'horizontal',
+	schema: [
+		{
+			// 组件需要在 #/adapter.ts内注册,并加上类型
+			component: 'Input',
+			// 对应组件的参数
+			// 字段名
+			fieldName: 'search_text',
+			// 界面显示的label
+		},
+		{
+			label:$t("common.range_time"),
+			component: 'RangePicker',
+			defaultValue: [dayjs(), dayjs()],
+			fieldName: 'range_time',
+			componentProps: {
+				disabledDate: disabledDate,
+			}
+		},
+	],
+	showCollapseButton: false,
+	// 是否可展开
+	submitButtonOptions: {
+		content: $t('common.search_submit_button'),
+	},
+	wrapperClass: 'grid-cols-1 md:grid-cols-3',
+};
+function onReset() {
+	gridApi.formApi.resetForm();
+	filterData.search_type = 'uname';
+	gridApi.reload();
+}
+// 列表
+const gridOptions = {
+	border: true,
+	stripe: true,
+	scrollbarConfig: {
+		x: {
+			visible: 'visible'
+		},
+		y: {
+			visible: 'auto'
+		}
+	},
+	// checkboxConfig: {
+	// 	highlight: true,
+	// },
+	columns: [
+		{ fixed: 'left',  title: $t('common.serial'), type: 'seq', width: 50},
+		{ field: 'create_time', title: $t('player_data.gameRecords.create_time'), width: 200, titlePrefix: {'content':$t('player_data.gameRecords.tips.create_time')} },
+		{ field: 'user_id', title: $t('player_data.gameRecords.user_id'), width: 120, titlePrefix: {'content':$t('player_data.gameRecords.tips.user_id')}},
+		{ field: 'uname', title: $t('player_data.gameRecords.uname'), width: 150, titlePrefix: {'content':$t('player_data.gameRecords.tips.uname')}},
+		{ field: 'nickname', title: $t('player_data.gameRecords.nickname'), width: 150, titlePrefix: {'content':$t('player_data.gameRecords.tips.nickname')}},
+		{ field: 'old_rtp', title: $t('game_control.player_control.old_rtp'), width: 150},
+		{ field: 'now_rtp', title: $t('game_control.player_control.now_rtp'), width: 150},
+		{ field: 'bet_amount', title: $t('game_control.player_control.bet_amount'), width: 150},
+		{ field: 'bet_count', title: $t('game_control.player_control.bet_count'), width: 150},
+		{ field: 'total_win_amount', title: $t('game_control.player_control.total_win_amount'), width: 150},
+		{ field: 'control_balance', title: $t('game_control.player_control.control_balance'), width: 150},
+		{ field: 'balance', title: $t('game_control.player_control.balance'), width: 150},
+		{ field: 'admin_name', title: $t('game_control.player_control.admin_name'), width: 150},
+		{ fixed: 'right', align: 'left', field: 'action', title: $t('common.action'), width: 200, slots: {"default":"action"} },
+	],
+	exportConfig: {},
+	// height: 'auto', // 如果设置为 auto,则必须确保存在父节点且不允许存在相邻元素,否则会出现高度闪动问题
+	keepSource: true,
+	treeConfig: {
+		transform: true, // 指定表格为树形表格
+	},
+	proxyConfig: {
+		ajax: {
+			query: async ({ page }) => {
+				let form = {
+					page: page.currentPage,
+					limit: page.pageSize,
+					compress: 0
+				};
+				const search = await gridApi.formApi.getValues();
+				if(search.range_time) {
+					form['start_time'] = search.range_time[0].format('YYYY-MM-DD');
+					form['end_time'] = search.range_time[1].format('YYYY-MM-DD');
+				}
+				if(search.search_text) {
+					form[filterData['search_type']] = search.search_text;
+				}
+				const list = await getFeedUserList(form);
+				return {
+					total: list.total,
+					items: list.list
+				}
+			},
+		},
+	},
+	rowConfig: {
+		isHover: true,
+	},
+	toolbarConfig: {
+		custom: true,
+		export: true,
+		// import: true,
+		refresh: true,
+		zoom: true,
+	},
+};
+const [Grid, gridApi] = useVbenVxeGrid({
+	formOptions,
+	gridOptions,
+});
+
+// 查看玩家详情
+import ExtraModal from './create.vue';
+import {cancelAllFeedUser, cancelFeedUser, getFeedUserList} from "#/api/game_control/feed_user_list.js";
+const [Modal, modalApi] = useVbenModal({
+	// 连接抽离的组件
+	connectedComponent: ExtraModal,
+	class:'w-[65%]',
+	onClosed: function () {
+		const _data = modalApi.getData();
+		if(_data.is_reload){
+			gridApi.reload();
+		}
+	}
+});
+const createFeedUser = (row) => {
+	modalApi.setData({
+		is_edit: true
+	}).open();
+}
+ const infoControl = (row) => {
+	modalApi.setData({
+		data: row,
+		is_view: true
+	}).open();
+ }
+
+const cancelControl = (row) => {
+	confirm($t('game_control.player_control.confirm_cancel_control2')).then(async () => {
+		let res = await cancelFeedUser({'id': row.id});
+		message.success($t('common.suc_msg'));
+		await gridApi.reload();
+		// alert('Confirmed');
+	}).catch(() => {});
+	return false;
+}
+const cancelAllFeedUserList = (row) => {
+	confirm($t('game_control.player_control.confirm_all_cancel_control2')).then(async () => {
+		let res = await cancelAllFeedUser();
+		message.success($t('common.suc_msg'));
+		await gridApi.reload();
+		// alert('Confirmed');
+	}).catch(() => {
+		// alert('Canceled');
+	});
+}
+
+</script>
+
+<template>
+	<Page>
+		<Grid>
+			<template #total_win_amount="{ row }">
+				<span style="color: green" v-if="row.total_win_amount >= 0">{{row.total_win_amount}}</span>
+				<span style="color: red" v-else>{{row.total_win_amount}}</span>
+			</template>
+			<template #action="{ row }">
+				<Button  type="link" danger @click="cancelControl(row)">{{$t('game_control.player_control.cancel_control')}}</Button>
+				<Button  type="link" danger @click="infoControl(row)">{{$t('game_control.player_control.view_info')}}</Button>
+			</template>
+			<template #game_title="{ row }">
+				<div>
+					<Tag  color="blue" size="large">{{row.game_type_text}}</Tag>
+					<Avatar shape="square" :src="row.game_image_url"></Avatar>
+					<span style="margin-left: .5rem;display: inline-block;">{{row.game_title}}</span>
+				</div>
+			</template>
+
+			<template #form-search_text="slotProps">
+				<Input-Group compact>
+					<Select name="search_type" style="width: 110px;" v-model:value="filterData.search_type">
+						<Select-Option value="uname">{{$t('player_data.search.uname')}}</Select-Option>
+						<Select-Option value="nickname">{{$t('player_data.search.nickname')}}</Select-Option>
+						<Select-Option value="user_id">{{$t('player_data.search.user_id')}}</Select-Option>
+					</Select>
+					<Input name="search_text" allowClear :placeholder="$t('player_data.placeholder') + '' + $t('player_data.search[\''+filterData.search_type+'\']')" v-bind="slotProps" style="width:calc(100% - 110px)"></Input>
+				</Input-Group>
+			</template>
+
+			<template #toolbar-actions>
+				<Button type="primary" @click="createFeedUser">{{$t('game_control.button.create_control')}}</Button>
+				<Button type="primary"  danger style="margin-left:10px;" @click="cancelAllFeedUserList">{{$t('game_control.button.all_cancel_control')}}</Button>
+			</template>
+		</Grid>
+		<Modal />
+	</Page>
+</template>
+
+<style scoped>
+.ant-input-group.ant-input-group-compact>*:not(:last-child) {
+	border-inline-end-width: 0;
+}
+</style>

+ 196 - 0
apps/web-antd/src/views/game_control/feed_user/record/index.vue

@@ -0,0 +1,196 @@
+<script setup>
+import {$t} from "@vben/locales";
+import {Avatar, Button, Card, Input, InputGroup, message, Select, SelectOption, Tag} from "ant-design-vue";
+import {Page, useVbenModal, confirm} from "@vben/common-ui";
+import {reactive, ref} from "vue";
+import dayjs from "dayjs";
+import {useVbenVxeGrid} from "#/adapter/vxe-table.js";
+import {getFeedUserList} from "#/api/game_control/feed_user_list.js";
+
+// 列表筛选
+const filterData = reactive({
+	'search_type':"uname",
+});
+const disabledDate = (current) => {
+	// Can not select days before today and today
+	return current && current > dayjs().endOf('day');
+};
+const formOptions = {
+	// 默认展开
+	collapsed: false,
+	// 所有表单项共用,可单独在表单内覆盖
+	commonConfig: {
+		// 所有表单项
+		componentProps: {
+			class: 'w-full',
+		},
+		labelWidth: 80
+	},
+	// 提交函数
+	// handleSubmit: onSubmit,
+	handleReset: onReset,
+	// 垂直布局,label和input在不同行,值为vertical
+	// 水平布局,label和input在同一行
+	layout: 'horizontal',
+	schema: [
+		{
+			// 组件需要在 #/adapter.ts内注册,并加上类型
+			component: 'Input',
+			// 对应组件的参数
+			// 字段名
+			fieldName: 'search_text',
+			// 界面显示的label
+		},
+		{
+			component: 'Select',
+			componentProps: {
+				filterOption: true,
+				name:'status',
+				options: [
+					{'label':"全部","value":""},
+					{'label':"生效中","value":"1"},
+					{'label':"已结束","value":"0"},
+				],
+				placeholder: $t('common.placeholder_select'),
+				defaultValue: ""
+			},
+			fieldName: 'status',
+			label: "养客状态",
+		},
+		{
+			label:$t("common.range_time"),
+			component: 'RangePicker',
+			defaultValue: [dayjs(), dayjs()],
+			fieldName: 'range_time',
+			componentProps: {
+				disabledDate: disabledDate,
+			}
+		},
+	],
+	showCollapseButton: false,
+	// 是否可展开
+	submitButtonOptions: {
+		content: $t('common.search_submit_button'),
+	},
+	wrapperClass: 'grid-cols-1 md:grid-cols-3',
+};
+function onReset() {
+	gridApi.formApi.resetForm();
+	filterData.search_type = 'uname';
+	gridApi.reload();
+}
+// 列表
+const gridOptions = {
+	border: true,
+	stripe: true,
+	scrollbarConfig: {
+		x: {
+			visible: 'visible'
+		},
+		y: {
+			visible: 'auto'
+		}
+	},
+	// checkboxConfig: {
+	// 	highlight: true,
+	// },
+	columns: [
+		{ fixed: 'left',  title: $t('common.serial'), type: 'seq', width: 50},
+		{ field: 'create_time', title: $t('feed_user.record.create_time'), width: 200},
+		{ field: 'user_id', title: $t('player_data.gameRecords.user_id'), width: 120, titlePrefix: {'content':$t('player_data.gameRecords.tips.user_id')}},
+		{ field: 'uname', title: $t('player_data.gameRecords.uname'), width: 150, titlePrefix: {'content':$t('player_data.gameRecords.tips.uname')}},
+		{ field: 'nickname', title: $t('player_data.gameRecords.nickname'), width: 150, titlePrefix: {'content':$t('player_data.gameRecords.tips.nickname')}},
+		{ field: 'status_text', title: $t('feed_user.record.status'), width: 150},
+		{ field: 'evaluation_period_text', title: $t('feed_user.record.evaluation_period'), width: 250, slots: {'default':'evaluation_period'}},
+		{ field: 'net_income', title: $t('feed_user.record.net_income'), width: 150},
+		{ field: 'total_win_amount', title: $t('feed_user.record.total_win_amount'), width: 150},
+		{ field: 'control_balance', title: $t('feed_user.record.control_balance'), width: 150},
+		{ field: 'balance', title: $t('feed_user.record.balance'), width: 150},
+		{ field: 'start_end_time', title: $t('feed_user.record.start_end_time'), width: 380},
+		{ field: 'bet_amount_effective_count', title: $t('feed_user.record.bet_amount_effective_count'), width: 150, slots:{'default':'bet_amount_effective_count'}},
+	],
+	exportConfig: {},
+	// height: 'auto', // 如果设置为 auto,则必须确保存在父节点且不允许存在相邻元素,否则会出现高度闪动问题
+	keepSource: true,
+	treeConfig: {
+		transform: true, // 指定表格为树形表格
+	},
+	proxyConfig: {
+		ajax: {
+			query: async ({ page }) => {
+				let form = {
+					page: page.currentPage,
+					limit: page.pageSize,
+					compress: 0,
+					is_end_time: 1
+				};
+				const search = await gridApi.formApi.getValues();
+				if(search.range_time) {
+					form['start_time'] = search.range_time[0].format('YYYY-MM-DD');
+					form['end_time'] = search.range_time[1].format('YYYY-MM-DD');
+				}
+				if(search.search_text) {
+					form[filterData['search_type']] = search.search_text;
+				}
+				form['status'] = search['status'] ?? "";
+				const list = await getFeedUserList(form);
+				return {
+					total: list.total,
+					items: list.list
+				}
+			},
+		},
+	},
+	rowConfig: {
+		isHover: true,
+	},
+	toolbarConfig: {
+		custom: true,
+		export: true,
+		// import: true,
+		refresh: true,
+		zoom: true,
+	},
+};
+const [Grid, gridApi] = useVbenVxeGrid({
+	formOptions,
+	gridOptions,
+});
+</script>
+
+<template>
+	<Page>
+		<Grid>
+			<template #total_win_amount="{ row }">
+				<span style="color: green" v-if="row.total_win_amount >= 0">{{row.total_win_amount}}</span>
+				<span style="color: red" v-else>{{row.total_win_amount}}</span>
+			</template>
+
+			<template #form-search_text="slotProps">
+				<Input-Group compact>
+					<Select name="search_type" style="width: 110px;" v-model:value="filterData.search_type">
+						<Select-Option value="uname">{{$t('player_data.search.uname')}}</Select-Option>
+						<Select-Option value="nickname">{{$t('player_data.search.nickname')}}</Select-Option>
+						<Select-Option value="user_id">{{$t('player_data.search.user_id')}}</Select-Option>
+					</Select>
+					<Input name="search_text" allowClear :placeholder="$t('player_data.placeholder') + '' + $t('player_data.search[\''+filterData.search_type+'\']')" v-bind="slotProps" style="width:calc(100% - 110px)"></Input>
+				</Input-Group>
+			</template>
+			<template #bet_amount_effective_count="{ row }">
+				{{row.now_effective_count}}/{{row.effective_count}}
+			</template>
+			<template #evaluation_period="{ row }">
+				{{row.evaluation_period_text}}<br/>
+				<span v-if="row.evaluation_period == 5">
+				{{row.custom_time_start}} ~ {{row.custom_time_end}}
+				</span>
+			</template>
+		</Grid>
+	</Page>
+</template>
+
+<style scoped>
+.ant-input-group.ant-input-group-compact>*:not(:last-child) {
+	border-inline-end-width: 0;
+}
+</style>

+ 2 - 2
apps/web-antd/vite.config.mts

@@ -9,8 +9,8 @@ export default defineConfig(async () => {
           '/api': {
             changeOrigin: true,
             rewrite: (path) => path.replace(/^\/api/, ''),
-              target: 'https://merchant.w115.net',
-              // target: 'http://merchant.uwigb.mynatapp.cc',
+              // target: 'https://merchant.w115.net',
+              target: 'http://merchant.uwigb.mynatapp.cc',
             // ws: true,
           },
         },