Bläddra i källkod

feta 修改页面内容

Young 4 månader sedan
förälder
incheckning
297fa6394c

+ 1 - 1
config/config.ts

@@ -85,7 +85,7 @@ export default defineConfig({
    * @name layout 插件
    * @doc https://umijs.org/docs/max/layout-menu
    */
-  title: '模饭星管理后台',
+  title: 'Sect管理后台',
   layout: {
     locale: true,
     ...defaultSettings,

+ 1 - 1
config/defaultSettings.ts

@@ -15,7 +15,7 @@ const Settings: ProLayoutProps & {
   fixedHeader: false,
   fixSiderbar: true,
   colorWeak: false,
-  title: '魔饭星',
+  title: 'Sect',
   pwa: true,
   logo: 'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
   iconfontUrl: '',

+ 8 - 8
config/routes.ts

@@ -55,8 +55,8 @@ export default [
         redirect: '/settings/members',
       },
       {
-        path: '/settings/members',
-        name: 'members',
+        path: '/settings/staff',
+        name: 'staff',
         component: './Settings/Staff',
       },
       {
@@ -65,14 +65,14 @@ export default [
         component: './Settings/Users',
       },
       {
-        path: '/settings/screens',
-        name: 'screen',
-        component: './Settings/Screens',
+        path: '/settings/role',
+        name: 'role',
+        component: './Settings/Role',
       },
       {
-        path: '/settings/idols',
-        name: 'idols',
-        component: './Settings/Idols',
+        path: '/settings/menu',
+        name: 'menu',
+        component: './Settings/Menu',
       },
       {
         path: '/settings/settings',

+ 4 - 2
src/locales/zh-CN/menu.ts

@@ -7,8 +7,10 @@ export default {
   'menu.settings': '系统设置',
   'menu.settings.users': '用户管理',
   'menu.settings.settings': '字典设置',
-  'menu.settings.members': '管理员管理',
-  'menu.settings.screen': '开机屏管理',
+  'menu.settings.staff': '管理员管理',
+  'menu.settings.role': '角色管理',
+  'menu.settings.menu': '菜单管理',
+
   'menu.settings.idols': '明星管理',
   'menu.admin.sub-page': '二级管理页',
   'menu.login': '登录',

+ 268 - 0
src/pages/Settings/Menu.tsx

@@ -0,0 +1,268 @@
+import {ActionType, PageContainer, ProForm, ProFormText,ProFormRadio, ProTable,} from '@ant-design/pro-components';
+import {Button, Card, Modal, TreeSelect} from 'antd';
+import React, {useEffect, useRef, useState} from 'react';
+import {AddOutline} from 'antd-mobile-icons';
+import {EditBundle, useEdit} from '@/core/hooks/useEdit';
+import {invoke} from '@/core/network';
+
+type MenuVO = {
+  id?: number;
+  name: string;
+  sortNum: number;
+  status: number;
+  type: number;
+  parentId?: number;
+  url?: string;
+  target?: string;
+  hasRefresh?: number;
+  perms?: string;
+  icon?: string;
+  children?: MenuVO[];
+};
+
+type MenuList = MenuVO[];
+
+class Service {
+
+  public static async listTree(query: { keyword?: string; status?: number }) {
+    return invoke<MenuList>('/apis/admin/menu/tree/list', {
+      method: 'POST',
+      data: query,
+    });
+  }
+
+  public static async remove(id: number) {
+    return invoke<void>(`/apis/admin/menu/remove?id=${id}`, {
+      method: 'DELETE',
+    });
+  }
+
+  public static async save(menu: Partial<MenuVO>) {
+    return invoke<MenuVO>('/apis/admin/menu/save', {
+      method: 'POST',
+      data: menu,
+    });
+  }
+
+  public static async get(id: number) {
+    return invoke<MenuVO>(`/apis/admin/menu/details?id=${id}`, {
+      method: 'GET',
+    });
+  }
+}
+
+const Edit: React.FC<{ bundle: EditBundle; treeData: any[]; onSuccess?: () => void }> = (props) => {
+  const {bundle, treeData} = props;
+  console.log("treeData" + treeData)
+
+  const [form] = ProForm.useForm<MenuVO>();
+  const [loading, setLoading] = React.useState(false);
+
+  const [value, setValue] = useState<string>();
+
+  const onChange = (newValue: string) => {
+    console.log(newValue);
+    setValue(newValue);
+  };
+
+  return (
+    <Modal
+      confirmLoading={loading}
+      title={bundle.id ? '编辑菜单' : '添加菜单'}
+      open={bundle.open}
+      onOk={async () => {
+        await form.validateFields();
+
+        try {
+          setLoading(true);
+
+          await Service.save({
+            id: bundle.id,
+            ...form.getFieldsValue(),
+          });
+        } finally {
+          setLoading(false);
+        }
+
+        props.onSuccess?.();
+        bundle.close();
+      }}
+      onClose={() => {
+        form.resetFields();
+        bundle.close();
+      }}
+      onCancel={() => {
+        form.resetFields();
+        bundle.close();
+      }}
+    >
+      <ProForm
+        className={'mt-5'}
+        form={form}
+        labelCol={{span: 4}}
+        layout={'horizontal'}
+        submitter={false}
+        request={async () => {
+          if (bundle.id !== undefined) {
+            console.log(bundle.id, 'bundle');
+            const res = await Service.get(bundle.id);
+
+            return res.data;
+          }
+
+          return Promise.resolve({});
+        }}
+      >
+
+        <ProFormRadio.Group name="type" label="类型" options={[{label: '目录', value: '1',}, {label: '菜单', value: '2'}, {label: '按钮', value: '3'}]}/>
+        <ProFormRadio.Group name="status" label="状态" options={[{label: '正常', value: '1',}, {label: '隐藏', value: '2'}]}/>
+        <ProFormRadio.Group name="target" label="目标" options={[{label: '页签', value: '1',}, {label: '新窗口', value: '2'}]}/>
+        <ProFormRadio.Group name="hasRefresh" label="是否刷新" options={[{label: '不刷新', value: '0',}, {label: '刷新', value: '1'}]}/>
+        <ProFormText name='name' label={'菜单名称'} placeholder={'请输入菜单名称'}
+                     rules={[{required: true, message: '请输入菜单名称',}, {
+                       min: 3,
+                       max: 30,
+                       message: '菜单名称长度必须在3-30之间',
+                     },]}></ProFormText>
+        <ProForm.Item label='父菜单' name='parentId'>
+          <TreeSelect
+            showSearch
+            style={{width: '100%'}}
+            value={value}
+            dropdownStyle={{maxHeight: 400, overflow: 'auto'}}
+            placeholder="请选择父菜单ID"
+            allowClear
+            treeDefaultExpandAll
+            onChange={onChange}
+            treeData={treeData}
+          />
+        </ProForm.Item>
+        <ProFormText name='icon' label={'图标'} placeholder={'请输入图标'}></ProFormText>
+
+        <ProFormText name='sortNum' label={'显示顺序'} placeholder={'请输入显示顺序'}></ProFormText>
+        <ProFormText name='url' label={'请求地址'} placeholder={'请输入请求地址'}></ProFormText>
+        <ProFormText name='perms' label={'权限标识'} placeholder={'请输入权限标识'}></ProFormText>
+      </ProForm>
+    </Modal>
+  );
+};
+
+const transformToTreeData = (data: MenuVO[]): any[] =>
+  data.map((item) => ({
+    title: item.name,
+    value: item.id,
+    key: item.id,
+    children: item.children ? transformToTreeData(item.children) : [],
+  }));
+const MenuExp: React.FC = () => {
+  const bundle = useEdit();
+  const ref = useRef<ActionType>();
+
+  const [treeData, setTreeData] = useState<any[]>([]);
+
+  const loadData = async () => {
+    const res = await Service.listTree({});
+    if (res.success) {
+      setTreeData(transformToTreeData(res.data));
+    }
+  };
+
+  useEffect(() => {
+    loadData();
+  }, []);
+
+  return (
+    <>
+      <PageContainer content={''}>
+        {bundle.open && <Edit bundle={bundle} treeData={treeData} onSuccess={() => ref.current?.reload()}></Edit>}
+        <Card>
+          <ProTable<MenuVO>
+            rowKey={'id'}
+            request={async (params) => {
+              const res = await Service.listTree({});
+
+              return {
+                success: res.success,
+                data: res.data,
+              };
+            }}
+
+            columns={[
+              {
+                title: 'ID',
+                dataIndex: 'id',
+              },
+              {
+                title: '菜单名称',
+                dataIndex: 'name',
+              },
+              {
+                title: '类型',
+                dataIndex: 'type',
+              },
+              {
+                title: '父菜单ID',
+                dataIndex: 'parentId',
+              },
+              {
+                title: '显示顺序',
+                dataIndex: 'sortNum',
+              },
+              {
+                title: '请求地址',
+                dataIndex: 'url',
+              },
+              {
+                title: '目标',
+                dataIndex: 'target',
+              },
+              {
+                title: '状态',
+                dataIndex: 'status',
+              },
+              {
+                title: '是否刷新',
+                dataIndex: 'hasRefresh',
+              },
+              {
+                title: '权限标识',
+                dataIndex: 'perms',
+              },
+              {
+                title: '操作',
+                valueType: 'option',
+                render: (_, record) => [
+                  <a key={'edit'} onClick={() => bundle.update(record.id as number)}>
+                    编辑
+                  </a>,
+                  <a
+                    key={'delete'}
+                    onClick={async () => {
+                      await Service.remove(record.id as number);
+                      ref.current?.reload();
+                    }}
+                  >
+                    删除
+                  </a>,
+                ],
+              },
+            ]}
+
+            actionRef={ref}
+            toolbar={{
+              actions: [
+                <Button icon={<AddOutline/>} key={'add'} type={'primary'} onClick={bundle.create}>
+                  添加
+                </Button>,
+              ],
+              settings: [],
+            }}
+            search={false}
+          ></ProTable>
+        </Card>
+      </PageContainer>
+    </>
+  );
+};
+
+export default MenuExp;

+ 196 - 0
src/pages/Settings/Role.tsx

@@ -0,0 +1,196 @@
+import {
+  ActionType,
+  PageContainer,
+  ProForm,
+  ProFormText,
+  ProTable,
+} from '@ant-design/pro-components';
+import { Button, Card, Modal } from 'antd';
+import React, { useRef } from 'react';
+import { AddOutline } from 'antd-mobile-icons';
+import { EditBundle, useEdit } from '@/core/hooks/useEdit';
+import { invoke } from '@/core/network';
+
+type Role = {
+  id?: number;
+  name: string;
+  sortNum: number;
+  status: number;
+};
+
+type RoleList = {
+  records: Role[];
+  total: number;
+  size: number;
+  current: number;
+};
+
+class Service {
+  public static async list(current: number, size: number) {
+    return invoke<RoleList>('/apis/admin/role/page', {
+      method: 'POST',
+      data: {
+        current,
+        size,
+      },
+    });
+  }
+
+  public static async remove(id: number) {
+    return invoke<void>(`/apis/admin/role/remove?id=${id}`, {
+      method: 'DELETE',
+    });
+  }
+
+  public static async save(member: Partial<Role>) {
+    return invoke<Role>('/apis/admin/role/save', {
+      method: 'POST',
+      data: member,
+    });
+  }
+
+  public static async get(id: number) {
+    return invoke<Role>(`/apis/admin/role/details?id=${id}`, {
+      method: 'GET',
+    });
+  }
+}
+
+const Edit: React.FC<{ bundle: EditBundle; onSuccess?: () => void }> = (props) => {
+  const { bundle } = props;
+  const [form] = ProForm.useForm<Role>();
+  const [loading, setLoading] = React.useState(false);
+
+  return (
+    <Modal
+      confirmLoading={loading}
+      title={bundle.id ? '编辑角色' : '添加角色'}
+      open={bundle.open}
+      onOk={async () => {
+        await form.validateFields();
+
+        try {
+          setLoading(true);
+
+          await Service.save({
+            id: bundle.id,
+            ...form.getFieldsValue(),
+          });
+        } finally {
+          setLoading(false);
+        }
+
+        props.onSuccess?.();
+        bundle.close();
+      }}
+      onClose={() => {
+        form.resetFields();
+        bundle.close();
+      }}
+      onCancel={() => {
+        form.resetFields();
+        bundle.close();
+      }}
+    >
+      <ProForm
+        className={'mt-5'}
+        form={form}
+        labelCol={{ span: 4 }}
+        layout={'horizontal'}
+        submitter={false}
+        request={async () => {
+          if (bundle.id !== undefined) {
+            console.log(bundle.id, 'bundle');
+            const res = await Service.get(bundle.id);
+
+            return res.data;
+          }
+
+          return Promise.resolve({});
+        }}
+      >
+        <ProFormText
+          name={'name'}
+          label={'角色名称'}
+          rules={[
+            {
+              required: true,
+              message: '请输入角色名称',
+            },
+            {
+              min: 3,
+              max: 30,
+              message: '角色名称长度必须在3-30之间',
+            },
+          ]}
+          placeholder={'请输入角色名称'}
+        ></ProFormText>
+      </ProForm>
+    </Modal>
+  );
+};
+
+const RoleExp: React.FC = () => {
+  const bundle = useEdit();
+  const ref = useRef<ActionType>();
+
+  return (
+    <PageContainer content={''}>
+      {bundle.open && <Edit bundle={bundle} onSuccess={() => ref.current?.reload()}></Edit>}
+      <Card>
+        <ProTable<Role>
+          rowKey={'id'}
+          request={async (params) => {
+            const res = await Service.list(params.current || 1, params.pageSize || 10);
+
+            return {
+              success: res.success,
+              total: res.data.total,
+              data: res.data.records,
+            };
+          }}
+          columns={[
+            {
+              title: 'ID',
+              dataIndex: 'id',
+            },
+            {
+              title: '角色名称',
+              dataIndex: 'name',
+            },
+            {
+              title: '操作',
+              valueType: 'option',
+              render: (_, record) => [
+                <a key={'edit'} onClick={() => bundle.update(record.id as number)}>
+                  编辑
+                </a>,
+                <a
+                  key={'delete'}
+                  onClick={async () => {
+                    await Service.remove(record.id as number);
+                    ref.current?.reload();
+                  }}
+                >
+                  删除
+                </a>,
+              ],
+            },
+          ]}
+          actionRef={ref}
+          toolbar={{
+            actions: [
+              <Button icon={<AddOutline />} key={'add'} type={'primary'} onClick={bundle.create}>
+                添加
+              </Button>,
+            ],
+            settings: [],
+          }}
+          search={false}
+        ></ProTable>
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default RoleExp;

+ 0 - 338
src/pages/Settings/Screens.tsx

@@ -1,338 +0,0 @@
-import React, { useRef, useState } from 'react';
-import {
-  ActionType,
-  PageContainer,
-  ProForm,
-  ProFormDateRangePicker,
-  ProFormText,
-  ProTable,
-} from '@ant-design/pro-components';
-import { Button, Calendar, Card, Image, Modal, Popover, Tabs, Tag } from 'antd';
-import { AddOutline } from 'antd-mobile-icons';
-import { EditBundle, useEdit } from '@/core/hooks/useEdit';
-import UIImage from '@/core/ui/UIImage';
-import { v4 } from 'uuid';
-import { invoke } from '@/core/network';
-import dayjs from 'dayjs';
-
-export interface LaunchImage {
-  id: number;
-  title: string;
-  uri: string;
-  effectiveTime: string;
-  takeDownTime: string;
-}
-
-export interface LaunchImageList {
-  records: LaunchImage[];
-  total: number;
-  size: number;
-  current: number;
-}
-
-class Service {
-  public static async list(current: number, size: number) {
-    return invoke<LaunchImageList>('/apis/admin/launch_img/page', {
-      method: 'POST',
-      data: {
-        current,
-        size,
-      },
-    });
-  }
-
-  public static async get(id: number) {
-    return invoke<LaunchImage>('/apis/admin/launch_img/details', {
-      method: 'GET',
-      params: {
-        id,
-      },
-    });
-  }
-
-  public static async remove(id: number) {
-    return invoke<void>(`/apis/admin/launch_img/remove?id=${id}`, {
-      method: 'DELETE',
-    });
-  }
-
-  public static async save(image: Partial<LaunchImage>) {
-    return invoke<LaunchImage>('/apis/admin/launch_img/save', {
-      method: 'POST',
-      data: image,
-    });
-  }
-}
-
-type Screen = {
-  id: number;
-  title: string;
-  image: string;
-  date: string[];
-};
-
-const Edit: React.FC<{ bundle: EditBundle; onSuccess?: () => void }> = (props) => {
-  const { bundle } = props;
-  const [form] = ProForm.useForm();
-  const [loading, setLoading] = useState<boolean>(false);
-
-  return (
-    <Modal
-      confirmLoading={loading}
-      open={bundle.open}
-      title={bundle.id ? '编辑开机屏' : '添加开机屏'}
-      onCancel={() => {
-        bundle.close();
-      }}
-      onClose={() => {
-        bundle.close();
-      }}
-      onOk={async () => {
-        await form.validateFields();
-
-        try {
-          setLoading(true);
-          const values = form.getFieldsValue();
-
-          const res = await Service.save({
-            id: bundle.id,
-            title: values.title,
-            uri: values.image.name,
-            effectiveTime: values.date[0].format('YYYY-MM-DD HH:mm:ss'),
-            takeDownTime: values.date[1].format('YYYY-MM-DD HH:mm:ss'),
-          });
-
-          if (!res.success) {
-            return;
-          }
-
-          props.onSuccess?.();
-          bundle.close();
-        } finally {
-          setLoading(false);
-        }
-      }}
-    >
-      <ProForm
-        className={'mt-5'}
-        form={form}
-        layout={'horizontal'}
-        labelCol={{ span: 4 }}
-        submitter={false}
-        request={async () => {
-          if (bundle.id) {
-            const data = await Service.get(bundle.id);
-
-            return {
-              title: data.data.title,
-              image: data.data.uri,
-              date: [data.data.effectiveTime, data.data.takeDownTime],
-            };
-          }
-
-          return Promise.resolve({
-            image: {
-              name: `screen/${v4()}`,
-            },
-          });
-        }}
-      >
-        <ProFormText name={'title'} label={'标题'} required={true} />
-        <ProForm.Item name={'image'} label={'图片'} required={true}>
-          <UIImage className={'h-[180px]'}></UIImage>
-        </ProForm.Item>
-        <ProFormDateRangePicker
-          name={'date'}
-          label={'生效时间'}
-          rules={[
-            {
-              required: true,
-              message: '请选择生效时间区间',
-            },
-          ]}
-        ></ProFormDateRangePicker>
-      </ProForm>
-    </Modal>
-  );
-};
-
-const Screens: React.FC<{}> = (props) => {
-  const edit = useEdit();
-  const ref = useRef<ActionType>();
-  const [records, setRecords] = useState<LaunchImage[]>([]);
-
-  const modeTable = () => {
-    return (
-      <ProTable<Screen>
-        rowKey={'id'}
-        request={async (params) => {
-          const res = await Service.list(params.current || 1, params.pageSize || 10);
-
-          setRecords(res.data.records);
-
-          console.log(res.data.records, 'records');
-
-          return {
-            success: res.success,
-            total: res.data.total,
-            data: res.data.records.map((record) => {
-              return {
-                id: record.id,
-                title: record.title,
-                image: record.uri,
-                date: [record.effectiveTime, record.takeDownTime],
-              };
-            }),
-          };
-        }}
-        columns={[
-          {
-            title: 'ID',
-            dataIndex: 'id',
-          },
-          {
-            title: '标题',
-            dataIndex: 'title',
-          },
-          {
-            title: '图片',
-            dataIndex: 'image',
-            render: (node, record) => {
-              return <img src={record.image} alt={record.title} className={'w-[64px]'} />;
-            },
-          },
-          {
-            title: '生效时间',
-            dataIndex: 'date',
-            valueType: 'dateRange',
-          },
-          {
-            title: '操作',
-            valueType: 'option',
-            render: (_, record) => [
-              <a key={'edit'} onClick={() => edit.update(record.id)}>
-                编辑
-              </a>,
-              <a
-                key={'delete'}
-                onClick={async () => {
-                  await Service.remove(record.id);
-                  ref.current?.reload();
-                }}
-              >
-                删除
-              </a>,
-            ],
-          },
-        ]}
-        actionRef={ref}
-        search={false}
-        toolbar={{
-          actions: [
-            <Button key="add" type="primary" icon={<AddOutline />} onClick={() => edit.create()}>
-              添加
-            </Button>,
-          ],
-          settings: [],
-        }}
-      ></ProTable>
-    );
-  };
-
-  const stringToColor = (str: string): string => {
-    // 计算字符串的哈希值
-    let hash = 0;
-    for (let i = 0; i < str.length; i++) {
-      const char = str.charCodeAt(i);
-      hash = (hash << 5) - hash + char;
-      hash |= 0; // 转换为32位整数
-    }
-
-    // 使用哈希值的低位来生成 RGB 颜色
-    let r = (hash & 0xff0000) >> 16;
-    let g = (hash & 0x00ff00) >> 8;
-    let b = hash & 0x0000ff;
-
-    // 混合颜色使其变浅
-    const mix = (color: number, base: number): number => Math.round((color + base) / 2.4);
-    r = mix(r, 255);
-    g = mix(g, 255);
-    b = mix(b, 255);
-
-    // 将 RGB 值转换为十六进制字符串,并确保两位数
-    const toHex = (n: number): string => n.toString(16).padStart(2, '0');
-
-    // 返回颜色的十六进制表示
-    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
-  };
-
-  const modeCalendar = () => {
-    return (
-      <Calendar
-        cellRender={(date) => {
-          const items = records.filter((i) => {
-            const effect = dayjs(i.effectiveTime);
-            const take = dayjs(i.takeDownTime);
-
-            //date between [effect,take]
-            return date.isAfter(effect) && date.isBefore(take);
-          });
-
-          return (
-            <div className={'flex flex-col gap-2'}>
-              {items.map((i, index) => {
-                return (
-                  <Popover
-                    key={`${index}`}
-                    title={i.title}
-                    content={
-                      <div>
-                        <Image src={i.uri} width={120}></Image>
-                      </div>
-                    }
-                  >
-                    <Tag
-                      key={`${index}`}
-                      style={{ borderRadius: 5 }}
-                      color={stringToColor(i.title)}
-                      onClick={() => {
-                        console.log('click');
-                      }}
-                    >
-                      {i.title}
-                    </Tag>
-                  </Popover>
-                );
-              })}
-            </div>
-          );
-        }}
-      ></Calendar>
-    );
-  };
-
-  return (
-    <PageContainer>
-      {edit.open && <Edit bundle={edit} onSuccess={() => ref.current?.reload()}></Edit>}
-      <Card>
-        <Tabs
-          defaultActiveKey={'1'}
-          items={[
-            {
-              label: '表单模式',
-              key: '1',
-              children: modeTable(),
-            },
-            {
-              label: '日历模式',
-              key: '2',
-              children: modeCalendar(),
-            },
-          ]}
-        ></Tabs>
-      </Card>
-    </PageContainer>
-  );
-};
-
-export default Screens;

+ 2 - 2
src/pages/User/Login/index.tsx

@@ -134,8 +134,8 @@ const Login: React.FC = () => {
             maxWidth: '75vw',
           }}
           logo={<img alt="logo" src="/logo.svg" />}
-          title="魔饭星"
-          subTitle={'欢迎登录魔饭星管理后台'}
+          title="Sect"
+          subTitle={'欢迎登录Sect管理后台'}
           initialValues={{
             autoLogin: true,
           }}