wjx há 1 ano atrás
pai
commit
45809a158b
22 ficheiros alterados com 2164 adições e 7 exclusões
  1. 13 0
      config/routerConfig.ts
  2. 2 0
      src/app.tsx
  3. 0 0
      src/assets/roleManage.svg
  4. 125 3
      src/components/QueryForm/index.tsx
  5. 4 0
      src/global.less
  6. 9 4
      src/pages/gameDataStatistics/components/TableData/index.tsx
  7. 164 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/assign.tsx
  8. 160 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/changeLog.tsx
  9. 7 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/index.less
  10. 185 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/index.tsx
  11. 56 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/inputUpdate.tsx
  12. 32 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/isTrue.tsx
  13. 161 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/roleCz.tsx
  14. 31 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendEmailDetails.tsx
  15. 122 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendEmailTable.tsx
  16. 75 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendMail.tsx
  17. 99 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPack.tsx
  18. 30 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPackDetails.tsx
  19. 123 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPackTable.tsx
  20. 522 0
      src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/tableConfig.tsx
  21. 20 0
      src/services/gameData/index.ts
  22. 224 0
      src/services/gameData/roleOperate.ts

+ 13 - 0
config/routerConfig.ts

@@ -118,6 +118,19 @@ const gameDataStatistics = {
                 }
             ]
         },
+        {
+            path: '/gameDataStatistics/roleOperate',
+            name: '角色运营管理',
+            access: 'roleOperate',
+            routes: [
+                {
+                    path: '/gameDataStatistics/roleOperate/roleRechargeRanking',
+                    name: '角色充值排行榜',
+                    access: 'roleRechargeRanking',
+                    component: './gameDataStatistics/roleOperate/roleRechargeRanking',
+                },
+            ]
+        },
         {
             path: '/gameDataStatistics/extensionData',
             name: '推广数据',

+ 2 - 0
src/app.tsx

@@ -16,6 +16,7 @@ import { ReactComponent as GameSvg } from '@/assets/game.svg'
 import { ReactComponent as GameServerSvg } from '@/assets/gameServer.svg'
 import { ReactComponent as MediaSvg } from '@/assets/media.svg'
 import { ReactComponent as PlayerSvg } from '@/assets/player.svg'
+import { ReactComponent as RoleManageSvg } from '@/assets/roleManage.svg'
 import versions from './utils/versions';
 
 
@@ -109,6 +110,7 @@ const IconMap = {
     gameServer: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><GameServerSvg /></span>,
     media: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MediaSvg /></span>,
     player: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><PlayerSvg /></span>,
+    roleManage: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RoleManageSvg /></span>,
     eye: <EyeOutlined />
 };
 //处理菜单

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
src/assets/roleManage.svg


+ 125 - 3
src/components/QueryForm/index.tsx

@@ -2,7 +2,7 @@ import { Button, Col, DatePicker, Form, Input, Radio, Row, Select, Space } from
 import React, { useEffect, useState } from "react"
 import moment from "moment"
 import { useAjax } from "@/Hook/useAjax"
-import { getAllOfOwnerUserApi, getChannelChoiceListApi, getGameChoiceListApi, getCpChoiceListApi, getGameChoiceParentListType1Api, getPayListApi, getSubUserWithSelfListApi, getTtAllUserListApi, getUserSystemTypeChoiceListApi, getUserVipLevelChoiceListApi } from "@/services/gameData"
+import { getAllOfOwnerUserApi, getChannelChoiceListApi, getGameChoiceListApi, getCpChoiceListApi, getGameChoiceParentListType1Api, getPayListApi, getSubUserWithSelfListApi, getTtAllUserListApi, getUserSystemTypeChoiceListApi, getUserVipLevelChoiceListApi, getRoleUserListApi } from "@/services/gameData"
 import { ActiveEnum, PayStatus, TYPE, gameClassifyEnum } from "./const"
 import { ADSTATUSEnum as ADSTTTATUSEnum } from "@/pages/gameDataStatistics/adlist/monitor/const"
 import { ADSTATUSEnum } from "@/pages/gameDataStatistics/adlist/tencentMonitor/const"
@@ -174,6 +174,17 @@ interface Props {
     isCreateRole?: boolean
     /** 是否开启 角色VIP 搜索 */
     isVipLevel?: boolean
+    /** 是否开启 是否转端 搜索 */
+    isIsChange?: boolean
+    /** 是否开启 邮件是否发送 搜索 */
+    isIsSendMail?: boolean
+    /** 是否开启 企业微信号 搜索 */
+    isWeChatCompany?: boolean
+    /** 是否开启 企业微信号 搜索 */
+    isWeChat?: boolean
+    isCustomerServerId?: boolean
+    isOperatorId?: boolean
+    isGsId?: boolean
 }
 /**
  * 游戏数据系统 请求参数
@@ -186,11 +197,15 @@ const QueryForm: React.FC<Props> = (props) => {
         onChange, initialValues, isSource, isAccount, isAccountId, isCompanyId, isAgentKey, isAgentName, isCpId, isCpName, isCpOrderId, isCpStatus, isCreateDay, isDevice, isGameName, isRechargeGameName, isGameId, isGameIds, isOrderGameId, isGameRoleId,
         isGameRoleName, isFirstRecharge, isSwitch, isMerchantNo, isOrderId, isMerchantOrderNo, isPayStatus, isPayWay, isProductName, isRegAgent, isAgentId, isPutAgent, isRegDay, isOs, isParentId, isProjectId, isProjectName, isPromotionId, isPromotionName,
         isSysUserName, isRechargeDate, isBGGameClassify, isGameUserId, isSysUserId, isUserName, isUserId, isSelectRanking, isGameType, isConsumeDay, rechargeDay, isBeginDay, isType, isAdTTStatus, isUserEnterType, isServerName, isServerId, isServerDay, isAdTXStatus,
-        payTimeDay, placeAnOrderDay, isPayIntervalTime, isActiveTypes, isNickname, isMobile, isRegIp, isIsAuth, isIsBindMobile, isIsRecharge, isUserStatus, isCreateRole, isRoleCount, isVipLevel, isCreateRoleDay
+        payTimeDay, placeAnOrderDay, isPayIntervalTime, isActiveTypes, isNickname, isMobile, isRegIp, isIsAuth, isIsBindMobile, isIsRecharge, isUserStatus, isCreateRole, isRoleCount, isVipLevel, isCreateRoleDay, isIsChange, isIsSendMail, isWeChatCompany, isWeChat,
+        isCustomerServerId, isOperatorId, isGsId
     } = props
     const [form] = Form.useForm()
     const [accountList, setAccountList] = useState<any[]>([])
     const [userIdList, setUserIdList] = useState<any[]>([])
+    const [gsList, setGsList] = useState<any[]>([])
+    const [customerList, setCustomerList] = useState<any[]>([])
+    const [operateList, setOperateList] = useState<any[]>([])
 
     const getAllOfOwnerUser = useAjax(() => getAllOfOwnerUserApi())
     const getTtAllUserList = useAjax(() => getTtAllUserListApi())
@@ -202,8 +217,26 @@ const QueryForm: React.FC<Props> = (props) => {
     const getCpChoiceList = useAjax(() => getCpChoiceListApi())
     const getPayList = useAjax(() => getPayListApi())
     const getUserVipLevelChoiceList = useAjax(() => getUserVipLevelChoiceListApi())
+    const getRoleUserList = useAjax((params) => getRoleUserListApi(params))
     /**************************/
 
+    useEffect(() => {
+        if (isCustomerServerId || isOperatorId || isGsId) {
+            const getList = async () => {
+                // 游戏GS
+                let gs = await getRoleUserList.run({ authType: 'GS' })
+                setGsList(gs ? Object.keys(gs)?.map(key => ({ userId: key, nickname: gs[key] })) : [])
+                // 运营
+                let operate = await getRoleUserList.run({ authType: 'OPERATE' })
+                setOperateList(operate ? Object.keys(operate)?.map(key => ({ userId: key, nickname: operate[key] })) : [])
+                // 客服
+                let customer = await getRoleUserList.run({ authType: 'CUSTOMER' })
+                setCustomerList(customer ? Object.keys(customer)?.map(key => ({ userId: key, nickname: customer[key] })) : [])
+            }
+            getList()
+        }
+    }, [isCustomerServerId, isOperatorId, isGsId])
+
     useEffect(() => {
         if (isAccountId) {
             // 请求广告账号列表
@@ -652,10 +685,22 @@ const QueryForm: React.FC<Props> = (props) => {
             {isGameRoleName && <Col><Form.Item name='roleName'>
                 <Input placeholder="游戏角色名" allowClear style={{ width: 140 }} />
             </Form.Item></Col>}
+
             {/* 游戏角色名id搜索 */}
             {isGameRoleId && <Col><Form.Item name='roleId'>
                 <Input placeholder="角色ID" allowClear style={{ width: 140 }} />
             </Form.Item></Col>}
+
+            {/* 客户微信号 */}
+            {isWeChat && <Col><Form.Item name='weChat'>
+                <Input placeholder="客户微信号" allowClear style={{ width: 140 }} />
+            </Form.Item></Col>}
+
+            {/* 客户微信号 */}
+            {isWeChatCompany && <Col><Form.Item name='weChatCompany'>
+                <Input placeholder="企业微信号" allowClear style={{ width: 140 }} />
+            </Form.Item></Col>}
+
             {/* 是否首充 */}
             {isFirstRecharge && <Col><Form.Item name='isFirstRecharge'>
                 <Select
@@ -808,6 +853,51 @@ const QueryForm: React.FC<Props> = (props) => {
                     {userIdList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
                 </Select>
             </Form.Item></Col>}
+            {/* 客服ID */}
+            {isCustomerServerId && <Col><Form.Item name='customerServerId'>
+                <Select
+                    maxTagCount={1}
+                    showSearch
+                    style={{ width: 120 }}
+                    allowClear
+                    placeholder={'请选择客服'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {customerList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                </Select>
+            </Form.Item></Col>}
+            {/* 运营ID */}
+            {isOperatorId && <Col><Form.Item name='operatorId'>
+                <Select
+                    maxTagCount={1}
+                    showSearch
+                    style={{ width: 120 }}
+                    allowClear
+                    placeholder={'请选择运营'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {operateList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                </Select>
+            </Form.Item></Col>}
+            {/* GSID */}
+            {isGsId && <Col><Form.Item name='gsId'>
+                <Select
+                    maxTagCount={1}
+                    showSearch
+                    style={{ width: 120 }}
+                    allowClear
+                    placeholder={'请选择GS'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {gsList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                </Select>
+            </Form.Item></Col>}
 
             {/* 玩家账号 */}
             {isUserName && <Col><Form.Item name='username'>
@@ -911,7 +1001,7 @@ const QueryForm: React.FC<Props> = (props) => {
                     <Select.Option value={'3'}>冻结</Select.Option>
                 </Select>
             </Form.Item></Col>}
-            
+
             {/* 是否创角 */}
             {isCreateRole && <Col><Form.Item name='createRole'>
                 <Select
@@ -928,6 +1018,38 @@ const QueryForm: React.FC<Props> = (props) => {
                 </Select>
             </Form.Item></Col>}
 
+            {/* 是否转端 */}
+            {isIsChange && <Col><Form.Item name='isChange'>
+                <Select
+                    showSearch
+                    allowClear
+                    style={{ width: 98 }}
+                    placeholder={'是否转端'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    <Select.Option value="1">是</Select.Option>
+                    <Select.Option value="0">不转端</Select.Option>
+                </Select>
+            </Form.Item></Col>}
+
+            {/* 邮件是否发送 */}
+            {isIsSendMail && <Col><Form.Item name='isSendMail'>
+                <Select
+                    showSearch
+                    allowClear
+                    style={{ width: 98 }}
+                    placeholder={'邮件是否发送'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    <Select.Option value="1">发送</Select.Option>
+                    <Select.Option value="0">不发送</Select.Option>
+                </Select>
+            </Form.Item></Col>}
+
             {/* vip等级 */}
             {isCreateRole && <Col><Form.Item name='vipLevel'>
                 <Select

+ 4 - 0
src/global.less

@@ -333,4 +333,8 @@ body {
 
 .ant-table-tbody > tr.ant-table-row:hover > td {
   background-color: #e9e9e9 !important;
+}
+
+.minHeight .ant-form-item-control-input {
+  min-height: auto;
 }

+ 9 - 4
src/pages/gameDataStatistics/components/TableData/index.tsx

@@ -22,6 +22,7 @@ interface Prosp {
     className?: string,//自定义class
     isdownload?: boolean,
     leftChild?: JSX.Element,
+    czChild?: JSX.Element,
     config?: any,
     configName?: any,
     page?: number,
@@ -51,6 +52,8 @@ interface Prosp {
     estimatedRowHeight?: number | (() => number)
     headerHeight?: number
     isVirtually?: boolean
+    rowSelection?: any
+
 }
 
 export const version = '1.0.0'
@@ -58,7 +61,7 @@ export const version = '1.0.0'
 function TableData(props: Prosp) {
 
     /*************************/
-    const { isZj, totalData, scroll, title, dataSource, expandedRowRender, isVirtually = true, sortData, headerHeight, className, rowClassName, leftChild, page = undefined, pageSize = undefined, size = 'small', total = 0, onChange, config, configName, ajax, fixed = { left: 0, right: 1 }, summary, estimatedRowHeight } = props
+    const { isZj, totalData, czChild, scroll, title, dataSource, expandedRowRender, isVirtually = true, rowSelection, sortData, headerHeight, className, rowClassName, leftChild, page = undefined, pageSize = undefined, size = 'small', total = 0, onChange, config, configName, ajax, fixed = { left: 0, right: 1 }, summary, estimatedRowHeight } = props
     const [visible, setVisible] = useState<boolean>(false)
     const [isFullscreen, setIsFullscreen] = useState<boolean>(true)
     const [oldSelectData, setoldSelectData] = useState<any[]>([])
@@ -91,7 +94,7 @@ function TableData(props: Prosp) {
             }
         }
     }, [originalColumns, sortData])
-    
+
 
     const { run: runSet } = useThrottleFn((newArr, newConfig, fixedData) => {
         console.log('设置配置改变重新赋值')
@@ -281,7 +284,8 @@ function TableData(props: Prosp) {
                 </div>
                 <Row gutter={[0, 16]}>
                     {header}
-                    <Tab {...{ size, newColumns, handelResize, className, isZj, totalData, rowClassName, scroll, isFull, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, summary }} />
+                    {<Col span={24}>{czChild && czChild}</Col>}
+                    <Tab {...{ size, newColumns, handelResize, className, isZj, totalData, rowSelection, rowClassName, scroll, isFull, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, summary }} />
                 </Row>
             </Card>
         </Col>
@@ -320,7 +324,7 @@ function TableData(props: Prosp) {
 
 /**表格 */
 const Tab = React.memo((props: any) => {
-    const { size, newColumns, className, handelResize, scroll, isFull, page, isZj, rowClassName, pageSize, totalData, dataSource, onChange, expandedRowRender, total, ajax, summary } = props
+    const { size, newColumns, className, handelResize, scroll, isFull, page, isZj, rowSelection, rowClassName, pageSize, totalData, dataSource, onChange, expandedRowRender, total, ajax, summary } = props
     let ran = Math.ceil(Math.random() * 100)
 
     useEffect(() => {
@@ -374,6 +378,7 @@ const Tab = React.memo((props: any) => {
                 bordered
                 sortDirections={['ascend', 'descend', null]}
                 current={page}
+                rowSelection={rowSelection}
                 pageSize={pageSize}
                 columns={newColumns}
                 dataSource={dataSource}

+ 164 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/assign.tsx

@@ -0,0 +1,164 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getRoleUserListApi } from "@/services/gameData"
+import { addAssignUserApi, editAssignUserApi } from "@/services/gameData/roleOperate"
+import { Col, DatePicker, Form, Input, Modal, Row, Select, Space, message } from "antd"
+import React, { useEffect, useState } from "react"
+import moment from "moment"
+
+interface Props {
+    data: any[]
+    id?: number,
+    startTime?: string,
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+const Assign: React.FC<Props> = ({ startTime, id, visible, data, onClose, onChange }) => {
+
+    /******************************************/
+    const [gsList, setGsList] = useState<any[]>([])
+    const [customerList, setCustomerList] = useState<any[]>([])
+    const [operateList, setOperateList] = useState<any[]>([])
+    const [form] = Form.useForm()
+    const getRoleUserList = useAjax((params) => getRoleUserListApi(params))
+    const addAssignUser = useAjax((params) => addAssignUserApi(params))
+    const editAssignUser = useAjax((params) => editAssignUserApi(params))
+    /******************************************/
+
+    useEffect(() => {
+        if (data?.length === 1) {
+            const { customer_service_id, oper_user_id, gs_id } = data[0]
+            console.log('----->', startTime)
+            let time = startTime ? moment(startTime) : undefined
+            form.setFieldsValue({ startTime: time, operUserId: oper_user_id?.toString(), customerServiceId: customer_service_id?.toString(), gsId: gs_id?.toString() })
+        }
+    }, [data])
+
+    useEffect(() => {
+        const getList = async () => {
+            // 游戏GS
+            let gs = await getRoleUserList.run({ authType: 'GS' })
+            setGsList(gs ? Object.keys(gs)?.map(key => ({ userId: key, nickname: gs[key] })) : [])
+            // 运营
+            let operate = await getRoleUserList.run({ authType: 'OPERATE' })
+            setOperateList(operate ? Object.keys(operate)?.map(key => ({ userId: key, nickname: operate[key] })) : [])
+            // 客服
+            let customer = await getRoleUserList.run({ authType: 'CUSTOMER' })
+            setCustomerList(customer ? Object.keys(customer)?.map(key => ({ userId: key, nickname: customer[key] })) : [])
+        }
+        getList()
+    }, [])
+
+    const handleOk = async () => {
+        let validate = await form.validateFields()
+        const { startTime, ...par } = validate
+        let params: any = { startTime: moment(startTime).format('YYYY-MM-DD'), ...par }
+        let roleInfoAndAgentParamList = data.map(item => {
+            return {
+                gameId: item.user_reg_game_id,
+                roleId: item.role_id,
+                serverId: item.server_id,
+                userId: item.user_id,
+                regAgentId: item.agent_id
+            }
+        })
+        params.roleInfoAndAgentParamList = roleInfoAndAgentParamList
+        if (id) {
+            params.id = id
+            editAssignUser.run(params).then(res => {
+                if (res) {
+                    message.success('更新成功')
+                    onChange?.()
+                }
+            })
+        } else {
+            addAssignUser.run(params).then(res => {
+                if (res) {
+                    message.success('指派成功')
+                    onChange?.()
+                }
+            })
+        }
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>指派</strong>
+            <span style={{ color: 'red' }}>操作有延时,请勿重复提交!!!</span>
+        </Space>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        confirmLoading={addAssignUser.loading}
+    >
+        <Form
+            name="basicExportAssign"
+            form={form}
+            layout="vertical"
+            autoComplete="off"
+        >
+            <Row gutter={[20, 0]}>
+                <Col span={12}>
+                    <Form.Item label="开始时间" name="startTime" rules={[{ required: true, message: '请选择开始时间' }]}>
+                        <DatePicker style={{ width: '100%' }} />
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="运营" name="operUserId">
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ width: '100%' }}
+                            allowClear
+                            placeholder={'请选择运营'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {operateList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                        </Select>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="客服" name="customerServiceId">
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ width: '100%' }}
+                            allowClear
+                            placeholder={'请选择客服'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {customerList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                        </Select>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="游戏GS" name="gsId">
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ width: '100%' }}
+                            allowClear
+                            placeholder={'请选择游戏GS'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {gsList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                        </Select>
+                    </Form.Item>
+                </Col>
+                <Col span={24}>
+                    <Form.Item label="备注" name="remark">
+                        <Input.TextArea placeholder="请输入备注" />
+                    </Form.Item>
+                </Col>
+            </Row>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(Assign)

+ 160 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/changeLog.tsx

@@ -0,0 +1,160 @@
+import { useAjax } from "@/Hook/useAjax"
+import { GetAssignUser, delAssignUserApi, getAssignUserApi } from "@/services/gameData/roleOperate"
+import { Button, Drawer, Input, Popconfirm, Select, Space, Table, Tag, message } from "antd"
+import React, { useEffect, useState } from "react"
+import { columnsChangeLog } from "./tableConfig"
+import { getRoleUserListApi } from "@/services/gameData"
+import Assign from "./assign"
+
+
+interface Props {
+    data: any
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+const ChangeLog: React.FC<Props> = ({ data, visible, onClose, onChange }) => {
+
+    /******************************/
+    const [gsList, setGsList] = useState<any[]>([])
+    const [customerList, setCustomerList] = useState<any[]>([])
+    const [operateList, setOperateList] = useState<any[]>([])
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [queryForm, setQueryForm] = useState<GetAssignUser>({ pageNum: 1, pageSize: 20 })
+    const [assignvisible, setAssignVisible] = useState<boolean>(false)
+    const [id, setId] = useState<number>()
+    const [startTime, setStartTime] = useState<string>()
+    const getAssignUser = useAjax((params) => getAssignUserApi(params))
+    const delAssignUser = useAjax((params) => delAssignUserApi(params))
+    const getRoleUserList = useAjax((params) => getRoleUserListApi(params))
+    /******************************/
+
+    useEffect(() => {
+        const getList = async () => {
+            // 游戏GS
+            let gs = await getRoleUserList.run({ authType: 'GS' })
+            setGsList(gs ? Object.keys(gs)?.map(key => ({ userId: key, nickname: gs[key] })) : [])
+            // 运营
+            let operate = await getRoleUserList.run({ authType: 'OPERATE' })
+            setOperateList(operate ? Object.keys(operate)?.map(key => ({ userId: key, nickname: operate[key] })) : [])
+            // 客服
+            let customer = await getRoleUserList.run({ authType: 'CUSTOMER' })
+            setCustomerList(customer ? Object.keys(customer)?.map(key => ({ userId: key, nickname: customer[key] })) : [])
+        }
+        getList()
+    }, [])
+
+    useEffect(() => {
+        getAssignUser.run({
+            ...queryForm,
+            gameId: data.user_reg_game_id,
+            roleId: data.role_id,
+            serverId: data.server_id,
+            userId: data.user_id,
+            regAgentId: data.agent_id
+        })
+    }, [queryForm])
+
+
+    const del = (ids: any[]) => {
+        delAssignUser.run(ids.toString()).then(res => {
+            if (res) {
+                onChange?.()
+                setSelectedRowKeys([])
+                getAssignUser.refresh()
+                message.success('删除成功')
+            }
+        })
+    }
+
+    const update = (data: any) => {
+        setId(data.id)
+        setStartTime(data.startTime)
+        setAssignVisible(true)
+    }
+
+    return <Drawer
+        title="变更记录"
+        visible={visible}
+        onClose={onClose}
+        width={'90%'}
+    >
+        <Space style={{ width: '100%', marginBottom: 10 }}>
+            <Select
+                showSearch
+                style={{ width: 120 }}
+                allowClear
+                placeholder={'请选择运营'}
+                value={queryForm?.operUserId}
+                onChange={(e) => setQueryForm({ ...queryForm, operUserId: e, pageNum: 1 })}
+                filterOption={(input, option) =>
+                    (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+            >
+                {operateList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+            </Select>
+            <Select
+                showSearch
+                style={{ width: 120 }}
+                allowClear
+                placeholder={'请选择客服'}
+                value={queryForm?.customerServiceId}
+                onChange={(e) => setQueryForm({ ...queryForm, customerServiceId: e, pageNum: 1 })}
+                filterOption={(input, option) =>
+                    (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+            >
+                {customerList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+            </Select>
+            <Select
+                showSearch
+                style={{ width: 120 }}
+                allowClear
+                value={queryForm?.gsId}
+                onChange={(e) => setQueryForm({ ...queryForm, gsId: e, pageNum: 1 })}
+                placeholder={'请选择游戏GS'}
+                filterOption={(input, option) =>
+                    (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                }
+            >
+                {gsList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+            </Select>
+            <Input placeholder="备注" allowClear value={queryForm?.remark} onChange={(e) => setQueryForm({ ...queryForm, remark: e.target.value, pageNum: 1 })} />
+        </Space>
+        <Table
+            dataSource={getAssignUser?.data?.records}
+            columns={columnsChangeLog(update, del)}
+            loading={getAssignUser.loading}
+            scroll={{ x: 380, y: 300 }}
+            className="uuTable"
+            rowKey={'id'}
+            pagination={
+                {
+                    total: getAssignUser?.data?.total || 0,//总共多少条数据,服务器给,设置后分页自动计算页数
+                    current: getAssignUser?.data?.current || 1,//当前页数,需state控制配合翻页
+                    pageSize: getAssignUser?.data?.size || 20,
+                    defaultCurrent: 1,//默认初始的当前页数
+                    pageSizeOptions: ['10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
+                    showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
+                    showSizeChanger: true, //手动开启条数筛选器,默认超过50条开启
+                    size: 'small',//设置分页尺寸
+                    onChange: (page: number, pageSize: number) => {
+                        setQueryForm({ ...queryForm, pageNum: page, pageSize })
+                    },
+                    showLessItems: true
+                }
+            }
+            rowSelection={{
+                selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                },
+            }}
+        />
+
+        {/* 指派 */}
+        {assignvisible && <Assign id={id} startTime={startTime} visible={assignvisible} data={[data]} onClose={() => setAssignVisible(false)} onChange={() => { setAssignVisible(false); getAssignUser?.refresh(); }}/>}
+    </Drawer>
+}
+
+export default React.memo(ChangeLog)

+ 7 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/index.less

@@ -0,0 +1,7 @@
+.badge .ant-badge-status-text {
+    font-size: 12px;
+}
+
+.uuTable .ant-pagination {
+    margin: 3px 0 0;
+}

+ 185 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/index.tsx

@@ -0,0 +1,185 @@
+import { useAjax } from "@/Hook/useAjax"
+import { RoleRechargeRankingProps, getRoleRechargeRankingListApi } from "@/services/gameData/roleOperate"
+import React, { useEffect, useState } from "react"
+import moment from "moment"
+import TableData from "../../components/TableData"
+import QueryForm from "@/components/QueryForm"
+import { getPresetsRanking } from "@/components/QueryForm/const"
+import columns12 from "./tableConfig"
+import SendMail from "./sendMail"
+import SendPack from "./sendPack"
+import { Button, Space } from "antd"
+import RoleCz from "./roleCz"
+import Assign from "./assign"
+import ChangeLog from "./changeLog"
+
+let ajax: any = null
+const RoleRechargeRanking: React.FC = () => {
+
+
+    /**********************************/
+    const [queryForm, setQueryForm] = useState<RoleRechargeRankingProps>({ pageNum: 1, pageSize: 20, sourceSystem: 'ZX_ONE' })
+    const [data, setData] = useState<any[]>([])
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [sendEmailvisible, setSendEmailVisible] = useState<boolean>(false)
+    const [sendPackvisible, setSendPackVisible] = useState<boolean>(false)
+    const [assignvisible, setAssignVisible] = useState<boolean>(false)
+    const [changeLogVisible, setChangeLogVisible] = useState<boolean>(false)
+    const [czvisible, setCzVisible] = useState<boolean>(false)
+    const getRoleRechargeRankingList = useAjax((params) => getRoleRechargeRankingListApi(params))
+    ajax = getRoleRechargeRankingList
+    /**********************************/
+
+    useEffect(() => {
+        getRoleRechargeRankingList.run(queryForm)
+    }, [queryForm])
+
+
+    const sendEmail = (data: any[]) => {
+        setData(data)
+        setSendEmailVisible(true)
+    }
+
+    const sendPack = (data: any[]) => {
+        setData(data)
+        setSendPackVisible(true)
+    }
+
+    const handleIsTrue = (value: string, data: any, type: string) => {
+        switch (type) {
+            case 'isChangeGameType': // 是否转端
+                break
+            case 'isAddCorpWechat': // 是否添加企微
+                break
+            case 'isRemoveGame': // 是否退游
+                break
+            case 'isWakeUp': // 是否唤醒
+                break
+        }
+    }
+
+    const handleSave = (row: any) => {
+        // const hide = message.loading(`广告“${row.adgroupId}”广告名称修改成<${row.adgroupName}>,修改中`, 0, () => {
+        //     message.success('修改成功');
+        // });
+        // editAdqAdgroups.run({ adgroupIds: [row.adgroupId], adgroupName: row.adgroupName }).then(res => {
+        //     message.success('修改广告名称成功')
+        //     listAjax.refresh()
+        //     hide()
+        // })
+    }
+
+    const roleHandle = (data: any[]) => {
+        setData(data)
+        setCzVisible(true)
+    }
+
+    // 指派
+    const assignHandle = (data: any[]) => {
+        setData(data)
+        setAssignVisible(true)
+    }
+
+    const changeLog = (data: any[]) => {
+        setData(data)
+        setChangeLogVisible(true)
+    }
+
+    return <div>
+        <TableData
+            czChild={<Space>
+                <Button type="primary" disabled={selectedRowKeys.length === 0} onClick={() => sendPack(selectedRowKeys)}>批量发送礼包</Button>
+                <Button type="primary" disabled={selectedRowKeys.length === 0} onClick={() => sendEmail(selectedRowKeys)}>批量发送邮件</Button>
+                <Button type="primary" disabled={selectedRowKeys.length === 0} onClick={() => roleHandle(selectedRowKeys)}>批量角色操作</Button>
+                <Button type="primary" disabled={selectedRowKeys.length === 0} onClick={() => assignHandle(selectedRowKeys)}>批量指派</Button>
+            </Space>}
+            leftChild={<QueryForm
+                initialValues={{ sourceSystem: 'ZX_ONE' }}
+                onChange={(data: any) => {
+                    console.log(data)
+                    const { rechargeDay, createRoleDay, parentId, mobile, ...par } = data
+                    let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                    newQueryForm.pageNum = 1
+                    newQueryForm.parentGameId = parentId
+                    newQueryForm.phone = mobile
+                    if (rechargeDay && rechargeDay?.length === 2) {
+                        newQueryForm['rechargeBeginDate'] = moment(rechargeDay[0]).format('YYYY-MM-DD')
+                        newQueryForm['rechargeEndDate'] = moment(rechargeDay[1]).format('YYYY-MM-DD')
+                    } else {
+                        delete newQueryForm['rechargeBeginDate']
+                        delete newQueryForm['rechargeEndDate']
+                    }
+                    if (createRoleDay && createRoleDay?.length === 2) {
+                        newQueryForm['createRoleBeginDate'] = moment(createRoleDay[0]).format('YYYY-MM-DD')
+                        newQueryForm['createRoleEndDate'] = moment(createRoleDay[1]).format('YYYY-MM-DD')
+                    } else {
+                        delete newQueryForm['createRoleBeginDate']
+                        delete newQueryForm['createRoleEndDate']
+                    }
+                    setQueryForm({ ...newQueryForm, ...par })
+                }}
+                isSource
+                rechargeDay={{ ranges: getPresetsRanking() }}
+                isCreateRoleDay={{ ranges: getPresetsRanking() }}
+                isParentId
+                isGameId
+                isIsChange
+                isMobile
+                isSysUserId
+                isGameRoleName
+                isGameRoleId
+                isCreateRole
+                isWeChatCompany
+                isWeChat
+                isCustomerServerId
+                isOperatorId
+                isGsId
+            />}
+            scroll={{ x: 1000, y: 600 }}
+            isVirtually={false}
+            ajax={getRoleRechargeRankingList}
+            fixed={{ left: 1, right: 1 }}
+            dataSource={getRoleRechargeRankingList?.data?.records?.map((item: any, index: number) => ({ ...item, id: Number(queryForm.pageNum.toString() + index.toString()) }))}
+            page={getRoleRechargeRankingList?.data?.current || 1}
+            pageSize={getRoleRechargeRankingList?.data?.size || 20}
+            total={getRoleRechargeRankingList?.data?.total || 0}
+            title='角色充值排行榜'
+            onChange={(props: any) => {
+                console.log('props--->', props)
+                let { pagination, sortData } = props
+                let { current, pageSize } = pagination
+                let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                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 })
+            }}
+            rowSelection={{
+                selectedRowKeys: selectedRowKeys.map((item: any) => item?.id?.toString()),
+                onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => {
+                    setSelectedRowKeys(selectedRows)
+                },
+            }}
+            config={columns12(sendEmail, sendPack, handleIsTrue, handleSave, roleHandle, assignHandle, changeLog)}
+            configName={'角色充值排行榜'}
+        />
+        {/* 发送邮件 */}
+        {sendEmailvisible && <SendMail visible={sendEmailvisible} data={data} onClose={() => { setSendEmailVisible(false) }} onChange={() => { setSendEmailVisible(false); ajax?.refresh(); setSelectedRowKeys([]) }} />}
+        {/* 发送礼包 */}
+        {sendPackvisible && <SendPack visible={sendPackvisible} data={data} onClose={() => setSendPackVisible(false)} onChange={() => { setSendPackVisible(false); ajax?.refresh(); setSelectedRowKeys([]) }} />}
+        {/* 角色操作 */}
+        {czvisible && <RoleCz visible={czvisible} data={data} onClose={() => setCzVisible(false)} onChange={() => { setCzVisible(false); ajax?.refresh(); setSelectedRowKeys([]) }} />}
+        {/* 指派 */}
+        {assignvisible && <Assign visible={assignvisible} data={data} onClose={() => setAssignVisible(false)} onChange={() => { setAssignVisible(false); ajax?.refresh(); setSelectedRowKeys([]) }} />}
+        {/* 变更记录 */}
+        {changeLogVisible && <ChangeLog data={data?.[0]} visible={changeLogVisible} onClose={() => setChangeLogVisible(false)} />}
+    </div>
+}
+
+export default RoleRechargeRanking

+ 56 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/inputUpdate.tsx

@@ -0,0 +1,56 @@
+import { Form, Input, InputRef } from "antd";
+import { Rule } from "antd/lib/form";
+import React, { useEffect, useRef, useState } from "react"
+
+interface Props {
+    title: string,
+    dataIndex: string
+    record: any;
+    handleSave: (data: any) => void
+    isNum?: boolean
+    rules?: Rule[]
+}
+
+const InputUpdate: React.FC<Props> = ({ title, dataIndex, record, handleSave, isNum }) => {
+    const [form] = Form.useForm();
+    const [editing, setEditing] = useState(false);
+    const inputRef = useRef<InputRef>(null);
+
+    useEffect(() => {
+        if (editing) {
+            inputRef.current!.focus({ cursor: 'all' });
+        }
+    }, [editing]);
+
+    const toggleEdit = () => {
+        setEditing(!editing);
+        form.setFieldsValue({ [dataIndex]: record[dataIndex] });
+    };
+
+    const save = async () => {
+        try {
+            const values = await form.validateFields();
+            if (values?.[dataIndex] !== (record as any)?.[dataIndex]) {
+                handleSave({ ...record, ...values });
+            }
+            toggleEdit();
+        } catch (errInfo) {
+            console.log('Save failed:', errInfo);
+        }
+    };
+
+    let childNode = editing ? (<Form form={form}><Form.Item
+        style={{ margin: 0 }}
+        className="minHeight"
+        name={dataIndex}
+    >
+        <Input ref={inputRef} size="small" onPressEnter={save} onBlur={save} bordered style={{ fontSize: 12 }}  placeholder="请输入"/>
+    </Form.Item></Form>) : (<span className="editable-cell-value-wrap ellipsisText" style={{ cursor: 'text' }} onClick={toggleEdit}>
+        {title || '--'}
+    </span>)
+
+    return childNode;
+}
+
+
+export default React.memo(InputUpdate)

+ 32 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/isTrue.tsx

@@ -0,0 +1,32 @@
+import { DownOutlined } from "@ant-design/icons";
+import { Dropdown, Menu, Space, Typography } from "antd";
+import React from "react"
+
+interface Props {
+    value: '1' | '0'
+    onChange?: (value: string) => void
+}
+/**
+ * 是否组件
+ * @returns 
+ */
+const IsTrue: React.FC<Props> = ({ value, onChange }) => {
+
+    const menu = (
+        <Menu selectedKeys={[value]} onClick={({ key }) => { onChange?.(key) }}>
+            <Menu.Item key='1' disabled={value === '1'}>是</Menu.Item>
+            <Menu.Item key='0' disabled={value === '0'}>否</Menu.Item>
+        </Menu>
+    );
+
+    return <Dropdown overlay={menu} trigger={['click']}>
+        <Typography.Link>
+            <Space style={{ fontSize: 12 }} size={4}>
+                {{ '1': '是', '0': '否' }[value]}
+                <DownOutlined style={{ fontSize: 12 }} />
+            </Space>
+        </Typography.Link>
+    </Dropdown>
+}
+
+export default React.memo(IsTrue)

+ 161 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/roleCz.tsx

@@ -0,0 +1,161 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getSubUserWithSelfListApi } from "@/services/gameData"
+import { modifyRoleDataApi } from "@/services/gameData/roleOperate"
+import { Col, Form, Input, Modal, Radio, Row, Select, Space, message } from "antd"
+import React, { useEffect, useState } from "react"
+
+interface Props {
+    data: any[]
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+/**
+ * 角色操作
+ * @returns 
+ */
+const RoleCz: React.FC<Props> = ({ data = [], visible, onClose, onChange }) => {
+
+    /**************************/
+    const [form] = Form.useForm()
+    const [userIdList, setUserIdList] = useState<any[]>([])
+
+    const getSubUserWithSelfList = useAjax(() => getSubUserWithSelfListApi())
+    const modifyRoleData = useAjax((params) => modifyRoleDataApi(params))
+    /**************************/
+
+    useEffect(() => {
+        if (data?.length === 1) {
+            const { user_wechat, user_phone, is_remove_game, is_wake_up ,remark, add_corp_user_id, is_add_corp_wechat, is_change_game_type} = data[0]
+            form.setFieldsValue({
+                userWechat: user_wechat,
+                userPhone: user_phone,
+                isRemoveGame: is_remove_game || '0',
+                isWakeUp: is_wake_up || '0',
+                remark,
+                add_corp_user_id,
+                isAddCorpWechat: is_add_corp_wechat || '0',
+                isChangeGameType: is_change_game_type || '0'
+            })
+        }
+    }, [data])
+
+    /** 投手列表 */
+    useEffect(() => {
+        getSubUserWithSelfList.run().then(res => {
+            setUserIdList(res ? Object.keys(res)?.map(key => ({ userId: key, nickname: res[key] })) : [])
+        })
+    }, [])
+
+    const handleOk = async () => {
+        let validate = await form.validateFields()
+        let params: any = { ...validate }
+        let roleInfoList = data.map(item => {
+            return {
+                gameId: item.user_reg_game_id,
+                roleId: item.role_id,
+                serverId: item.server_id,
+                userId: item.user_id
+            }
+        })
+        params.roleInfoList = roleInfoList
+        console.log('validate--->', params)
+        modifyRoleData.run(params).then(res => {
+            if (res) {
+                message.success('成功')
+                onChange?.()
+            }
+        })
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>角色操作</strong>
+            <span style={{ color: 'red' }}>操作有延时,请勿重复提交!!!</span>
+        </Space>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        width={900}
+        confirmLoading={modifyRoleData.loading}
+    >
+        <Form
+            name="basicExportRoleCz"
+            form={form}
+            layout="vertical"
+            autoComplete="off"
+            initialValues={{ isAddCorpWechat: '0', isChangeGameType: '0', isRemoveGame: '0', isWakeUp: '0' }}
+        >
+            <Row gutter={[20, 0]}>
+                <Col span={12}>
+                    <Form.Item label="客户微信" name="userWechat">
+                        <Input placeholder="请输入客户微信" />
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="客户企微号" name="addCorpUserId">
+                        <Input placeholder="请输入客户企微号" />
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="客户手机" name="userPhone">
+                        <Input placeholder="请输入客户手机" />
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item label="投手" name='putUserId'>
+                        <Select
+                            showSearch
+                            allowClear
+                            placeholder={'请选择投手'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {userIdList.map((item: any) => <Select.Option value={item.userId} key={item.userId}>{item.nickname}</Select.Option>)}
+                        </Select>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item name='isAddCorpWechat' label="是否添加企微">
+                        <Radio.Group>
+                            <Radio value={'1'}>已添加企微</Radio>
+                            <Radio value={'0'}>未添加企微</Radio>
+                        </Radio.Group>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item name='isChangeGameType' label="是否转端">
+                        <Radio.Group>
+                            <Radio value={'1'}>已转端</Radio>
+                            <Radio value={'0'}>未转端</Radio>
+                        </Radio.Group>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item name='isRemoveGame' label="是否退游">
+                        <Radio.Group>
+                            <Radio value={'1'}>已退游</Radio>
+                            <Radio value={'0'}>未退游</Radio>
+                        </Radio.Group>
+                    </Form.Item>
+                </Col>
+                <Col span={12}>
+                    <Form.Item name='isWakeUp' label="是否唤醒">
+                        <Radio.Group>
+                            <Radio value={'1'}>已唤醒</Radio>
+                            <Radio value={'0'}>未唤醒</Radio>
+                        </Radio.Group>
+                    </Form.Item>
+                </Col>
+                <Col span={24}>
+                    <Form.Item label="备注" name="remark">
+                        <Input.TextArea placeholder="请输入备注" />
+                    </Form.Item>
+                </Col>
+            </Row>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(RoleCz)

+ 31 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendEmailDetails.tsx

@@ -0,0 +1,31 @@
+import { Popover } from "antd"
+import React from "react"
+import SendEmailTable from "./sendEmailTable"
+
+
+interface Props {
+    data: any
+    onChange?: () => void
+}
+/**
+ * 发送Email详细
+ * @returns 
+ */
+const SendEmailDetails: React.FC<Props> = ({ data, onChange }) => {
+
+
+    return <Popover
+        title={<strong>发送邮件记录详情</strong>}
+        trigger="click"
+        placement="left"
+        destroyTooltipOnHide={true}
+        zIndex={101}
+        content={<div style={{ width: 650 }}>
+            <SendEmailTable data={data} />
+        </div>}
+    >
+        <a style={{ fontSize: 12 }}>详情</a>
+    </Popover>
+}
+
+export default React.memo(SendEmailDetails)

+ 122 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendEmailTable.tsx

@@ -0,0 +1,122 @@
+import { useAjax } from "@/Hook/useAjax"
+import { delSendEmailLogApi, getSendEmailListApi, getSendEmailLogProps } from "@/services/gameData/roleOperate"
+import { Button, DatePicker, Popconfirm, Space, Table, Tag, message } from "antd"
+import React, { useEffect, useState } from "react"
+import { columnsSendEmailLog } from "./tableConfig"
+import './index.less'
+import moment from "moment"
+import SendMail from "./sendMail"
+
+interface Props {
+    data: any
+    onChange?: () => void
+}
+const SendEmailTable: React.FC<Props> = ({ data, onChange }) => {
+
+    /*********************************/
+    const [queryForm, setQueryForm] = useState<getSendEmailLogProps>({ pageNum: 1, pageSize: 20 })
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [sendEmialvisible, setSendEmialVisible] = useState<boolean>(false)
+    const [ids, setIds] = useState<string[]>([])
+    const getSendEmailList = useAjax((params) => getSendEmailListApi(params))
+    const delSendEmailLog = useAjax((params) => delSendEmailLogApi(params))
+    /*********************************/
+
+    useEffect(() => {
+        getSendEmailList.run({
+            ...queryForm,
+            gameId: data.user_reg_game_id,
+            roleId: data.role_id,
+            serverId: data.server_id,
+            userId: data.user_id
+        })
+    }, [queryForm, data])
+
+    const del = (ids: any[]) => {
+        delSendEmailLog.run(ids.toString()).then(res => {
+            if (res) {
+                onChange?.()
+                getSendEmailList.refresh()
+                message.success('删除成功')
+            }
+        })
+    }
+
+    const update = (ids: any[]) => {
+        setIds(ids)
+        setSendEmialVisible(true)
+    }
+
+    return <Space style={{ width: '100%' }} direction="vertical">
+        <Space>
+            <DatePicker.RangePicker
+                size="small"
+                showTime
+                allowClear
+                placeholder={['发送开始时间', '发送结束时间']}
+                value={queryForm?.startDateTime && queryForm?.endDateTime ? [moment(queryForm?.startDateTime), moment(queryForm?.endDateTime)] as any : undefined}
+                onChange={(e) => {
+                    if (e && e.length === 2) {
+                        setQueryForm({ ...queryForm, pageNum: 1, startDateTime: moment(e[0]).format('YYYY-MM-DD HH:mm:ss'), endDateTime: moment(e[1]).format('YYYY-MM-DD HH:mm:ss') })
+                    } else {
+                        setQueryForm({ ...queryForm, startDateTime: undefined, endDateTime: undefined })
+                    }
+                }}
+            />
+            <Button type="primary" onClick={() => { update(selectedRowKeys) }} disabled={selectedRowKeys.length === 0}>批量更新</Button>
+            <Popconfirm
+                title="确定删除?"
+                disabled={selectedRowKeys.length === 0}
+                onConfirm={() => { del(selectedRowKeys) }}
+            >
+                <Button danger onClick={() => { }} disabled={selectedRowKeys.length === 0}>批量删除</Button>
+            </Popconfirm>
+        </Space>
+        <Table
+            dataSource={getSendEmailList?.data?.records}
+            columns={columnsSendEmailLog(update, del)}
+            loading={getSendEmailList.loading}
+            scroll={{ x: 380, y: 300 }}
+            className="uuTable"
+            rowKey={'id'}
+            pagination={
+                {
+                    total: getSendEmailList?.data?.total || 0,//总共多少条数据,服务器给,设置后分页自动计算页数
+                    current: getSendEmailList?.data?.current || 1,//当前页数,需state控制配合翻页
+                    pageSize: getSendEmailList?.data?.size || 20,
+                    defaultCurrent: 1,//默认初始的当前页数
+                    pageSizeOptions: ['10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
+                    showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
+                    showSizeChanger: true, //手动开启条数筛选器,默认超过50条开启
+                    size: 'small',//设置分页尺寸
+                    onChange: (page: number, pageSize: number) => {
+                        setQueryForm({ ...queryForm, pageNum: page, pageSize })
+                    },
+                    showLessItems: true
+                }
+            }
+            rowSelection={{
+                selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                },
+            }}
+        />
+
+        {/* 发送礼包 */}
+        {sendEmialvisible && <SendMail
+            visible={sendEmialvisible}
+            ids={ids as any}
+            data={[data]}
+            onClose={() => setSendEmialVisible(false)}
+            onChange={() => {
+                setSendEmialVisible(false);
+                getSendEmailList?.refresh();
+                setIds([])
+                setSelectedRowKeys([])
+            }}
+        />}
+    </Space>
+}
+
+export default React.memo(SendEmailTable)

+ 75 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendMail.tsx

@@ -0,0 +1,75 @@
+import { useAjax } from "@/Hook/useAjax"
+import { addOrUpdateEmailApi } from "@/services/gameData/roleOperate"
+import { DatePicker, Form, Modal, message } from "antd"
+import React from "react"
+import moment from "moment"
+
+interface Props {
+    data: any[]
+    ids?: number[];
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+/**
+ * 发送Email
+ * @returns 
+ */
+const SendMail: React.FC<Props> = ({ data = [], ids, visible, onClose, onChange }) => {
+
+
+    /************************************/
+    const [form] = Form.useForm()
+    const addOrUpdateEmail = useAjax((params) => addOrUpdateEmailApi(params))
+    /************************************/
+
+    const handleOk = async () => {
+        let validate = await form.validateFields()
+        const { sendTime, giftId } = validate
+        let params: any = { giftId, sendTime: moment(sendTime).format('YYYY-MM-DD HH:mm:ss') }
+        if (ids) {
+            params.ids = ids
+        }
+        let roleInfoList = data.map(item => {
+            return {
+                gameId: item.user_reg_game_id,
+                roleId: item.role_id,
+                serverId: item.server_id,
+                userId: item.user_id
+            }
+        })
+        params.roleInfoList = roleInfoList
+        addOrUpdateEmail.run(params).then(res => {
+            if (res) {
+                message.success('成功')
+                onChange?.()
+            }
+        })
+    }
+
+    return <Modal
+        title={`${ids && ids?.length > 0 ? '修改' : '发送'}邮件记录`}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        confirmLoading={addOrUpdateEmail.loading}
+    >
+        <Form
+            name="basicExportEmail"
+            form={form}
+            labelCol={{ span: 4 }}
+            wrapperCol={{ span: 20 }}
+            autoComplete="off"
+        >
+            <Form.Item
+                label="发送时间"
+                name="sendTime"
+                rules={[{ required: true, message: '请选择发送时间' }]}
+            >
+                <DatePicker style={{ width: '100%' }} showTime/>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(SendMail)

+ 99 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPack.tsx

@@ -0,0 +1,99 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getPackListApi } from "@/services/gameData"
+import { addOrUpdateGiftApi } from "@/services/gameData/roleOperate"
+import { DatePicker, Form, Modal, Select, message } from "antd"
+import React, { useEffect } from "react"
+import moment from "moment"
+
+interface Props {
+    data: any[]
+    ids?: number[];
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+/**
+ * 发送Email
+ * @returns 
+ */
+const SendPack: React.FC<Props> = ({ data = [], ids, visible, onClose, onChange }) => {
+
+
+    /************************************/
+    const [form] = Form.useForm()
+    const getPackList = useAjax((params) => getPackListApi(params))
+    const addOrUpdateGift = useAjax((params) => addOrUpdateGiftApi(params))
+    /************************************/
+
+    useEffect(() => {
+        getPackList.run({})
+    }, [data])
+
+    const handleOk = async () => {
+        let validate = await form.validateFields()
+        const { sendTime, giftId } = validate
+        let params: any = { giftId, sendTime: moment(sendTime).format('YYYY-MM-DD HH:mm:ss') }
+        if (ids) {
+            params.ids = ids
+        }
+        let roleInfoList = data.map(item => {
+            return {
+                gameId: item.user_reg_game_id,
+                roleId: item.role_id,
+                serverId: item.server_id,
+                userId: item.user_id
+            }
+        })
+        params.roleInfoList = roleInfoList
+        addOrUpdateGift.run(params).then(res => {
+            if (res) {
+                message.success('成功')
+                onChange?.()
+            }
+        })
+    }
+
+    return <Modal
+        title={`${ids && ids?.length > 0 ? '修改' : '发送'}礼包记录`}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        confirmLoading={addOrUpdateGift.loading}
+    >
+        <Form
+            name="basicExportPack"
+            form={form}
+            labelCol={{ span: 4 }}
+            wrapperCol={{ span: 20 }}
+            autoComplete="off"
+        >
+            <Form.Item
+                label="发送时间"
+                name="sendTime"
+                rules={[{ required: true, message: '请选择发送时间' }]}
+            >
+                <DatePicker style={{ width: '100%' }} showTime />
+            </Form.Item>
+            <Form.Item
+                label="礼包"
+                name="giftId"
+                rules={[{ required: true, message: '请选择礼包' }]}
+            >
+                <Select
+                    maxTagCount={1}
+                    showSearch
+                    style={{ minWidth: 140 }}
+                    allowClear
+                    placeholder={'请选择礼包'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                >
+                    {getPackList?.data?.map((item: { id: React.Key | null | undefined; giftName: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined }) => <Select.Option value={item.id} key={item.id}>{item.giftName}</Select.Option>)}
+                </Select>
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(SendPack)

+ 30 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPackDetails.tsx

@@ -0,0 +1,30 @@
+import { Popover } from "antd"
+import React from "react"
+import SendPackTable from "./sendPackTable"
+
+
+interface Props {
+    data: any
+    onChange?: () => void
+}
+/**
+ * 发送礼包详细
+ * @returns 
+ */
+const SendPackDetails: React.FC<Props> = ({ data, onChange }) => {
+
+    return <Popover
+        title={<strong>发送礼包记录详情</strong>}
+        trigger="click"
+        placement="left"
+        zIndex={101}
+        destroyTooltipOnHide={true}
+        content={<div style={{ width: 650 }}>
+            <SendPackTable data={data} />
+        </div>}
+    >
+        <a style={{ fontSize: 12 }}>详情</a>
+    </Popover>
+}
+
+export default React.memo(SendPackDetails)

+ 123 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/sendPackTable.tsx

@@ -0,0 +1,123 @@
+import { useAjax } from "@/Hook/useAjax"
+import { delSendGiftLogApi, getSendGiftLogListApi, getSendGiftLogProps } from "@/services/gameData/roleOperate"
+import { Button, DatePicker, Input, Popconfirm, Space, Table, Tag, message } from "antd"
+import React, { useEffect, useState } from "react"
+import { columnsSendPackLog } from "./tableConfig"
+import './index.less'
+import moment from "moment"
+import SendPack from "./sendPack"
+
+interface Props {
+    data: any
+    onChange?: () => void
+}
+const SendPackTable: React.FC<Props> = ({ data, onChange }) => {
+
+    /*********************************/
+    const [queryForm, setQueryForm] = useState<getSendGiftLogProps>({ pageNum: 1, pageSize: 20 })
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [sendPackvisible, setSendPackVisible] = useState<boolean>(false)
+    const [ids, setIds] = useState<string[]>([])
+    const getSendGiftLogList = useAjax((params) => getSendGiftLogListApi(params))
+    const delSendGiftLog = useAjax((params) => delSendGiftLogApi(params))
+    /*********************************/
+
+    useEffect(() => {
+        getSendGiftLogList.run({
+            ...queryForm,
+            gameId: data.user_reg_game_id,
+            roleId: data.role_id,
+            serverId: data.server_id,
+            userId: data.user_id
+        })
+    }, [queryForm, data])
+
+    const del = (ids: any[]) => {
+        delSendGiftLog.run(ids.toString()).then(res => {
+            if (res) {
+                onChange?.()
+                getSendGiftLogList.refresh()
+                message.success('删除成功')
+            }
+        })
+    }
+
+    const update = (ids: any[]) => {
+        setIds(ids)
+        setSendPackVisible(true)
+    }
+
+    return <Space style={{ width: '100%' }} direction="vertical">
+        <Space>
+            <Input placeholder="礼包名称" style={{ width: 120 }} allowClear value={queryForm.giftName} size="small" onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, giftName: e.target.value })} />
+            <DatePicker.RangePicker
+                size="small"
+                showTime
+                allowClear
+                placeholder={['发送开始时间', '发送结束时间']}
+                value={queryForm?.startDateTime && queryForm?.endDateTime ? [moment(queryForm?.startDateTime), moment(queryForm?.endDateTime)] as any : undefined}
+                onChange={(e) => {
+                    if (e && e.length === 2) {
+                        setQueryForm({ ...queryForm, pageNum: 1, startDateTime: moment(e[0]).format('YYYY-MM-DD HH:mm:ss'), endDateTime: moment(e[1]).format('YYYY-MM-DD HH:mm:ss') })
+                    } else {
+                        setQueryForm({ ...queryForm, startDateTime: undefined, endDateTime: undefined })
+                    }
+                }}
+            />
+            <Button type="primary" onClick={() => { update(selectedRowKeys) }} disabled={selectedRowKeys.length === 0}>批量更新</Button>
+            <Popconfirm
+                title="确定删除?"
+                disabled={selectedRowKeys.length === 0}
+                onConfirm={() => { del(selectedRowKeys) }}
+            >
+                <Button danger onClick={() => { }} disabled={selectedRowKeys.length === 0}>批量删除</Button>
+            </Popconfirm>
+        </Space>
+        <Table
+            dataSource={getSendGiftLogList?.data?.records}
+            columns={columnsSendPackLog(update, del)}
+            loading={getSendGiftLogList.loading}
+            scroll={{ x: 380, y: 300 }}
+            className="uuTable"
+            rowKey={'id'}
+            pagination={
+                {
+                    total: getSendGiftLogList?.data?.total || 0,//总共多少条数据,服务器给,设置后分页自动计算页数
+                    current: getSendGiftLogList?.data?.current || 1,//当前页数,需state控制配合翻页
+                    pageSize: getSendGiftLogList?.data?.size || 20,
+                    defaultCurrent: 1,//默认初始的当前页数
+                    pageSizeOptions: ['10', '20', '30', '40', '50', '60', '70', '80', '90', '100'],
+                    showTotal: (total) => <Tag color="cyan">总共{total}数据</Tag>,
+                    showSizeChanger: true, //手动开启条数筛选器,默认超过50条开启
+                    size: 'small',//设置分页尺寸
+                    onChange: (page: number, pageSize: number) => {
+                        setQueryForm({ ...queryForm, pageNum: page, pageSize })
+                    },
+                    showLessItems: true
+                }
+            }
+            rowSelection={{
+                selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                },
+            }}
+        />
+
+        {/* 发送礼包 */}
+        {sendPackvisible && <SendPack
+            visible={sendPackvisible}
+            ids={ids as any}
+            data={[data]}
+            onClose={() => setSendPackVisible(false)}
+            onChange={() => {
+                setSendPackVisible(false);
+                getSendGiftLogList?.refresh();
+                setIds([])
+                setSelectedRowKeys([])
+            }}
+        />}
+    </Space>
+}
+
+export default React.memo(SendPackTable)

+ 522 - 0
src/pages/gameDataStatistics/roleOperate/roleRechargeRanking/tableConfig.tsx

@@ -0,0 +1,522 @@
+import ProgressTable from "@/components/ProgressTable"
+import WidthEllipsis from "@/components/widthEllipsis"
+import { Badge, Popconfirm, Space, Statistic } from "antd"
+import React from "react"
+import SendEmailDetails from "./sendEmailDetails"
+import './index.less'
+import SendPackDetails from "./sendPackDetails"
+// import IsTrue from "./isTrue"
+// import InputUpdate from "./inputUpdate"
+
+function columns12(
+    sendEmail: (data: any[]) => void,
+    sendPack: (data: any[]) => void,
+    handleIsTrue: (value: string, data: any, type: string) => void,
+    handleSave: (data: any) => void,
+    roleHandle: (data: any[]) => void,
+    assignHandle: (data: any[]) => void,
+    changeLog: (data: any[]) => void
+) {
+
+    let newArr: { label: string, data: any[] }[] = [
+        {
+            label: '角色信息',
+            data: [
+                { title: '游戏', dataIndex: 'role_reg_parent_game_name', label: '角色信息', align: 'center', width: 70, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '游戏区服', dataIndex: 'server_name', label: '玩家信息', align: 'center', width: 90, default: 4, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '区服ID', dataIndex: 'server_id', label: '角色信息', align: 'center', width: 70, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '角色名称', dataIndex: 'role_name', label: '角色信息', align: 'center', width: 90, default: 2, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '角色创建时间', dataIndex: 'role_create_time', label: '角色信息', align: 'center', width: 140, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '角色VIP等级', dataIndex: 'vip_level', label: '角色信息', align: 'center', width: 60, default: 3, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '角色等级', dataIndex: 'role_level', label: '角色信息', align: 'center', width: 65, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '角色攻击力', dataIndex: 'combat_num', label: '角色信息', align: 'center', width: 70, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+            ],
+        },
+        {
+            label: '玩家信息',
+            data: [
+                { title: '玩家ID', dataIndex: 'user_id', label: '玩家信息', align: 'center', width: 70 },
+                { title: '玩家账号', dataIndex: 'username', label: '玩家信息', align: 'center', width: 120, default: 1, render: (a: string, b: any) => (<WidthEllipsis isCopy value={a} />) },
+                { title: '注册渠道', dataIndex: 'agent_name', label: '玩家信息', align: 'center', width: 80, default: 5, render: (a: string, b: any) => (<WidthEllipsis isCopy value={a} />) },
+                { title: '注册渠道ID', dataIndex: 'agent_id', label: '玩家信息', align: 'center', width: 80 },
+                { title: '注册时间', dataIndex: 'user_create_time', label: '玩家信息', align: 'center', width: 140, default: 6, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '注册游戏', dataIndex: 'user_reg_game_name', label: '玩家信息', align: 'center', width: 70, default: 7, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '玩家操作系统', dataIndex: 'os', label: '玩家信息', align: 'center', width: 70, default: 8 },
+                // { title: '玩家等级标签', dataIndex: '13', label: '玩家信息', align: 'center', width: 70, default: 14 },
+                { title: '最近充值游戏', dataIndex: 'user_last_recharge_game_name', label: '玩家信息', align: 'center', width: 70, default: 15, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '最近充值时间', dataIndex: 'user_pay_time', label: '玩家信息', align: 'center', width: 140, default: 16, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '最近活跃时间', dataIndex: 'user_active_time', label: '玩家信息', align: 'center', width: 140, default: 17, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                {
+                    title: '注册充值时间差',
+                    dataIndex: 'regPayTimeDiff',
+                    align: 'center',
+                    width: 140,
+                    default: 18,
+                    render: (a: string, b: any) => {
+                        let diff = new Date(b.user_pay_time).getTime() - new Date(b.user_create_time).getTime()
+                        function secondsToDhms(seconds: any) {
+                            const days = Math.floor(seconds / (3600 * 24));
+                            const hours = Math.floor((seconds % (3600 * 24)) / 3600);
+                            const minutes = Math.floor((seconds % 3600) / 60);
+                            const remainingSeconds = seconds % 60;
+                            return `${days ? days + "天" : ''}${hours ? hours + "小时" : ''}${minutes ? minutes + "分" : ''}${remainingSeconds ? remainingSeconds + "秒" : ''}`
+                        }
+                        return diff ? <WidthEllipsis value={secondsToDhms(diff / 1000)} /> : '--'
+                    }
+                },
+                {
+                    title: '最近充值时间距今',
+                    dataIndex: 'nowPayTimeDiff',
+                    align: 'center',
+                    width: 130,
+                    default: 19,
+                    render: (a: string, b: any) => {
+                        let diff = new Date().getTime() - new Date(b.user_pay_time).getTime()
+                        function secondsToDhms(seconds: any) {
+                            const days = Math.floor(seconds / (3600 * 24));
+                            const hours = Math.floor((seconds % (3600 * 24)) / 3600);
+                            const minutes = Math.floor((seconds % 3600) / 60);
+                            const remainingSeconds = seconds % 60;
+                            return `${days ? days + "天" : ''}${hours ? hours + "小时" : ''}${minutes ? minutes + "分" : ''}${remainingSeconds ? remainingSeconds.toFixed(0) + "秒" : ''}`
+                        }
+                        return diff ? <span style={Math.floor((diff / 1000) / (3600 * 24)) > 1 ? { color: 'red' } : {}}><WidthEllipsis value={secondsToDhms(diff / 1000)} /></span> : '--'
+                    }
+                },
+            ]
+        },
+        {
+            label: '角色充值信息',
+            data: [
+                {
+                    title: '当天充值金额', dataIndex: 'today_amount', label: '角色充值信息', align: 'center', width: 70, default: 9, sorter: true,
+                    className: 'progress',
+                    render: (a: number) => {
+                        return <ProgressTable
+                            strokeColor={{
+                                from: '#108ee9',
+                                to: '#87d068',
+                            }}
+                            percent={a ? a / 2000 * 100 : 0}
+                            value={a || 0}
+                            valueStyle={a >= 1000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }}
+                        />
+                    },
+                },
+                { title: '首充金额', dataIndex: 'role_first_amount', label: '角色充值信息', align: 'center', width: 70, default: 10, sorter: true, render: (a: string) => <Statistic value={a || 0} /> },
+                { title: '最近充值金额', dataIndex: 'role_last_amount', label: '角色充值信息', align: 'center', width: 70, default: 11, sorter: true, render: (a: string) => <Statistic value={a || 0} /> },
+                {
+                    title: '累计充值金额', dataIndex: 'amount', label: '角色充值信息', align: 'center', width: 100, default: 12, sorter: true, className: 'progress',
+                    render: (a: number) => {
+                        return <ProgressTable
+                            strokeColor={{
+                                from: '#ff5900',
+                                to: '#ffd380',
+                            }}
+                            percent={a ? a / 10000 * 100 : 0}
+                            value={a || 0}
+                            valueStyle={a >= 5000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }}
+                        />
+                    },
+                },
+                { title: '平均单价', dataIndex: 'avg_amount', label: '角色充值信息', align: 'center', width: 70, default: 13, sorter: true, render: (a: string) => <Statistic value={a || 0} /> },
+                { title: '累计充值次数', dataIndex: 'amount_count', label: '角色充值信息', align: 'center', width: 70, default: 14, sorter: true, render: (a: string) => <Statistic value={a || 0} /> },
+            ],
+        },
+        {
+            label: '客户运营操作',
+            data: [
+                {
+                    title: '邮件是否发送', dataIndex: 'is_send_mail', label: '客户运营操作', width: 130, align: 'center',
+                    render: (a: any, b: any) => {
+                        return <Space>
+                            {a ? <Badge className="badge" status="success" text="已发送" /> : <Badge className="badge" status="warning" text="未发送" />}
+                            <a style={{ fontSize: 12 }} onClick={() => sendEmail([b])}>发送</a>
+                            <SendEmailDetails data={b} />
+                        </Space>
+                    },
+                },
+                {
+                    title: '最新发送礼包', dataIndex: 'send_gift_id', label: '客户运营操作', align: 'center', width: 138,
+                    render: (a: any, b: any) => {
+                        return <Space>
+                            <div style={{ width: 50, textAlign: 'left' }}><WidthEllipsis value={b?.gift_name} /></div>
+                            <a style={{ fontSize: 12 }} onClick={() => sendPack([b])}>发送</a>
+                            <SendPackDetails data={b} />
+                        </Space>
+                    },
+                },
+                {
+                    title: '是否转端', dataIndex: 'is_change_game_type', label: '客户运营操作', align: 'center', width: 60,
+                    render: (a: any, b: any) => {
+                        // return <IsTrue value={a ? '1' : '0'} onChange={(value) => handleIsTrue(value, b, 'isChangeGameType')} />
+                        return a ? '是' : '否'
+                    },
+                },
+                {
+                    title: '是否添加企微', dataIndex: 'is_add_corp_wechat', label: '客户运营操作', align: 'center', width: 60,
+                    render: (a: any, b: any) => {
+                        // return <IsTrue value={a ? '1' : '0'} onChange={(value) => handleIsTrue(value, b, 'isAddCorpWechat')} />
+                        return a ? '是' : '否'
+                    },
+                },
+                {
+                    title: '企微号', dataIndex: 'add_corp_user_id', label: '客户运营操作', align: 'center', width: 80, className: 'padding0',
+                    render: (a: any, b: any) => {
+                        // return <InputUpdate title={a || ''} dataIndex={'add_corp_user_id'} record={b} handleSave={handleSave} />
+                        return <WidthEllipsis value={a} />
+                    },
+                },
+                {
+                    title: '客户微信号', dataIndex: 'user_wechat', label: '客户运营操作', align: 'center', width: 80,
+                    render: (a: any, b: any) => {
+                        // return <InputUpdate title={a || ''} dataIndex={'user_wechat'} record={b} handleSave={handleSave} />
+                        return <WidthEllipsis value={a} />
+                    },
+                },
+                {
+                    title: '客户手机号', dataIndex: 'user_phone', label: '客户运营操作', align: 'center', width: 80,
+                    render: (a: any, b: any) => {
+                        // return <InputUpdate title={a || ''} dataIndex={'user_phone'} record={b} handleSave={handleSave} />
+                        return <WidthEllipsis value={a} />
+                    },
+                },
+                {
+                    title: '是否退游', dataIndex: 'is_remove_game', label: '客户运营操作', align: 'center', width: 50,
+                    render: (a: any, b: any) => {
+                        // return <IsTrue value={a ? '1' : '0'} onChange={(value) => handleIsTrue(value, b, 'isRemoveGame')} />
+                        return a ? '是' : '否'
+                    },
+                },
+                {
+                    title: '是否唤醒', dataIndex: 'is_wake_up', label: '客户运营操作', align: 'center', width: 50,
+                    render: (a: any, b: any) => {
+                        // return <IsTrue value={a ? '1' : '0'} onChange={(value) => handleIsTrue(value, b, 'isWakeUp')} />
+                        return a ? '是' : '否'
+                    },
+                },
+                {
+                    title: '备注', dataIndex: 'remark', label: '客户运营操作', align: 'center', width: 80,
+                    render: (a: any, b: any) => {
+                        // return <InputUpdate title={a || ''} dataIndex={'remark'} record={b} handleSave={handleSave} />
+                        return <WidthEllipsis value={a} />
+                    },
+                },
+                // {
+                //     title: '角色操作',
+                //     dataIndex: 'rolecz',
+                //     label: '客户运营操作',
+                //     align: 'center',
+                //     width: 80,
+                //     render: (a: any, b: any) => {
+                //         return <a onClick={() => { roleHandle([b]) }}>角色操作</a>
+                //     }
+                // }
+            ]
+        },
+        {
+            label: '客户管理操作',
+            data: [
+                { title: 'GS', dataIndex: 'gs_name', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '客服', dataIndex: 'customer_service_name', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '运营', dataIndex: 'oper_user_name', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '投手', dataIndex: 'put_user_name', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '创建者', dataIndex: 'create_by', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '更新者', dataIndex: 'update_by', label: '客户管理操作', align: 'center', width: 80, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '创建时间', dataIndex: 'create_time', label: '客户管理操作', align: 'center', width: 140, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '更新时间', dataIndex: 'update_time', label: '客户管理操作', align: 'center', width: 140, render: (a: string, b: any) => (<WidthEllipsis value={a} />) },
+                { title: '是否删除', dataIndex: 'is_delete', label: '客户管理操作', align: 'center', width: 70, render: (a: string, b: any) => (<WidthEllipsis value={a ? '删除' : '‘正常'} />) },
+                // {
+                //     title: '指派',
+                //     dataIndex: 'zp',
+                //     label: '客户管理操作',
+                //     align: 'center',
+                //     width: 110,
+                //     render: (a: any, b: any) => {
+                //         return <Space>
+                //             <a onClick={() => { assignHandle([b]) }}>指派</a>
+                //             <a onClick={() => { changeLog([b]) }}>变更记录</a>
+                //         </Space>
+                //     }
+                // }
+            ]
+        },
+        {
+            label: '操作',
+            data: [
+                {
+                    title: '操作',
+                    dataIndex: 'cz',
+                    label: '操作',
+                    align: 'center',
+                    width: 180,
+                    default: 20,
+                    render: (a: any, b: any) => {
+                        return <Space>
+                            <a onClick={() => { roleHandle([b]) }}>角色操作</a>
+                            <a onClick={() => { assignHandle([b]) }}>指派</a>
+                            <a onClick={() => { changeLog([b]) }}>变更记录</a>
+                        </Space>
+                    }
+                }
+            ]
+        }
+    ]
+
+    return newArr
+}
+
+export const columnsSendEmailLog = (update: (data: any) => void, del: (id: number[]) => void): any[] => {
+
+    return [
+        {
+            title: '发送时间',
+            dataIndex: 'sendTime',
+            key: 'sendTime',
+            width: 145,
+            ellipsis: true,
+            fixed: 'left'
+        },
+        {
+            title: '操作人',
+            dataIndex: 'createName',
+            key: 'createName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateName',
+            key: 'updateName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 70,
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a style={{ fontSize: 12 }} onClick={() => update([b])}>更新</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del([b.id])}
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+    ]
+};
+
+
+export const columnsSendPackLog = (update: (data: any) => void, del: (id: number[]) => void): any[] => {
+
+    return [
+        {
+            title: '礼包',
+            dataIndex: 'giftName',
+            key: 'giftName',
+            width: 100,
+            ellipsis: true,
+            fixed: 'left'
+        },
+        {
+            title: '发送时间',
+            dataIndex: 'sendTime',
+            key: 'sendTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '操作人',
+            dataIndex: 'createName',
+            key: 'createName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateName',
+            key: 'updateName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 70,
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a style={{ fontSize: 12 }} onClick={() => update([b])}>更新</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del([b.id])}
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+    ]
+};
+
+
+/**
+ * 变更记录
+ * @param update 
+ * @param del 
+ * @returns 
+ */
+export const columnsChangeLog = (update: (data: any) => void, del: (id: number[]) => void): any[] => {
+
+    return [
+        {
+            title: '开始时间',
+            dataIndex: 'startTime',
+            key: 'startTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '结束时间',
+            dataIndex: 'endTime',
+            key: 'endTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '游戏名称',
+            dataIndex: 'gameName',
+            key: 'gameName',
+            width: 100,
+            ellipsis: true,
+            fixed: 'left'
+        },
+        {
+            title: '角色名称',
+            dataIndex: 'roleName',
+            key: 'roleName',
+            width: 100,
+            ellipsis: true,
+            fixed: 'left'
+        },
+        {
+            title: '区服名称',
+            dataIndex: 'serverName',
+            key: 'serverName',
+            width: 100,
+            ellipsis: true,
+            fixed: 'left'
+        },
+        {
+            title: '客服',
+            dataIndex: 'customerServiceName',
+            key: 'customerServiceName',
+            width: 100,
+            ellipsis: true
+        },
+        {
+            title: '运营',
+            dataIndex: 'operUserName',
+            key: 'operUserName',
+            width: 100,
+            ellipsis: true
+        },
+        {
+            title: '运营',
+            dataIndex: 'gsName',
+            key: 'gsName',
+            width: 100,
+            ellipsis: true
+        },
+        
+        {
+            title: '创建人',
+            dataIndex: 'createName',
+            key: 'createName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateName',
+            key: 'updateName',
+            width: 60,
+            ellipsis: true
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            width: 145,
+            ellipsis: true
+        },
+        {
+            title: '备注',
+            dataIndex: 'remark',
+            key: 'remark',
+            width: 200,
+            ellipsis: true
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 70,
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a style={{ fontSize: 12 }} onClick={() => update(b)}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del([b.id])}
+                    >
+                        <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+    ]
+};
+
+export default columns12

+ 20 - 0
src/services/gameData/index.ts

@@ -53,6 +53,18 @@ export async function getSubUserWithSelfListApi() {
     return request(gameApi + '/manage/choice/agent/user/list');
 }
 
+/**
+ * 获取不同角色列表
+ * @param params 
+ * @returns 
+ */
+export async function getRoleUserListApi(params: {authType: 'GS' | 'CUSTOMER' | 'OPERATE'}) {
+    return request(gameApi + '/manage/game/auth/role/auth/user', {
+        method: 'GET',
+        params
+    });
+}
+
 /**
  * 渠道选择列表
  * @returns 
@@ -99,4 +111,12 @@ export async function getPayListApi() {
  */
 export async function getUserVipLevelChoiceListApi() {
     return request(gameApi + `/manage/choice/vip/level/list`);
+}
+
+/**
+ * 获取礼包列表
+ * @returns 
+ */
+export async function getPackListApi(data: { gameId?: number, giftName?: string }) {
+    return request(gameApi + `/manage/game/gift/list`, { method: 'POST', data });
 }

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

@@ -0,0 +1,224 @@
+import { request } from 'umi';
+import { api } from '../api';
+import { Paging, SortProps } from './rankingList';
+let wapi = api + '/gameData'
+
+export interface RoleRechargeRankingProps extends Paging, SortProps {
+    createRoleBeginDate?: string,  // 角色创建时间(开始)
+    createRoleEndDate?: string,
+    rechargeBeginDate?: string,    // 充值开始时间
+    rechargeEndDate?: string,
+
+    customerServerId?: number,  // 客服ID
+    operatorId?: number         // 运营ID
+    gsId?: number               // GS_ID
+    serverIds?: number[]        // 区服ID(列表)
+
+    gameId?: number,            // 子游戏ID
+    parentGameId?: number,      // 父游戏ID
+    isChange?: number,          // 是否转端: 1 -> 转端 ; 0 -> 不转端
+    isSendMail?: number         // 邮件是否发送: 1 -> 发送 ; 0 -> 不发送
+    phone?: number              // 客户手机号
+    pitcherId?: number          // 投手ID
+    roleName?: string,          // 角色名
+    roleId?: string,            // 角色ID
+    vipLevel?: number           // 角色VIP等级
+    weChat?: string,            // 客户微信号
+    weChatCompany?: string,     // 企业微信号
+}
+/**
+ * 角色充值排行榜
+ * @param data 
+ * @returns 
+ */
+export async function getRoleRechargeRankingListApi(data: RoleRechargeRankingProps) {
+    return request(wapi + `/role/rechargeRanking`, {
+        method: 'POST',
+        data
+    });
+}
+
+
+/**
+ * 礼包记录新增或者更新
+ * @param data 
+ * @returns 
+ */
+export async function addOrUpdateGiftApi(data: { giftId: number, ids?: number[], roleInfoList: any[], sendTime: string }) {
+    return request(api + `/manage/role/gift/record/add/or/update`, {
+        method: 'POST',
+        data
+    });
+}
+
+/**
+ * 删除礼包记录
+ * @param ids 
+ * @returns 
+ */
+export async function delSendGiftLogApi(ids: string) {
+    return request(api + `/manage/role/gift/record/delete/${ids}`, {
+        method: 'DELETE'
+    });
+}
+
+export interface getSendGiftLogProps extends Paging {
+    startDateTime?: string,
+    endDateTime?: string,
+    giftName?: string,
+    gameId?: number,
+    roleId?: number,
+    serverId?: number
+    userId?: number
+}
+/**
+ * 发送礼包记录接口
+ * @param data 
+ * @returns 
+ */
+export async function getSendGiftLogListApi(data: getSendGiftLogProps) {
+    return request(api + `/manage/role/gift/record/list`, {
+        method: 'POST',
+        data
+    });
+}
+
+
+export interface getSendEmailLogProps extends Paging {
+    startDateTime?: string,
+    endDateTime?: string,
+
+    gameId?: number,
+    roleId?: number,
+    serverId?: number
+    userId?: number
+}
+/**
+ * 发送邮件记录接口
+ * @param data 
+ * @returns 
+ */
+export async function getSendEmailListApi(data: getSendEmailLogProps) {
+    return request(api + `/manage/role/mail/record/list`, {
+        method: 'POST',
+        data
+    });
+}
+
+
+/**
+ * 删除邮件记录
+ * @param ids 
+ * @returns 
+ */
+export async function delSendEmailLogApi(ids: string) {
+    return request(api + `/manage/role/mail/record/delete/${ids}`, {
+        method: 'DELETE'
+    });
+}
+
+
+/**
+ * 邮件记录新增或者更新
+ * @param data 
+ * @returns 
+ */
+export async function addOrUpdateEmailApi(data: { ids?: number[], roleInfoList: any[], sendTime: string }) {
+    return request(api + `/manage/role/mail/record/add/or/update`, {
+        method: 'POST',
+        data
+    });
+}
+
+
+export interface modifyRoleProps {
+    addCorpUserId?: string // 企微号
+    isAddCorpWechat?: string
+    isChangeGameType?: string
+    isRemoveGame?: string
+    isWakeUp?: string
+    putUserId?: number
+    remark?: string
+    roleInfoList?: any
+    userPhone?: string
+    userWechat?: string
+}
+/**
+ * 角色信息管理
+ * @param data 
+ * @returns 
+ */
+export async function modifyRoleDataApi(data: modifyRoleProps) {
+    return request(api + `/manage/role/operate/modify`, {
+        method: 'POST',
+        data
+    });
+}
+
+
+export interface AssignUserProps {
+    startTime: string,
+    customerServiceId?: number,
+    gsId?: number,
+    operUserId?: number,
+    remark?: string,
+    roleInfoAndAgentParamList: any
+}
+/**
+ * 指派
+ * @param data 
+ * @returns 
+ */
+export async function addAssignUserApi(data: AssignUserProps) {
+    return request(api + `/manage/role/assign/record/config/sysUser`, {
+        method: 'POST',
+        data
+    });
+}
+
+/**
+ * 删除
+ * @param ids 
+ * @returns 
+ */
+export async function delAssignUserApi(ids: string) {
+    return request(api + `/manage/role/assign/record/delete/${ids}`, {
+        method: 'DELETE'
+    });
+}
+
+
+export interface GetAssignUser extends Paging {
+    customerServiceId?: number,
+    gameId?: number,
+    gsId?: number,
+    operUserId?: number,
+    regAgentId?: number,
+    remark?: string,
+    roleId?: number,
+    serverId?: number,
+    userId?: number
+}
+/**
+ * 
+ * @param data 
+ * @returns 
+ */
+export async function getAssignUserApi(data: GetAssignUser) {
+    return request(api + `/manage/role/assign/record/list`, {
+        method: 'POST',
+        data
+    });
+}
+
+/**
+ * 修改
+ * @param data 
+ * @returns 
+ */
+export async function editAssignUserApi(data: AssignUserProps) {
+    return request(api + `/manage/role/assign/record/modify`, {
+        method: 'POST',
+        data
+    });
+}

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff