wjx пре 5 месеци
родитељ
комит
64a6aa213c

+ 6 - 0
config/routerConfig.ts

@@ -169,6 +169,12 @@ const gameDataStatistics = {
                     name: '游戏策略配置',
                     access: 'strategy',
                     component: './gameDataStatistics/roleOperate/strategy',
+                },
+                {
+                    path: '/gameDataStatistics/roleOperate/codeSender',
+                    name: '游戏发码器',
+                    access: 'codeSender',
+                    component: './gameDataStatistics/roleOperate/codeSender',
                 }
             ]
         },

+ 36 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/enabledSwitch.tsx

@@ -0,0 +1,36 @@
+import { useAjax } from "@/Hook/useAjax"
+import { updatePackLinkCodeApi } from "@/services/gameData/roleOperate"
+import { Switch } from "antd"
+import React from "react"
+
+interface Props {
+    id: number
+    value?: boolean
+    onChange?: () => void
+}
+
+/**
+ * 修改状态
+ * @returns 
+ */
+const EnabledSwitch: React.FC<Props> = ({ id, value, onChange }) => {
+
+    /************************************/
+    const updatePackLinkCode = useAjax((params) => updatePackLinkCodeApi(params))
+    /************************************/
+
+    return <Switch
+        checked={value}
+        loading={updatePackLinkCode.loading}
+        size="small"
+        onChange={(checked) => {
+            updatePackLinkCode.run({ ids: [id], enabled: checked }).then(res => {
+                if (res) {
+                    onChange?.()
+                }
+            })
+        }}
+    />
+}
+
+export default React.memo(EnabledSwitch)

+ 240 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/index.tsx

@@ -0,0 +1,240 @@
+import { Button, Card, Col, DatePicker, Form, Input, message, Row, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../../components/TableData/index.less'
+import { PlusOutlined } from "@ant-design/icons"
+import Tables from "@/components/Tables"
+import { delPackLinkApi, getPackConfigListApi, getPackLinkListApi, GetPackLinkListProps } from "@/services/gameData/roleOperate"
+import { useAjax } from "@/Hook/useAjax"
+import moment from "moment"
+import ModifyLink from "./modifyLink"
+import columnsPos from "./tableConfig"
+import LinkCode from "./linkCode"
+import LinkLog from "./linkLog"
+import LinkLqLog from "./linkLqLog"
+
+export type ConfigDataProps = { label: string, value: number, packList: { label: string, value: string }[] }
+
+/**
+ * 游戏发码器
+ * @returns 
+ */
+const CodeSender: React.FC = () => {
+
+    /*************************************/
+    const [form] = Form.useForm()
+    const gameId = Form.useWatch('gameId', form)
+    const [queryFrom, setQueryForm] = useState<GetPackLinkListProps>({ pageNum: 1, pageSize: 20 })
+    const [configData, setConfigData] = useState<ConfigDataProps[]>([])
+    const [visible, setVisible] = useState<boolean>(false)
+    const [initialValues, setInitialValues] = useState<any>()
+    const [toCodeData, setToCodeData] = useState<{ data: any, visible: boolean }>({ data: undefined, visible: false })
+    const [toLogData, setToLogData] = useState<{ data: any, visible: boolean }>({ data: undefined, visible: false })
+    const [toLqData, setToLqData] = useState<{ data: any, visible: boolean }>({ data: undefined, visible: false })
+
+    const getPackConfigList = useAjax(() => getPackConfigListApi())
+    const getPackLinkList = useAjax((params) => getPackLinkListApi(params))
+    const delPackLink = useAjax((params) => delPackLinkApi(params))
+    /*************************************/
+
+    useEffect(() => {
+        getPackConfigList.run().then(res => {
+            if (res) {
+                setConfigData(res?.map((item: { t1: any, t2: any }) => {
+                    const gameId = Object.keys(item.t1)[0]
+                    const ganemName = item.t1[gameId]
+                    return { label: ganemName, value: Number(gameId), packList: Object.keys(item.t2).map(key => ({ label: item.t2[key], value: key })) }
+                }))
+            }
+        })
+    }, [])
+
+    useEffect(() => {
+        getPackLinkList.run(queryFrom)
+    }, [queryFrom])
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryFrom))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.createTime?.length > 1) {
+            params.createBeginTime = moment(params.createTime[0]).format('YYYY-MM-DD')
+            params.createEndTime = moment(params.createTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.createBeginTime
+            delete params?.createEndTime
+        }
+        if (params?.updateTime?.length > 1) {
+            params.updateBeginTime = moment(params.updateTime[0]).format('YYYY-MM-DD')
+            params.updateEndTime = moment(params.updateTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.updateBeginTime
+            delete params?.updateEndTime
+        }
+        setQueryForm(params)
+    }
+
+    const toCode = (data: any) => {
+        setToCodeData({ data, visible: true })
+    }
+
+    const editPack = (data: any) => {
+        setInitialValues(data)
+        setVisible(true)
+    }
+
+    const del = (id: number) => {
+        delPackLink.run({ id }).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getPackLinkList.refresh()
+            }
+        })
+    }
+
+    const handle = (data: any, type: string) => {
+        switch (type) {
+            case 'log':
+                setToLogData({ data, visible: true })
+                break
+            case 'lq':
+                setToLqData({ data, visible: true })
+                break
+        }
+    }
+
+    return <Card
+        style={{ borderRadius: 8 }}
+        headStyle={{ textAlign: 'left' }}
+        bodyStyle={{ padding: '5px 10px' }}
+    >
+        <div style={{ textAlign: 'center', fontWeight: 'bold', padding: '4px 6px 6px', fontSize: 16, marginBottom: 4, position: 'relative' }}>
+            发码器
+        </div>
+
+        <Space style={{ width: '100%' }} direction="vertical" size={10}>
+            <Form layout="inline" className='queryForm' name="basicGameServer" form={form} onFinish={onFinish}>
+                <Row gutter={[0, 6]}>
+                    <Col><Form.Item name='codeLink'>
+                        <Input placeholder="礼包链接" allowClear style={{ width: 140 }} />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='gameId'>
+                        <Select
+                            showSearch
+                            style={{ minWidth: 140 }}
+                            allowClear
+                            placeholder={'请选择游戏'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                            onChange={() => {
+                                form.setFieldsValue({ codeType: undefined })
+                            }}
+                        >
+                            {configData?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                        </Select>
+                    </Form.Item></Col>
+                    {gameId && <Col><Form.Item name='codeType'>
+                        <Select
+                            maxTagCount={1}
+                            showSearch
+                            style={{ minWidth: 140 }}
+                            allowClear
+                            placeholder={'请选择礼包码类型'}
+                            filterOption={(input, option) =>
+                                (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                            }
+                        >
+                            {configData?.find(item => item.value === gameId)?.packList?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                        </Select>
+                    </Form.Item></Col>}
+                    <Col><Form.Item name='createTime'>
+                        <DatePicker.RangePicker placeholder={['创建时间开始', '创建时间结束']} />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='updateTime'>
+                        <DatePicker.RangePicker placeholder={['更新时间开始', '更新时间结束']} />
+                    </Form.Item></Col>
+                    <Col>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => form.resetFields()}>重置</Button>
+                            <Button icon={<PlusOutlined />} type="primary" onClick={() => { setVisible(true); }}>新增礼包码链接</Button>
+                        </Space>
+                    </Col>
+                </Row>
+            </Form>
+
+            <div className={`${style['small']}`}>
+                <Tables
+                    className={`all_table content_table_body`}
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    current={queryFrom.pageNum}
+                    pageSize={queryFrom.pageSize}
+                    columns={columnsPos(toCode, editPack, del, handle)}
+                    dataSource={getPackLinkList?.data?.records}
+                    scroll={{ x: 1000, y: 600 }}
+                    onChange={(pagination: any, filters: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortType'] = sortData?.order === 'ascend' ? 'asc' : 'desc'
+                            newQueryForm['sortFiled'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortType']
+                            delete newQueryForm['sortFiled']
+                        }
+                        newQueryForm.pageNum = current
+                        newQueryForm.pageSize = pageSize
+                        setQueryForm({ ...newQueryForm })
+                    }}
+                    size="small"
+                    total={getPackLinkList?.data?.total}
+                    loading={getPackLinkList?.loading}
+                    defaultPageSize={20}
+                />
+            </div>
+        </Space>
+
+        {/* 新增链接 */}
+        {visible && <ModifyLink
+            configData={configData}
+            visible={visible}
+            initialValues={initialValues}
+            onClose={() => {
+                setVisible(false)
+                setInitialValues(undefined)
+            }}
+            onChange={() => {
+                setVisible(false)
+                setInitialValues(undefined)
+                getPackLinkList.refresh()
+            }}
+        />}
+
+        {/* 礼包码 */}
+        {toCodeData?.visible && <LinkCode
+            {...toCodeData}
+            onClose={() => {
+                setToCodeData({ data: undefined, visible: false })
+            }}
+        />}
+
+        {/* 访问记录 */}
+        {toLogData?.visible && <LinkLog
+            {...toLogData}
+            onClose={() => {
+                setToLogData({ data: undefined, visible: false })
+            }}
+        />}
+
+
+        {/* 访问记录 */}
+        {toLqData?.visible && <LinkLqLog
+            {...toLqData}
+            onClose={() => {
+                setToLqData({ data: undefined, visible: false })
+            }}
+        />}
+    </Card>
+}
+
+export default CodeSender

+ 202 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/linkCode.tsx

@@ -0,0 +1,202 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getPackLinkCodeListApi, GetPackLinkCodeListProps, updatePackLinkCodeApi } from "@/services/gameData/roleOperate"
+import { Button, Col, DatePicker, Form, Input, message, Modal, Row, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../../components/TableData/index.less'
+import moment from "moment"
+import Tables from "@/components/Tables"
+import ModifyCode from "./modifyCode"
+import columnsPosCode from "./tableConfigCode"
+
+interface Props {
+    data: any
+    visible?: boolean
+    onClose?: () => void
+}
+
+/**
+ * 链接礼包码
+ * @param param0 
+ * @returns 
+ */
+const LinkCode: React.FC<Props> = ({ data, visible, onClose }) => {
+
+    /******************************/
+    const [form] = Form.useForm()
+    const [addCodevisible, setaddCodeVisible] = useState<boolean>(false)
+    const [selectedRows, setSelectedRows] = useState<any[]>([])
+
+    const [queryFrom, setQueryForm] = useState<GetPackLinkCodeListProps>({ pageNum: 1, pageSize: 20, linkId: data.id })
+    const getPackLinkCodeList = useAjax((params) => getPackLinkCodeListApi(params))
+    const updatePackLinkCode = useAjax((params) => updatePackLinkCodeApi(params))
+    /******************************/
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryFrom))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.createTime?.length > 1) {
+            params.createBeginTime = moment(params.createTime[0]).format('YYYY-MM-DD')
+            params.createEndTime = moment(params.createTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.createBeginTime
+            delete params?.createEndTime
+        }
+        if (params?.updateTime?.length > 1) {
+            params.updateBeginTime = moment(params.updateTime[0]).format('YYYY-MM-DD')
+            params.updateEndTime = moment(params.updateTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.updateBeginTime
+            delete params?.updateEndTime
+        }
+        setQueryForm(params)
+    }
+
+    useEffect(() => {
+        getPackLinkCodeList.run(queryFrom)
+    }, [queryFrom])
+
+
+    const handleEnabled = (checked: boolean) => {
+        updatePackLinkCode.run({ ids: selectedRows.map(item => item.id), enabled: checked }).then(res => {
+            if (res) {
+                message.success('修改成功')
+                getPackLinkCodeList.refresh()
+                setSelectedRows([])
+            }
+        })
+    }
+
+    return <Modal
+        title={<Space>
+            <strong>{data?.gameName}({data?.codeTypeName}) 链接礼包码</strong>
+            <a onClick={() => { setaddCodeVisible(true); }}>新增礼包码</a>
+            {selectedRows?.length > 0 && <>
+                <a style={{ color: '#52C41A' }} onClick={() => handleEnabled(true)}>修改为可用</a>
+                <a style={{ color: 'red' }} onClick={() => handleEnabled(false)}>修改为不可用</a>
+            </>}
+        </Space>}
+        visible={visible}
+        onCancel={onClose}
+        footer={null}
+        width={1000}
+    >
+        <Space style={{ width: '100%' }} direction="vertical" size={10}>
+            <Form layout="inline" className='queryForm' name="basicGameServer" form={form} onFinish={onFinish}>
+                <Row gutter={[0, 6]}>
+                    <Col><Form.Item name='code'>
+                        <Input placeholder="礼包码" allowClear style={{ width: 120 }} />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='enabled'>
+                        <Select
+                            showSearch
+                            style={{ width: 120 }}
+                            allowClear
+                            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>
+                    <Col><Form.Item name='createTime'>
+                        <DatePicker.RangePicker style={{ width: 250 }} placeholder={['创建时间开始', '创建时间结束']} />
+                    </Form.Item></Col>
+                    <Col><Form.Item name='updateTime'>
+                        <DatePicker.RangePicker style={{ width: 250 }} placeholder={['更新时间开始', '更新时间结束']} />
+                    </Form.Item></Col>
+                    <Col>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => form.resetFields()}>重置</Button>
+                        </Space>
+                    </Col>
+                </Row>
+            </Form>
+
+            <div className={`${style['small']}`}>
+                <Tables
+                    className={`all_table content_table_body`}
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    current={queryFrom.pageNum}
+                    pageSize={queryFrom.pageSize}
+                    columns={columnsPosCode(() => getPackLinkCodeList.refresh())}
+                    dataSource={getPackLinkCodeList?.data?.records}
+                    scroll={{ x: 900, y: 600 }}
+                    onChange={(pagination: any, filters: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortType'] = sortData?.order === 'ascend' ? 'asc' : 'desc'
+                            newQueryForm['sortFiled'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortType']
+                            delete newQueryForm['sortFiled']
+                        }
+                        newQueryForm.pageNum = current
+                        newQueryForm.pageSize = pageSize
+                        setQueryForm({ ...newQueryForm })
+                    }}
+                    size="small"
+                    total={getPackLinkCodeList?.data?.total}
+                    loading={getPackLinkCodeList?.loading}
+                    defaultPageSize={20}
+                    rowSelection={{
+                        selectedRowKeys: selectedRows.map(item => item.id.toString()),
+                        onSelect: (record: { id: number }, selected: boolean) => {
+                            if (selected) {
+                                selectedRows.push({ ...record })
+                                setSelectedRows([...selectedRows])
+                            } else {
+                                let newSelectAccData = selectedRows.filter((item: { id: number }) => item.id !== record.id)
+                                setSelectedRows([...newSelectAccData])
+                            }
+                        },
+                        onSelectAll: (selected: boolean, selectedRowss: { id: number }[], changeRows: { id: number }[]) => {
+                            if (selected) {
+                                let newSelectAccData = [...selectedRows]
+                                changeRows.forEach((item: { id: number }) => {
+                                    let index = newSelectAccData.findIndex((ite: { id: number }) => ite.id === item.id)
+                                    if (index === -1) {
+                                        let data: any = { ...item }
+                                        newSelectAccData.push(data)
+                                    }
+                                })
+                                setSelectedRows([...newSelectAccData])
+                            } else {
+                                let newSelectAccData = selectedRows.filter((item: { id: number }) => {
+                                    let index = changeRows.findIndex((ite: { id: number }) => ite.id === item.id)
+                                    if (index !== -1) {
+                                        return false
+                                    } else {
+                                        return true
+                                    }
+                                })
+                                setSelectedRows([...newSelectAccData])
+                            }
+                        }
+                    }}
+                />
+            </div>
+        </Space>
+
+        {/* 新增礼包码 */}
+        {addCodevisible && <ModifyCode
+            visible={addCodevisible}
+            gameId={data.gameId}
+            linkId={data.id}
+            onChange={() => {
+                setaddCodeVisible(false)
+                getPackLinkCodeList.refresh()
+            }}
+            onClose={() => {
+                setaddCodeVisible(false)
+            }}
+        />}
+    </Modal>
+}
+
+
+export default React.memo(LinkCode)

+ 105 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/linkLog.tsx

@@ -0,0 +1,105 @@
+import { useAjax } from "@/Hook/useAjax"
+import { GetPackLinkCodeListProps, getPackLinkLogListApi } from "@/services/gameData/roleOperate"
+import { Button, Col, DatePicker, Form, Modal, Row, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../../components/TableData/index.less'
+import moment from "moment"
+import Tables from "@/components/Tables"
+import columnsPosLog from "./tableConfigLog"
+
+interface Props {
+    data: any
+    visible?: boolean
+    onClose?: () => void
+}
+
+/**
+ * 链接访问记录
+ * @param param0 
+ * @returns 
+ */
+const LinkLog: React.FC<Props> = ({ data, visible, onClose }) => {
+
+    /******************************/
+    const [form] = Form.useForm()
+
+    const [queryFrom, setQueryForm] = useState<GetPackLinkCodeListProps>({ pageNum: 1, pageSize: 20, linkId: data.id })
+    const getPackLinkLogList = useAjax((params) => getPackLinkLogListApi(params))
+    /******************************/
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryFrom))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.createTime?.length > 1) {
+            params.createBeginTime = moment(params.createTime[0]).format('YYYY-MM-DD')
+            params.createEndTime = moment(params.createTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.createBeginTime
+            delete params?.createEndTime
+        }
+        setQueryForm(params)
+    }
+
+    useEffect(() => {
+        getPackLinkLogList.run(queryFrom)
+    }, [queryFrom])
+
+
+    return <Modal
+        title={<strong>{data?.gameName}({data?.codeTypeName}) 链接访问记录</strong>}
+        visible={visible}
+        onCancel={onClose}
+        footer={null}
+        width={750}
+    >
+        <Space style={{ width: '100%' }} direction="vertical" size={10}>
+            <Form layout="inline" className='queryForm' name="basicGameServer" form={form} onFinish={onFinish}>
+                <Row gutter={[0, 6]}>
+                    <Col><Form.Item name='createTime'>
+                        <DatePicker.RangePicker style={{ width: 250 }} placeholder={['创建时间开始', '创建时间结束']} />
+                    </Form.Item></Col>
+                    <Col>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => form.resetFields()}>重置</Button>
+                        </Space>
+                    </Col>
+                </Row>
+            </Form>
+
+            <div className={`${style['small']}`}>
+                <Tables
+                    className={`all_table content_table_body`}
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    current={queryFrom.pageNum}
+                    pageSize={queryFrom.pageSize}
+                    columns={columnsPosLog()}
+                    dataSource={getPackLinkLogList?.data?.records}
+                    scroll={{ x: 680, y: 600 }}
+                    onChange={(pagination: any, filters: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortType'] = sortData?.order === 'ascend' ? 'asc' : 'desc'
+                            newQueryForm['sortFiled'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortType']
+                            delete newQueryForm['sortFiled']
+                        }
+                        newQueryForm.pageNum = current
+                        newQueryForm.pageSize = pageSize
+                        setQueryForm({ ...newQueryForm })
+                    }}
+                    size="small"
+                    total={getPackLinkLogList?.data?.total}
+                    loading={getPackLinkLogList?.loading}
+                    defaultPageSize={20}
+                />
+            </div>
+        </Space>
+    </Modal>
+}
+
+
+export default React.memo(LinkLog)

+ 105 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/linkLqLog.tsx

@@ -0,0 +1,105 @@
+import { useAjax } from "@/Hook/useAjax"
+import { GetPackLinkCodeListProps, getPackLinkLqLogListApi } from "@/services/gameData/roleOperate"
+import { Button, Col, DatePicker, Form, Modal, Row, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../../components/TableData/index.less'
+import moment from "moment"
+import Tables from "@/components/Tables"
+import columnsPosLqLog from "./tableConfigLqLog"
+
+interface Props {
+    data: any
+    visible?: boolean
+    onClose?: () => void
+}
+
+/**
+ * 链接访问记录
+ * @param param0 
+ * @returns 
+ */
+const LinkLqLog: React.FC<Props> = ({ data, visible, onClose }) => {
+
+    /******************************/
+    const [form] = Form.useForm()
+
+    const [queryFrom, setQueryForm] = useState<GetPackLinkCodeListProps>({ pageNum: 1, pageSize: 20, linkId: data.id })
+    const getPackLinkLqLogList = useAjax((params) => getPackLinkLqLogListApi(params))
+    /******************************/
+
+    const onFinish = (data: any) => {
+        let oldQueryFrom = JSON.parse(JSON.stringify(queryFrom))
+        let params = { ...oldQueryFrom, ...data, pageNum: 1 }
+        if (params?.createTime?.length > 1) {
+            params.createBeginTime = moment(params.createTime[0]).format('YYYY-MM-DD')
+            params.createEndTime = moment(params.createTime[1]).format('YYYY-MM-DD')
+        } else {
+            delete params?.createBeginTime
+            delete params?.createEndTime
+        }
+        setQueryForm(params)
+    }
+
+    useEffect(() => {
+        getPackLinkLqLogList.run(queryFrom)
+    }, [queryFrom])
+
+
+    return <Modal
+        title={<strong>{data?.gameName}({data?.codeTypeName}) 链接领取记录</strong>}
+        visible={visible}
+        onCancel={onClose}
+        footer={null}
+        width={1000}
+    >
+        <Space style={{ width: '100%' }} direction="vertical" size={10}>
+            <Form layout="inline" className='queryForm' name="basicGameServer" form={form} onFinish={onFinish}>
+                <Row gutter={[0, 6]}>
+                    <Col><Form.Item name='createTime'>
+                        <DatePicker.RangePicker style={{ width: 250 }} placeholder={['创建时间开始', '创建时间结束']} />
+                    </Form.Item></Col>
+                    <Col>
+                        <Space>
+                            <Button type="primary" htmlType="submit">搜索</Button>
+                            <Button onClick={() => form.resetFields()}>重置</Button>
+                        </Space>
+                    </Col>
+                </Row>
+            </Form>
+
+            <div className={`${style['small']}`}>
+                <Tables
+                    className={`all_table content_table_body`}
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    current={queryFrom.pageNum}
+                    pageSize={queryFrom.pageSize}
+                    columns={columnsPosLqLog()}
+                    dataSource={getPackLinkLqLogList?.data?.records}
+                    scroll={{ x: 800, y: 600 }}
+                    onChange={(pagination: any, filters: any, sortData: any) => {
+                        let { current, pageSize } = pagination
+                        let newQueryForm = JSON.parse(JSON.stringify(queryFrom))
+                        if (sortData && sortData?.order) {
+                            newQueryForm['sortType'] = sortData?.order === 'ascend' ? 'asc' : 'desc'
+                            newQueryForm['sortFiled'] = sortData?.field
+                        } else {
+                            delete newQueryForm['sortType']
+                            delete newQueryForm['sortFiled']
+                        }
+                        newQueryForm.pageNum = current
+                        newQueryForm.pageSize = pageSize
+                        setQueryForm({ ...newQueryForm })
+                    }}
+                    size="small"
+                    total={getPackLinkLqLogList?.data?.total}
+                    loading={getPackLinkLqLogList?.loading}
+                    defaultPageSize={20}
+                />
+            </div>
+        </Space>
+    </Modal>
+}
+
+
+export default React.memo(LinkLqLog)

+ 59 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/modifyCode.tsx

@@ -0,0 +1,59 @@
+import { useAjax } from "@/Hook/useAjax";
+import { AddPackLinkCodeApi } from "@/services/gameData/roleOperate";
+import { Form, Input, message, Modal } from "antd";
+import React from "react"
+
+interface Props {
+    gameId: number,
+    linkId: number
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+}
+
+const ModifyCode: React.FC<Props> = ({ gameId, linkId, visible, onChange, onClose }) => {
+
+    /**************************************/
+    const [form] = Form.useForm()
+
+    const AddPackLinkCode = useAjax((params) => AddPackLinkCodeApi(params))
+    /**************************************/
+
+
+    const handleOk = async () => {
+        form.submit()
+        let { codeList } = await form.validateFields()
+        let newCodeList: string[] = codeList ? codeList?.split(/[,,\n\s]+/ig).filter((item: any) => item) : []
+        const params = { codeList: newCodeList, gameId, linkId }
+        console.log(params)
+        AddPackLinkCode.run(params).then(res => {
+            if (res) {
+                message.success('新增成功')
+                onChange?.()
+            }
+        })
+    }
+
+
+    return <Modal
+        title={<strong>{'新增'}礼包码</strong>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+        confirmLoading={AddPackLinkCode.loading}
+    >
+        <Form
+            name="basicPackCode"
+            form={form}
+            autoComplete="off"
+            layout="vertical"
+            colon={false}
+        >
+            <Form.Item label={<strong>礼包码</strong>} name="codeList" rules={[{ required: true, message: '请输入礼包码!' }]}>
+                <Input.TextArea rows={6} placeholder="请输入礼包码(多个,,空格换行)" />
+            </Form.Item>
+        </Form>
+    </Modal>
+}
+
+export default React.memo(ModifyCode)

+ 228 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/modifyLink.tsx

@@ -0,0 +1,228 @@
+import { Form, InputNumber, message, Modal, Select, Space } from "antd"
+import React from "react"
+import { ConfigDataProps } from "."
+import { useAjax } from "@/Hook/useAjax"
+import { addPackLinkApi, updatePackLinkApi } from "@/services/gameData/roleOperate"
+
+
+interface Props {
+    configData: ConfigDataProps[]
+    visible?: boolean
+    onClose?: () => void
+    onChange?: () => void
+    initialValues?: any
+}
+
+/**
+ * 礼包码链接新增修改
+ * @param param0 
+ * @returns 
+ */
+const ModifyLink: React.FC<Props> = ({ configData, visible, onChange, onClose, initialValues }) => {
+
+    /*************************************/
+    const [form] = Form.useForm()
+    const gameId = Form.useWatch('gameId', form)
+    const payMin = Form.useWatch(['conditionDTO', 'pay', 'min'], form)
+    const payMax = Form.useWatch(['conditionDTO', 'pay', 'max'], form)
+    const regTimeMin = Form.useWatch(['conditionDTO', 'regTime', 'min'], form)
+    const regTimeMax = Form.useWatch(['conditionDTO', 'regTime', 'max'], form)
+    const unLoginMin = Form.useWatch(['conditionDTO', 'unLogin', 'min'], form)
+    const unLoginMax = Form.useWatch(['conditionDTO', 'unLogin', 'max'], form)
+
+
+    const addPackLink = useAjax((params) => addPackLinkApi(params))
+    const updatePackLink = useAjax((params) => updatePackLinkApi(params))
+    /*************************************/
+
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (initialValues?.id) {
+            updatePackLink.run({ ...data, id: initialValues?.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    onChange && onChange()
+                }
+            })
+        } else {
+            addPackLink.run(data).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    onChange && onChange()
+                }
+            })
+        }
+
+    }
+
+
+    return <Modal
+        title={<strong>{initialValues?.id ? '修改' : '新增'}礼包码链接</strong>}
+        visible={visible}
+        onCancel={onClose}
+        onOk={handleOk}
+    >
+        <Form
+            name="basicPackLink"
+            form={form}
+            autoComplete="off"
+            layout="vertical"
+            colon={false}
+            initialValues={initialValues?.id ? initialValues : { conditionDTO: { pay: { min: null, max: null }, regTime: { min: null, max: null }, unLogin: { min: null, max: null } } }}
+        >
+            <Form.Item label={<strong>游戏</strong>} name="gameId" rules={[{ required: true, message: '请选择游戏!' }]}>
+                <Select
+                    showSearch
+                    style={{ minWidth: 140 }}
+                    allowClear
+                    placeholder={'请选择游戏'}
+                    filterOption={(input, option) =>
+                        (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                    }
+                    disabled={initialValues?.id}
+                    onChange={() => {
+                        form.setFieldsValue({ codeType: undefined })
+                    }}
+                >
+                    {configData?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                </Select>
+            </Form.Item>
+            {gameId && <>
+                <Form.Item label={<strong>礼包码类型</strong>} name="codeType" 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
+                        }
+                    >
+                        {configData?.find(item => item.value === gameId)?.packList?.map((item: any) => <Select.Option value={item.value} key={item.value}>{item.label}</Select.Option>)}
+                    </Select>
+                </Form.Item>
+                <Form.Item label={<strong>付费区间(元)</strong>}>
+                    <Space style={{ width: '100%' }}>
+                        <Form.Item
+                            name={['conditionDTO', 'pay', 'min']}
+                            noStyle
+                            rules={[
+                                { required: !!payMax ? true : false, message: '请填写最小值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && payMax && value > payMax) {
+                                            return Promise.reject(new Error('最小值必须小于等于最大值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最小值" style={{ width: 150 }} />
+                        </Form.Item>
+                        <span>-</span>
+                        <Form.Item
+                            name={['conditionDTO', 'pay', 'max']}
+                            noStyle
+                            rules={[
+                                { required: !!payMin ? true : false, message: '请填写最大值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && payMin && value > payMin) {
+                                            return Promise.reject(new Error('最大值必须大于等于最小值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最大值" style={{ width: 150 }} />
+                        </Form.Item>
+                    </Space>
+                </Form.Item>
+                <Form.Item label={<strong>注册时间(分钟)</strong>}>
+                    <Space style={{ width: '100%' }}>
+                        <Form.Item
+                            name={['conditionDTO', 'regTime', 'min']}
+                            noStyle
+                            rules={[
+                                { required: !!regTimeMax ? true : false, message: '请填写最小值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && regTimeMax && value > regTimeMax) {
+                                            return Promise.reject(new Error('最小值必须小于等于最大值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最小值" style={{ width: 150 }} />
+                        </Form.Item>
+                        <span>-</span>
+                        <Form.Item
+                            name={['conditionDTO', 'regTime', 'max']}
+                            noStyle
+                            rules={[
+                                { required: !!regTimeMin ? true : false, message: '请填写最大值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && regTimeMin && value > regTimeMin) {
+                                            return Promise.reject(new Error('最大值必须大于等于最小值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最大值" style={{ width: 150 }} />
+                        </Form.Item>
+                    </Space>
+                </Form.Item>
+                <Form.Item label={<strong>未登录时间(分钟)</strong>}>
+                    <Space style={{ width: '100%' }}>
+                        <Form.Item
+                            name={['conditionDTO', 'unLogin', 'min']}
+                            noStyle
+                            rules={[
+                                { required: !!unLoginMax ? true : false, message: '请填写最小值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && unLoginMax && value > unLoginMax) {
+                                            return Promise.reject(new Error('最小值必须小于等于最大值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最小值" style={{ width: 150 }} />
+                        </Form.Item>
+                        <span>-</span>
+                        <Form.Item
+                            name={['conditionDTO', 'unLogin', 'max']}
+                            noStyle
+                            rules={[
+                                { required: !!unLoginMin ? true : false, message: '请填写最大值' },
+                                () => ({
+                                    validator(_, value) {
+                                        if (value && unLoginMin && value > unLoginMin) {
+                                            return Promise.reject(new Error('最大值必须大于等于最小值'));
+                                        }
+                                        return Promise.resolve();
+                                    },
+                                })
+                            ]}
+                        >
+                            <InputNumber placeholder="最大值" style={{ width: 150 }} />
+                        </Form.Item>
+                    </Space>
+                </Form.Item>
+            </>}
+        </Form>
+    </Modal>
+}
+
+export default React.memo(ModifyLink)

+ 119 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/tableConfig.tsx

@@ -0,0 +1,119 @@
+
+
+import { Row, Col, Popconfirm } from "antd"
+import WidthEllipsis from "@/components/widthEllipsis"
+import React from "react"
+
+function columnsPos(toCode: (data: any) => void, editPack: (data: any) => void, del: (id: number) => void, handle: (data: any, type: string) => void) {
+
+    let newArr: any = [
+        {
+            title: '游戏',
+            dataIndex: 'gameName',
+            key: 'gameName',
+            align: 'center',
+            width: 90,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '礼包类型',
+            dataIndex: 'codeTypeName',
+            key: 'codeTypeName',
+            align: 'center',
+            width: 90,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '礼包链接',
+            dataIndex: 'codeLink',
+            key: 'codeLink',
+            width: 230,
+            render: (a: string) => (<WidthEllipsis value={a} isCopy />)
+        },
+        {
+            title: '付费区间(元)',
+            dataIndex: 'pay',
+            key: 'pay',
+            width: 100,
+            align: 'center',
+            render: (_: any, b: any) => (b?.conditionDTO?.pay?.min !== null ? <WidthEllipsis value={b?.conditionDTO?.pay?.min + '~' + b?.conditionDTO?.pay?.max} /> : '--')
+        },
+        {
+            title: '注册时间(分钟)',
+            dataIndex: 'regTime',
+            key: 'regTime',
+            width: 100,
+            align: 'center',
+            render: (_: any, b: any) => (b?.conditionDTO?.regTime?.min !== null ? <WidthEllipsis value={b?.conditionDTO?.regTime?.min + '~' + b?.conditionDTO?.regTime?.max} /> : '--')
+        },
+        {
+            title: '未登录时间(分钟)',
+            dataIndex: 'unLogin',
+            key: 'unLogin',
+            width: 100,
+            align: 'center',
+            render: (_: any, b: any) => (b?.conditionDTO?.unLogin?.min !== null ? <WidthEllipsis value={b?.conditionDTO?.unLogin?.min + '~' + b?.conditionDTO?.unLogin?.max} /> : '--')
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 100,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 135,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateByName',
+            key: 'updateByName',
+            align: 'center',
+            width: 100,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            align: 'center',
+            width: 135,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            width: 220,
+            fixed: 'right',
+            render: (a: string, b: any) => (
+                <Row justify='center' gutter={[10, 0]}>
+                    <Col><a style={{ fontSize: "12px" }} onClick={() => { toCode(b) }}>礼包码</a></Col>
+                    <Col><a style={{ fontSize: "12px" }} onClick={() => { handle(b, 'log') }}>访问记录</a></Col>
+                    <Col><a style={{ fontSize: "12px" }} onClick={() => { handle(b, 'lq') }}>领取记录</a></Col>
+                    <Col><a style={{ fontSize: "12px" }} onClick={() => { editPack(b) }}>修改</a></Col>
+                    <Col>
+                        <Popconfirm
+                            title="确定删除?"
+                            onConfirm={() => { del(b.id) }}
+                            okText="是"
+                            cancelText="否"
+                        >
+                            <a style={{ fontSize: "12px", color: 'red' }}>删除</a>
+                        </Popconfirm>
+                    </Col>
+                </Row>
+            )
+        }
+    ]
+    return newArr
+}
+
+
+export default columnsPos

+ 62 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/tableConfigCode.tsx

@@ -0,0 +1,62 @@
+
+
+import WidthEllipsis from "@/components/widthEllipsis"
+import React from "react"
+import EnabledSwitch from "./enabledSwitch"
+
+function columnsPosCode(refresh: () => void) {
+
+    let newArr: any = [
+        {
+            title: '礼包码',
+            dataIndex: 'code',
+            key: 'code',
+            width: 100,
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '可用',
+            dataIndex: 'enabled',
+            key: 'enabled',
+            align: 'center',
+            width: 70,
+            render: (a: boolean, b: any) => (<EnabledSwitch value={a} id={b.id} onChange={refresh}/>)
+        },
+        {
+            title: '创建人',
+            dataIndex: 'createByName',
+            key: 'createByName',
+            align: 'center',
+            width: 100,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 135,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新人',
+            dataIndex: 'updateByName',
+            key: 'updateByName',
+            align: 'center',
+            width: 100,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        },
+        {
+            title: '更新时间',
+            dataIndex: 'updateTime',
+            key: 'updateTime',
+            align: 'center',
+            width: 135,
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        }
+    ]
+    return newArr
+}
+
+
+export default columnsPosCode

+ 44 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/tableConfigLog.tsx

@@ -0,0 +1,44 @@
+
+
+import WidthEllipsis from "@/components/widthEllipsis"
+import React from "react"
+
+function columnsPosLog() {
+
+    let newArr: any = [
+        {
+            title: '企业ID',
+            dataIndex: 'corpId',
+            key: 'corpId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '客服号ID',
+            dataIndex: 'corpUserId',
+            key: 'corpUserId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '企微用户ID',
+            dataIndex: 'externalUserId',
+            key: 'externalUserId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        }
+    ]
+    return newArr
+}
+
+
+export default columnsPosLog

+ 76 - 0
src/pages/gameDataStatistics/roleOperate/codeSender/tableConfigLqLog.tsx

@@ -0,0 +1,76 @@
+
+
+import WidthEllipsis from "@/components/widthEllipsis"
+import React from "react"
+
+function columnsPosLqLog() {
+
+    let newArr: any = [
+        {
+            title: '礼包码',
+            dataIndex: 'code',
+            key: 'code',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '企业ID',
+            dataIndex: 'corpId',
+            key: 'corpId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '客服号ID',
+            dataIndex: 'corpUserId',
+            key: 'corpUserId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '企微用户ID',
+            dataIndex: 'externalUserId',
+            key: 'externalUserId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a} isCopy/>)
+        },
+        {
+            title: '角色名称',
+            dataIndex: 'roleName',
+            key: 'roleName',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a}/>)
+        },
+        {
+            title: '角色ID',
+            dataIndex: 'roleId',
+            key: 'roleId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a}/>)
+        },
+        {
+            title: '玩家ID',
+            dataIndex: 'userId',
+            key: 'userId',
+            width: 100,
+            align: 'center',
+            render: (a: string) => (<WidthEllipsis value={a}/>)
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            render: (a: string) => (<WidthEllipsis value={a} />)
+        }
+    ]
+    return newArr
+}
+
+
+export default columnsPosLqLog

+ 1 - 1
src/pages/gameDataStatistics/roleOperate/pack/stagingModal.tsx

@@ -73,7 +73,7 @@ const StagingModal: React.FC<Props> = ({ visible, onClose, onChange, initialValu
                     filterOption={(input, option) =>
                         (option?.children as any)?.toLowerCase().indexOf(input.toLowerCase()) >= 0
                     }
-                    onChange={(e, option) => {
+                    onChange={(e, option: any) => {
                         setSuperGameId(option?.['data-super-id'])
                     }}
                 >

+ 177 - 2
src/services/gameData/roleOperate.ts

@@ -281,7 +281,7 @@ export async function getSendLogListApi(data: { taskId: number, pageNum: number,
 export async function reSendMsgByTaskIdApi(data: any) {
     return request(wapi + '/role/reSendMsgByTaskId', {
         method: 'POST',
-        params:data
+        params: data
     });
 }
 
@@ -291,7 +291,7 @@ export async function reSendMsgByTaskIdApi(data: any) {
 export async function reSendMsgByResultIdApi(data: any) {
     return request(wapi + '/role/reSendMsgByResultId', {
         method: 'POST',
-        params:data
+        params: data
     });
 }
 
@@ -664,3 +664,178 @@ export async function gameServerlistexcelApi(data: any) {
         responseType: 'blob'
     });
 }
+
+/**
+ * 获取发码器配置
+ * @returns 
+ */
+export async function getPackConfigListApi() {
+    return request(apiManage + `/game/gift/pack/config/list`, {
+        method: 'GET'
+    })
+}
+
+export interface GetPackLinkListProps {
+    pageNum: number,
+    pageSize: number,
+    codeLink?: string,
+    codeType?: string,
+    createBeginTime?: string
+    createEndTime?: string
+    gameId?: number
+    updateBeginTime?: string
+    updateEndTime?: string
+}
+/**
+ * 查询礼包码链接列表
+ * @param data 
+ * @returns 
+ */
+export async function getPackLinkListApi(data: GetPackLinkListProps) {
+    return request(apiManage + `/game/gift/pack/link/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface AddPackLinkProps {
+    gameId: number
+    codeType: string
+    conditionDTO: {
+        pay: {
+            max: number,
+            min: number
+        },
+        regTime: {
+            max: number,
+            min: number
+        },
+        unLogin: {
+            max: number,
+            min: number
+        }
+    }
+}
+/**
+ * 新增
+ * @param data 
+ * @returns 
+ */
+export async function addPackLinkApi(data: AddPackLinkProps) {
+    return request(apiManage + `/game/gift/pack/link/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+/**
+ * 修改
+ * @param data 
+ * @returns 
+ */
+export async function updatePackLinkApi(data: AddPackLinkProps) {
+    return request(apiManage + `/game/gift/pack/link/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 删除礼包码链接
+ * @param params 
+ * @returns 
+ */
+export async function delPackLinkApi(params: {id: number}) {
+    return request(apiManage + `/game/gift/pack/link/delete`, {
+        method: 'DELETE',
+        params
+    })
+}
+
+export interface GetPackLinkCodeListProps {
+    pageNum: number
+    pageSize: number
+    linkId: number
+    code?: string
+    createBeginTime?: string
+    createEndTime?: string
+    updateBeginTime?: string
+    updateEndTime?: string
+    enabled?: number
+}
+
+/**
+ * 查询链接礼包码
+ * @param data 
+ * @returns 
+ */
+export async function getPackLinkCodeListApi(data: GetPackLinkCodeListProps) {
+    return request(apiManage + `/game/gift/pack/link/code/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+export interface AddPackLinkCodeProps {
+    codeList?: string[]
+    linkId: number
+    gameId: number
+}
+
+/**
+ * 新增链接礼包码
+ * @param data 
+ * @returns 
+ */
+export async function AddPackLinkCodeApi(data: AddPackLinkCodeProps) {
+    return request(apiManage + `/game/gift/pack/link/code/add`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 修改礼包码状态
+ * @param data 
+ * @returns 
+ */
+export async function updatePackLinkCodeApi(data: { ids: number[], enabled: boolean }) {
+    return request(apiManage + `/game/gift/pack/link/code/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+export interface GetPackLinkLogListProps {
+    pageNum: number
+    pageSize: number
+    linkId: number
+    createBeginTime?: string
+    createEndTime?: string
+}
+
+/**
+ * 查询链接访问记录
+ * @param data 
+ * @returns 
+ */
+export async function getPackLinkLogListApi(data: GetPackLinkLogListProps) {
+    return request(apiManage + `/game/gift/pack/link/log/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 领取记录
+ * @param data 
+ * @returns 
+ */
+export async function getPackLinkLqLogListApi(data: GetPackLinkLogListProps) {
+    return request(apiManage + `/game/gift/pack/code/log/list`, {
+        method: 'POST',
+        data
+    })
+}