فهرست منبع

feat: add message editor (close #59)

JustSong 2 سال پیش
والد
کامیت
d8aaec9b84

+ 17 - 0
web/src/App.js

@@ -18,6 +18,7 @@ import { StatusContext } from './context/Status';
 import Message from './pages/Message';
 import Channel from './pages/Channel';
 import EditChannel from './pages/Channel/EditChannel';
+import EditMessage from './pages/Message/EditMessage';
 
 const Home = lazy(() => import('./pages/Home'));
 const About = lazy(() => import('./pages/About'));
@@ -141,6 +142,22 @@ function App() {
           </PrivateRoute>
         }
       />
+      <Route
+        path='/editor'
+        element={
+          <PrivateRoute>
+            <EditMessage />
+          </PrivateRoute>
+        }
+      />
+      <Route
+        path='/editor/:id'
+        element={
+          <PrivateRoute>
+            <EditMessage />
+          </PrivateRoute>
+        }
+      />
       <Route
         path='/login'
         element={

+ 5 - 0
web/src/components/Header.js

@@ -18,6 +18,11 @@ const headerButtons = [
     to: '/message',
     icon: 'mail',
   },
+  {
+    name: '编辑',
+    to: '/editor',
+    icon: 'edit',
+  },
   {
     name: '通道',
     to: '/channel',

+ 10 - 0
web/src/components/MessagesTable.js

@@ -11,6 +11,7 @@ import { API, openPage, showError, showSuccess, showWarning } from '../helpers';
 
 import { ITEMS_PER_PAGE } from '../constants';
 import { renderTimestamp } from '../helpers/render';
+import { Link } from 'react-router-dom';
 
 function renderStatus(status) {
   switch (status) {
@@ -317,6 +318,15 @@ const MessagesTable = () => {
                       >
                         查看
                       </Button>
+                      <Button
+                        size={'small'}
+                        primary
+                        loading={loading}
+                        as={Link}
+                        to={'/editor/' + message.id}
+                      >
+                        编辑
+                      </Button>
                       <Button
                         size={'small'}
                         color={'yellow'}

+ 15 - 37
web/src/components/PushSetting.js

@@ -1,6 +1,7 @@
 import React, { useEffect, useState } from 'react';
 import { Button, Form, Grid, Header, Message } from 'semantic-ui-react';
 import { API, showError, showSuccess, testChannel } from '../helpers';
+import { loadUser, loadUserChannels } from '../helpers/loader';
 
 const PushSetting = () => {
   let [user, setUser] = useState({
@@ -16,44 +17,19 @@ const PushSetting = () => {
     setUser((inputs) => ({ ...inputs, [name]: value }));
   };
 
-  const loadUser = async () => {
-    let res = await API.get(`/api/user/self`);
-    const { success, message, data } = res.data;
-    if (success) {
-      if (data.channel === '') {
-        data.channel = 'email';
+  useEffect(() => {
+    const loader = async () => {
+      let user = await loadUser();
+      if (user) {
+        setUser(user);
       }
-      if (data.token === ' ') {
-        data.token = '';
+      let channels = await loadUserChannels();
+      if (channels) {
+        setChannels(channels);
       }
-      setUser(data);
-    } else {
-      showError(message);
-    }
-    setLoading(false);
-  };
-
-  const loadUserChannels = async () => {
-    let res = await API.get(`/api/channel?brief=true`);
-    const { success, message, data } = res.data;
-    if (success) {
-      data.forEach((channel) => {
-        channel.key = channel.name;
-        channel.text = channel.name;
-        channel.value = channel.name;
-        if (channel.description === '') {
-          channel.description = '无备注信息';
-        }
-      });
-      setChannels(data);
-    } else {
-      showError(message);
-    }
-  };
-
-  useEffect(() => {
-    loadUser().then();
-    loadUserChannels().then();
+      setLoading(false);
+    };
+    loader().then();
   }, []);
 
   const submit = async (which) => {
@@ -106,7 +82,9 @@ const PushSetting = () => {
           <Button onClick={() => submit('general')} loading={loading}>
             保存
           </Button>
-          <Button onClick={() => testChannel(user.username, user.token, '')}>测试</Button>
+          <Button onClick={() => testChannel(user.username, user.token, '')}>
+            测试
+          </Button>
         </Form>
       </Grid.Column>
     </Grid>

+ 38 - 0
web/src/helpers/loader.js

@@ -0,0 +1,38 @@
+import { API } from './api';
+import { showError } from './utils';
+
+export const loadUser = async () => {
+  let res = await API.get(`/api/user/self`);
+  const { success, message, data } = res.data;
+  if (success) {
+    if (data.channel === '') {
+      data.channel = 'email';
+    }
+    if (data.token === ' ') {
+      data.token = '';
+    }
+    return data;
+  } else {
+    showError(message);
+    return null;
+  }
+};
+
+export const loadUserChannels = async () => {
+  let res = await API.get(`/api/channel?brief=true`);
+  const { success, message, data } = res.data;
+  if (success) {
+    data.forEach((channel) => {
+      channel.key = channel.name;
+      channel.text = channel.name;
+      channel.value = channel.name;
+      if (channel.description === '') {
+        channel.description = '无备注信息';
+      }
+    });
+    return data;
+  } else {
+    showError(message);
+    return null;
+  }
+};

+ 158 - 0
web/src/pages/Message/EditMessage.js

@@ -0,0 +1,158 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Form, Segment } from 'semantic-ui-react';
+import { useParams } from 'react-router-dom';
+import { API, showError, showSuccess } from '../../helpers';
+import { loadUser, loadUserChannels } from '../../helpers/loader';
+
+const EditMessage = () => {
+  const params = useParams();
+  const messageId = params.id;
+  const isEditing = messageId !== undefined;
+  let [user, setUser] = useState({
+    id: '',
+    username: '',
+    channel: '',
+    token: '',
+  });
+  let [channels, setChannels] = useState([]);
+  const [loading, setLoading] = useState(isEditing);
+  const originInputs = {
+    title: '',
+    description: '',
+    content: '',
+    url: '',
+    channel: '',
+    to: '',
+    async: false,
+  };
+
+  const [inputs, setInputs] = useState(originInputs);
+  const { title, description, content, url, channel, to, async } = inputs;
+
+  const handleInputChange = (e, { name, value }) => {
+    setInputs((inputs) => ({ ...inputs, [name]: value }));
+  };
+
+  const loadMessage = async () => {
+    let res = await API.get(`/api/message/${messageId}`);
+    const { success, message, data } = res.data;
+    if (success) {
+      data.id = 0;
+      setInputs(data);
+    } else {
+      showError(message);
+    }
+    setLoading(false);
+  };
+
+  useEffect(() => {
+    if (isEditing) {
+      loadMessage().then();
+    }
+    const loader = async () => {
+      let user = await loadUser();
+      if (user) {
+        setUser(user);
+      }
+      let channels = await loadUserChannels();
+      if (channels) {
+        setChannels(channels);
+      }
+    };
+    loader().then();
+  }, []);
+
+  const send = async () => {
+    if (!description && !content) return;
+    let res = await API.post(`/push/${user.username}/`, {
+      ...inputs,
+      token: user.token,
+    });
+    const { success, message } = res.data;
+    if (success) {
+      if (isEditing) {
+        showSuccess('消息重发成功!');
+      } else {
+        showSuccess('消息发送成功!');
+        setInputs(originInputs);
+      }
+    } else {
+      showError(message);
+    }
+  };
+
+  return (
+    <>
+      <Segment loading={loading} className={'clearing'}>
+        <Form>
+          <Form.Group widths='equal'>
+            <Form.Input
+              label='标题'
+              placeholder='请输入消息标题'
+              value={inputs.title}
+              name='title'
+              onChange={handleInputChange}
+            />
+            <Form.Input
+              label='接收者'
+              placeholder='请输入接收者,不填使用默认接收者'
+              value={inputs.to}
+              name='to'
+              onChange={handleInputChange}
+            />
+            <Form.Select
+              label='推送方式'
+              placeholder='请选择推送方式,否则使用默认方式'
+              name='channel'
+              options={channels}
+              value={inputs.channel}
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Form.Group widths='equal'>
+            <Form.Input
+              label='描述'
+              placeholder='请输入消息描述'
+              value={inputs.description}
+              name='description'
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Form.Group widths='equal'>
+            <Form.TextArea
+              label='内容'
+              placeholder='请输入消息内容'
+              value={inputs.content}
+              name='content'
+              onChange={handleInputChange}
+              style={{ minHeight: 200, fontFamily: 'JetBrains Mono, Consolas' }}
+            />
+          </Form.Group>
+          <Form.Group widths='equal'>
+            <Form.Input
+              label='链接'
+              placeholder='请输入消息链接'
+              value={inputs.url}
+              type={'url'}
+              name='content'
+              onChange={handleInputChange}
+            />
+          </Form.Group>
+          <Button type='submit' floated='right' onClick={send}>
+            发送
+          </Button>
+          <Button
+            floated='right'
+            onClick={() => {
+              handleInputChange(null, { name: 'async', value: !async });
+            }}
+          >
+            {async ? '异步' : '同步'}
+          </Button>
+        </Form>
+      </Segment>
+    </>
+  );
+};
+
+export default EditMessage;