Browse Source

feat:1.3完成

lvkun 3 years ago
parent
commit
73941d1d8b

+ 2 - 1
.eslintignore

@@ -5,4 +5,5 @@
 public
 dist
 .umi
-mock
+mock
+src/

+ 6 - 0
config/config.ts

@@ -133,6 +133,12 @@ export default defineConfig({
               icon: 'dashboard',
               component: './club',
             },
+            {
+              path: '/weapp',
+              name: '小程序管理',
+              icon: 'WechatOutlined',
+              component: './weapp',
+            },
             {
               path: '/complaint',
               name: '投诉管理',

+ 8 - 7
src/components/upload/data.d.ts

@@ -1,7 +1,8 @@
-import type {  postOrPutPlateType } from '@/pages/tieba/plate/data'
-
-export type PicturesWallType = {
-    maxCount: number,
-    setCoverFn: (url: string) => void,
-    imgUrl: string
-}
+import type { postOrPutPlateType } from '@/pages/tieba/plate/data';
+
+export type PicturesWallType = {
+  maxCount: number;
+  setCoverFn: (url: string) => void;
+  imgUrl: string;
+  desc: string;
+};

+ 102 - 110
src/components/upload/index.tsx

@@ -1,110 +1,102 @@
-import React, { Component } from 'react'
-
-import { Upload, Modal } from 'antd';
-import { PlusOutlined } from '@ant-design/icons';
-
-import type { PicturesWallType } from './data'
-
-import { baseUrl } from '@/utils/request'
-
-function getBase64(file) {
-    return new Promise((resolve, reject) => {
-      const reader = new FileReader();
-      reader.readAsDataURL(file);
-      reader.onload = () => resolve(reader.result);
-      reader.onerror = error => reject(error);
-    });
-  }
-
-// {
-//     uid: '-1',
-//     name: 'image.png',
-//     status: 'done',
-//     url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-// }
-
-class PicturesWall extends Component<PicturesWallType> {
-    state = {
-      previewVisible: false,
-      previewImage: '',
-      previewTitle: '',
-      fileList: [],
-    };
-    
-
-    componentDidMount () {
-        const { imgUrl } = this.props
-        console.log(imgUrl);
-          if (!imgUrl) return
-          this.setState({
-            fileList: [
-                {
-                    uid: '-1',
-                    name: 'image.png',
-                    status: 'done',
-                    url: imgUrl,
-                },
-            ]
-        })
-    }
-
-    handleCancel = () => this.setState({ previewVisible: false });
-  
-    handlePreview = async (file: Record<string, any>) => {
-      if (!file.url && !file.preview) {
-        file.preview = await getBase64(file.originFileObj);
-      }
-  
-      this.setState({
-        previewImage: file.url || file.preview,
-        previewVisible: true,
-        previewTitle: file.name || file.url.substring(file.url.lastIndexOf('/') + 1),
-      });
-    };
-  
-    handleChange = ({ fileList }: {fileList: Record<string, any>}): void => {
-        console.log(fileList);
-        
-        this.setState({ fileList })
-
-        if (fileList[0]?.status === 'done') {
-            // fileList.response.data
-            this.props.setCoverFn(fileList[0].response.data)
-        }
-    };
-    render() {
-      const { previewVisible, previewImage, fileList, previewTitle } = this.state;
-      const uploadButton = (
-        <div>
-          <PlusOutlined />
-          <div style={{ marginTop: 8 }}>上传贴吧图标</div>
-        </div>
-      );
-      const { maxCount } = this.props
-      return (
-        <>
-          <Upload
-            action={`${baseUrl}/forum/file/uploadImage`}
-            listType="picture-card"
-            fileList={fileList}
-            maxCount={maxCount}
-            onPreview={this.handlePreview}
-            onChange={this.handleChange}
-          >
-            {fileList.length >= maxCount ? null : uploadButton}
-          </Upload>
-          <Modal
-            visible={previewVisible}
-            title={previewTitle}
-            footer={null}
-            onCancel={this.handleCancel}
-          >
-            <img alt="example" style={{ width: '100%' }} src={previewImage} />
-          </Modal>
-        </>
-      );
-    }
-  }
-
-  
-  export default PicturesWall
+import React, { Component } from 'react';
+
+import { Upload, Modal } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+
+import type { PicturesWallType } from './data';
+
+import { baseUrl } from '@/utils/request';
+
+function getBase64(file) {
+  return new Promise((resolve, reject) => {
+    const reader = new FileReader();
+    reader.readAsDataURL(file);
+    reader.onload = () => resolve(reader.result);
+    reader.onerror = (error) => reject(error);
+  });
+}
+
+class PicturesWall extends Component<PicturesWallType> {
+  state = {
+    previewVisible: false,
+    previewImage: '',
+    previewTitle: '',
+    fileList: [],
+  };
+
+  componentDidMount() {
+    const { imgUrl } = this.props;
+    console.log(this.props, '2233');
+    if (!imgUrl) return;
+    this.setState({
+      fileList: [
+        {
+          uid: '-1',
+          name: 'image.png',
+          status: 'done',
+          url: imgUrl,
+        },
+      ],
+    });
+  }
+
+  handleCancel = () => this.setState({ previewVisible: false });
+
+  handlePreview = async (file: Record<string, any>) => {
+    if (!file.url && !file.preview) {
+      file.preview = await getBase64(file.originFileObj);
+    }
+
+    this.setState({
+      previewImage: file.url || file.preview,
+      previewVisible: true,
+      previewTitle: file.name || file.url.substring(file.url.lastIndexOf('/') + 1),
+    });
+  };
+
+  handleChange = ({ fileList }: { fileList: Record<string, any> }): void => {
+    console.log(fileList);
+
+    this.setState({ fileList });
+
+    if (fileList[0]?.status === 'done') {
+      // fileList.response.data
+      this.props.setCoverFn(fileList[0].response.data);
+    }
+  };
+  render() {
+    const { maxCount, desc } = this.props;
+    const { previewVisible, previewImage, fileList, previewTitle } = this.state;
+    const uploadButton = (
+      <div>
+        <PlusOutlined />
+        <div style={{ marginTop: 8 }}>{desc}</div>
+      </div>
+    );
+
+    return (
+      <>
+        <Upload
+          action={`${baseUrl}/forum/file/uploadImage`}
+          listType="picture-card"
+          fileList={fileList}
+          maxCount={maxCount}
+          onPreview={this.handlePreview}
+          onChange={this.handleChange}
+        >
+          {fileList.length >= maxCount ? null : uploadButton}
+        </Upload>
+        <Modal
+          visible={previewVisible}
+          title={previewTitle}
+          footer={null}
+          onCancel={this.handleCancel}
+        >
+          <img alt="example" style={{ width: '100%' }} src={previewImage} />
+        </Modal>
+      </>
+    );
+  }
+}
+
+export default PicturesWall;

+ 4 - 4
src/models/user.ts

@@ -46,7 +46,6 @@ export type UserModelType = {
 
 const UserModel: UserModelType = {
   namespace: 'user',
-
   state: {
     currentUser: {},
     roleList: [],
@@ -68,14 +67,15 @@ const UserModel: UserModelType = {
       });
     },
     *getRoleList(_, { call, put }) {
-      console.log(_, 'call(getRoleList)');
-      const { payload } = _;
+      const { payload, callback } = _;
       const r = yield call(getRoleList, payload);
-      console.log(r);
+
       yield put({
         type: 'saveRoleList',
         payload: r.data.records,
       });
+
+      callback();
     },
   },
 

+ 148 - 6
src/pages/agent/list/detail.tsx

@@ -1,7 +1,23 @@
-import React, { useEffect, useState } from 'react';
-import { Col, Modal, Row, Image, Avatar, Space, Card, Descriptions, Tag, Spin } from 'antd';
+import React, { useEffect, useState, useRef } from 'react';
+import {
+  Col,
+  Modal,
+  Row,
+  Image,
+  Avatar,
+  Space,
+  Card,
+  Descriptions,
+  Tag,
+  Spin,
+  Input,
+  Popconfirm,
+  message,
+} from 'antd';
 import { getAgentDetail } from '@/services/agent';
 
+import { editAgentInfo } from '@/services/agent';
+
 import type { roleListType } from './index';
 
 type DetailModalType = {
@@ -24,15 +40,36 @@ type userInfoType = {
   phoneNumber: string;
   followBarNum: number | null;
   followBars: followBarsType[];
+  tags: string[];
   [key: string]: any;
 };
 
+const tabList = [
+  {
+    key: 'tieba',
+    tab: '关注的吧',
+  },
+  {
+    key: 'tag',
+    tab: '角色标签',
+  },
+];
+
+const newTagName = '+ 新增标签';
+
 const DetailModal: React.FC<DetailModalType> = ({ visible, id, closeModal }) => {
   const [userInfo, setUserInfo]: [userInfoType, (params: userInfoType) => void] = useState<
     Record<string, any>
   >({});
+
   const [spinning, setSpinning] = useState<boolean>(false);
 
+  const [tabKey, setTabKey] = useState<'tieba' | 'tag'>('tieba');
+
+  const [tagModalVisable, setTagModalVisable] = useState<boolean>(false);
+
+  const [tagName, setTagName] = useState<string>('');
+
   // 获取详情
   const GetAgentDetail = async () => {
     setSpinning(true);
@@ -52,7 +89,7 @@ const DetailModal: React.FC<DetailModalType> = ({ visible, id, closeModal }) =>
   const renderFollowBar = (): React.ReactNode => {
     return (
       <Row gutter={[16, 16]}>
-        {userInfo.followBars &&
+        {userInfo.followBars && userInfo.followBars.length > 0 ? (
           userInfo.followBars.map((item) => (
             <Col key={item.id} span={4}>
               <Space direction="vertical" align="center">
@@ -66,11 +103,114 @@ const DetailModal: React.FC<DetailModalType> = ({ visible, id, closeModal }) =>
                 <span>{item.label}</span>
               </Space>
             </Col>
-          ))}
+          ))
+        ) : (
+          <div>暂无关注的吧</div>
+        )}
       </Row>
     );
   };
 
+  // 打开新增标签弹窗
+  const openTagModal = () => setTagModalVisable(true);
+
+  // 关闭新增标签弹窗
+  const closeTagModal = () => setTagModalVisable(false);
+
+  // 新增标签
+  const addCustomTag = () => {};
+  // 删除标签
+  const clearTags = (e: React.MouseEvent<HTMLElement, MouseEvent> | undefined, _tag: string) => {
+    e && e.preventDefault();
+
+    const tags = userInfo.tags;
+
+    const residueTags = tags.filter((tag) => tag !== _tag && tag !== newTagName);
+
+    EditAgentInfo(residueTags, 'delete');
+  };
+
+  // 添加标签
+  const addTags = () => {
+    const residueTags = userInfo.tags.filter((_tag) => _tag !== newTagName);
+
+    residueTags.push(tagName);
+
+    EditAgentInfo(residueTags, 'add');
+  };
+
+  // 更改代理商信息
+  const EditAgentInfo = async (residueTags: string[], type: 'delete' | 'add') => {
+    const $par = {
+      ...userInfo,
+      tags: residueTags,
+    };
+
+    const { data, code } = await editAgentInfo($par);
+
+    message.success(type === 'add' ? '增加成功' : '删除成功');
+
+    if (code === 0 && data) {
+      setUserInfo({
+        ...userInfo,
+        tags: residueTags,
+      });
+
+      setTagModalVisable(false);
+
+      setTagName('');
+    }
+  };
+
+  const changeTags = (e) => {
+    console.log(e.target.value);
+
+    setTagName(e.target.value);
+  };
+
+  // 渲染自定义标签
+  const renderCustomTag = () => {
+    const redisueTagCount =
+      userInfo.tags && userInfo.tags.filter((tag) => tag !== newTagName).length;
+
+    let tags: string[] = [];
+
+    if (redisueTagCount === 2) {
+      tags = userInfo.tags;
+    } else if (redisueTagCount === 1) {
+      tags = userInfo.tags.concat(newTagName);
+    } else {
+      tags = [newTagName];
+    }
+
+    return (
+      <>
+        {tags.map((tag, index) => (
+          <a key={tag + index}>
+            {tag === newTagName ? (
+              <Tag closable={false} onClick={openTagModal}>
+                {tag}
+              </Tag>
+            ) : (
+              <Popconfirm title="确定要删除这个标签吗?" onConfirm={(e) => clearTags(e, tag)}>
+                <Tag color="blue">{tag}&nbsp; x</Tag>
+              </Popconfirm>
+            )}
+          </a>
+        ))}
+        <Modal title="新增标签" visible={tagModalVisable} onCancel={closeTagModal} onOk={addTags}>
+          <Input
+            width={100}
+            value={tagName}
+            onChange={changeTags}
+            maxLength={4}
+            placeholder="请填写标签内容(最多四个字)"
+          />
+        </Modal>
+      </>
+    );
+  };
+
   // 渲染用户角色
   const renderAuthRoles = (): React.ReactNode => {
     return (
@@ -113,10 +253,12 @@ const DetailModal: React.FC<DetailModalType> = ({ visible, id, closeModal }) =>
           </Col>
           <Col span={16}>
             <Card
-              title={`关注的贴吧板块${userInfo?.followBars?.length}`}
+              tabList={tabList}
               style={{ width: '100%', height: '100%' }}
+              activeTabKey={tabKey}
+              onTabChange={(key) => setTabKey(key as 'tieba' | 'tag')}
             >
-              {renderFollowBar()}
+              {tabKey === 'tieba' ? renderFollowBar() : renderCustomTag()}
             </Card>
           </Col>
         </Row>

+ 19 - 13
src/pages/agent/list/index.tsx

@@ -13,7 +13,7 @@ type User = {
   nickName: string;
   phoneNumber: number;
   region: string;
-  authRoles: string;
+  authRoles: any[];
   followBarNum: number;
   postsNum: number;
   state: number;
@@ -204,18 +204,6 @@ const Agent: React.FC = () => {
             key="region"
             render={(region) => <span>{region || '未知地区'}</span>}
           />
-          <Table.Column<User>
-            title="认证角色"
-            dataIndex="authRoles"
-            key="authRoles"
-            render={(authRoles) => (
-              <>
-                {authRoles.map((item: Record<string, any>) => {
-                  return <span key={item.id}>{item.roleLabel}</span>;
-                })}
-              </>
-            )}
-          />
           <Table.Column<User> title="关注贴吧板块" dataIndex="followBarNum" key="followBarNum" />
           <Table.Column<User> title="发帖数" dataIndex="postsNum" key="postsNum" />
           <Table.Column<User> title="发帖数" dataIndex="postsNum" key="postsNum" />
@@ -226,6 +214,24 @@ const Agent: React.FC = () => {
             render={(text) => <Tag color="blue"> {text === 1 ? '正常' : '禁用中'} </Tag>}
           />
           <Table.Column<User> title="日期" dataIndex="createTime" key="stcreateTimeate" />
+          <Table.Column<User>
+            title="加入天数"
+            dataIndex="joinday"
+            key="joinday"
+            render={(test, records) => (
+              <Space>
+                <Row style={{ flexDirection: 'column' }}>
+                  {records.authRoles.map((_auth) => (
+                    <Col key={_auth.roleLabel} style={{ marginTop: '5px' }}>
+                      <Tag>
+                        {_auth.roleLabel} {_auth.joinDays}天
+                      </Tag>
+                    </Col>
+                  ))}
+                </Row>
+              </Space>
+            )}
+          />
           <Table.Column<User>
             key="action"
             title="操作"

+ 70 - 10
src/pages/club/ClubModal.tsx

@@ -1,8 +1,14 @@
-import React from 'react';
-import { Modal, Input, Row, Col } from 'antd';
+import React, { useEffect, useState } from 'react';
+import { Modal, Input, Row, Col, Select } from 'antd';
 
 import { role } from '@/types/role';
 
+import { getWeappList } from '@/services/weapp';
+
+import { WeappItem } from '@/types/index';
+
+const { Option } = Select;
+
 const layout = {
   labelCol: { span: 5 },
   wrapperCol: { span: 16 },
@@ -14,17 +20,51 @@ interface IProps {
   onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
   onsubmit: () => void;
   closeModal: () => void;
+  onChangeMiniProgramList: (selectWeapp: WeappItem[]) => void;
 }
 
-const ClubModal: React.FC<IProps> = ({ visible, curClub, onsubmit, onChange, closeModal }) => {
+const ClubModal: React.FC<IProps> = ({
+  visible,
+  curClub,
+  onsubmit,
+  onChange,
+  closeModal,
+  onChangeMiniProgramList,
+}) => {
+  const [weappList, setWeappList] = useState<WeappItem[]>([]);
+
+  const [selectWeapp, setSelectWeapp] = useState<WeappItem[]>([]);
+
+  const [loading, setLoading] = useState<boolean>(false);
+
+  useEffect(() => {
+    if (visible) {
+      GetWeappList();
+    }
+  }, [visible]);
+
+  const GetWeappList = async () => {
+    // setLoading(true)
+    const { data, status } = await getWeappList({ isPage: 1, pageSize: 10000, page: 1, total: 0 });
+    // setLoading(false)
+    if (status === 200) {
+      console.log(data);
+      setWeappList(data.entityList);
+    }
+  };
+
+  const handleChange = (value: string[]) => {
+    const r = weappList.filter((weapp) => value.includes(weapp.id));
+
+    onChangeMiniProgramList(r);
+  };
+
+  const onOk = () => {
+    onsubmit();
+  };
+
   return (
-    <Modal
-      width="400px"
-      visible={visible}
-      title="修改俱乐部名称"
-      onOk={() => onsubmit()}
-      onCancel={closeModal}
-    >
+    <Modal width="600px" visible={visible} title="修改俱乐部名称" onOk={onOk} onCancel={closeModal}>
       <Row align="middle">
         <Col span={6}>俱乐部分类:</Col>
         <Col span={12}>{curClub.previousLabel}</Col>
@@ -35,6 +75,26 @@ const ClubModal: React.FC<IProps> = ({ visible, curClub, onsubmit, onChange, clo
           <Input value={curClub.label} onChange={(e) => onChange(e)} />
         </Col>
       </Row>
+      <Row align="middle" style={{ marginTop: '14px' }}>
+        <Col span={6}>添加小程序:</Col>
+        <Col span={12}>
+          <Select
+            mode="multiple"
+            allowClear
+            style={{ width: '100%' }}
+            placeholder="点击添加小程序"
+            defaultValue={curClub?.miniProgramList?.map((weapp) => weapp.id)}
+            onChange={handleChange}
+          >
+            {weappList.map((weapp) => (
+              <Option key={weapp.id} value={weapp.id}>
+                {weapp.name}
+              </Option>
+            ))}
+            {/* {children} */}
+          </Select>
+        </Col>
+      </Row>
     </Modal>
   );
 };

+ 18 - 1
src/pages/club/index.tsx

@@ -16,6 +16,7 @@ import { role } from '@/types/role';
 import { putRoleList } from '@/services/role';
 
 import ClubModal from './ClubModal';
+import { WeappItem } from '@/types';
 
 interface IProps {
   dispatch: Dispatch;
@@ -34,6 +35,7 @@ const Club: React.FC<IProps> = ({ dispatch, roleList }) => {
       label: '',
       previousLabel: '',
       userCount: 0,
+      miniProgramList: [],
     },
   });
 
@@ -82,6 +84,7 @@ const Club: React.FC<IProps> = ({ dispatch, roleList }) => {
         draft.loading = true;
       }),
     );
+    console.log(curClub, 'curClub');
 
     const { code, data } = await putRoleList(curClub);
 
@@ -131,6 +134,18 @@ const Club: React.FC<IProps> = ({ dispatch, roleList }) => {
     );
   };
 
+  // 修改小程序列表
+  const onChangeMiniProgramList = (records: WeappItem[]) => {
+    setState(
+      produce(state, (draft) => {
+        draft.curClub = {
+          ...curClub,
+          miniProgramList: records,
+        };
+      }),
+    );
+  };
+
   return (
     <PageContainer title="俱乐部管理">
       <Card>
@@ -153,7 +168,7 @@ const Club: React.FC<IProps> = ({ dispatch, roleList }) => {
                 <a onClick={() => openModal(records as role)}>修改</a>
                 <Divider type="vertical" />
                 <a onClick={() => forbiddenState(records as role)}>
-                  {records.isForbidden ? '禁用' : '启用'}
+                  {!records.isForbidden ? '禁用' : '启用'}
                 </a>
               </Space>
             )}
@@ -163,11 +178,13 @@ const Club: React.FC<IProps> = ({ dispatch, roleList }) => {
 
       {/* 修改名称 */}
       <ClubModal
+        key={curClub.id}
         visible={visable}
         curClub={curClub}
         closeModal={closeModal}
         onsubmit={onsubmit}
         onChange={onChange}
+        onChangeMiniProgramList={onChangeMiniProgramList}
       />
     </PageContainer>
   );

+ 1 - 1
src/pages/complaint/admin/index.tsx

@@ -51,7 +51,7 @@ const Complaint: React.FC = () => {
     console.log(record, 'record.itemEnable');
 
     const $par = {
-      id: record.id,
+      id: record.itemId,
       enable: record.itemEnable ? 0 : 1,
       type: articleType === 'POSTS' ? 0 : 1,
     };

+ 94 - 86
src/pages/complaint/admin/modal.tsx

@@ -1,86 +1,94 @@
-import React, { useState, useEffect} from 'react'
-import { Modal, Form, Table, Spin } from 'antd'
-
-import type { AdminModalType } from './data'
-import { getComplaintDetail } from '@/services/complaint'
-import { filterTimestamp } from '@/filters/index'
-
-const request = (url: string): Promise<any>  => {
-    return new Promise( (resolve, reject) => {
-        const xhr = new XMLHttpRequest()
-        xhr.open('GET', url, false)
-        xhr.onreadystatechange =  function ()  {
-            if (xhr.readyState === 4 && xhr.status === 200) {
-                resolve(xhr.response)
-            } else {
-                reject(new Error('置换帖子的contenturl出错'))
-            }
-        }
-        xhr.send()
-    }).catch(e => e)
-}
-
-const sendRequest = async (url: string ) => await request(url)
-
-const layout = {
-    labelCol: { span: 5 },
-    wrapperCol: { span: 16 },
-};
-
-const AdminModal: React.FC<AdminModalType> = ({visible, complaintData, articleType, closeModal}) => {
-    console.log(complaintData, 'complaintData');
-    
-    const [ postsDetail, setPostsDetail] =  useState<Record<string, any>>({})
-
-    const [ complaintList, setComplaintList] = useState([])
-
-    const [spinning, setSpinning] = useState(false)
-
-    // 查询详情
-    const GetComplaintDetail = async (): Promise<void> => {
-        setSpinning(true)
-        const {code, data} = await getComplaintDetail({itemId: complaintData.itemId, articleType})
-        setSpinning(false)
-        if (code === 0) {
-            const { list, article } = data
-            // eslint-disable-next-line no-await-in-loop
-            if (article) {
-                article.content = article.hasOwnProperty('contentUrl') && await sendRequest(article.contentUrl)
-                setPostsDetail(article)
-            } 
-            setComplaintList(list)
-
-            console.log(data, '查询详情');
-        }
-    }
-
-    useEffect( () => {
-        GetComplaintDetail()
-    }, [])
-
-    return (
-        <Modal visible={visible} title="查看详情" onCancel={closeModal} width={600}>
-            <Spin spinning={spinning}>
-                <Form {...layout}>
-                    <Form.Item label="标题">{postsDetail.label}</Form.Item>
-                    <Form.Item label="内容">{postsDetail.content}</Form.Item>
-                    <Form.Item label="分类名称">{postsDetail.categoryLabel}</Form.Item>
-                    <Form.Item label="所属贴吧板块">{postsDetail.barLabel}</Form.Item>
-                    <Form.Item label="发布人">{postsDetail.authorName}</Form.Item>
-                    <Form.Item label="发布时间">{filterTimestamp(postsDetail.createTime)}</Form.Item>
-                    <Form.Item label="投诉情况">
-                        <Table dataSource={complaintList} pagination={false}>
-                            <Table.Column title="投诉人" key="userName" dataIndex="userName"></Table.Column>
-                            <Table.Column title="投诉原因" key="reason" dataIndex="reason"></Table.Column>
-                            <Table.Column title="投诉时间" key="createTime" dataIndex="createTime"
-                                render={text => <span>{filterTimestamp(text)}</span>}
-                            ></Table.Column>
-                        </Table>
-                    </Form.Item>
-                </Form>
-            </Spin>
-        </Modal>
-    )
-}
-
-export default AdminModal
+import React, { useState, useEffect } from 'react';
+import { Modal, Form, Table, Spin } from 'antd';
+
+import type { AdminModalType } from './data';
+import { getComplaintDetail } from '@/services/complaint';
+import { filterTimestamp } from '@/filters/index';
+
+const request = (url: string): Promise<any> => {
+  return new Promise((resolve, reject) => {
+    const xhr = new XMLHttpRequest();
+    xhr.open('GET', url, false);
+    xhr.onreadystatechange = function () {
+      if (xhr.readyState === 4 && xhr.status === 200) {
+        resolve(xhr.response);
+      } else {
+        reject(new Error('置换帖子的contenturl出错'));
+      }
+    };
+    xhr.send();
+  }).catch((e) => e);
+};
+
+const sendRequest = async (url: string) => await request(url);
+
+const layout = {
+  labelCol: { span: 5 },
+  wrapperCol: { span: 16 },
+};
+
+const AdminModal: React.FC<AdminModalType> = ({
+  visible,
+  complaintData,
+  articleType,
+  closeModal,
+}) => {
+  console.log(complaintData, 'complaintData');
+
+  const [postsDetail, setPostsDetail] = useState<Record<string, any>>({});
+
+  const [complaintList, setComplaintList] = useState([]);
+
+  const [spinning, setSpinning] = useState(false);
+
+  // 查询详情
+  const GetComplaintDetail = async (): Promise<void> => {
+    setSpinning(true);
+    const { code, data } = await getComplaintDetail({ itemId: complaintData.itemId, articleType });
+    setSpinning(false);
+    if (code === 0) {
+      const { list, article } = data;
+      // eslint-disable-next-line no-await-in-loop
+      if (article) {
+        // article.content = article.hasOwnProperty('contentUrl') && await sendRequest(article.contentUrl).value
+        setPostsDetail(article);
+      }
+      setComplaintList(list);
+
+      console.log(data, '查询详情');
+    }
+  };
+
+  useEffect(() => {
+    GetComplaintDetail();
+  }, []);
+
+  return (
+    <Modal visible={visible} title="查看详情" onCancel={closeModal} width={600}>
+      <Spin spinning={spinning}>
+        <Form {...layout}>
+          <Form.Item label="标题">{postsDetail.label}</Form.Item>
+          <Form.Item label="内容">{postsDetail.content}</Form.Item>
+          <Form.Item label="分类名称">{postsDetail.categoryLabel}</Form.Item>
+          <Form.Item label="所属贴吧板块">{postsDetail.barLabel}</Form.Item>
+          <Form.Item label="发布人">{postsDetail.authorName}</Form.Item>
+          <Form.Item label="发布时间">{filterTimestamp(postsDetail.createTime)}</Form.Item>
+          <Form.Item label="投诉情况">
+            <Table dataSource={complaintList} pagination={false}>
+              <Table.Column title="投诉人" key="userName" dataIndex="userName"></Table.Column>
+              <Table.Column title="投诉原因" key="reason" dataIndex="reason"></Table.Column>
+              <Table.Column
+                title="投诉时间"
+                key="createTime"
+                dataIndex="createTime"
+                render={(text) => <span>{filterTimestamp(text)}</span>}
+              ></Table.Column>
+            </Table>
+          </Form.Item>
+        </Form>
+      </Spin>
+    </Modal>
+  );
+};
+
+export default AdminModal;

+ 69 - 4
src/pages/tieba/classify/index.tsx

@@ -4,8 +4,15 @@ import { Input, Space, Row, Col, Card, Button, Table, Modal, message, Popconfirm
 import { PageContainer } from '@ant-design/pro-layout';
 
 import { getClassify, postOrPutClassify, deleteClassify } from '@/services/tieba';
+
 import { filterTimestamp } from '@/filters/index';
 
+import { ConnectState } from '@/models/connect';
+
+import { connect, Dispatch } from 'umi';
+
+import { Role } from '@/models/user';
+
 const { Search } = Input;
 
 type ModalEleType = {
@@ -48,7 +55,12 @@ const ModalEle: React.FC<ModalEleType> = ({
   );
 };
 
-class Classify extends Component<React.Component> {
+interface IProps {
+  dispatch: Dispatch;
+  roleList: Role[];
+}
+
+class Classify extends Component<IProps> {
   state = {
     queryParams: {
       curPage: 1,
@@ -56,6 +68,7 @@ class Classify extends Component<React.Component> {
       total: 0,
       type: 0,
       id: '',
+      roleId: Math.random().toString(36).substring(2),
     },
     visible: false, // 弹窗
     loading: false,
@@ -63,6 +76,24 @@ class Classify extends Component<React.Component> {
   };
 
   componentDidMount() {
+    this.props.dispatch({
+      type: 'user/getRoleList',
+      payload: {
+        curPage: 1,
+        passSize: 100,
+      },
+      callback: () => {
+        console.log(this.props.roleList, 'this.props.roleList');
+        this.setState({
+          ...this.state,
+          queryParams: {
+            ...this.state.queryParams,
+            roleId: this.props.roleList[0].id,
+          },
+        });
+      },
+    });
+
     this.getClassify();
   }
 
@@ -146,9 +177,27 @@ class Classify extends Component<React.Component> {
     );
   };
 
+  // 切换tabs
+  onTabChange = (activeKey: string) => {
+    console.log(activeKey, 'activeKey');
+    this.setState(
+      {
+        ...this.state,
+        queryParams: {
+          ...this.state.queryParams,
+          roleId: activeKey,
+        },
+      },
+      () => {
+        this.getClassify();
+      },
+    );
+  };
+
   render() {
     const { classifyList, visible, queryParams, loading } = this.state;
-    const { type, total, label } = queryParams;
+    const { type, total, label, roleId } = queryParams;
+    const { roleList } = this.props;
     const {
       onSearch,
       openModal,
@@ -157,9 +206,21 @@ class Classify extends Component<React.Component> {
       changeLabel,
       changePagination,
       deleteClassify,
+      onTabChange,
     } = this;
+
     return (
-      <PageContainer title="贴吧的分类管理">
+      <PageContainer
+        title="贴吧的分类管理"
+        tabActiveKey={roleId}
+        tabList={roleList.map((role) => {
+          return {
+            key: role.id,
+            tab: role.label,
+          };
+        })}
+        onTabChange={onTabChange}
+      >
         <Card>
           <Row justify="space-between">
             <Col>
@@ -221,4 +282,8 @@ class Classify extends Component<React.Component> {
   }
 }
 
-export default Classify;
+// Classify
+
+export default connect(({ user }: ConnectState) => ({
+  roleList: user.roleList,
+}))(Classify);

+ 46 - 28
src/pages/tieba/plate/ModalForm.tsx

@@ -36,27 +36,18 @@ const ModalForm: React.FC<ModalFormType> = ({
   const [classifyList, setClassifyList] = useState<Record<string, any>[]>([]);
   const [state, setState] = useState<number>(0);
 
-  // 获取贴吧分类
-  const GetClassify = async (): Promise<void> => {
-    const { code, data } = await getClassify({ curPage: 1, label: '' });
-    if (code === 0) {
-      console.log(data);
-      const { records } = data;
-      setClassifyList(records);
-    }
-  };
-
   // 反填表单
   const onFill = (): void => {
     console.log(formData, 'formData');
 
     form.setFieldsValue(formData);
+
     setAvatarUrl(formData.avatarUrl);
     setCover(formData.cover);
   };
 
   useEffect(() => {
-    GetClassify();
+    // GetClassify();
 
     if (type === 1) {
       onFill();
@@ -86,6 +77,22 @@ const ModalForm: React.FC<ModalFormType> = ({
     form.submit();
   };
 
+  // 选择roleList
+  const changeRoleList = async (_roleId: string) => {
+    console.log(_roleId);
+
+    form.setFieldsValue({
+      categoryId: '',
+    });
+
+    const { code, data } = await getClassify({ curPage: 1, label: '', roleId: _roleId });
+    if (code === 0) {
+      console.log(data);
+      const { records } = data;
+      setClassifyList(records);
+    }
+  };
+
   return (
     <Modal
       visible={visible}
@@ -104,42 +111,53 @@ const ModalForm: React.FC<ModalFormType> = ({
       ]}
     >
       <Form {...layout} form={form} name="control-hooks" onFinish={onFinish}>
-        <Form.Item name="label" label="贴吧板块标题" rules={[{ required: true }]}>
-          <Input maxLength={50} />
-        </Form.Item>
-        <Form.Item name="cover" label="图标">
-          <PicturesWall key="1" imgUrl={formData.avatarUrl} setCoverFn={setAvaFn} maxCount={1} />
-        </Form.Item>
-        <Form.Item name="categoryId" label="分类" rules={[{ required: true }]}>
-          <Select placeholder="请选择分类" allowClear>
-            {classifyList.length &&
-              classifyList.map((item) => (
+        <Form.Item name="roleIds" label="展示可见范围" rules={[{ required: true }]}>
+          <Select
+            allowClear
+            style={{ width: '100%' }}
+            placeholder="请选择可见范围"
+            onChange={changeRoleList}
+          >
+            {roleList.length &&
+              roleList.map((item) => (
                 <Option key={item.id} value={item.id}>
                   {item.label}
                 </Option>
               ))}
           </Select>
         </Form.Item>
-        <Form.Item name="roleIds" label="展示可见范围" rules={[{ required: true }]}>
-          {/*        onChange={handleChange} */}
-          <Select mode="multiple" allowClear style={{ width: '100%' }} placeholder="请选择可见范围">
-            {roleList.length &&
-              roleList.map((item) => (
+        <Form.Item name="categoryId" label="分类" rules={[{ required: true }]}>
+          <Select placeholder="请选择分类(根据可见范围)" allowClear>
+            {classifyList.length &&
+              classifyList.map((item) => (
                 <Option key={item.id} value={item.id}>
                   {item.label}
                 </Option>
               ))}
           </Select>
         </Form.Item>
+        <Form.Item name="label" label="贴吧板块标题" rules={[{ required: true }]}>
+          <Input maxLength={50} />
+        </Form.Item>
+        <Form.Item name="cover" label="图标">
+          <PicturesWall
+            key="1"
+            imgUrl={formData.avatarUrl}
+            setCoverFn={setAvaFn}
+            maxCount={1}
+            desc="上传贴吧图标"
+          />
+        </Form.Item>
+
         <Form.Item name="description" label="板块介绍" rules={[{ required: true }]}>
           <Input.TextArea maxLength={50}></Input.TextArea>
         </Form.Item>
 
-        <Form.Item label="封面">
+        {/* <Form.Item label="封面">
           <Form.Item name="cover" valuePropName="fileList" getValueFromEvent={normFile} noStyle>
             <PicturesWall imgUrl={formData.cover} key="2" setCoverFn={setCoverFn} maxCount={1} />
           </Form.Item>
-        </Form.Item>
+        </Form.Item> */}
       </Form>
     </Modal>
   );

+ 42 - 41
src/pages/tieba/plate/data.d.ts

@@ -1,41 +1,42 @@
-export type ColumnType = {
-    id: string,
-    avatarUrl: string,
-    categoryId: string,
-    categoryLabel: string,
-    createTime: number,
-    creator: string,
-    description: string,
-    follow: boolean,
-    followerNum: number,
-    label: string,
-    orderValue: number,
-    postNum: number,
-    roleIds: string[],
-    state: number,
-    action?: React.ReactNode
-}
-
-export type postOrPutPlateType = {
-    avatarUrl: string,
-    categoryId: string,
-    cover: string,
-    description: string,
-    id?: string,
-    label: string
-    type: typeEnum
-}
-
-export type ModalFormType = {
-    visible: boolean,
-    changeForm: (params: postOrPutPlateType) => void,
-    closeModal: () => void,
-    type: typeEnum,
-    formData: postOrPutPlateType,
-    roleList: Record<string, any>
-}
-
-export enum typeEnum  {
-    add,
-    edit
-}
+export type ColumnType = {
+  id: string;
+  avatarUrl: string;
+  categoryId: string;
+  categoryLabel: string;
+  createTime: number;
+  creator: string;
+  description: string;
+  follow: boolean;
+  followerNum: number;
+  label: string;
+  orderValue: number;
+  postNum: number;
+  roleIds: string[];
+  state: number;
+  action?: React.ReactNode;
+};
+
+export type postOrPutPlateType = {
+  avatarUrl: string;
+  categoryId: string;
+  cover: string;
+  description: string;
+  id?: string;
+  label: string;
+  type: typeEnum;
+  [key: string]: any;
+};
+
+export type ModalFormType = {
+  visible: boolean;
+  changeForm: (params: postOrPutPlateType) => void;
+  closeModal: () => void;
+  type: typeEnum;
+  formData: postOrPutPlateType;
+  roleList: Record<string, any>;
+};
+
+export enum typeEnum {
+  add,
+  edit,
+}

+ 5 - 0
src/pages/tieba/plate/index.tsx

@@ -51,6 +51,7 @@ class Plate extends Component<React.Component> {
       queryParams: { ...this.state.queryParams, typeId: value },
     });
   };
+
   // 搜索
   onSearch = (): void => {
     this.getPlate();
@@ -72,8 +73,12 @@ class Plate extends Component<React.Component> {
 
   // 提交表单
   changeForm = async (params: postOrPutPlateType): Promise<void> => {
+    console.log(params, 'paramsparamsparams');
+
     const { code, data } = await postOrPutPlate({
       ...params,
+      roleId: params.roleIds,
+      roleIds: [params.roleIds],
       type: this.state.type,
       id: this.state.id,
     });

+ 232 - 0
src/pages/weapp/index.tsx

@@ -0,0 +1,232 @@
+import React, { useEffect, useState } from 'react';
+
+import { PageContainer } from '@ant-design/pro-layout';
+
+import {
+  Card,
+  Table,
+  Row,
+  Col,
+  Button,
+  Space,
+  message,
+  Image,
+  Tag,
+  Divider,
+  Popconfirm,
+} from 'antd';
+
+import WeappModal from './modal';
+
+import { addWeappList, getWeappList, deleteWeappList } from '@/services/weapp';
+
+import { Pagination } from '@/types/common';
+
+import { WeappItem } from '@/types/index';
+
+interface IState {
+  visable: boolean;
+  type: 'add' | 'edit';
+  queryParams: Pagination;
+  weappList: WeappItem[];
+  curWeapp: WeappItem;
+  loading: boolean;
+}
+
+export interface WeappInfo {
+  appId: string;
+  cover: string;
+  name: string;
+}
+
+const Weapp: React.FC = () => {
+  const [state, setState] = useState<IState>({
+    visable: false,
+    type: 'add',
+    queryParams: {
+      isPage: 1,
+      page: 1,
+      pageSize: 10,
+      total: 0,
+    },
+    loading: false,
+    weappList: [],
+    curWeapp: { appId: '', cover: '', name: '', id: '' },
+  });
+
+  const { visable, type, queryParams, weappList, curWeapp, loading } = state;
+  console.log(
+    visable,
+    type,
+    queryParams,
+    weappList,
+    curWeapp,
+    ' visable, type, queryParams, weappList, curWeapp',
+  );
+
+  useEffect(() => {
+    GetWeappList();
+  }, [queryParams.page]);
+
+  // 打开弹窗
+  const openModal = () => {
+    setState({
+      ...state,
+      visable: true,
+    });
+  };
+
+  // 关闭弹窗
+  const closeModal = () => {
+    setState({
+      ...state,
+      curWeapp: { appId: '', cover: '', name: '', id: '' },
+      visable: false,
+    });
+  };
+  const onSubmit = (_records: WeappInfo) => {
+    AddWeappList(_records);
+  };
+
+  // 翻页
+  const changePagintion = (page: number) => {
+    setState({
+      ...state,
+      queryParams: {
+        page: page,
+        pageSize: 10,
+        isPage: 1,
+        total: queryParams.total,
+      },
+    });
+  };
+
+  // 编辑小程序信息
+  const editWeapp = (_records: WeappItem) => {
+    console.log(_records);
+    setState({
+      ...state,
+      visable: true,
+      type: 'edit',
+      curWeapp: _records,
+    });
+  };
+
+  // 查询小程序
+  const GetWeappList = async () => {
+    setState({
+      ...state,
+      loading: true,
+    });
+    const { data, status } = await getWeappList(queryParams);
+    if (status === 200) {
+      console.log(data);
+      setState({
+        ...state,
+        curWeapp: { appId: '', cover: '', name: '', id: '' },
+        weappList: data.entityList,
+        visable: false,
+        loading: false,
+        queryParams: {
+          pageSize: 10,
+          page: queryParams.page,
+          total: data.total,
+          isPage: 1,
+        },
+      });
+    }
+  };
+
+  // 增加小程序
+  const AddWeappList = async (_records: WeappInfo) => {
+    const $par = {
+      ..._records,
+      type,
+    };
+    const { status } = await addWeappList($par);
+    if (status === 200) {
+      message.success(type === 'add' ? '添加成功' : '编辑成功');
+      GetWeappList();
+    }
+  };
+  // 删除小程序
+  const DeleteWeappList = async (id: string) => {
+    const { status } = await deleteWeappList(id);
+    if (status === 200) {
+      message.success('删除成功');
+      GetWeappList();
+    }
+  };
+
+  const columns = [
+    {
+      title: '小程序图标',
+      key: 'cover',
+      dataIndex: 'cover',
+      render: (cover: string) => <Image width={60} height={60} src={cover} />,
+    },
+    {
+      title: '小程序名字',
+      key: 'name',
+      dataIndex: 'name',
+      render: (name: string) => <Tag color="cyan">{name}</Tag>,
+    },
+    {
+      title: 'appId',
+      key: 'appId',
+      dataIndex: 'appId',
+    },
+    {
+      title: '操作',
+      dataIndex: 'action',
+      key: 'action',
+      render: (text, records) => (
+        <Space>
+          <a onClick={() => editWeapp(records)}>编辑</a>
+          <Divider type="vertical" />
+          <Popconfirm
+            title="确定要删除这个小程序吗?"
+            onConfirm={() => DeleteWeappList(records.id)}
+          >
+            <a>删除</a>
+          </Popconfirm>
+        </Space>
+      ),
+    },
+  ];
+
+  return (
+    <PageContainer title="小程序管理">
+      <Card>
+        <Row style={{ marginBottom: '20px' }}>
+          <Col push={22}>
+            <Button type="primary" onClick={openModal}>
+              新增小程序
+            </Button>
+          </Col>
+        </Row>
+        <Table
+          loading={loading}
+          dataSource={weappList}
+          columns={columns}
+          pagination={{
+            total: queryParams.total,
+            defaultCurrent: 1,
+            current: queryParams.page,
+            onChange: changePagintion,
+          }}
+        />
+      </Card>
+
+      <WeappModal
+        visible={visable}
+        onSubmit={onSubmit}
+        closeModal={closeModal}
+        curWeapp={curWeapp}
+        type={type}
+      />
+    </PageContainer>
+  );
+};
+
+export default Weapp;

+ 124 - 0
src/pages/weapp/modal.tsx

@@ -0,0 +1,124 @@
+import React, { useEffect, useState } from 'react';
+
+import { Modal, Input, Row, Col, message } from 'antd';
+
+import PicturesWall from '@/components/upload';
+
+import { WeappItem } from '@/types/index';
+
+interface IProps {
+  visible: boolean;
+  onSubmit: (state: IState) => void;
+  closeModal: () => void;
+  curWeapp: WeappItem;
+  type: 'edit' | 'add';
+}
+
+interface IState {
+  cover: string;
+  name: string;
+  appId: string;
+  id: string;
+}
+
+const WeappModal: React.FC<IProps> = ({ visible, onSubmit, closeModal, curWeapp, type }) => {
+  console.log(curWeapp, 'curWeapp');
+
+  const [state, setState] = useState<IState>({
+    cover: '',
+    name: '',
+    appId: '',
+    id: '',
+  });
+
+  useEffect(() => {
+    setState({
+      cover: curWeapp.cover,
+      name: curWeapp.name,
+      appId: curWeapp.appId,
+      id: curWeapp.id,
+    });
+  }, [curWeapp]);
+
+  const { cover, name, appId, id } = state;
+
+  console.log(cover, name, appId, id, 'cover, name , appId, id');
+
+  const setCoverFn = (_cover: string) => {
+    setState({
+      ...state,
+      cover: _cover,
+    });
+  };
+
+  const onChangeName = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setState({
+      ...state,
+      name: e.target.value,
+    });
+  };
+
+  const onChangeAppid = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setState({
+      ...state,
+      appId: e.target.value,
+    });
+  };
+
+  const onOk = () => {
+    if (verfiyState()) {
+      onSubmit(state);
+    } else {
+      message.warning('请填写完整');
+    }
+  };
+
+  const verfiyState = (): boolean => {
+    let r: boolean = true;
+    Object.keys(state).forEach((key) => {
+      console.log(state[key], 'state[key]');
+      if (type === 'add' && key === 'id') {
+      } else if (!state[key]) {
+        r = false;
+      }
+    });
+    return r;
+  };
+
+  return (
+    <Modal
+      width="400px"
+      visible={visible}
+      title={type === 'add' ? '新增小程序' : '编辑小程序'}
+      onOk={onOk}
+      onCancel={closeModal}
+    >
+      <Row align="middle">
+        <Col span={6}>小程序图标:</Col>
+        <Col span={6}>
+          <PicturesWall
+            key={cover}
+            imgUrl={cover}
+            desc="点击上传"
+            maxCount={1}
+            setCoverFn={setCoverFn}
+          />
+        </Col>
+      </Row>
+      <Row align="middle" style={{ marginTop: '14px' }}>
+        <Col span={6}>小程序名称:</Col>
+        <Col span={12}>
+          <Input value={name} onChange={onChangeName} />
+        </Col>
+      </Row>
+      <Row align="middle" style={{ marginTop: '14px' }}>
+        <Col span={6}>appId:</Col>
+        <Col span={12}>
+          <Input value={appId} onChange={onChangeAppid} />
+        </Col>
+      </Row>
+    </Modal>
+  );
+};
+
+export default WeappModal;

+ 35 - 33
src/services/agent.ts

@@ -1,33 +1,35 @@
-import request from '@/utils/request';
-
-
-// 获取论坛代理商列表
-type getAgentType = {
-    curPage: number,
-    state?: number ,
-    roleId?: string,
-    label?: string | number,
-    pageSize: number
-}
-
-export async function getAgent(params: getAgentType): Promise<any> {
-    return request('/forum/admin/agent', {
-        params
-    });
-}
-
-
-// 获取代理商详情
-export async function getAgentDetail (id: string): Promise<any> {
-    return request(`/forum/admin/agent/${id}`)
-}
-
-
-// 代理商 停用或者启用
-type stopOrOpenAgentType = {
-    state: number
-}
-export async function stopOrOpenAgent (id: string, params: stopOrOpenAgentType): Promise<any> {
-    return request(`/forum/admin/agent/${id}`, { method: 'PATCH',  params })
-}
-
+import request from '@/utils/request';
+
+// 获取论坛代理商列表
+type getAgentType = {
+  curPage: number;
+  state?: number;
+  roleId?: string;
+  label?: string | number;
+  pageSize: number;
+};
+
+export async function getAgent(params: getAgentType): Promise<any> {
+  return request('/forum/admin/agent', {
+    params,
+  });
+}
+
+// 获取代理商详情
+export async function getAgentDetail(id: string): Promise<any> {
+  return request(`/forum/admin/agent/${id}`);
+}
+
+// 代理商 停用或者启用
+type stopOrOpenAgentType = {
+  state: number;
+};
+export async function stopOrOpenAgent(id: string, params: stopOrOpenAgentType): Promise<any> {
+  return request(`/forum/admin/agent/${id}`, { method: 'PATCH', params });
+}
+
+// 更改代理商信息
+
+export async function editAgentInfo(data: any) {
+  return request(`/forum/admin/agent`, { method: 'PUT', data });
+}

+ 102 - 105
src/services/tieba.ts

@@ -1,105 +1,102 @@
-import request from '@/utils/request';
-
-import type { postOrPutPlateType} from '@/pages/tieba/plate/data'
-
-
-// 查询分类
-
-type getClassifyType = {
-    curPage: number,
-    label: string
-}
-
-export async function getClassify( params: getClassifyType ): Promise<any> {
-  return request('/forum/admin/category', { params });
-}
-
-
-// 新增获取 修改分类
-enum typeEnum {
-    add,
-    yes
-}
-
-type postOrPutClassifyType = {
-    id?: string,
-    label: string,
-    type: typeEnum
-}
-
-export async function postOrPutClassify( data: postOrPutClassifyType ): Promise<any> {
-    const { type } = data
-    return request('/forum/admin/category', { data , method: type === 0 ? 'POST' : 'PATCH'});
-}
-
-// 删除分类
-export async function deleteClassify( id: string ): Promise<any> {
-    return request(`/forum/admin/category/${id}`, { method: 'DELETE'});
-}
-
-// 板块管理
-type getPlateType = {
-    curPage: number,
-    label?: string,
-    typeId?: string,
-    pageSize?: number
-}
-export async function getPlate( params: getPlateType ): Promise<any> {
-    return request(`/forum/admin/bar`, { params });
-}
-
-// 新增或者编辑贴吧
-
-export async function postOrPutPlate( data: postOrPutPlateType ): Promise<any> {
-    const { type } = data
-    return request(`/forum/admin/bar`, {method: type === 0 ? 'POST' : 'PUT',  data });
-}
-
-
-// 改变发布状态
-
-type changeTiebaStateType = {
-    id: string,
-    state: number
-}
-
-export async function changeTiebaState ( params: changeTiebaStateType ): Promise<any> {
-    return request(`/forum/admin/bar`, {method: "PATCH",  params });
-}
-
-
-// 帖子 ------------------------------------------------------
-// 查询帖子
-type getPostsType = {
-    curPage: number,
-    barId?: string
-    categoryId?: string
-    label?: string,
-    type: number
-}
-
-export async function getPosts ( params: getPostsType ): Promise<any> {
-    const { type } = params
-    return request( type === 0 ?  `/forum/admin/posts` : '/forum/admin/issue', { params });
-}
-
-
-// 封禁 or 解封
-
-type enablePostsType = {
-    id: string,
-    enable: number,
-    type: number
-}
-
-export async function enablePosts ( params: enablePostsType ): Promise<any> {
-    const { type } = params
-    return request( type === 0 ?  `/forum/admin/posts` : '/forum/admin/issue', {method: 'PATCH', params });
-}
-
-
-
-// 查详情
-export async function getPostsDetail ( id: string, type: number ): Promise<any> {
-    return request( type === 0 ?  `/forum/admin/posts/${id}` : `/forum/admin/issue/${id}`);
-}
+import request from '@/utils/request';
+
+import type { postOrPutPlateType } from '@/pages/tieba/plate/data';
+
+// 查询分类
+
+type getClassifyType = {
+  curPage: number;
+  label: string;
+  roleId: string;
+};
+
+export async function getClassify(params: getClassifyType): Promise<any> {
+  return request('/forum/admin/category', { params });
+}
+
+// 新增获取 修改分类
+enum typeEnum {
+  add,
+  yes,
+}
+
+type postOrPutClassifyType = {
+  id?: string;
+  label: string;
+  type: typeEnum;
+};
+
+export async function postOrPutClassify(data: postOrPutClassifyType): Promise<any> {
+  const { type } = data;
+  return request('/forum/admin/category', { data, method: type === 0 ? 'POST' : 'PATCH' });
+}
+
+// 删除分类
+export async function deleteClassify(id: string): Promise<any> {
+  return request(`/forum/admin/category/${id}`, { method: 'DELETE' });
+}
+
+// 板块管理
+type getPlateType = {
+  curPage: number;
+  label?: string;
+  typeId?: string;
+  pageSize?: number;
+};
+export async function getPlate(params: getPlateType): Promise<any> {
+  return request(`/forum/admin/bar`, { params });
+}
+
+// 新增或者编辑贴吧
+
+export async function postOrPutPlate(data: postOrPutPlateType): Promise<any> {
+  const { type } = data;
+  return request(`/forum/admin/bar`, { method: type === 0 ? 'POST' : 'PUT', data });
+}
+
+// 改变发布状态
+
+type changeTiebaStateType = {
+  id: string;
+  state: number;
+};
+
+export async function changeTiebaState(params: changeTiebaStateType): Promise<any> {
+  return request(`/forum/admin/bar`, { method: 'PATCH', params });
+}
+
+// 帖子 ------------------------------------------------------
+// 查询帖子
+type getPostsType = {
+  curPage: number;
+  barId?: string;
+  categoryId?: string;
+  label?: string;
+  type: number;
+};
+
+export async function getPosts(params: getPostsType): Promise<any> {
+  const { type } = params;
+  return request(type === 0 ? `/forum/admin/posts` : '/forum/admin/issue', { params });
+}
+
+// 封禁 or 解封
+
+type enablePostsType = {
+  id: string;
+  enable: number;
+  type: number;
+};
+
+export async function enablePosts(params: enablePostsType): Promise<any> {
+  const { type } = params;
+  return request(type === 0 ? `/forum/admin/posts` : '/forum/admin/issue', {
+    method: 'PATCH',
+    params,
+  });
+}
+
+// 查详情
+export async function getPostsDetail(id: string, type: number): Promise<any> {
+  return request(type === 0 ? `/forum/admin/posts/${id}` : `/forum/admin/issue/${id}`);
+}

+ 47 - 0
src/services/weapp.ts

@@ -0,0 +1,47 @@
+import request from '@/utils/request';
+
+import { WeappInfo } from '@/pages/weapp/index';
+
+import { Pagination } from '@/types/common';
+
+/**
+ *
+ * @description 获取小程序
+ *
+ */
+interface GetWeappList extends Pagination {}
+
+export async function getWeappList(params: GetWeappList) {
+  return request('/forum/admin/mini/program', {
+    params,
+  });
+}
+
+/**
+ *
+ * @description 添加小程序
+ *
+ */
+
+interface AddWeappList extends WeappInfo {
+  type: 'add' | 'edit';
+}
+
+export async function addWeappList(data: AddWeappList) {
+  return request('/forum/admin/mini/program', {
+    data,
+    method: data.type === 'add' ? 'POST' : 'PUT',
+  });
+}
+
+/**
+ *
+ * @description 删除小程序
+ *
+ */
+
+export async function deleteWeappList(id: string) {
+  return request(`/forum/admin/mini/program/${id}`, {
+    method: 'DELETE',
+  });
+}

+ 6 - 0
src/types/common.ts

@@ -0,0 +1,6 @@
+export interface Pagination {
+  isPage: 1;
+  page: number;
+  pageSize: number;
+  total: number;
+}

+ 7 - 0
src/types/index.ts

@@ -0,0 +1,7 @@
+export * from './common';
+
+export * from './weapp';
+
+export * from './club';
+
+export * from './role';

+ 3 - 0
src/types/role.ts

@@ -1,8 +1,11 @@
+import { WeappItem } from './weapp';
+
 export interface role {
   id: string;
   isForbidden: boolean | null;
   label: string;
   previousLabel?: string;
   userCount: number;
+  miniProgramList: WeappItem[];
   [key: string]: any;
 }

+ 7 - 0
src/types/weapp.ts

@@ -0,0 +1,7 @@
+export interface WeappItem {
+  appId: string;
+  cover: string;
+  id: string;
+  name: string;
+  [key: string]: any;
+}