From 3a0aa8679776714307bb5cc1a75f2d594bb76936 Mon Sep 17 00:00:00 2001 From: vapao Date: Sun, 28 Mar 2021 00:33:32 +0800 Subject: [PATCH] add module --- spug_api/apps/account/views.py | 1 + spug_api/apps/notice/__init__.py | 3 + spug_api/apps/notice/models.py | 24 ++++ spug_api/apps/notice/urls.py | 10 ++ spug_api/apps/notice/views.py | 64 +++++++++ spug_api/spug/settings.py | 1 + spug_api/spug/urls.py | 1 + .../pages/{home => dashboard}/AlarmTrend.js | 0 .../pages/{home => dashboard}/LoginActive.js | 0 .../pages/{home => dashboard}/RequestTop.js | 0 .../{home => dashboard}/StatisticCard.js | 0 spug_web/src/pages/dashboard/index.js | 33 +++++ .../{home => dashboard}/index.module.css | 0 spug_web/src/pages/home/Nav.js | 30 +++++ spug_web/src/pages/home/Notice.js | 121 ++++++++++++++++++ spug_web/src/pages/home/Todo.js | 15 +++ spug_web/src/pages/home/index.js | 51 ++++---- spug_web/src/pages/home/index.module.less | 69 ++++++++++ spug_web/src/pages/login/index.js | 1 + spug_web/src/routes.js | 19 +-- 20 files changed, 406 insertions(+), 37 deletions(-) create mode 100644 spug_api/apps/notice/__init__.py create mode 100644 spug_api/apps/notice/models.py create mode 100644 spug_api/apps/notice/urls.py create mode 100644 spug_api/apps/notice/views.py rename spug_web/src/pages/{home => dashboard}/AlarmTrend.js (100%) rename spug_web/src/pages/{home => dashboard}/LoginActive.js (100%) rename spug_web/src/pages/{home => dashboard}/RequestTop.js (100%) rename spug_web/src/pages/{home => dashboard}/StatisticCard.js (100%) create mode 100644 spug_web/src/pages/dashboard/index.js rename spug_web/src/pages/{home => dashboard}/index.module.css (100%) create mode 100644 spug_web/src/pages/home/Nav.js create mode 100644 spug_web/src/pages/home/Notice.js create mode 100644 spug_web/src/pages/home/Todo.js create mode 100644 spug_web/src/pages/home/index.module.less diff --git a/spug_api/apps/account/views.py b/spug_api/apps/account/views.py index 429b0f7..a32fece 100644 --- a/spug_api/apps/account/views.py +++ b/spug_api/apps/account/views.py @@ -201,6 +201,7 @@ def handle_user_info(user, x_real_ip): History.objects.create(user=user, ip=x_real_ip) verify_ip = AppSetting.get_default('verify_ip', True) return json_response({ + 'id': user.id, 'access_token': user.access_token, 'nickname': user.nickname, 'is_supper': user.is_supper, diff --git a/spug_api/apps/notice/__init__.py b/spug_api/apps/notice/__init__.py new file mode 100644 index 0000000..89f622a --- /dev/null +++ b/spug_api/apps/notice/__init__.py @@ -0,0 +1,3 @@ +# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug +# Copyright: (c) +# Released under the AGPL-3.0 License. diff --git a/spug_api/apps/notice/models.py b/spug_api/apps/notice/models.py new file mode 100644 index 0000000..4cd0faa --- /dev/null +++ b/spug_api/apps/notice/models.py @@ -0,0 +1,24 @@ +# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug +# Copyright: (c) +# Released under the AGPL-3.0 License. +from django.db import models +from libs.mixins import ModelMixin +import json + + +class Notice(models.Model, ModelMixin): + title = models.CharField(max_length=100) + content = models.TextField() + is_stress = models.BooleanField(default=False) + read_ids = models.TextField(default='[]') + sort_id = models.IntegerField(default=0, db_index=True) + created_at = models.DateTimeField(auto_now_add=True) + + def to_view(self): + tmp = self.to_dict() + tmp['read_ids'] = json.loads(self.read_ids) + return tmp + + class Meta: + db_table = 'notices' + ordering = ('-sort_id',) diff --git a/spug_api/apps/notice/urls.py b/spug_api/apps/notice/urls.py new file mode 100644 index 0000000..018889b --- /dev/null +++ b/spug_api/apps/notice/urls.py @@ -0,0 +1,10 @@ +# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug +# Copyright: (c) +# Released under the AGPL-3.0 License. +from django.urls import path + +from .views import * + +urlpatterns = [ + path('', NoticeView.as_view()), +] diff --git a/spug_api/apps/notice/views.py b/spug_api/apps/notice/views.py new file mode 100644 index 0000000..923aca5 --- /dev/null +++ b/spug_api/apps/notice/views.py @@ -0,0 +1,64 @@ +# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug +# Copyright: (c) +# Released under the AGPL-3.0 License. +from django.views.generic import View +from libs import json_response, JsonParser, Argument +from apps.notice.models import Notice +import json + + +class NoticeView(View): + def get(self, request): + notices = Notice.objects.all() + return json_response([x.to_view() for x in notices]) + + def post(self, request): + form, error = JsonParser( + Argument('id', type=int, required=False), + Argument('title', help='请输入标题'), + Argument('content', help='请输入内容'), + Argument('is_stress', type=bool, default=False), + ).parse(request.body) + if error is None: + if form.is_stress: + Notice.objects.update(is_stress=False) + if form.id: + Notice.objects.filter(pk=form.id).update(**form) + else: + notice = Notice.objects.create(**form) + notice.sort_id = notice.id + notice.save() + return json_response(error=error) + + def patch(self, request): + form, error = JsonParser( + Argument('id', type=int, help='参数错误'), + Argument('sort', filter=lambda x: x in ('up', 'down'), required=False), + Argument('read', required=False) + ).parse(request.body) + if error is None: + notice = Notice.objects.filter(pk=form.id).first() + if not notice: + return json_response(error='未找到指定记录') + if form.sort: + if form.sort == 'up': + tmp = Notice.objects.filter(sort_id__gt=notice.sort_id).last() + else: + tmp = Notice.objects.filter(sort_id__lt=notice.sort_id).first() + if tmp: + tmp.sort_id, notice.sort_id = notice.sort_id, tmp.sort_id + tmp.save() + if form.read: + read_ids = json.loads(notice.read_ids) + read_ids.append(str(request.user.id)) + notice.read_ids = json.dumps(read_ids) + notice.save() + return json_response(error=error) + + def delete(self, request): + form, error = JsonParser( + Argument('id', type=int, help='参数错误') + ).parse(request.GET) + if error is None: + Notice.objects.filter(pk=form.id).delete() + return json_response(error=error) diff --git a/spug_api/spug/settings.py b/spug_api/spug/settings.py index 2bf952d..33735b9 100644 --- a/spug_api/spug/settings.py +++ b/spug_api/spug/settings.py @@ -47,6 +47,7 @@ INSTALLED_APPS = [ 'apps.deploy', 'apps.notify', 'apps.repository', + 'apps.notice', ] MIDDLEWARE = [ diff --git a/spug_api/spug/urls.py b/spug_api/spug/urls.py index 0635e8f..cad3b36 100644 --- a/spug_api/spug/urls.py +++ b/spug_api/spug/urls.py @@ -33,5 +33,6 @@ urlpatterns = [ path('home/', include('apps.home.urls')), path('notify/', include('apps.notify.urls')), path('file/', include('apps.file.urls')), + path('notice/', include('apps.notice.urls')), path('apis/', include('apps.apis.urls')), ] diff --git a/spug_web/src/pages/home/AlarmTrend.js b/spug_web/src/pages/dashboard/AlarmTrend.js similarity index 100% rename from spug_web/src/pages/home/AlarmTrend.js rename to spug_web/src/pages/dashboard/AlarmTrend.js diff --git a/spug_web/src/pages/home/LoginActive.js b/spug_web/src/pages/dashboard/LoginActive.js similarity index 100% rename from spug_web/src/pages/home/LoginActive.js rename to spug_web/src/pages/dashboard/LoginActive.js diff --git a/spug_web/src/pages/home/RequestTop.js b/spug_web/src/pages/dashboard/RequestTop.js similarity index 100% rename from spug_web/src/pages/home/RequestTop.js rename to spug_web/src/pages/dashboard/RequestTop.js diff --git a/spug_web/src/pages/home/StatisticCard.js b/spug_web/src/pages/dashboard/StatisticCard.js similarity index 100% rename from spug_web/src/pages/home/StatisticCard.js rename to spug_web/src/pages/dashboard/StatisticCard.js diff --git a/spug_web/src/pages/dashboard/index.js b/spug_web/src/pages/dashboard/index.js new file mode 100644 index 0000000..1baf57c --- /dev/null +++ b/spug_web/src/pages/dashboard/index.js @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug + * Copyright (c) + * Released under the AGPL-3.0 License. + */ +import React from 'react'; +import { Row, Col } from 'antd'; +import { AuthDiv } from 'components'; +import StatisticsCard from './StatisticCard'; +import AlarmTrend from './AlarmTrend'; +import RequestTop from './RequestTop'; +import LoginActive from './LoginActive'; + +class HomeIndex extends React.Component { + render() { + return ( + + + + + + + + + + + + + ) + } +} + +export default HomeIndex diff --git a/spug_web/src/pages/home/index.module.css b/spug_web/src/pages/dashboard/index.module.css similarity index 100% rename from spug_web/src/pages/home/index.module.css rename to spug_web/src/pages/dashboard/index.module.css diff --git a/spug_web/src/pages/home/Nav.js b/spug_web/src/pages/home/Nav.js new file mode 100644 index 0000000..ca0f9e2 --- /dev/null +++ b/spug_web/src/pages/home/Nav.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { Avatar, Button, Card, Col, Row } from 'antd'; +import styles from './index.module.less'; + +function NavIndex(props) { + return ( + 编辑}> + + + + } + title="Gitlab" + description="Gitlab 内部代码仓库,请使用公司LDAP账户登录"/> + + + + + } + title="Wiki系统" + description="文档系统,技术架构及技术文档"/> + + + + + ) +} + +export default NavIndex \ No newline at end of file diff --git a/spug_web/src/pages/home/Notice.js b/spug_web/src/pages/home/Notice.js new file mode 100644 index 0000000..fa05ee6 --- /dev/null +++ b/spug_web/src/pages/home/Notice.js @@ -0,0 +1,121 @@ +import React, { useEffect, useState } from 'react'; +import { Button, Card, List, Modal, Form, Input, Switch, Divider, Badge } from 'antd'; +import { DownSquareOutlined, PlusOutlined, UpSquareOutlined } from '@ant-design/icons'; +import { http } from 'libs'; +import styles from "./index.module.less"; + +function NoticeIndex(props) { + const id = localStorage.getItem('id'); + const [form] = Form.useForm(); + const [fetching, setFetching] = useState(true); + const [loading, setLoading] = useState(false); + const [isEdit, setIsEdit] = useState(false); + const [records, setRecords] = useState([]); + const [record, setRecord] = useState(); + const [notice, setNotice] = useState(); + + useEffect(() => { + fetch() + }, []) + + function fetch() { + setFetching(true); + http.get('/api/notice/') + .then(res => setRecords(res)) + .finally(() => setFetching(false)) + } + + function handleSubmit() { + setLoading(true); + const formData = form.getFieldsValue(); + formData['id'] = record.id; + http.post('/api/notice/', formData) + .then(() => { + fetch() + setRecord(null) + }) + .finally(() => setLoading(false)) + } + + function showForm(info) { + setRecord(info); + setTimeout(() => form.resetFields()) + } + + function handleSort(e, info, sort) { + e.stopPropagation(); + http.patch('/api/notice/', {id: info.id, sort}) + .then(() => fetch()) + } + + function handleRead() { + if (!notice.read_ids.includes(id)) { + const formData = {id: notice.id, read: 1}; + http.patch('/api/notice/', formData) + .then(() => fetch()) + } + setNotice(null); + } + + return ( + setIsEdit(!isEdit)}>{isEdit ? '完成' : '编辑'}}> + {isEdit ? ( + +
showForm({})}>新建公告
+ {records.map(item => ( + showForm(item)}> +
+ handleSort(e, item, 'up')}/> + + handleSort(e, item, 'down')}/> + {item.title} +
+
+ ))} +
+ ) : ( + + {records.map(item => ( + setNotice(item)}> + {item.title} + {item.created_at.substr(0, 10)} + + ))} + {records.length === 0 && ( +
暂无公告信息
+ )} +
+ )} + console.log('after close')} + onCancel={() => setRecord(null)} + confirmLoading={loading} + onOk={handleSubmit}> +
+ + + + + + + + + +
+
+ {notice ? ( + + {notice.content} + + ) : null} +
+ ) +} + +export default NoticeIndex \ No newline at end of file diff --git a/spug_web/src/pages/home/Todo.js b/spug_web/src/pages/home/Todo.js new file mode 100644 index 0000000..98c8104 --- /dev/null +++ b/spug_web/src/pages/home/Todo.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { Button, Card, List } from 'antd'; + +function TodoIndex(props) { + return ( + + + 已完成}>发布申请 测试未附件 需要你审核。 + 工单 资源添加 需要你审核。 + + + ) +} + +export default TodoIndex \ No newline at end of file diff --git a/spug_web/src/pages/home/index.js b/spug_web/src/pages/home/index.js index 1baf57c..3c04e43 100644 --- a/spug_web/src/pages/home/index.js +++ b/spug_web/src/pages/home/index.js @@ -1,33 +1,28 @@ -/** - * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug - * Copyright (c) - * Released under the AGPL-3.0 License. - */ import React from 'react'; import { Row, Col } from 'antd'; -import { AuthDiv } from 'components'; -import StatisticsCard from './StatisticCard'; -import AlarmTrend from './AlarmTrend'; -import RequestTop from './RequestTop'; -import LoginActive from './LoginActive'; +import { Breadcrumb } from 'components'; +import NoticeIndex from './Notice'; +import TodoIndex from './Todo'; +import NavIndex from './Nav'; -class HomeIndex extends React.Component { - render() { - return ( - - - - - - - - - - - - - ) - } +function HomeIndex() { + return ( +
+ + 首页 + 工作台 + + + + + + + + + + +
+ ) } -export default HomeIndex +export default HomeIndex \ No newline at end of file diff --git a/spug_web/src/pages/home/index.module.less b/spug_web/src/pages/home/index.module.less new file mode 100644 index 0000000..8e48ea6 --- /dev/null +++ b/spug_web/src/pages/home/index.module.less @@ -0,0 +1,69 @@ +.notice { + :global(.ant-card-body) { + height: 220px; + padding: 0 24px; + } + + button { + padding-right: 0; + } + + .title { + cursor: pointer; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + + } + + .title:hover { + color: #1890ff + } + + .add { + display: flex; + justify-content: center; + align-items: center; + margin-top: 8px; + height: 30px; + border-radius: 2px; + border: 1px dashed #d9d9d9; + font-size: 12px; + cursor: pointer; + } + + .add:hover { + border: 1px dashed #1890ff; + color: #1890ff; + } + + .item { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + :global(.anticon) { + cursor: pointer; + color: #1890ff; + } + } +} + +.nav { + margin-top: 12px; + + :global(.ant-card) { + height: 120px; + background-color: #fafafa; + cursor: pointer; + + :global(.ant-card-meta-description) { + display: -webkit-box; + text-overflow: ellipsis; + overflow: hidden; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + } + } + +} \ No newline at end of file diff --git a/spug_web/src/pages/login/index.js b/spug_web/src/pages/login/index.js index 9d10af5..0932918 100644 --- a/spug_web/src/pages/login/index.js +++ b/spug_web/src/pages/login/index.js @@ -64,6 +64,7 @@ export default function () { } function doLogin(data) { + localStorage.setItem('id', data['id']); localStorage.setItem('token', data['access_token']); localStorage.setItem('nickname', data['nickname']); localStorage.setItem('is_supper', data['is_supper']); diff --git a/spug_web/src/routes.js b/spug_web/src/routes.js index 37c51d9..8bee9a7 100644 --- a/spug_web/src/routes.js +++ b/spug_web/src/routes.js @@ -5,6 +5,7 @@ */ import React from 'react'; import { + DashboardOutlined, DesktopOutlined, CloudServerOutlined, CodeOutlined, @@ -15,41 +16,41 @@ import { AlertOutlined, SettingOutlined } from '@ant-design/icons'; + import HomeIndex from './pages/home'; - +import DashboardIndex from './pages/dashboard'; import HostIndex from './pages/host'; - import ExecTask from './pages/exec/task'; import ExecTemplate from './pages/exec/template'; - import DeployApp from './pages/deploy/app'; import DeployRepository from './pages/deploy/repository'; import DeployRequest from './pages/deploy/request'; import DoExt1Index from './pages/deploy/do/Ext1Index'; import DoExt2Index from './pages/deploy/do/Ext2Index'; - import ScheduleIndex from './pages/schedule'; - import ConfigEnvironment from './pages/config/environment'; import ConfigService from './pages/config/service'; import ConfigApp from './pages/config/app'; import ConfigSetting from './pages/config/setting'; - import MonitorIndex from './pages/monitor'; - import AlarmIndex from './pages/alarm/alarm'; import AlarmGroup from './pages/alarm/group'; import AlarmContact from './pages/alarm/contact'; - import SystemAccount from './pages/system/account'; import SystemRole from './pages/system/role'; import SystemSetting from './pages/system/setting'; - import WelcomeIndex from './pages/welcome/index'; import WelcomeInfo from './pages/welcome/info'; export default [ {icon: , title: '工作台', auth: 'home.home.view', path: '/home', component: HomeIndex}, + { + icon: , + title: 'Dashboard', + auth: 'home.home.view', + path: '/dashboard', + component: DashboardIndex + }, {icon: , title: '主机管理', auth: 'host.host.view', path: '/host', component: HostIndex}, { icon: , title: '批量执行', auth: 'exec.task.do|exec.template.view', child: [