wjx 1 year ago
parent
commit
dd44903efa

+ 6 - 0
config/routerConfig.ts

@@ -158,6 +158,12 @@ const gameDataStatistics = {
                     name: '游戏VIP档位',
                     access: 'vip',
                     component: './gameDataStatistics/roleOperate/vip',
+                },
+                {
+                    path: '/gameDataStatistics/roleOperate/strategy',
+                    name: '游戏策略配置',
+                    access: 'strategy',
+                    component: './gameDataStatistics/roleOperate/strategy',
                 }
             ]
         },

+ 165 - 0
src/pages/gameDataStatistics/roleOperate/strategy/index.tsx

@@ -0,0 +1,165 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getGameListNewApi } from "@/services/gameData"
+import { StrategyListProps, delStrategyApi, getStrategyListApi } from "@/services/gameData/roleOperate"
+import { PlusOutlined } from "@ant-design/icons"
+import { Button, Card, Col, Form, Row, Select, Space, message } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../../components/TableData/index.less'
+import StrategyModal from "./strategyModal"
+import Tables from "@/components/Tables"
+import columnsPos from "./tableConfig"
+
+export const strategyType = [
+    { label: '追踪玩家', value: 1, tips: '单笔充值金额大于XX,并且注册时间在XX小时内的玩家' },
+    { label: '玩家流失', value: 2, tips: '累计充值金额大于XX,并且最近游戏距今时间超过XX小时的玩家' },
+    { label: '新用户追踪', value: 3, tips: '新用户注册创角首日充值大于XX的用户' }
+]
+/**
+ * 游戏策略配置
+ * @returns 
+ */
+const Strategy: React.FC = () => {
+
+    /****************************/
+    const [form] = Form.useForm()
+    const [queryFrom, setQueryForm] = useState<StrategyListProps>({ pageNum: 1, pageSize: 20 })
+    const [superGameList, setSuperGameList] = useState<any[]>([])
+    const [initialValues, setInitialValues] = useState<any>({})
+    const [visible, setVisible] = useState<boolean>(false)
+    const getStrategyList = useAjax((params) => getStrategyListApi(params))
+    const delStrategy = useAjax((params) => delStrategyApi(params))
+    const getGameList = useAjax((params) => getGameListNewApi(params))
+    /****************************/
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryFrom))
+        setQueryForm({ ...oldQueryFrom, ...data, pageNum: 1 })
+    }
+
+    useEffect(() => {
+        getStrategyList.run(queryFrom)
+    }, [queryFrom])
+
+    useEffect(() => {
+        getGameList.run({ sourceSystem: 'ZX_ONE' }).then(res => {
+            if (res) {
+                const { superGameList } = res
+                setSuperGameList(superGameList)
+            }
+        })
+    }, [])
+
+    const editVip = (data: any) => {
+        setInitialValues(data)
+        setVisible(true)
+    }
+
+    const del = (id: number) => {
+        delStrategy.run(id).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getStrategyList.refresh()
+            }
+        })
+    }
+
+    return <Card
+        style={{ borderRadius: 8 }}
+        headStyle={{ textAlign: 'left' }}
+        bodyStyle={{ padding: '5px 10px' }}
+    >
+        <div style={{ textAlign: 'center', fontWeight: 'bold', padding: '4px 6px 6px', fontSize: 16, marginBottom: 4, position: 'relative' }}>
+            游戏策略配置
+        </div>
+
+        <Space style={{ width: '100%' }} direction="vertical" size={10}>
+            <Form layout="inline" className='queryForm' initialValues={initialValues} name="basicGameVip" form={form} onFinish={onFinish}>
+                <Row gutter={[0, 6]}>
+                    <Col><Form.Item name='superGameId'>
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ minWidth: 140 }}
+                            allowClear
+                            placeholder={'请选择超父游戏'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {superGameList?.map((item: any) => <Select.Option value={item.super_game_id} key={item.super_game_id}>{item.super_game_name}</Select.Option>)}
+                        </Select>
+                    </Form.Item></Col>
+                    <Col><Form.Item name='type'>
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ minWidth: 140 }}
+                            allowClear
+                            placeholder={'请选择策略类型'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {strategyType?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                        </Select>
+                    </Form.Item></Col>
+                    <Col>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => form.resetFields()}>重置</Button>
+                            <Button icon={<PlusOutlined />} type="primary" onClick={() => { setVisible(true); setInitialValues({}) }}>新增游戏策略配置</Button>
+                        </Space>
+                    </Col>
+                </Row>
+            </Form>
+
+            <div className={`${style['small']}`}>
+                <Tables
+                    className={`all_table content_table_body`}
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    current={queryFrom.pageNum}
+                    pageSize={queryFrom.pageSize}
+                    columns={columnsPos(editVip, del)}
+                    dataSource={getStrategyList?.data?.records}
+                    scroll={{ x: 1000, y: 600 }}
+                    onChange={(pagination: any, filters: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortType'] = sortData?.order === 'ascend' ? 'asc' : 'desc'
+                            newQueryForm['sortFiled'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortType']
+                            delete newQueryForm['sortFiled']
+                        }
+                        newQueryForm.pageNum = current
+                        newQueryForm.pageSize = pageSize
+                        setQueryForm({ ...newQueryForm })
+                    }}
+                    size="small"
+                    total={getStrategyList?.data?.total}
+                    loading={getStrategyList?.loading}
+                    defaultPageSize={20}
+                />
+            </div>
+        </Space>
+
+        {visible && <StrategyModal
+            superGameList={superGameList}
+            visible={visible}
+            initialValues={initialValues}
+            onChange={() => {
+                setInitialValues({});
+                getStrategyList.refresh()
+                setVisible(false)
+            }}
+            onClose={() => {
+                setInitialValues({})
+                setVisible(false)
+            }}
+        />}
+    </Card>
+}
+
+export default Strategy

+ 180 - 0
src/pages/gameDataStatistics/roleOperate/strategy/strategyModal.tsx

@@ -0,0 +1,180 @@
+import { Form, Input, InputNumber, Modal, Select, message } from "antd";
+import React, { useEffect, useState } from "react"
+import { strategyType } from ".";
+import { FieldTimeOutlined } from "@ant-design/icons";
+import { useAjax } from "@/Hook/useAjax";
+import { StrategyProps, modalStrategyApi } from "@/services/gameData/roleOperate";
+
+
+
+interface Props {
+    superGameList: any[]
+    initialValues?: any
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+const StrategyModal: React.FC<Props> = ({ superGameList, initialValues, visible, onClose, onChange }) => {
+
+    /*****************************/
+    const [form] = Form.useForm<StrategyProps>()
+    const type = Form.useWatch('type', form)
+    const time = Form.useWatch('time', form)
+    const amount = Form.useWatch('amount', form)
+    const [help, setHelp] = useState<string>()
+    const modalStrategy = useAjax((params) => modalStrategyApi(params))
+    /*****************************/
+
+    useEffect(() => {
+        if (type) {
+            let timeTips: string = ''
+            let amountTips: string = ''
+            switch (type) {
+                case 1:
+                    // 单笔充值金额大于XX,并且注册时间在XX小时内的玩家
+                    timeTips = time ? `注册时间在${time}小时内的玩家` : ''
+                    amountTips = amount ? `单笔充值金额大于${amount}元` : ''
+                    break
+                case 2:
+                    // 累计充值金额大于XX,并且最近游戏距今时间超过XX小时的玩家
+                    amountTips = amount ? `单笔充值金额大于${amount}元` : ''
+                    timeTips = time ? `最近游戏距今时间超过${time}小时的玩家` : ''
+                    break
+                case 3:
+                    // '新用户注册创角首日充值大于XX的用户'
+                    amountTips = amount ? `新用户注册创角首日充值大于${amount}元的用户` : ''
+                    break
+            }
+            let helpTips = ''
+            if (timeTips && amountTips) {
+                helpTips = amountTips + ',并且' + timeTips + ',发送钉钉消息'
+            } else if (timeTips) {
+                helpTips = timeTips + ',发送钉钉消息'
+            } else if (amountTips) {
+                helpTips = amountTips + ',发送钉钉消息'
+            }
+            setHelp(helpTips)
+            form.setFieldsValue({ configExplain: helpTips })
+        } else {
+            setHelp(undefined)
+            form.setFieldsValue({ configExplain: undefined })
+        }
+    }, [type, time, amount])
+
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        console.log('===========>', data)
+        if (!data?.amount && !data?.time) {
+            message.error('时间和金额最少填一项')
+            return
+        }
+        if (initialValues?.id) {
+            data.id = initialValues?.id
+        }
+        modalStrategy.run(data).then(res => {
+            if (res) {
+                if (initialValues?.id) {
+                    message.success('修改成功')
+                } else {
+                    message.success('新增成功')
+                }
+                onChange?.()
+            }
+        })
+    }
+
+
+    // 配置 formatter 和 parser
+    const integerFormatter = (value: any) => {
+        // 去除非数字字符
+        if (value && value !== 0) {
+            const intValue = parseInt(value, 10);
+            return isNaN(intValue) ? 0 : intValue as any;
+        }
+        return value
+    };
+
+    const integerParser = (value: any) => {
+        // 去除非数字字符
+        if (value && value !== 0) {
+            const intValue = parseInt(value, 10);
+            return isNaN(intValue) ? '' : String(intValue);
+        }
+        return value
+    };
+
+    return <Modal
+        title={`${initialValues?.id ? '修改' : '新增'}游戏策略`}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        confirmLoading={modalStrategy.loading}
+    >
+        <Form
+            name="basicStrategy"
+            labelCol={{ span: 4 }}
+            wrapperCol={{ span: 20 }}
+            form={form}
+            autoComplete="off"
+            labelAlign="left"
+            colon={false}
+            initialValues={{ ...initialValues }}
+        >
+            <Form.Item label="超父游戏" name='superGameId' rules={[{ required: true, message: '请选择超父游戏!' }]}>
+                <Select
+                    showSearch
+                    allowClear
+                    placeholder={'请选择超父游戏'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {superGameList?.map((item: any) => <Select.Option value={item.super_game_id} key={item.super_game_id}>{item.super_game_name}</Select.Option>)}
+                </Select>
+            </Form.Item>
+            <Form.Item label="策略类型" help={help} name='type' rules={[{ required: true, message: '请选择策略类型!' }]}>
+                <Select
+                    showSearch
+                    style={{ minWidth: 140 }}
+                    allowClear
+                    placeholder={'请选择策略类型'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {strategyType?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                </Select>
+            </Form.Item>
+            <Form.Item
+                label="金额(元)"
+                name='amount'
+            >
+                <InputNumber
+                    style={{ width: '100%' }}
+                    prefix="¥"
+                    min={0}
+                    formatter={integerFormatter}
+                    parser={integerParser}
+                />
+            </Form.Item>
+            {type !== 3 && <Form.Item
+                label="时间(小时)"
+                name='time'
+            >
+                <InputNumber
+                    style={{ width: '100%' }}
+                    prefix={<FieldTimeOutlined />}
+                    min={0}
+                    formatter={integerFormatter}
+                    parser={integerParser}
+                />
+            </Form.Item>}
+            <Form.Item label="策略说明" name='configExplain' rules={[{ required: true, message: '请选择配置说明!' }]}>
+                <Input.TextArea placeholder="配置说明:请写出具体的配置细节。如:首次充值金额大于500并且注册时间24小时内的用户,发送钉钉消息" />
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(StrategyModal)

+ 123 - 0
src/pages/gameDataStatistics/roleOperate/strategy/tableConfig.tsx

@@ -0,0 +1,123 @@
+
+
+import { Row, Col, Popconfirm, Tag } from "antd"
+import { DeleteOutlined, EditOutlined } from "@ant-design/icons"
+import WidthEllipsis from "@/components/widthEllipsis"
+import React from "react"
+
+function columnsPos(editPack: (data: any) => void, del: (id: number) => void) {
+
+    let newArr: any = [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 50
+        },
+        {
+            title: '超父游戏名称',
+            dataIndex: 'superGameName',
+            key: 'superGameName',
+            align: 'center',
+            width: 90,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '策略类型',
+            dataIndex: 'type',
+            key: 'type',
+            align: 'center',
+            width: 100,
+            render: (a: number, b: any) => {
+                return {1: <Tag color="#f50">追踪玩家</Tag>, 2: <Tag color="#2db7f5">玩家流失</Tag>, 3: <Tag color="#87d068">新用户追踪</Tag>}[a]
+            }
+        },
+        {
+            title: '金额(元)',
+            dataIndex: 'amount',
+            key: 'amount',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return (a && a !== 0) ? a : '--'
+            }
+        },
+        {
+            title: '时间(小时)',
+            dataIndex: 'time',
+            key: 'time',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return (a && a !== 0) ? a : '--'
+            }
+        },
+        {
+            title: '策略说明',
+            dataIndex: 'configExplain',
+            key: 'configExplain',
+            width: 150,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createName',
+            key: 'createName',
+            align: 'center',
+            width: 100,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 135,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateName',
+            key: 'updateName',
+            align: 'center',
+            width: 100,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            align: 'center',
+            width: 135,
+            render: (a: string, b: any) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            fixed: 'right',
+            render: (a: string, b: any) => (
+                <Row justify='center' gutter={[10, 0]}>
+                    <Col><a style={{ fontSize: "12px" }} onClick={() => { editPack(b) }}><EditOutlined /> 修改</a></Col>
+                    <Col>
+                        <Popconfirm
+                            title="确定删除?"
+                            onConfirm={() => { del(b.id) }}
+                            okText="是"
+                            cancelText="否"
+                        >
+                            <a style={{ fontSize: "12px", color: 'red' }}><DeleteOutlined /> 删除</a>
+                        </Popconfirm>
+                    </Col>
+                </Row>
+            )
+        }
+    ]
+    return newArr
+}
+
+
+export default columnsPos

+ 48 - 0
src/services/gameData/roleOperate.ts

@@ -495,4 +495,52 @@ export async function modalGameVipApi(data: modalGameVipApi) {
         method: 'POST',
         data
     });
+}
+
+export interface StrategyListProps extends Paging {
+    superGameId?: number,
+    type?: number
+}
+
+/**
+ * 游戏策略配置列表
+ * @param data 
+ * @returns 
+ */
+export async function getStrategyListApi(data: StrategyListProps) {
+    return request(apiManage + '/game/policy/config/listOfPage', {
+        method: 'POST',
+        data
+    });
+}
+
+/**
+ * 删除策略配置
+ * @param id 
+ * @returns 
+ */
+export async function delStrategyApi(id: number) {
+    return request(apiManage + `/game/policy/config/deleteById/${id}`, {
+        method: 'DELETE'
+    });
+}
+
+export interface StrategyProps {
+    superGameId: number
+    amount: number
+    configExplain?: string,
+    time: number,
+    type: number,
+    id?: number
+}
+/**
+ * 新增修改策略
+ * @param data 
+ * @returns 
+ */
+export async function modalStrategyApi(data: StrategyProps) {
+    return request(apiManage + '/game/policy/config/add/or/update', {
+        method: 'POST',
+        data
+    });
 }