shenwu 8 ヶ月 前
コミット
f410eac0e8

+ 3 - 3
config/routes.tsx

@@ -58,13 +58,13 @@ const newMenu = [
     access: 'isShow',
     component: './MiniApp/BookManage',
   },
-  // 小程序端管理
+  // 组件管理
   {
     name: 'miniApp.appManage',
     icon: 'icon-xiaochengxu-wx',
-    path: '/miniApp/moduleConfig',
+    path: '/miniApp/compConfig',
     access: "isShow",
-    component: './Admin',
+    component: './MiniApp/CompConfig',
   },
   // 书币充值模板
   {

+ 1 - 0
package.json

@@ -51,6 +51,7 @@
     "@ant-design/icons": "^4.8.1",
     "@ant-design/pro-components": "^2.6.48",
     "@umijs/route-utils": "^2.2.2",
+    "ahooks": "^3.8.1",
     "antd": "^5.13.2",
     "antd-style": "^3.6.1",
     "classnames": "^2.5.1",

BIN
public/phone.png


+ 33 - 0
src/models/appPageConifg.tsx

@@ -0,0 +1,33 @@
+import { useReducer } from "react"
+
+type State = {
+    tabs: 0 | 1,//男生女生选项
+    compAc: number,//当前选中的组件,切换tabs请0
+    manList: any[]//男生配置
+    womanList: any[]//女生配置
+}
+type Action = {
+    params?: any,
+    type: "setAll",
+}
+export function reducer(state: State, action: Action) {
+    let { type, params } = action
+    switch (type) {
+        case 'setAll':
+            return { ...state, ...params }
+        default:
+            return state;
+    }
+}
+export default (): { state: State, dispatch: React.Dispatch<Action> } => {
+    const [state, dispatch] = useReducer(reducer, {
+        tabs: 0,
+        compAc: 0,
+        manList: [],
+        womanList: []
+    })
+    return {
+        state,
+        dispatch
+    }
+}

+ 62 - 0
src/pages/MiniApp/CompConfig/DrawerBox/content.tsx

@@ -0,0 +1,62 @@
+import { useDrop } from "ahooks";
+import { Col, Row, Space } from "antd";
+import React, { useState, useImperativeHandle, forwardRef, useRef } from "react";
+import styles from './index.less'
+import Banners from "../components/banners";
+import { useModel } from "@umijs/max";
+
+// 使用 forwardRef 以支持传递 ref
+const DragItem = () => {
+    const { state, dispatch } = useModel("appPageConifg")
+    const [isHovering, setIsHovering] = useState(false);
+    const dropRef = useRef(null);
+    useDrop(dropRef, {
+        onDom: (content: string, e) => {
+            console.log(`custom: ${content} dropped`);
+        },
+        onDragOver: (e) => {
+            // console.log("拖拽中",e)
+        },
+        onDragEnter: () => setIsHovering(true),
+        onDragLeave: () => setIsHovering(false),
+    });
+    console.log("state",state)
+    return <div ref={dropRef}>
+        <div className={styles.comp}>
+            <Banners data={[]} />
+        </div>
+    </div>
+};
+// 定义组件的 Props 类型
+interface Props {
+}
+const Content = forwardRef((props: Props, ref) => {
+    const { state, dispatch } = useModel("appPageConifg")
+    // 使用 useImperativeHandle 暴露方法给父组件
+    useImperativeHandle(ref, () => ({
+    }));
+    return <div className={styles.phone}>
+        <div className={styles.content} >
+            {/* 头部男女tabs */}
+            <Space className={styles.tabs} size={[20, 20]}>
+                {
+                    ["男生", "女生"].map((s, i) => {
+                        return <strong
+                            key={i}
+                            className={`${styles.tabs_text} ${state?.tabs === i ? styles.tabs_ac : ""}`}
+                            onClick={() => {
+                                dispatch({ type: "setAll", params: { tabs: i, compAc: 0 } })
+                            }}
+                        >
+                            {s}
+                        </strong>
+                    })
+                }
+
+            </Space>
+            {/* 内容 */}
+            <DragItem  />
+        </div>
+    </div>
+})
+export default Content;

+ 69 - 0
src/pages/MiniApp/CompConfig/DrawerBox/index.less

@@ -0,0 +1,69 @@
+// 左侧组件列表
+.pageList {
+    .banners {
+        width: 12vw;
+        height: calc(12vw / 3);
+        border: 1px solid #f3f3f3;
+        border-radius: 5px;
+    }
+}
+
+//中间内容区域
+.phone {
+    width: 25vw; /* 宽度设置为窗口宽度的50% */
+    height: calc(22vw *2); /* 按比例设置高度,保持与原始比例一致 */
+    background: url('../../../../../public/phone.png') no-repeat;
+    background-size: 210%;
+    background-position-x: 50%;
+    background-position-y: 50%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    // border: 1px solid #000;
+    .content {
+        width: 17vw; /* 内容宽度相对于窗口宽度 */
+        height: calc(18vw *2.1); /* 根据比例缩放高度 */
+        // border: 1px solid #000;
+        .tabs{ 
+             margin-bottom: 20px;
+            .tabs_text{
+                width: 48px;
+                height: 33px;
+                font-family: PingFangSC, PingFang SC;
+                font-weight: 500;
+                font-size: calc(100vw / 130);
+                color: #999;
+                line-height: 33px;
+                font-style: normal; 
+                cursor: pointer;
+            }
+            .tabs_ac{
+                font-size: calc(100vw / 100);  
+                color: #000;
+                position: relative;
+                &::after{
+                    content: "";
+                    width: 20px;
+                    height: 2px;
+                    background-color: red;
+                    display: inline-block;
+                    position: absolute;
+                    left: 50%;
+                    bottom: -2px;
+                    transform: translateX(-50%);
+                }
+            }
+        }
+        .comp{
+            &:hover{
+                border:1px solid rgb(72, 159, 247);
+                border-radius: 10px;
+            }
+        }
+        .comp{}
+    }
+}
+
+
+// 右侧参数设置
+.set {}

+ 62 - 0
src/pages/MiniApp/CompConfig/DrawerBox/index.tsx

@@ -0,0 +1,62 @@
+import { Button, Col, Drawer, Row } from "antd";
+import React, { useState, useImperativeHandle, forwardRef, useRef } from "react";
+import PageList from "./pageList";
+import Content from "./content";
+import SetData from "./set";
+import { useToken } from "@ant-design/pro-components";
+import { useSize } from "ahooks";
+// 定义组件的 Props 类型
+interface Props {
+}
+
+// 使用 forwardRef 以支持传递 ref
+const ModalForm = forwardRef((props: Props, ref) => {
+    const [open, setOpen] = useState(false);
+    const headerSize = useSize(document.querySelector('.ant-drawer-header'));
+    const { token } = useToken()
+    const PageListRef = useRef<any>(null);
+    const ContentRef = useRef<any>(null);
+    const SetDataRef = useRef<any>(null);
+    // close 方法,用于关闭 Drawer
+    const close = () => {
+        setOpen(false); // 将 open 设置为 false,关闭 Drawer
+    };
+
+    const openDrawer = () => {
+        setOpen(true); // 将 open 设置为 true,打开 Drawer
+    };
+
+    // 使用 useImperativeHandle 暴露方法给父组件
+    useImperativeHandle(ref, () => ({
+        close,        // 暴露 close 方法
+        openDrawer,   // 也可以暴露打开的方法
+    }));
+    return (
+        <Drawer
+            title="新建配置"
+            placement="right"
+            open={open}
+            onClose={close}
+            width={'85vw'}
+            extra={<Button type='primary'>保存配置</Button>}
+            maskClosable={false}
+        >
+            <Row wrap style={{height:`calc(100vh - ${(headerSize?.height || 0) + 50}px)`}}>
+                {/* 左侧页面列表 pageList*/}
+                <Col  xl={5} xs={24}>
+                    <PageList ref={PageListRef}/>
+                </Col>
+                {/* 中间展示 content*/}
+                <Col xl={14}  xs={24} style={{ background: token.colorBgContainerDisabled,display:'flex',alignItems:'center',justifyContent:'center' }}>
+                    <Content ref={ContentRef}/>
+                </Col>
+                {/* 右侧参数设置 set*/}
+                <Col xl={5} xs={24}>
+                    <SetData ref={SetDataRef}/>
+                </Col>
+            </Row>
+        </Drawer>
+    );
+});
+
+export default ModalForm;

+ 53 - 0
src/pages/MiniApp/CompConfig/DrawerBox/pageList.tsx

@@ -0,0 +1,53 @@
+import { useDrag } from "ahooks";
+import { Col, Row, Space, Typography } from "antd";
+import React, { useState, useImperativeHandle, forwardRef, useRef } from "react";
+import styles from './index.less'
+const DragItem = ({ children }: any) => {
+    const dragRef = useRef(null);
+    const [dragging, setDragging] = useState(false);
+
+    useDrag(children.key, dragRef, {
+        onDragStart: () => {
+            console.log(children.key)
+            setDragging(true);
+        },
+        onDragEnd: () => {
+            setDragging(false);
+        },
+    });
+
+    return (
+        <div
+            ref={dragRef}
+            style={{display:'inline-block'}}
+        >
+            {children}
+        </div>
+    );
+};
+// 定义组件的 Props 类型
+interface Props {
+}
+// 使用 forwardRef 以支持传递 ref
+const PageList = forwardRef((props: Props, ref) => {
+
+    // 使用 useImperativeHandle 暴露方法给父组件
+    useImperativeHandle(ref, () => ({
+    }));
+
+    return <Row gutter={[20, 20]} className={styles.pageList}>
+        <Col span={24}>
+            <Typography.Title level={4} >顶部组件</Typography.Title>
+            <DragItem ><div key="banners" className={styles.banners}>banners</div></DragItem>
+        </Col>
+        <Col span={24}>
+            <Typography.Title level={4} >内容组件</Typography.Title>
+            <Space>
+                <DragItem ><div key="hot_books">热门书籍</div></DragItem>
+                <DragItem ><div key="hot_category">热门分类</div></DragItem>
+            </Space>
+        </Col>
+    </Row>
+});
+
+export default PageList;

+ 20 - 0
src/pages/MiniApp/CompConfig/DrawerBox/set.tsx

@@ -0,0 +1,20 @@
+import { Col, Drawer, Row } from "antd";
+import React, { useState, useImperativeHandle, forwardRef } from "react";
+
+// 定义组件的 Props 类型
+interface Props {
+}
+
+// 使用 forwardRef 以支持传递 ref
+const SetData = forwardRef((props: Props, ref) => {
+
+    // 使用 useImperativeHandle 暴露方法给父组件
+    useImperativeHandle(ref, () => ({
+    }));
+
+    return <Row>
+         <Col>设置区域</Col>
+    </Row>
+});
+
+export default SetData;

+ 23 - 0
src/pages/MiniApp/CompConfig/components/banners.tsx

@@ -0,0 +1,23 @@
+import { useToken } from "@ant-design/pro-components";
+import { Carousel } from "antd";
+import styles from './index.less'
+function Banners(props: { data: any[] }) {
+    const { token } = useToken()
+    let { data } = props
+    return <Carousel autoplay autoplaySpeed={5000} className={styles.banners}>
+        {
+            data?.length === 0 ? <div style={{ borderRadius: "10px" }}>
+                <div className={styles.banners_box} style={{ background: token.colorBgBase, display: "flex", alignItems: 'center', justifyContent: 'center' }}>
+                    选中我在右侧配置内容
+                </div>
+            </div> : data?.map((item: any, index: number) => {
+                return <div key={index} style={{ borderRadius: "10px" }}>
+                    <div  className={styles.banners_box}  >
+                        <img src="https://bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/0d5f0b28395be160af37a59150f14ab3.jpg" style={{ objectFit: 'cover', width: "100%", height: "100%", borderRadius: "10px" }} />
+                    </div>
+                </div>
+            })
+        }
+    </Carousel>
+}
+export default Banners

+ 0 - 0
src/pages/MiniApp/CompConfig/components/hot_books.tsx


+ 0 - 0
src/pages/MiniApp/CompConfig/components/hot_category.tsx


+ 9 - 0
src/pages/MiniApp/CompConfig/components/index.less

@@ -0,0 +1,9 @@
+.banners {
+    .banners_box {
+        width: 100%;
+        height: calc(100vw / 20);
+        overflow: hidden;
+        border-radius: 10px;
+        font-size: calc(100vw / 150);
+    }
+}

+ 0 - 0
src/pages/MiniApp/CompConfig/components/phone.tsx


+ 366 - 0
src/pages/MiniApp/CompConfig/formConfig.tsx

@@ -0,0 +1,366 @@
+import { convertEnumArr } from "@/utils";
+import { ProFormColumnsType } from "@ant-design/pro-components";
+
+function formConfig(enumList?: any): ProFormColumnsType<{
+    name: string;
+    state: string;
+}>[] {
+    let templateTypeEnum = enumList?.TEMPLATE_TYPE?.values || []
+    let ORDER_TYPEEnum = enumList?.ORDER_TYPE?.values || []
+    let COLOREnum = enumList?.COLOR?.values || []
+    let VIP_DAYSEnum = enumList?.VIP_DAYS?.values || []
+    return [
+        {
+            title: '模板名称',
+            dataIndex: 'templateName',
+            colProps:{
+                span:24
+            },
+            formItemProps: {
+                rules: [
+                    {
+                        required: true,
+                        message: '此项为必填项',
+                    },
+                ],
+            }
+        },
+        {
+            title: '充值模板描述',
+            dataIndex: 'templateDescription',
+            valueType: 'textarea',
+        },
+        {
+            title: '备注',
+            dataIndex: 'remark',
+            valueType: 'textarea',
+        },
+        {
+            title: '首充档位',
+            dataIndex: 'rechargeConfigList',
+            valueType: 'formList',
+            fieldProps: {
+                min: 4,
+                max: 6,
+                creatorButtonProps: {
+                    creatorButtonText: "添加一个首充档位配置",
+                },
+            },
+            colProps: {
+                span: 24
+            },
+            columns: [
+                {
+                    valueType: 'group',
+                    colProps: {
+                        span: 24
+                    },
+                    columns: [
+                        {
+                            title: '类型',
+                            dataIndex: 'gearType',
+                            valueType: 'select',
+                            width: "100%",
+                            initialValue:1,
+                            fieldProps: { placeholder: "请选择档位类型" },
+                            colProps: {
+                                span: 4
+                            },
+                            formItemProps: {
+                                rules: [
+                                    {
+                                        required: true,
+                                        message: '此项为必填项',
+                                    },
+                                ],
+                            },
+                            valueEnum: new Map(ORDER_TYPEEnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                        },
+                        {
+                            valueType: 'dependency',
+                            name: ['gearType'],
+                            columns: ({ gearType }) => {
+                                return [
+                                    {
+                                        title: '价格',
+                                        dataIndex: 'price',
+                                        valueType: 'money',
+                                        width: "100%",
+                                        hideInForm: gearType === 3,
+                                        fieldProps: { placeholder: "输入价格" },
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: '赠送',
+                                        dataIndex: 'gift',
+                                        valueType: 'digit',
+                                        width: "100%",
+                                        hideInForm: gearType !== 1,
+                                        fieldProps: { placeholder: "输入赠送书币数" },
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: 'vip天数',
+                                        dataIndex: 'vipDays',
+                                        valueType: 'select',
+                                        width: "100%",
+                                        hideInForm: gearType !== 2,
+                                        fieldProps: { placeholder: "选择VIP天数" },
+                                        colProps: {
+                                            span: 4
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        },
+                                        valueEnum: new Map(VIP_DAYSEnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                                    },
+                                    {
+                                        title: '价格',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "价格在小说管理设置", disabled: true },
+                                        hideInForm: gearType !== 3,
+                                        colProps: {
+                                            span: 5
+                                        }
+                                    },
+                                    {
+                                        title: '文案',
+                                        dataIndex: 'description',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "输入展示文案" },
+                                        hideInForm: gearType !== 3,
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: '角标',
+                                        dataIndex: 'subscript',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "右上角角标文案" },
+                                        colProps: {
+                                            span: 5
+                                        }
+                                    },
+                                    {
+                                        title: '底色',
+                                        dataIndex: "color",
+                                        valueType: "radioButton",
+                                        width: "100%",
+                                        initialValue: false,
+                                        colProps: {
+                                            span: 4
+                                        },
+                                        valueEnum: new Map(COLOREnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                                    }
+                                ]
+                            }
+
+                        },
+                    ]
+                }
+            ]
+        },
+        {
+            title: '非首充档位',
+            dataIndex: 'rechargeConfigList1',
+            valueType: 'formList',
+            fieldProps: {
+                min: 4,
+                max: 6,
+                creatorButtonProps: {
+                    creatorButtonText: "添加一个非首充档位配置",
+                },
+            },
+            colProps: {
+                span: 24
+            },
+            columns: [
+                {
+                    valueType: 'group',
+                    colProps: {
+                        span: 24
+                    },
+                    columns: [
+                        {
+                            title: '类型',
+                            dataIndex: 'gearType',
+                            valueType: 'select',
+                            width: "100%",
+                            initialValue:1,
+                            fieldProps: { placeholder: "请选择档位类型" },
+                            colProps: {
+                                span: 4
+                            },
+                            formItemProps: {
+                                rules: [
+                                    {
+                                        required: true,
+                                        message: '此项为必填项',
+                                    },
+                                ],
+                            },
+                            valueEnum: new Map(ORDER_TYPEEnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                        },
+                        {
+                            valueType: 'dependency',
+                            name: ['gearType'],
+                            columns: ({ gearType }) => {
+                                return [
+                                    {
+                                        title: '价格',
+                                        dataIndex: 'price',
+                                        valueType: 'money',
+                                        width: "100%",
+                                        hideInForm: gearType === 3,
+                                        fieldProps: { placeholder: "输入价格" },
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: '赠送',
+                                        dataIndex: 'gift',
+                                        valueType: 'digit',
+                                        width: "100%",
+                                        hideInForm: gearType !== 1,
+                                        fieldProps: { placeholder: "输入赠送书币数" },
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: 'vip天数',
+                                        dataIndex: 'vipDays',
+                                        valueType: 'select',
+                                        width: "100%",
+                                        hideInForm: gearType !== 2,
+                                        fieldProps: { placeholder: "选择VIP天数" },
+                                        colProps: {
+                                            span: 4
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        },
+                                        valueEnum: new Map(VIP_DAYSEnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                                    },
+                                    {
+                                        title: '价格',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "价格在小说管理设置", disabled: true },
+                                        hideInForm: gearType !== 3,
+                                        colProps: {
+                                            span: 5
+                                        }
+                                    },
+                                    {
+                                        title: '文案',
+                                        dataIndex: 'description',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "输入展示文案" },
+                                        hideInForm: gearType !== 3,
+                                        colProps: {
+                                            span: 5
+                                        },
+                                        formItemProps: {
+                                            rules: [
+                                                {
+                                                    required: true,
+                                                    message: '此项为必填项',
+                                                },
+                                            ],
+                                        }
+                                    },
+                                    {
+                                        title: '角标',
+                                        dataIndex: 'subscript',
+                                        width: "100%",
+                                        fieldProps: { placeholder: "右上角角标文案" },
+                                        colProps: {
+                                            span: 5
+                                        }
+                                    },
+                                    {
+                                        title: '底色',
+                                        dataIndex: "color",
+                                        valueType: "radioButton",
+                                        width: "100%",
+                                        initialValue: false,
+                                        colProps: {
+                                            span: 4
+                                        },
+                                        valueEnum: new Map(COLOREnum?.map((item: { value: any; description: any; }) => [item.value, item.description]))
+                                    }
+                                ]
+                            }
+
+                        },
+                    ]
+                }
+            ]
+        },
+        {
+            title: '激活当前模板',
+            dataIndex: 'activateTemplate',
+            valueType: 'switch',
+            formItemProps: { layout: 'horizontal' },
+            colProps: { offset: 19 }
+        },
+    ]
+}
+
+export default formConfig

+ 206 - 0
src/pages/MiniApp/CompConfig/index.tsx

@@ -0,0 +1,206 @@
+import { BetaSchemaForm, PageContainer, ProFormInstance, useToken } from "@ant-design/pro-components"
+import { useAjax } from "@/Hook/useAjax"
+import { useModel } from "@umijs/max"
+import React, { JSXElementConstructor, Key, ReactElement, ReactNode, ReactPortal, useEffect, useMemo, useRef, useState } from "react"
+import { appRechargeTemplateList, appRechargeTemplateSave, appRechargeTemplateSwitch, appRechargeTemplateRemove, appRechargeTemplateUpdate, appRechargeTemplateInfo } from "@/services/miniApp/moduleConfig"
+import { Button, Card, Col, Empty, message, Modal, Row, Space } from "antd"
+import { DeleteOutlined, EditOutlined, ExclamationCircleFilled, PlusOutlined } from "@ant-design/icons"
+import formConfig from "./formConfig"
+import { Template } from "./template"
+import { createStyles } from "antd-style";
+import DrawerBox from "./DrawerBox"
+const useStyles = createStyles(({ token }) => {
+    return {
+        active: {
+            position: 'relative',
+            "&::after": {
+                content: '""', // 修正了这里的拼写错误
+                display: "block",
+                opacity: 1,
+                border: '15px solid #1890ff',
+                borderBlockEnd: '15px solid transparent',
+                borderInlineStart: '15px solid transparent', // 修正了多余的分号
+                borderStartEndRadius: '6px',
+                position: 'absolute',
+                top: 0,
+                right: 0
+            }
+        }
+    }
+})
+type DataItem = {
+    name: string;
+    state: string;
+};
+const Page: React.FC = () => {
+    let { token } = useToken()
+    let { initialState } = useModel("@@initialState")
+    let { enumList } = useModel("global", (global) => {
+        return {
+            enumList: global.state.enumList
+        }
+    })
+    let [open, setOpen] = useState<any>(null)
+    let [editValues, setEditValues] = useState<any>({})
+    let [activeT, setActiveT] = useState<any>()
+    let { styles } = useStyles()
+    let getList = useAjax((params) => appRechargeTemplateList(params))//获取模板列表
+    let add = useAjax((params) => appRechargeTemplateSave(params))//新增模板
+    let switchT = useAjax((params) => appRechargeTemplateSwitch(params))//切换模板
+    let editT = useAjax((params) => appRechargeTemplateUpdate(params))//编辑模板
+    let delT = useAjax((params) => appRechargeTemplateRemove(params))//删除模板
+    let infoT = useAjax((id) => appRechargeTemplateInfo(id))//模板详情
+    const formRef = useRef<ProFormInstance>();
+    const DrawerBoxRef = useRef<{ close: () => void; openDrawer: () => void }>(null);
+    let publicData = useMemo(() => {
+        return {
+            appId: initialState?.selectApp?.id || "",
+            distributorId: initialState?.currentUser?.distributorId,
+            appType: initialState?.selectApp?.appType || ""
+        }
+    }, [initialState?.selectApp, initialState?.currentUser?.distributorId])
+    // 获取表单数据
+    useEffect(() => {
+        getList.run(publicData).then(res => {
+            if (res.data) {
+                let activeObj = res?.data?.find((item: { activateTemplate: any }) => item.activateTemplate)
+                activeObj && setActiveT(activeObj.id)
+            }
+        })
+    }, [publicData])
+    // 提交表单
+    const submit = async (values: any) => {
+        let api = editValues?.id ? editT : add
+        if (!values.activateTemplate) {
+            values.activateTemplate = false
+        }
+        if (editValues?.id) {
+            values.id = editValues?.id
+        }
+        values.rechargeConfigs = []
+        if (values.rechargeConfigList?.length > 0) {//首充
+            values.rechargeConfigs.push({
+                firstRecharge: true,//是否首充档位;true:首充 false:非首充
+                rechargeConfigList: values.rechargeConfigList,
+            })
+        }
+        if (values.rechargeConfigList1?.length > 0) {//非首充
+            values.rechargeConfigs.push(
+                {
+                    firstRecharge: false,//是否首充档位;true:首充 false:非首充
+                    rechargeConfigList: values.rechargeConfigList1,
+                }
+            )
+        }
+        delete values.rechargeConfigList
+        delete values.rechargeConfigList1
+        api.run({ ...values, ...publicData }).then(res => {
+            if (res.code === 200) {
+                getList.refresh()
+                message.success(values.id ? "编辑模板成功" : "新建模板成功!")
+                closeForm(false)
+            }
+        })
+    }
+    // 关闭表单弹窗和重置表单内容
+    const closeForm = (b: boolean, values?: any) => {
+        if (!b) {
+            setEditValues({})
+            formRef?.current?.resetFields?.()
+            setOpen(b)
+        } else {
+            setOpen(b)
+            if (values) {
+                infoT.run(values.id).then(res => {
+                    if (res.code === 200) {
+                        let data = res.data
+                        for (let item of data.rechargeConfigs) {
+                            if (item.firstRecharge) {
+                                data.rechargeConfigList = item.rechargeConfigList
+                            } else {
+                                data.rechargeConfigList1 = item.rechargeConfigList
+                            }
+                        }
+                        setEditValues(data)
+                        formRef?.current?.setFieldsValue(data)
+                    }
+                })
+            }
+        }
+    }
+    // 模板切换
+    const switchTemplate = (id: any) => {
+        let params = { ...publicData, id }
+        Modal.confirm({
+            title: '切换充值模板',
+            content: '是否要切换为当前充值模板?',
+            icon: <ExclamationCircleFilled />,
+            okText: "切换",
+            onOk: () => {
+                switchT.run(params).then(res => {
+                    if (res.code === 200) {
+                        setActiveT(id)
+                        getList.refresh()
+                        message.success("切换成功")
+                    }
+                })
+            }
+        });
+    }
+    // 模板删除
+    const delTemplate = (id: any) => {
+        Modal.confirm({
+            title: '删除充值模板',
+            content: '是否要删除当前充值模板?',
+            icon: <ExclamationCircleFilled />,
+            okType: 'danger',
+            okText: "删除",
+            onOk: () => {
+                delT.run(id).then(res => {
+                    if (res.code === 200) {
+                        getList.refresh()
+                        message.success("删除成功")
+                    }
+                })
+            }
+        });
+    }
+    console.log(DrawerBoxRef)
+    return <PageContainer
+        extra={getList?.data?.data?.length > 0 && <Button type="primary" onClick={DrawerBoxRef?.current?.openDrawer}><PlusOutlined />新增配置</Button>}
+    >
+        {/* 模板列表 */}
+        <Row gutter={[20, 20]}>
+            {
+                getList?.data?.data?.map((item: { id: Key | null | undefined; templateName: string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | null | undefined; rechargeConfigs: any[]; templateType: number; templateDescription: string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | null | undefined }) => {
+                    return <Col key={item.id} style={{ cursor: 'pointer' }} onClick={() => { switchTemplate(item.id) }} xs={{ span: 24 }} sm={{ span: 8 }}>
+                        <Card className={activeT === item.id ? styles.active : ""} style={{ background: activeT === item.id ? token.colorPrimaryBgHover : token.colorFillAlter }} hoverable>
+                            <h3 style={{ fontSize: 16, fontWeight: 500, color: token.colorText, fontFamily: 'PingFang SC' }}>{item.templateName}</h3>
+                            {
+                                item.rechargeConfigs?.map((config, index) => {
+                                    return <React.Fragment key={index}>
+                                        <Template data={config} enmuList={enumList} />
+                                    </React.Fragment>
+                                })
+                            }
+                            <p style={{ marginTop: 20, color: token.colorTextSecondary, fontSize: 12 }}>{item.templateDescription}</p>
+                            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'end' }}>
+                                <Space>
+                                    <Button size='small' type='primary' onClick={(e) => { e.stopPropagation(); closeForm(true, item) }}><EditOutlined /></Button>
+                                    <Button size='small' type="primary" danger onClick={(e) => { e.stopPropagation(); delTemplate(item.id) }}><DeleteOutlined /></Button>
+                                </Space>
+                            </div>
+                        </Card>
+                    </Col>
+                })
+            }
+            {
+                getList?.data?.data?.length === 0 && <div style={{ width: "100%", height: "100%", marginTop: "10%" }}><Empty description={<Space direction='vertical'><div>暂无模板数据</div><Button type="primary" onClick={() => { closeForm(true) }}><PlusOutlined />新增模板</Button></Space>} /></div>
+            }
+        </Row>
+        {/* 模板弹窗 */}
+        <DrawerBox ref={DrawerBoxRef}/>
+    </PageContainer>
+
+}
+export default Page

+ 142 - 0
src/pages/MiniApp/CompConfig/template.tsx

@@ -0,0 +1,142 @@
+import { Card, Col, Row } from "antd";
+import jb from '../../../../public//jb.png';
+import vip from '../../../../public//vip.png';
+import top from '../../../../public//top-r1.png';
+import { createStyles } from "antd-style";
+
+
+export function Template(params: { data: any, enmuList: any }) {
+    let { data, enmuList } = params
+    let { firstRecharge, rechargeConfigList } = data
+    const useStyles = createStyles((props) => {
+        let { token } = props
+        let navTheme = localStorage.getItem("navTheme")//全局 2亮色 3黑色
+        return {
+            cardBox: {
+                position: 'relative'
+            },
+            bgVip: {
+                backgroundImage: "linear-gradient(180deg, #fff1dc 60%, #fffdfa 100%)",
+                color: navTheme == "3" ? "#000" : "unset"
+            },
+            bg: {
+                backgroundImage: 'linear-gradient(180deg, #ffeced 60%, #fffafa 100%)',
+                color: navTheme == "3" ? "#000" : "unset"
+            },
+            topRight: {
+                lineHeight: '20px',
+                background: "#ff2441",
+                position: 'absolute',
+                top: 0,
+                right: 0,
+                fontSize: 12,
+                borderRadius: "0px 8px 0 8px",
+            },
+            topRightVip: {
+                background: "#ffd89d !important",
+            },
+            // 
+            topRightSpan1: { display: 'inline-block', background: `url(${top}) no-repeat`, minWidth: 40, height: 20, padding: "0 10px 0 6px", backgroundSize: "100% 100%", color: "#fff" },
+            topRightSpan2: { color: "#fff", display: 'inline-block', padding: "0 5px" },
+            del: { fontSize: 14, textDecoration: 'line-through', marginLeft: 7, color: token.colorTextTertiary, fontWeight: "500" }
+        }
+    });
+    let { styles } = useStyles()
+
+    return <Card bordered={false} >
+        <Row gutter={[0, 15]}>
+            {
+                rechargeConfigList?.map((item: any, index: any) => {
+                    let {
+                        color,//背景色
+                        description,//整数购买文案
+                        extra,//右上角描述值
+                        gearType,//充值类型
+                        gift,//赠送
+                        obtain,//获得书币/vip每天价格
+                        price,//价格
+                        subscript,//右上角文案 首充|超值等
+                        vipDays,//vip天数
+                    } = item
+                    switch (gearType) {
+                        case 1://充值
+                            return <Col key={index} span={11} offset={index % 2 === 0 ? 0 : 1} >
+                                <Card className={`${styles.cardBox} ${color && styles.bg}`} styles={{ body: { maxHeight: 90, padding: '20px 15px' } }}>
+                                    {/* 右上角 */}
+                                    <div className={`${styles.topRight}`} style={subscript ? { borderRadius: "8px 8px 0 8px", } : {}} >
+                                        {
+                                            <>
+                                                {subscript && <span className={styles.topRightSpan1}>{subscript}</span>}
+                                                <span className={styles.topRightSpan2} style={subscript ? { transform: 'translateX(-4px)' } : {}}>多送{extra}元</span>
+                                            </>
+                                        }
+                                    </div>
+                                    <div style={{ display: 'flex', flexFlow: 'column' }}>
+                                        <strong >
+                                            <span style={{ fontSize: '20px', marginRight: 3 }} >¥</span>
+                                            <span style={{ fontSize: 22, fontFamily: 'Yuewen Font' }}>{price}</span>
+                                        </strong>
+                                        <span style={{ fontSize: 12, marginTop: 2, color: "#777", display: 'block' }}>{obtain}书币 送{gift}书券</span>
+                                    </div>
+                                    {color && <img src={jb} style={{ position: 'absolute', right: 0, bottom: 0, width: 50 }} />}
+                                </Card>
+                            </Col>
+                        case 2://vip
+                            let vipEnum = enmuList?.VIP_DAYS?.values;
+                            let vipStr = vipEnum?.find((i: { value: any; }) => i.value === vipDays)?.description || ""
+                            return <Col key={index} span={11} offset={index % 2 === 0 ? 0 : 1} >
+                                <Card className={`${styles.cardBox} ${color && styles.bgVip}`} styles={{ body: { maxHeight: 90, padding: '20px 15px' } }}>
+                                    {/* 右上角 */}
+                                    <div className={`${styles.topRight} ${styles.topRightVip}`} style={subscript ? { borderRadius: "8px 8px 0 8px", } : {}}>
+                                        {
+                                            <>
+                                                {subscript && <span className={styles.topRightSpan1}>{subscript}</span>}
+                                                <span className={styles.topRightSpan2}>{vipStr}</span>
+                                            </>
+                                        }
+                                    </div>
+                                    <div style={{ display: 'flex', flexFlow: 'column' }}>
+                                        <strong >
+                                            <span style={{ fontSize: '20px', marginRight: 3 }} >¥</span>
+                                            <span style={{ fontSize: 22, fontFamily: 'Yuewen Font' }}>{price}</span>
+                                        </strong>
+                                        <span style={{ fontSize: 12, marginTop: 2, color: "#777", display: 'block' }}>¥{obtain}元/天</span>
+                                    </div>
+                                    {color && <img src={vip} style={{ position: 'absolute', right: 0, bottom: 0, width: 30 }} />}
+                                </Card>
+                            </Col>
+                        case 3://整本
+                            return <Col key={index} span={11} offset={index % 2 === 0 ? 0 : 1} >
+                                <Card className={`${styles.cardBox} `} styles={{ body: { maxHeight: 90, padding: '20px 15px' } }}>
+                                    {/* 右上角 */}
+                                    <div className={styles.topRight}>
+                                        {
+                                            subscript ? <> <span>{item.description}</span></> : <>
+                                                <span className={styles.topRightSpan1}>{subscript}</span>
+                                                <span className={styles.topRightSpan2}>{item.description}</span>
+                                            </>
+                                        }
+                                    </div>
+                                    <div style={{ display: 'flex', flexFlow: 'column' }}>
+                                        <strong >
+                                            <span style={{ fontSize: '20px', marginRight: 3 }} >¥</span>
+                                            <span style={{ fontSize: 22, fontFamily: 'Yuewen Font' }}>{price}</span>
+                                            {/* {isVip && item.gift && <span className={styles.del}>¥{item.gift}</span>} */}
+                                        </strong>
+                                        {/* {isVip ?
+                                    <span style={{ fontSize: 12, marginTop: 2, color: "#777", display: 'block' }}>{isOne ? "仅" : "¥"}{item.extra}元/天</span>
+                                    :
+                                    <span style={{ fontSize: 12, marginTop: 2, color: "#777", display: 'block' }}>{item.gift}书币 送{item.extra}书券</span>} */}
+                                    </div>
+                                    {/* {isOne && <img src={isVip ? vip : jb} style={{ position: 'absolute', right: 0, bottom: 0, width: isVip ? 30 : 50 }} />} */}
+                                </Card>
+                            </Col>
+                        default:
+                            break;
+                    }
+
+                })
+            }
+        </Row>
+    </Card >
+}

+ 19 - 0
src/services/miniApp/compConfig/index.tsx

@@ -0,0 +1,19 @@
+import { api } from '@/services/api';
+import { request } from '@umijs/max';
+type Page = {
+    pageSize: number | string,
+    pageNum: number | string
+}
+interface PublicParams{
+    miniappId: string,//微信小程序组件ID
+    distributorId: string,//分销商ID
+    appType: string,//小程序类型 1:微信小程序、2:抖音小程序
+}
+export interface Params extends Page,PublicParams{}
+/**微信小程序组件分页列表 */
+export async function appRechargeTemplateList(params: Params) {
+    return request(api + '/admin/appRechargeTemplate/list', {
+        method: 'GET',
+        params
+    });
+}