mirror of https://github.com/raiots/TasksManager
Compare commits
52 Commits
Author | SHA1 | Date |
---|---|---|
|
f889efc8cd | |
|
8bee3fca96 | |
|
d813066e03 | |
|
483432b913 | |
|
f8cb32e702 | |
|
8d710ac088 | |
|
b0433783c8 | |
|
97a5e22795 | |
|
1936734006 | |
|
3b891ad229 | |
|
ca826cfb19 | |
|
6103d632d0 | |
|
b87ea4c9bd | |
|
fc4bbc748c | |
|
6a46184fda | |
|
c6e965b38a | |
|
b0cdc33fb1 | |
|
6495b8cf2b | |
|
86bf9bd1c6 | |
|
1fa02689f8 | |
|
066dd333a8 | |
|
69af43c01c | |
|
92971237f5 | |
|
df9e174bd3 | |
|
3047ba6ed5 | |
|
ead6413fcc | |
|
4f827bdee4 | |
|
7930bd4c89 | |
|
3283478b42 | |
|
81c2e817d1 | |
|
177ff0ea9a | |
|
a194cb664d | |
|
1f0f65dfb9 | |
|
a31135cfcc | |
|
27f54562df | |
|
d7359e3ea8 | |
|
3d08ffce71 | |
|
80f3f7f9d8 | |
|
a6022a3865 | |
|
151fcaacb5 | |
|
39ab683cd6 | |
|
8b4d29a71d | |
|
42ce5e6fc8 | |
|
004ba6a0a6 | |
|
4748f2b290 | |
|
6ef6ecff5b | |
|
4bfc8eb5cd | |
|
58489517de | |
|
7a73917e1c | |
|
15f0165ae4 | |
|
15f80ce25d | |
|
3f3f91f408 |
|
@ -0,0 +1 @@
|
|||
static/** linguist-vendored
|
|
@ -0,0 +1,20 @@
|
|||
name: Qodana
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- 'releases/*'
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Qodana Scan'
|
||||
uses: JetBrains/qodana-action@main
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
|
@ -0,0 +1,30 @@
|
|||
name: Django CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
- name: Run Tests
|
||||
run: |
|
||||
python manage.py test
|
|
@ -3,3 +3,8 @@
|
|||
/packagesdir/
|
||||
/db.sqlite3
|
||||
/identifier.sqlite
|
||||
migrations/
|
||||
|
||||
node_modules
|
||||
.temp
|
||||
.cache
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# 从仓库拉取 带有 python 3.8 的 Linux 环境
|
||||
FROM python:3.8
|
||||
|
||||
# 设置 python 环境变量
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
# 创建 code 文件夹并将其设置为工作目录
|
||||
RUN mkdir /code
|
||||
WORKDIR /code
|
||||
# 更新 pip
|
||||
RUN pip install pip -U
|
||||
# 将 requirements.txt 复制到容器的 code 目录
|
||||
ADD requirements.txt /code/
|
||||
# 安装库
|
||||
RUN pip install -r requirements.txt -i https://mirrors.ustc.edu.cn/pypi/web/simple
|
||||
# 将当前目录复制到容器的 code 目录
|
||||
ADD . /code/
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Raiot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
26
README.md
26
README.md
|
@ -9,7 +9,7 @@ Author: [面壁的雨](https://raiot.me)
|
|||
|
||||
到时候会出一个文档的(或许
|
||||
|
||||
from 2020-7-13 to 2021-2-2
|
||||
from 2020-7-13 to 2021-3-31
|
||||
|
||||
鬼知道为什么会用这么长时间
|
||||
|
||||
|
@ -18,3 +18,27 @@ from 2020-7-13 to 2021-2-2
|
|||
#### 注意!
|
||||
|
||||
* 此版本仅为beta版本,请勿在生产环境使用
|
||||
|
||||
### 介绍
|
||||
|
||||
这是一个由Django开发的任务分发系统
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
#### feature:
|
||||
|
||||
* 员工可以看到自己负责的承办任务和协办任务
|
||||
* 部门全年任务时间表
|
||||
* 高定制度的后台管理页面
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
JetBrains OS License
|
||||
|
||||
TasksManager has been developed with PyCharm IDE under JetBrains Open Source License.
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ For the full list of settings and their values, see
|
|||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
|
@ -25,7 +26,7 @@ SECRET_KEY = '&!38pk#dv=r!_c(+b&oegc0m(ndzoue+ez*7kvjv2uubuqootp'
|
|||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = ['192.168.31.198', '127.0.0.1', '172.20.22.40', '110.42.209.79']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
@ -38,6 +39,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'import_export',
|
||||
'apps.users',
|
||||
'apps.tasks',
|
||||
]
|
||||
|
@ -134,7 +136,10 @@ LOGIN_REDIRECT_URL = '/'
|
|||
# 自定义用户模型
|
||||
AUTH_USER_MODEL = 'users.User'
|
||||
|
||||
# 它确定库是否会在数据导入中使用数据库事务,以确保安全。
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
|
||||
SITE_NAME = '任务管理工具'
|
||||
# SimpleUI 配置
|
||||
|
||||
# 离线模式
|
||||
|
@ -145,8 +150,89 @@ SIMPLEUI_HOME_INFO = False
|
|||
SIMPLEUI_HOME_QUICK = True
|
||||
SIMPLEUI_HOME_ACTION = True
|
||||
SIMPLEUI_ANALYSIS = False
|
||||
SIMPLEUI_HOME_TITLE = '任务管理系统'
|
||||
SIMPLEUI_HOME_TITLE = SITE_NAME
|
||||
# SIMPLEUI_HOME_PAGE = 'https://www.baidu.com' # 可用于嵌入其他链接,这里可以直接方便的嵌入报表链接
|
||||
SIMPLEUI_HOME_ICON = 'el el-icon-platform-eleme'
|
||||
# ICON 支持element-ui和fontawesome eg:fa fa-user
|
||||
# https://zhuanlan.zhihu.com/p/113447102
|
||||
# https://zhuanlan.zhihu.com/p/113447102
|
||||
|
||||
# 指定simpleui默认的主题,指定一个文件名,相对路径就从simpleui的theme目录读取
|
||||
SIMPLEUI_DEFAULT_THEME = 'ant.design.css'
|
||||
|
||||
SIMPLEUI_CONFIG = {
|
||||
'system_keep': False,
|
||||
'menu_display': ['任务管理', '系统配置', '关于'], # 开启排序和过滤功能, 不填此字段为默认排序和全部显示, 空列表[] 为全部不显示.
|
||||
'dynamic': True, # 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时动态展示菜单内容
|
||||
'menus': [{
|
||||
'name': '任务管理',
|
||||
'icon': 'fas fa-code',
|
||||
'models': [{
|
||||
'app': 'tasks',
|
||||
'name': '年度任务',
|
||||
'url': 'tasks/task'
|
||||
}, {
|
||||
'name': '工作包',
|
||||
'url': 'tasks/todo?o=3'
|
||||
}]
|
||||
}, {
|
||||
'app': 'admin',
|
||||
'name': '系统配置',
|
||||
'icon': 'fas fa-user-shield',
|
||||
'models': [{
|
||||
'name': '用户',
|
||||
'icon': 'fa fa-user',
|
||||
'url': 'users/user',
|
||||
}, {
|
||||
'name': '部门',
|
||||
'url': 'users/department'
|
||||
}, {
|
||||
'name': '任务属性',
|
||||
'url': 'users/taskproperty',
|
||||
}, {
|
||||
'name': '权限组',
|
||||
'url': 'users/mygroup',
|
||||
}, {
|
||||
'name': '评价等级定义',
|
||||
'url': 'users/qualitymark',
|
||||
}, {
|
||||
'name': '评价等级考核系数',
|
||||
'url': 'users/markvalue'
|
||||
}]
|
||||
}, {
|
||||
# 自2021.02.01+ 支持多级菜单,models 为子菜单名
|
||||
'name': '多级菜单测试',
|
||||
'icon': 'fa fa-file',
|
||||
# 二级菜单
|
||||
'models': [{
|
||||
'name': 'Baidu',
|
||||
'icon': 'far fa-surprise',
|
||||
# 第三级菜单 ,
|
||||
'models': [
|
||||
{
|
||||
'name': '爱奇艺',
|
||||
'url': 'https://www.iqiyi.com/dianshiju/'
|
||||
# 第四级就不支持了,element只支持了3级
|
||||
}, {
|
||||
'name': '百度问答',
|
||||
'icon': 'far fa-surprise',
|
||||
'url': 'https://zhidao.baidu.com/'
|
||||
}
|
||||
]
|
||||
}, {
|
||||
'name': '内网穿透',
|
||||
'url': 'https://www.wezoz.com',
|
||||
'icon': 'fab fa-github'
|
||||
}]
|
||||
}, {
|
||||
'name': '动态菜单测试',
|
||||
'icon': 'fa fa-desktop',
|
||||
'models': [{
|
||||
'name': time.time(),
|
||||
'url': 'http://baidu.com',
|
||||
'icon': 'far fa-surprise'
|
||||
}]
|
||||
}, {
|
||||
'name': '关于',
|
||||
'url': '/about'
|
||||
}]
|
||||
}
|
||||
|
|
|
@ -15,12 +15,13 @@ Including another URLconf
|
|||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from TasksManager.settings import SITE_NAME
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('apps.tasks.urls', namespace='tasks')),
|
||||
]
|
||||
|
||||
admin.site.site_header = '任务进度管理系统'
|
||||
admin.site.site_title = '任务进度管理系统'
|
||||
admin.site.index_title = u'任务进度管理系统'
|
||||
admin.site.site_header = SITE_NAME
|
||||
admin.site.site_title = SITE_NAME
|
||||
admin.site.index_title = SITE_NAME
|
||||
|
|
|
@ -1,13 +1,39 @@
|
|||
from datetime import datetime
|
||||
import re
|
||||
|
||||
from django.contrib import admin
|
||||
from django.http import JsonResponse
|
||||
from django.utils.html import format_html
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
from import_export.formats import base_formats
|
||||
|
||||
from . import models
|
||||
from apps.users.models import TaskProperty
|
||||
from apps.users.models import TaskProperty, User
|
||||
from .resources import TodoResources, TaskResources
|
||||
|
||||
|
||||
class TaskAdmin(admin.ModelAdmin):
|
||||
class TodoInline(admin.StackedInline):
|
||||
|
||||
# 在Inline中同样筛选仅本部门的承办人、协办人
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'related_task':
|
||||
kwargs["queryset"] = models.Task.objects.filter(department=request.user.department)
|
||||
elif db_field.name == 'main_executor':
|
||||
kwargs["queryset"] = User.objects.filter(department=request.user.department)
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'sub_executor':
|
||||
kwargs["queryset"] = User.objects.filter(department=request.user.department)
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
model = models.Todo
|
||||
extra = 0
|
||||
# classes = ['collapse']
|
||||
#TODO 选择年度任务时排序 https://www.codenong.com/40740869/
|
||||
|
||||
|
||||
class TaskAdmin(ImportExportModelAdmin):
|
||||
resource_class = TaskResources
|
||||
|
||||
# def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
# if db_field.name == "related_task":
|
||||
|
@ -20,14 +46,36 @@ class TaskAdmin(admin.ModelAdmin):
|
|||
# pass
|
||||
# # kwargs["queryset"] = models.Task.objects.get(id=2).related_task
|
||||
|
||||
# 所属单位默认为访问用户的部门
|
||||
def get_changeform_initial_data(self, request):
|
||||
return {'department': request.user.department}
|
||||
|
||||
# 年度任务编辑界面仅显示本部门的任务属性
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'task_property':
|
||||
kwargs["queryset"] = TaskProperty.objects.filter(own_department=request.user.department)
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
# 仅显示当前部门的年度任务,除非为超管
|
||||
def get_queryset(self, request):
|
||||
qs = super(TaskAdmin, self).get_queryset(request)
|
||||
if request.user.is_superuser:
|
||||
return qs
|
||||
else:
|
||||
return qs.filter(department=request.user.department)
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
mvDict = dict(request.POST)
|
||||
# 解决当工作包协办人数均为0时报错
|
||||
# print(len(mvDict['related_task-0-sub_executor']))
|
||||
try:
|
||||
obj.related_task__sub_executor_count = int(len(mvDict['related_task-0-sub_executor']))
|
||||
except:
|
||||
obj.related_task__sub_executor_count = 0
|
||||
|
||||
super().save_model(request, obj, form, change)
|
||||
|
||||
|
||||
list_display = (
|
||||
'task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group', 'principal',
|
||||
'leader', 'task_note',
|
||||
|
@ -38,60 +86,291 @@ class TaskAdmin(admin.ModelAdmin):
|
|||
'fields': (
|
||||
('task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group',
|
||||
'principal', 'leader'),
|
||||
'task_note', 'related_task', 'department'),
|
||||
'task_note', 'department'),
|
||||
}),
|
||||
)
|
||||
inlines = [TodoInline]
|
||||
raw_id_fields = ("principal", "leader",)
|
||||
autocomplete_fields = ('related_task',)
|
||||
search_fields = ('related_task',)
|
||||
list_display_links = ('task_topic',)
|
||||
# autocomplete_fields = ('related_task',)
|
||||
# search_fields = ('related_task',)
|
||||
|
||||
# 导入导出功能限制
|
||||
def get_export_formats(self): # 该方法是限制格式为XLS
|
||||
formats = (
|
||||
base_formats.XLS,
|
||||
)
|
||||
return [f for f in formats if f().can_export()]
|
||||
|
||||
def has_import_permission(self, request): # 这是隐藏导入按钮,如果隐藏其他按钮也可以这样操作,
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class TodoAdmin(admin.ModelAdmin):
|
||||
def sub_executor(self, obj):
|
||||
return ', '.join([a.real_name for a in obj.sub_executor.all()])
|
||||
# class TodoAdmin(admin.ModelAdmin):
|
||||
class TodoAdmin(ImportExportModelAdmin):
|
||||
resource_class = TodoResources
|
||||
|
||||
fieldsets = (
|
||||
# 工作包页面仅显示所属本部门的年度任务、承办人、协办人
|
||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'related_task':
|
||||
kwargs["queryset"] = models.Task.objects.filter(department=request.user.department)
|
||||
elif db_field.name == 'main_executor':
|
||||
kwargs["queryset"] = User.objects.filter(department=request.user.department)
|
||||
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name == 'sub_executor':
|
||||
kwargs["queryset"] = User.objects.filter(department=request.user.department)
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
# 仅显示当前部门的工作包,除非为超管
|
||||
def get_queryset(self, request):
|
||||
qs = super(TodoAdmin, self).get_queryset(request)
|
||||
if request.user.is_superuser:
|
||||
return qs
|
||||
else:
|
||||
return qs.filter(related_task__department=request.user.department)
|
||||
|
||||
fieldsets = [
|
||||
(None, {
|
||||
'fields': (
|
||||
'todo_topic', 'todo_note', 'deadline', 'duty_group', 'main_executor', 'sub_executor', 'predict_work',
|
||||
'evaluate_factor',
|
||||
)
|
||||
'fields': [
|
||||
'related_task', 'todo_topic', 'todo_note', 'deadline', 'duty_group', 'main_executor', 'sub_executor',
|
||||
'sub_executor_count', 'predict_work', 'evaluate_factor', 'real_work'
|
||||
],
|
||||
'description': ''
|
||||
}),
|
||||
('完成情况', {
|
||||
'fields': (
|
||||
'real_work', 'complete_note', 'quality_mark', 'maturity'
|
||||
)
|
||||
})
|
||||
)
|
||||
|
||||
(None, {
|
||||
'fields': [],
|
||||
}),
|
||||
]
|
||||
list_display = (
|
||||
'todo_topic',
|
||||
'deadline',
|
||||
'todo_note',
|
||||
'task_id',
|
||||
'lined_task',
|
||||
'task_origin',
|
||||
'duty_department',
|
||||
'lined_task',
|
||||
# 'duty_department',
|
||||
'duty_group',
|
||||
'main_executor',
|
||||
# 'sub_executor',
|
||||
'list_sub_executor',
|
||||
'predict_work',
|
||||
'evaluate_factor',
|
||||
'maturity',
|
||||
'real_work',
|
||||
'complete_note',
|
||||
'quality_mark',
|
||||
)
|
||||
list_filter = ('deadline', )
|
||||
list_editable = ['quality_mark']
|
||||
list_filter = ('deadline',)
|
||||
list_display_links = ('todo_topic', 'deadline', )
|
||||
date_hierarchy = 'deadline'
|
||||
list_per_page = 20
|
||||
raw_id_fields = ("main_executor", "sub_executor")
|
||||
list_per_page = 70 # 目的是取消自动分页,好像有bug
|
||||
# raw_id_fields = ("sub_executor",)
|
||||
search_fields = ('todo_topic',)
|
||||
# ordering = ('task_id',)
|
||||
ordering = ('related_task', )
|
||||
readonly_fields = ["attachment"]
|
||||
|
||||
def approval_state(self, obj):
|
||||
return format_html('<span style="color:{};">{}</span>', 'green', obj.approval)
|
||||
|
||||
def task_id(self, obj):
|
||||
return obj.task_id
|
||||
def lined_task(self, obj):
|
||||
return obj.lined_task
|
||||
task_id.admin_order_field = 'task__task_id'
|
||||
task_id.admin_order_field = 'related_task__task_id'
|
||||
task_id.short_description = '任务编号'
|
||||
lined_task.admin_order_field = 'task__task_topic'
|
||||
|
||||
def task_origin(self, obj):
|
||||
return obj.task_origin
|
||||
task_origin.short_description = '任务来源'
|
||||
|
||||
def duty_department(self, obj):
|
||||
return obj.duty_group
|
||||
duty_department.short_description = '责任部门'
|
||||
|
||||
def lined_task(self, obj):
|
||||
return obj.related_task
|
||||
lined_task.short_description = '任务名称'
|
||||
|
||||
# 导入导出功能限制
|
||||
def get_export_formats(self): # 该方法是限制格式为XLS
|
||||
formats = (
|
||||
base_formats.XLS,
|
||||
)
|
||||
return [f for f in formats if f().can_export()]
|
||||
|
||||
def has_import_permission(self, request): # 这是隐藏导入按钮,如果隐藏其他按钮也可以这样操作,
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# 对工作包页面选择其所属的年度任务中,对年度任务进行筛选。条件为:年度任务的完成时间不早于今年或年度任务中有工作包的完成时间晚于今年
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
form = super(TodoAdmin, self).get_form(request, obj, **kwargs)
|
||||
query = models.Task.objects.filter(
|
||||
department=request.user.department, deadline__year__gte=datetime.now().strftime('%Y')).order_by('task_id')\
|
||||
| models.Task.objects.filter(
|
||||
department=request.user.department, related_task__deadline__year__gte=datetime.now().strftime('%Y'))\
|
||||
.order_by('task_id')
|
||||
form.base_fields['related_task'].queryset = query.distinct()
|
||||
return form
|
||||
|
||||
# def save_model(self, request, obj, form, change):
|
||||
# # 这一行代码写了一个晚上呜呜! 解决了当保存时,无法从未保存的数据中获取协办人数的问题!
|
||||
# mvDict = dict(request.POST)
|
||||
# dicts = request.POST
|
||||
# print(dicts)
|
||||
# for key, values in dicts:
|
||||
# print(key, values)
|
||||
# obj.user = request.user
|
||||
|
||||
# obj.sub_executor_count = int(len(mvDict['sub_executor']))
|
||||
# super().save_model(request, obj, form, change)
|
||||
|
||||
# 增加批量操作按钮
|
||||
actions = ['bulk_action']
|
||||
|
||||
def bulk_action(self, request, queryset):
|
||||
post = request.POST
|
||||
# 这里获取到数据后,可以做些业务处理
|
||||
# post中的_action 是方法名
|
||||
# post中 _selected 是选中的数据,逗号分割
|
||||
if not post.get('_selected'):
|
||||
return JsonResponse(data={
|
||||
'status': 'error',
|
||||
'msg': '请先选中数据!'
|
||||
})
|
||||
else:
|
||||
return JsonResponse(data={
|
||||
'status': 'success',
|
||||
'msg': '处理成功!'
|
||||
})
|
||||
|
||||
# 显示的文本,与django admin一致
|
||||
bulk_action.short_description = '批量操作'
|
||||
# icon,参考element-ui icon与https://fontawesome.com
|
||||
bulk_action.icon = 'el-icon-files'
|
||||
|
||||
# 指定element-ui的按钮类型,参考https://element.eleme.cn/#/zh-CN/component/button
|
||||
bulk_action.type = 'warning'
|
||||
|
||||
# 给按钮追加自定义的颜色
|
||||
bulk_action.style = 'color:white;'
|
||||
|
||||
# 指定为弹出层,这个参数最关键
|
||||
bulk_action.layer = {
|
||||
# 弹出层中的输入框配置
|
||||
|
||||
# 这里指定对话框的标题
|
||||
'title': '弹出层输入框',
|
||||
# 提示信息
|
||||
'tips': '这个弹出对话框是需要在admin中进行定义,数据新增编辑等功能,需要自己来实现。',
|
||||
# 确认按钮显示文本
|
||||
'confirm_button': '确认提交',
|
||||
# 取消按钮显示文本
|
||||
'cancel_button': '取消',
|
||||
|
||||
# 弹出层对话框的宽度,默认50%
|
||||
'width': '40%',
|
||||
|
||||
# 表单中 label的宽度,对应element-ui的 label-width,默认80px
|
||||
'labelWidth': "80px",
|
||||
'params': [{
|
||||
# 这里的type 对应el-input的原生input属性,默认为input
|
||||
'type': 'input',
|
||||
# key 对应post参数中的key
|
||||
'key': 'name',
|
||||
# 显示的文本
|
||||
'label': '名称',
|
||||
# 为空校验,默认为False
|
||||
'require': True
|
||||
}, {
|
||||
'type': 'select',
|
||||
'key': 'type',
|
||||
'label': '类型',
|
||||
'width': '200px',
|
||||
# size对应elementui的size,取值为:medium / small / mini
|
||||
'size': 'small',
|
||||
# value字段可以指定默认值
|
||||
'value': '0',
|
||||
'options': [{
|
||||
'key': '0',
|
||||
'label': '收入'
|
||||
}, {
|
||||
'key': '1',
|
||||
'label': '支出'
|
||||
}]
|
||||
}, {
|
||||
'type': 'number',
|
||||
'key': 'money',
|
||||
'label': '金额',
|
||||
# 设置默认值
|
||||
'value': 1000
|
||||
}, {
|
||||
'type': 'date',
|
||||
'key': 'date',
|
||||
'label': '日期',
|
||||
}, {
|
||||
'type': 'datetime',
|
||||
'key': 'datetime',
|
||||
'label': '时间',
|
||||
}, {
|
||||
'type': 'rate',
|
||||
'key': 'star',
|
||||
'label': '评价等级'
|
||||
}, {
|
||||
'type': 'color',
|
||||
'key': 'color',
|
||||
'label': '颜色'
|
||||
}, {
|
||||
'type': 'slider',
|
||||
'key': 'slider',
|
||||
'label': '滑块'
|
||||
}, {
|
||||
'type': 'switch',
|
||||
'key': 'switch',
|
||||
'label': 'switch开关'
|
||||
}, {
|
||||
'type': 'input_number',
|
||||
'key': 'input_number',
|
||||
'label': 'input number'
|
||||
}, {
|
||||
'type': 'checkbox',
|
||||
'key': 'checkbox',
|
||||
# 必须指定默认值
|
||||
'value': [],
|
||||
'label': '复选框',
|
||||
'options': [{
|
||||
'key': '0',
|
||||
'label': '收入'
|
||||
}, {
|
||||
'key': '1',
|
||||
'label': '支出'
|
||||
}, {
|
||||
'key': '2',
|
||||
'label': '收益'
|
||||
}]
|
||||
}, {
|
||||
'type': 'radio',
|
||||
'key': 'radio',
|
||||
'label': '单选框',
|
||||
'options': [{
|
||||
'key': '0',
|
||||
'label': '收入'
|
||||
}, {
|
||||
'key': '1',
|
||||
'label': '支出'
|
||||
}, {
|
||||
'key': '2',
|
||||
'label': '收益'
|
||||
}]
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
admin.site.register(models.Task, TaskAdmin)
|
||||
admin.site.register(models.Todo, TodoAdmin)
|
||||
|
|
|
@ -1,14 +1,38 @@
|
|||
from django import forms
|
||||
from .models import Todo
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
username = forms.CharField(error_messages={'required': '用户名不能为空'})
|
||||
password = forms.CharField()
|
||||
remember = forms.BooleanField(required=False)
|
||||
|
||||
|
||||
# TODO 数据不可为空
|
||||
class TodoForm(forms.ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
# (confused by Form & ModelForm https://stackoverflow.com/questions/2303268/djangos-forms-form-vs-forms-modelform)
|
||||
# maturity = forms.ChoiceField(widget=forms.Select(attrs={'class': 'form-control'}), choices=(
|
||||
# ('0%', '0%'),
|
||||
# ('10%', '10%'),
|
||||
# ('50%', '50%'),
|
||||
# ('90%', '90%'),
|
||||
# ('100%', '100%')
|
||||
# ))
|
||||
# real_work = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
|
||||
# sub_executor = forms.MultipleChoiceField(widget=forms.SelectMultiple(attrs={'class': 'form-control'}))
|
||||
|
||||
class Meta:
|
||||
model = Todo
|
||||
fields = ['maturity', 'complete_note']
|
||||
labels ={'text': ''}
|
||||
widgets = {'row': '3'}
|
||||
fields = ['maturity', 'real_work', 'sub_executor', 'evaluate_factor', 'complete_note']
|
||||
widgets = {'complete_note': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||||
# 'evaluate_factor': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TodoForm, self).__init__(*args, **kwargs)
|
||||
# self.fields['sub_executor'].widget.attrs['class'] = 'form-control'
|
||||
fields = ['maturity', 'real_work', 'sub_executor', 'evaluate_factor', 'complete_note']
|
||||
for i in fields:
|
||||
self.fields[i].widget.attrs['class'] = 'form-control'
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 13:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Task',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('task_topic', models.CharField(max_length=50, verbose_name='任务名称')),
|
||||
('task_id', models.CharField(max_length=50, unique=True, verbose_name='任务编号')),
|
||||
('task_note', models.CharField(max_length=100, verbose_name='任务说明')),
|
||||
('task_origin', models.CharField(max_length=150, verbose_name='任务来源')),
|
||||
('aim_value', models.CharField(max_length=50, verbose_name='目标值')),
|
||||
('deadline', models.DateField(verbose_name='完成时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '年度任务',
|
||||
'verbose_name_plural': '年度任务',
|
||||
'ordering': ['deadline'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Todo',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('todo_topic', models.CharField(max_length=50, verbose_name='工作事项')),
|
||||
('todo_note', models.CharField(blank=True, max_length=100, verbose_name='工作要求及交付物')),
|
||||
('predict_work', models.DecimalField(decimal_places=1, max_digits=5, verbose_name='预计工作量')),
|
||||
('evaluate_factor', models.DecimalField(decimal_places=1, max_digits=5, verbose_name='折算系数')),
|
||||
('maturity', models.CharField(blank=True, choices=[('0%', '0%'), ('10%', '10%'), ('20%', '20%'), ('30%', '30%'), ('40%', '40%'), ('50%', '50%'), ('60%', '60%'), ('70%', '70%'), ('80%', '80%'), ('90%', '90%'), ('100%', '100%')], default='0%', max_length=5, verbose_name='成熟度')),
|
||||
('real_work', models.DecimalField(blank=True, decimal_places=1, max_digits=5, null=True, verbose_name='实际工作量')),
|
||||
('complete_note', models.TextField(blank=True, max_length=150, verbose_name='完成情况说明')),
|
||||
('deadline', models.DateField(verbose_name='完成时间')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '每月工作事项',
|
||||
'verbose_name_plural': '每月工作事项',
|
||||
'ordering': ['deadline'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -1,69 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 13:31
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('tasks', '0001_initial'),
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='duty_group',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.mygroup', verbose_name='承办单位'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='main_executor',
|
||||
field=models.ManyToManyField(related_name='main_executor', to=settings.AUTH_USER_MODEL, verbose_name='承/督办人'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='quality_mark',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='users.qualitymark', verbose_name='质量评价'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='sub_executor',
|
||||
field=models.ManyToManyField(blank=True, default='', related_name='sub_executor', to=settings.AUTH_USER_MODEL, verbose_name='协办人'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='department',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='所属单位'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='duty_group',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.mygroup', verbose_name='责任单位'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='leader',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='leader', to=settings.AUTH_USER_MODEL, verbose_name='主管领导'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='principal',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='principal', to=settings.AUTH_USER_MODEL, verbose_name='负责人'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='related_task',
|
||||
field=models.ManyToManyField(blank=True, to='tasks.Todo', verbose_name='任务节点'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='task',
|
||||
name='task_property',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.taskproperty', verbose_name='任务属性'),
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 13:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_auto_20210129_1332'),
|
||||
('tasks', '0002_auto_20210129_1331'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='department',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='department', to='users.department', verbose_name='所属单位'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='duty_group',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='duty_group', to='users.department', verbose_name='责任单位'),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 14:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0004_auto_20210129_1434'),
|
||||
('tasks', '0003_auto_20210129_1335'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='todo',
|
||||
name='duty_group',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='承办单位'),
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-30 20:21
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0005_auto_20210130_2021'),
|
||||
('tasks', '0004_auto_20210129_1434'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='department',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='department', to='users.department', verbose_name='所属单位'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='task',
|
||||
name='duty_group',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='duty_group', to='users.department', verbose_name='责任单位'),
|
||||
),
|
||||
]
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-02-01 19:00
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('tasks', '0005_auto_20210130_2021'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='todo',
|
||||
name='main_executor',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='main_executor',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_executor', to=settings.AUTH_USER_MODEL, verbose_name='承/督办人'),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-02-01 19:01
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0006_auto_20210201_1900'),
|
||||
('tasks', '0006_auto_20210201_1900'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='todo',
|
||||
name='quality_mark',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.qualitymark', verbose_name='质量评价'),
|
||||
),
|
||||
]
|
|
@ -1,17 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-02-01 22:27
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tasks', '0007_auto_20210201_1901'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='todo',
|
||||
options={'ordering': ['deadline'], 'verbose_name': '工作包', 'verbose_name_plural': '工作包'},
|
||||
),
|
||||
]
|
|
@ -1,4 +1,8 @@
|
|||
import time
|
||||
|
||||
from django.db import models
|
||||
from django.http import request
|
||||
|
||||
from apps.users.models import User, MyGroup, QualityMark, TaskProperty, Department
|
||||
|
||||
|
||||
|
@ -12,35 +16,34 @@ class Todo(models.Model):
|
|||
blank=True,
|
||||
max_length=100
|
||||
)
|
||||
deadline = models.DateField(verbose_name='完成时间')
|
||||
# related_task = models.ManyToManyField(Task, verbose_name="关联的主任务")
|
||||
duty_group = models.ForeignKey('users.Department', on_delete=models.CASCADE, verbose_name='承办单位')
|
||||
main_executor = models.ForeignKey(User, related_name='main_executor', on_delete=models.SET_NULL, verbose_name='承/督办人', null=True)
|
||||
sub_executor = models.ManyToManyField(User, related_name='sub_executor', verbose_name='协办人', default='', blank=True)
|
||||
predict_work = models.DecimalField('预计工作量', max_digits=5, decimal_places=1)
|
||||
evaluate_factor = models.DecimalField('折算系数', max_digits=5, decimal_places=1)
|
||||
main_executor = models.ForeignKey(User, related_name='main_executor', on_delete=models.CASCADE,
|
||||
verbose_name='承/督办人', blank=True, null=True)
|
||||
sub_executor = models.ManyToManyField(User, related_name='sub_executor', verbose_name='协办人', blank=True)
|
||||
sub_executor_count = models.CharField('协办人数', max_length=32)
|
||||
related_task = models.ForeignKey('Task', related_name='related_task', on_delete=models.CASCADE, verbose_name='年度任务')
|
||||
predict_work = models.DecimalField('预计工作量', default=0, max_digits=5, decimal_places=1, blank=False)
|
||||
evaluate_factor = models.DecimalField('折算系数', max_digits=5, decimal_places=1, blank=True, default='1') #Todo 小数点后2位
|
||||
maturity = models.CharField(
|
||||
verbose_name='成熟度',
|
||||
max_length=5,
|
||||
choices=(
|
||||
('0%', '0%'),
|
||||
('10%', '10%'),
|
||||
('20%', '20%'),
|
||||
('30%', '30%'),
|
||||
('40%', '40%'),
|
||||
('50%', '50%'),
|
||||
('60%', '60%'),
|
||||
('70%', '70%'),
|
||||
('80%', '80%'),
|
||||
('90%', '90%'),
|
||||
('100%', '100%')
|
||||
),
|
||||
blank=True,
|
||||
default='0%',
|
||||
)
|
||||
real_work = models.DecimalField('实际工作量', max_digits=5, decimal_places=1, blank=True, null=True)
|
||||
complete_note = models.TextField('完成情况说明', max_length=150, blank=True)
|
||||
quality_mark = models.ForeignKey('users.QualityMark', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='质量评价')
|
||||
deadline = models.DateField(verbose_name='完成时间')
|
||||
real_work = models.DecimalField('实际工作量', default=0, max_digits=5, decimal_places=1, blank=False)
|
||||
complete_note = models.TextField('完成情况说明', max_length=500, blank=True)
|
||||
quality_mark = models.ForeignKey('users.QualityMark', on_delete=models.SET_NULL, blank=True, null=True,
|
||||
verbose_name='质量评价')
|
||||
attachment = models.FileField('交付物查看', blank=True)
|
||||
|
||||
def __str__(self):
|
||||
date = str(self.deadline)
|
||||
|
@ -52,55 +55,74 @@ class Todo(models.Model):
|
|||
verbose_name = '工作包'
|
||||
verbose_name_plural = '工作包'
|
||||
|
||||
@property
|
||||
def lined_task(self):
|
||||
lined_task = Task.objects.filter(related_task=self)
|
||||
for task in lined_task:
|
||||
return task.task_topic
|
||||
# TODO 不知道有没有不用for循环直接查的
|
||||
# def save(self, *args, **kwargs):
|
||||
# super(Todo, self).save(*args, **kwargs)
|
||||
# print(request.HttpRequest)
|
||||
# # 直接保存报错 needs to have a value for field "id" before this many-to-many relationship can be used.
|
||||
# # self.sub_executor_count = int(self.sub_executor.count())
|
||||
# # Todo.objects.update(sub_executor_count=self.sub_executor.count())
|
||||
|
||||
@property
|
||||
def task_id(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.task_id
|
||||
return self.related_task.task_id
|
||||
|
||||
@property
|
||||
def task_origin(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.task_origin
|
||||
return self.related_task.task_origin
|
||||
|
||||
@property
|
||||
def duty_department(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.duty_group
|
||||
return self.related_task.duty_group
|
||||
|
||||
@property
|
||||
def last_month_list(self):
|
||||
return self.deadline
|
||||
|
||||
@property
|
||||
def get_tatal_num(self):
|
||||
def get_total_num(self):
|
||||
return self.objects.all().count()
|
||||
|
||||
@property
|
||||
def points(self):
|
||||
return int(self.predict_work * self.evaluate_factor)
|
||||
|
||||
@property
|
||||
def main_workload(self):
|
||||
return int(self.predict_work) * int(self.evaluate_factor)
|
||||
|
||||
@property
|
||||
def sub_workload(self):
|
||||
return self.predict_work * (1-int(self.evaluate_factor))/self.sub_executor.count
|
||||
|
||||
@classmethod
|
||||
def sub_member(cls):
|
||||
return cls.sub_executor.count
|
||||
|
||||
def list_sub_executor(self):
|
||||
return ', '.join([a.real_name for a in self.sub_executor.all()])
|
||||
list_sub_executor.short_description = '协办人'
|
||||
|
||||
|
||||
class Task(models.Model):
|
||||
task_topic = models.CharField(
|
||||
verbose_name='任务名称',
|
||||
max_length=50
|
||||
)
|
||||
task_id = models.CharField(max_length=50, unique=True, verbose_name='任务编号')
|
||||
task_id = models.CharField(max_length=50, unique=True, verbose_name='编号')
|
||||
task_note = models.CharField(
|
||||
verbose_name='任务说明',
|
||||
max_length=100
|
||||
max_length=100,
|
||||
blank=True
|
||||
)
|
||||
task_origin = models.CharField(max_length=150, verbose_name='任务来源')
|
||||
task_origin = models.CharField(max_length=150, verbose_name='任务来源', blank=True)
|
||||
task_property = models.ForeignKey('users.TaskProperty', on_delete=models.CASCADE, verbose_name='任务属性')
|
||||
related_task = models.ManyToManyField(Todo, verbose_name='任务节点', blank=True)
|
||||
department = models.ForeignKey('users.Department', related_name='department', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='所属单位')
|
||||
duty_group = models.ForeignKey('users.Department', related_name='duty_group', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='责任单位')
|
||||
principal = models.ForeignKey(User, related_name='principal', verbose_name='负责人', on_delete=models.CASCADE)
|
||||
leader = models.ForeignKey(User, related_name='leader', verbose_name='主管领导', on_delete=models.CASCADE)
|
||||
aim_value = models.CharField(max_length=50, verbose_name='目标值')
|
||||
department = models.ForeignKey('users.Department', related_name='department', on_delete=models.SET_NULL, blank=True,
|
||||
null=True, verbose_name='所属单位')
|
||||
duty_group = models.ForeignKey('users.Department', related_name='duty_group', on_delete=models.SET_NULL, blank=True,
|
||||
null=True, verbose_name='责任单位')
|
||||
principal = models.ForeignKey(User, related_name='principal', verbose_name='负责人', on_delete=models.CASCADE, blank=True, null=True)
|
||||
leader = models.ForeignKey(User, related_name='leader', verbose_name='主管领导', on_delete=models.CASCADE, blank=True, null=True)
|
||||
aim_value = models.CharField(max_length=50, verbose_name='目标值', blank=True)
|
||||
# start_date = models.DateField(verbose_name='起始日期')
|
||||
deadline = models.DateField(verbose_name='完成时间')
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
from django.db import connection
|
||||
|
||||
def my_annotate():
|
||||
query1 = '''
|
||||
CREATE TEMPORARY TABLE work_cal AS
|
||||
SELECT tasks_todo_sub_executor.*, tasks_todo.*
|
||||
FROM tasks_todo_sub_executor
|
||||
INNER JOIN tasks_todo
|
||||
ON tasks_todo_sub_executor.todo_id = tasks_todo.id
|
||||
ORDER BY user_id;
|
||||
'''
|
||||
query2 = '''
|
||||
-- 计算每个工作包协办人数量并插入临时表
|
||||
UPDATE work_cal SET sub_executor_count = (
|
||||
SELECT COUNT(*) FROM tasks_todo_sub_executor
|
||||
WHERE work_cal.todo_id = tasks_todo_sub_executor.todo_id);
|
||||
'''
|
||||
query3 = '''
|
||||
SELECT exe_id, SUM(total_pre_work), SUM(total_real_work) FROM
|
||||
(
|
||||
-- 承办任务
|
||||
SELECT main_executor_id AS exe_id, SUM(predict_work * evaluate_factor) AS total_pre_work,
|
||||
SUM(real_work * tasks_todo.evaluate_factor) AS total_real_work
|
||||
FROM tasks_todo
|
||||
GROUP BY exe_id
|
||||
UNION ALL
|
||||
-- 协办任务
|
||||
SELECT user_id AS exe_id, SUM(predict_work * (1 -evaluate_factor) / sub_executor_count) AS total_pre_work,
|
||||
SUM(real_work * (1 - evaluate_factor) / sub_executor_count) AS total_real_work
|
||||
FROM work_cal
|
||||
GROUP BY exe_id
|
||||
) AS init
|
||||
GROUP BY exe_id
|
||||
'''
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(query1)
|
||||
cursor.execute(query2)
|
||||
cursor.execute(query3)
|
||||
raw = cursor.fetchall()
|
||||
return raw
|
|
@ -0,0 +1,16 @@
|
|||
from import_export import resources
|
||||
|
||||
from .models import Todo, Task
|
||||
|
||||
|
||||
class TodoResources(resources.ModelResource):
|
||||
class Meta:
|
||||
model = Todo
|
||||
fields = ('todo_topic', 'todo_note', 'deadline', 'duty_group__name', 'main_executor__real_name',
|
||||
'sub_executor__real_name', 'sub_executor_count', 'related_task', 'predict_work', 'evaluate_factor',
|
||||
'maturity', 'real_work', 'complete_note', 'quality_mark')
|
||||
|
||||
|
||||
class TaskResources(resources.ModelResource):
|
||||
class Meta:
|
||||
model = Task
|
|
@ -7,26 +7,36 @@ register = template.Library()
|
|||
|
||||
|
||||
@register.filter(name='quarter_cate')
|
||||
def quarter_cate(value, quarter):
|
||||
def quarter_cate(value, year_quarter):
|
||||
year_now = datetime.now().strftime('%Y')
|
||||
month = value.deadline.strftime('%m')
|
||||
year = value.deadline.strftime('%Y')
|
||||
month = int(month)
|
||||
quarter = int(quarter)
|
||||
# year = int(year)
|
||||
# year_now = int(year) 不知道为什么,如果转整数会把2021和2022认为相同
|
||||
# print(quarter)
|
||||
req_year = str(year_quarter[1])
|
||||
quarter = int(year_quarter[0])
|
||||
# 可能造成性能损失,每次数据库会调出符合“当年”的任务或工作包的全部任务下工作包,并逐个判断
|
||||
if year == req_year:
|
||||
if quarter == 1 and 1 <= month <= 3:
|
||||
return str(value) + ' '
|
||||
|
||||
if quarter == 1 and 1 <= month <= 3:
|
||||
return value
|
||||
elif quarter == 2 and 4 <= month <= 6:
|
||||
return str(value) + ' '
|
||||
|
||||
elif quarter == 2 and 4 <= month <= 6:
|
||||
return value
|
||||
elif quarter == 3 and 7 <= month <= 9:
|
||||
return str(value) + ' '
|
||||
|
||||
elif quarter == 3 and 7 <= month <= 9:
|
||||
return value
|
||||
|
||||
elif quarter == 4 and 10 <= month <= 12:
|
||||
return value
|
||||
elif quarter == 4 and 10 <= month <= 12:
|
||||
return str(value) + ' '
|
||||
|
||||
else:
|
||||
return ''
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
@register.filter(name='last_month')
|
||||
def last_month(value):
|
||||
curent_date = datetime.strptime(value, '%Y年%m月')
|
||||
|
@ -34,6 +44,7 @@ def last_month(value):
|
|||
last_month = last_date.strftime('%Y/%m')
|
||||
return last_month
|
||||
|
||||
|
||||
@register.filter(name='next_month')
|
||||
def next_month(value):
|
||||
curent_date = datetime.strptime(value, '%Y年%m月')
|
||||
|
@ -41,7 +52,28 @@ def next_month(value):
|
|||
next_month = next_date.strftime('%Y/%m')
|
||||
return next_month
|
||||
|
||||
|
||||
@register.filter(name='this_month')
|
||||
def this_month(value):
|
||||
curent_date = datetime.strptime(value, '%Y年%m月')
|
||||
return curent_date.strftime('%m')
|
||||
return curent_date.strftime('%m')
|
||||
|
||||
|
||||
@register.filter(name='last_year')
|
||||
def last_year(value):
|
||||
curent_year = value[1]
|
||||
last_year = curent_year - 1
|
||||
return last_year
|
||||
|
||||
|
||||
@register.filter(name='next_year')
|
||||
def next_year(value):
|
||||
curent_year = value[1]
|
||||
next_year = curent_year + 1
|
||||
return next_year
|
||||
|
||||
|
||||
@register.filter(name='this_year')
|
||||
def this_year(value):
|
||||
curent_year = value[1]
|
||||
return curent_year
|
|
@ -1,3 +1,212 @@
|
|||
import decimal
|
||||
|
||||
from django.core import serializers
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db.models.functions import TruncMonth
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.shortcuts import render
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
from django.db.models import Sum, F, FloatField, Count, Q
|
||||
|
||||
from django.utils import timezone
|
||||
from django.views import View
|
||||
|
||||
from apps.users.models import User
|
||||
from apps.tasks.models import Todo
|
||||
import pandas as pd
|
||||
from collections import defaultdict, Counter, ChainMap
|
||||
from copy import deepcopy
|
||||
|
||||
|
||||
# 用于按用户名合并任务,会对指定字段进行累加,类似GROUP BY(SUM),但会丢失无需计算的部分,因此之前需要单独构建姓名字典
|
||||
def solve(dataset, group_by_key, sum_value_keys):
|
||||
dic = defaultdict(Counter)
|
||||
for item in dataset:
|
||||
key = item[group_by_key]
|
||||
vals = {k: item[k] for k in sum_value_keys}
|
||||
dic[key].update(vals)
|
||||
return dic
|
||||
|
||||
def cal_method(main_list, sub_list, user_name):
|
||||
# print(main_list, sub_list, user_name)
|
||||
|
||||
# sub_credit = sub_list
|
||||
# main_credit = main_list
|
||||
|
||||
total_data = []
|
||||
season = 1
|
||||
# 分别计算每个季度的工作量、评价
|
||||
for main_credit, sub_credit in zip(main_list, sub_list):
|
||||
# TODO 对于完成质量的核算,先对评价求和,再除以已评价的承办任务数
|
||||
# for i in main_credit:
|
||||
# print(i['main_executor__quality_mark__mark_value__mark_value'])
|
||||
# print(main_credit)
|
||||
quality_dict = {}
|
||||
for i in main_credit:
|
||||
quality_dict[i['username']] = []
|
||||
if i['main_executor__quality_mark__mark_value__mark_value'] != None:
|
||||
quality_dict[i['username']].append(i['main_executor__quality_mark__mark_value__mark_value'])
|
||||
|
||||
for key, value in quality_dict.items():
|
||||
if value:
|
||||
quality_dict[key] = sum(value) / len(value)
|
||||
# print(quality_dict)
|
||||
|
||||
main_credit = solve(main_credit, 'username', ['main_pre_cal', 'main_real_cal', 'main_count', 'main_executor__evaluate_factor'])
|
||||
main_credit = dict(main_credit)
|
||||
sub_credit = solve(sub_credit, 'username', ['sub_pre_cal', 'sub_real_cal', 'sub_count'])
|
||||
sub_credit = dict(sub_credit)
|
||||
|
||||
# 按用户名合并承办与协办任务字典
|
||||
total_credit = deepcopy(main_credit)
|
||||
for key in sub_credit.keys():
|
||||
if key in total_credit:
|
||||
total_credit[key].update(sub_credit[key])
|
||||
else:
|
||||
total_credit[key] = sub_credit[key]
|
||||
|
||||
# print(total_credit['admin']['sub_pre_cal'])
|
||||
# 根据字典内容,计算总工作量
|
||||
for key, value in total_credit.items():
|
||||
# print(value)
|
||||
value['pre_cal'] = value['sub_pre_cal'] + value['main_pre_cal']
|
||||
value['real_cal'] = value['sub_real_cal'] + value['main_real_cal']
|
||||
value['real_name'] = user_name[key]
|
||||
value['season'] = season
|
||||
|
||||
# 由于不计算协办任务,quality_dict会有空值情况
|
||||
try:
|
||||
value['quality'] = quality_dict[key]
|
||||
except:
|
||||
value['quality'] = 0
|
||||
|
||||
for value in total_credit.values():
|
||||
# print(value)
|
||||
real_cal_season = "real_cal_" + str(season)
|
||||
value[real_cal_season] = value.pop("real_cal")
|
||||
quality_season = "quality_" + str(season)
|
||||
value[quality_season] = value.pop("quality")
|
||||
# print(total_credit)
|
||||
total_data.append(total_credit)
|
||||
# print(season)
|
||||
season += 1
|
||||
|
||||
# new_credit = []
|
||||
# for item in total_credit:
|
||||
# for key, value in item.items():
|
||||
# print(total_data, season)
|
||||
dd = defaultdict(list)
|
||||
for d in total_data: # you can list as many input dicts as you want here
|
||||
for key, value in d.items():
|
||||
dd[key].append(value)
|
||||
# print(dd)
|
||||
return dd
|
||||
|
||||
|
||||
class TestView(View):
|
||||
def get(self, request, year=2021, month=7):
|
||||
user_info = User.objects.filter(department=request.user.department) \
|
||||
.values_list('username', 'real_name')
|
||||
user_name = {}
|
||||
for user in user_info:
|
||||
user_name[user[0]] = user[1]
|
||||
|
||||
work_cal = User.objects.filter(department=request.user.department, main_executor__deadline__year=2021) \
|
||||
.order_by('main_executor__deadline').values('main_executor__todo_topic', 'main_executor__deadline')
|
||||
|
||||
# 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。
|
||||
main_credit = User.objects.filter(department=request.user.department,
|
||||
main_executor__deadline__year=year) \
|
||||
.annotate(main_count=Count('main_executor')) \
|
||||
.order_by('username') \
|
||||
.values('username', 'main_executor', 'main_executor__predict_work', 'main_executor__real_work',
|
||||
'main_executor__evaluate_factor', 'main_executor__maturity', 'main_executor__quality_mark__mark_value__mark_value', 'main_executor__deadline', 'main_count') # 这里的annotate不知会不会有问题
|
||||
|
||||
sub_count = Todo.objects.filter(sub_executor__department=request.user.department,
|
||||
deadline__year=year) \
|
||||
.annotate(sub_count=Count('sub_executor')).values('id', 'sub_count')
|
||||
sub_credit = User.objects.filter(department=request.user.department,
|
||||
sub_executor__deadline__year=year) \
|
||||
.order_by('username') \
|
||||
.values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work',
|
||||
'sub_executor__evaluate_factor', 'sub_executor__maturity', 'sub_executor__quality_mark__mark_value__mark_value', 'sub_executor__deadline')
|
||||
|
||||
# 构建工作包id对应协办人人数的字典
|
||||
sub_exe_count = {}
|
||||
for i in sub_count:
|
||||
key = i['id']
|
||||
value = i['sub_count']
|
||||
sub_exe_count[key] = value
|
||||
# print(sub_exe_count)
|
||||
|
||||
# 计算每个承办任务的预计、实际工作量,并插入字典
|
||||
for i in main_credit:
|
||||
# 将成熟度由百分数转小数,以便其后与其他变量计算 eg. 50% -> 0.5
|
||||
i['main_executor__maturity'] = decimal.Decimal(float(i['main_executor__maturity'].strip('%')) / 100)
|
||||
# print(i['main_executor__maturity'])
|
||||
i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
|
||||
i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
# 将协办任务对应的人数插入,计算每个协办任务的预计、实际工作量,并插入字典
|
||||
for i in sub_credit:
|
||||
sub_todo_id = i['sub_executor']
|
||||
i['sub_exe_count'] = sub_exe_count[sub_todo_id]
|
||||
i['sub_executor__maturity'] = decimal.Decimal(float(i['sub_executor__maturity'].strip('%')) / 100)
|
||||
i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
|
||||
i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
|
||||
i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数,在累加时导致计算错误
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
|
||||
|
||||
main_Q1st, main_Q2nd, main_Q3th, main_Q4th = [], [], [], []
|
||||
for i in main_credit:
|
||||
# print(i['main_executor__deadline'].month)
|
||||
deadline_month = i['main_executor__deadline'].month
|
||||
if 1 <= deadline_month <= 3:
|
||||
main_Q1st.append(i)
|
||||
elif 4 <= deadline_month <= 6:
|
||||
main_Q2nd.append(i)
|
||||
elif 7 <= deadline_month <= 9:
|
||||
main_Q3th.append(i)
|
||||
elif 10 <= deadline_month <= 12:
|
||||
main_Q4th.append(i)
|
||||
# print(Q1st, Q2nd, Q3th, Q4th)
|
||||
|
||||
sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th = [], [], [], []
|
||||
for i in sub_credit:
|
||||
# print(i['main_executor__deadline'].month)
|
||||
deadline_month = i['sub_executor__deadline'].month
|
||||
if 1 <= deadline_month <= 3:
|
||||
sub_Q1st.append(i)
|
||||
elif 4 <= deadline_month <= 6:
|
||||
sub_Q2nd.append(i)
|
||||
elif 7 <= deadline_month <= 9:
|
||||
sub_Q3th.append(i)
|
||||
elif 10 <= deadline_month <= 12:
|
||||
sub_Q4th.append(i)
|
||||
|
||||
main_list = [main_Q1st, main_Q2nd, main_Q3th, main_Q4th]
|
||||
sub_list = [sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th]
|
||||
result = cal_method(main_list, sub_list, user_name)
|
||||
print(result)
|
||||
stat = {}
|
||||
for key, value in result.items():
|
||||
stat[key] = {}
|
||||
for j in value:
|
||||
stat[key]['real_name'] = j['real_name']
|
||||
season = str(j['season'])
|
||||
real_cal_season = 'real_cal_' + season
|
||||
quality_season = 'quality_' + season
|
||||
stat[key][real_cal_season] = j[real_cal_season]
|
||||
stat[key][quality_season] = j[quality_season]
|
||||
print(j)
|
||||
print(stat)
|
||||
|
||||
return HttpResponse(stat)
|
||||
# return render(request, 'tasks/index.html', context)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
from django.urls import path
|
||||
from apps.tasks import views
|
||||
from django.urls import path, include
|
||||
# import debug_toolbar
|
||||
from apps.tasks import views, tests
|
||||
from TasksManager import settings
|
||||
|
||||
app_name = 'tasks'
|
||||
urlpatterns = [
|
||||
path('', views.IndexView.as_view(), name='index'),
|
||||
path('<int:year>/<int:month>/', views.IndexView.as_view(), name='index_month'),
|
||||
path('test/', tests.TestView.as_view(), name='test'),
|
||||
path('login/', views.UserLoginView.as_view(), name='login'),
|
||||
path('logout/', views.UserLogoutView.as_view(), name='logout'),
|
||||
path('todolist/', views.TodoListView.as_view(), name='todolist'),
|
||||
|
@ -12,4 +16,13 @@ urlpatterns = [
|
|||
path('group_todolist/<int:year>/<int:month>/', views.GroupTodoList.as_view(), name='group_todolist_month'),
|
||||
path('todo/<int:pk>/', views.TodoEntryView.as_view(), name='todo_detail'),
|
||||
path('tasklist/', views.TaskListView.as_view(), name='tasklist'),
|
||||
path('tasklist/<int:year>/', views.TaskListView.as_view(), name='tasklist_year'),
|
||||
path('about/', views.AboutView.as_view(), name='about'),
|
||||
]
|
||||
#
|
||||
# if settings.DEBUG:
|
||||
# import debug_toolbar
|
||||
# urlpatterns = [
|
||||
# path('__debug__/', include(debug_toolbar.urls)),
|
||||
# ] + urlpatterns
|
||||
|
||||
|
|
|
@ -1,20 +1,363 @@
|
|||
import re
|
||||
from collections import defaultdict, Counter
|
||||
from copy import deepcopy
|
||||
|
||||
from django.contrib import auth, messages
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Sum, F, FloatField, Count, Q
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
import django.utils.timezone as timezone
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.views import View
|
||||
from django.db import connection
|
||||
# Create your views here.
|
||||
from apps.tasks.models import Todo, Task
|
||||
from apps.tasks.forms import TodoForm
|
||||
from apps.users.models import User
|
||||
from . import my_query
|
||||
from functools import reduce
|
||||
import pandas as pd
|
||||
|
||||
import decimal
|
||||
|
||||
|
||||
class IndexView(View):
|
||||
def get(self, request):
|
||||
tasks = Task.objects.filter().order_by('task_id')
|
||||
context = {'tasks': tasks}
|
||||
return render(request, 'tasks/tasklist.html', context)
|
||||
@method_decorator(login_required)
|
||||
def get(self, request, year=None, month=None):
|
||||
# 搞成了!!! 当月份更改时自动变更首页显示的月份! 其实就是用的上面那个链接的方法,之前不知道为啥没去用
|
||||
# https://stackoverflow.com/questions/63072235/django-localdate-doesnt-return-correct-date
|
||||
# https://stackoverflow.com/questions/13225890/django-default-timezone-now-saves-records-using-old-time
|
||||
# https://stackoverflow.com/questions/38237777/django-timezone-now-vs-timezone-now
|
||||
if year is None:
|
||||
year = timezone.now().year
|
||||
if month is None:
|
||||
month = timezone.now().month
|
||||
#TODO 部门不用部门下用户累加
|
||||
# 建立username和真实姓名的对应字典,并在工作量计算完成后插入结果集
|
||||
user_info = User.objects.filter(department=request.user.department)\
|
||||
.values_list('username', 'real_name')
|
||||
user_name = {}
|
||||
for user in user_info:
|
||||
user_name[user[0]] = user[1]
|
||||
# print(user_name)
|
||||
|
||||
# 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。
|
||||
main_credit = User.objects.filter(department=request.user.department,
|
||||
main_executor__deadline__year=year, main_executor__deadline__month=month)\
|
||||
.annotate(main_count=Count('main_executor'))\
|
||||
.order_by('username')\
|
||||
.values('username', 'main_executor', 'main_executor__predict_work', 'main_executor__real_work',
|
||||
'main_executor__evaluate_factor', 'main_count') # 这里的annotate不知会不会有问题
|
||||
|
||||
sub_count = Todo.objects.filter(sub_executor__department=request.user.department,
|
||||
deadline__year=year, deadline__month=month) \
|
||||
.annotate(sub_count=Count('sub_executor')).values('id', 'sub_count')
|
||||
sub_credit = User.objects.filter(department=request.user.department,
|
||||
sub_executor__deadline__year=year, sub_executor__deadline__month=month) \
|
||||
.order_by('username') \
|
||||
.values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work',
|
||||
'sub_executor__evaluate_factor')
|
||||
|
||||
# 构建工作包id对应协办人人数的字典
|
||||
sub_exe_count = {}
|
||||
for i in sub_count:
|
||||
key = i['id']
|
||||
value = i['sub_count']
|
||||
sub_exe_count[key] = value
|
||||
# print(sub_exe_count)
|
||||
|
||||
# 计算每个承办任务的预计、实际工作量,并插入字典
|
||||
for i in main_credit:
|
||||
i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor']
|
||||
i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor']
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
# 将协办任务对应的人数插入,计算每个协办任务的预计、实际工作量,并插入字典
|
||||
for i in sub_credit:
|
||||
sub_todo_id = i['sub_executor']
|
||||
i['sub_exe_count'] = sub_exe_count[sub_todo_id]
|
||||
i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count']
|
||||
i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count']
|
||||
i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数,在累加时导致计算错误
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
# 用于按用户名合并任务,会对指定字段进行累加,类似GROUP BY(SUM),但会丢失无需计算的部分,因此之前需要单独构建姓名字典
|
||||
def solve(dataset, group_by_key, sum_value_keys):
|
||||
dic = defaultdict(Counter)
|
||||
for item in dataset:
|
||||
key = item[group_by_key]
|
||||
vals = {k: item[k] for k in sum_value_keys}
|
||||
dic[key].update(vals)
|
||||
return dic
|
||||
main_credit = solve(main_credit, 'username', ['main_pre_cal', 'main_real_cal', 'main_count'])
|
||||
main_credit = dict(main_credit)
|
||||
sub_credit = solve(sub_credit, 'username', ['sub_pre_cal', 'sub_real_cal', 'sub_count'])
|
||||
sub_credit = dict(sub_credit)
|
||||
|
||||
# 按用户名合并承办与协办任务字典
|
||||
total_credit = deepcopy(main_credit)
|
||||
for key in sub_credit.keys():
|
||||
if key in total_credit:
|
||||
total_credit[key].update(sub_credit[key])
|
||||
else:
|
||||
total_credit[key] = sub_credit[key]
|
||||
|
||||
# print(total_credit['admin']['sub_pre_cal'])
|
||||
# 根据字典内容,计算总工作量
|
||||
for key, value in total_credit.items():
|
||||
# print(value)
|
||||
value['pre_cal'] = value['sub_pre_cal'] + value['main_pre_cal']
|
||||
value['real_cal'] = value['sub_real_cal'] + value['main_real_cal']
|
||||
value['real_name'] = user_name[key]
|
||||
# total_credit = dict(main_credit.items() + sub_credit.items())
|
||||
# for value in sub_credit.values():
|
||||
# dict(value)
|
||||
# print(sub_credit)
|
||||
#
|
||||
# new_pair = {}
|
||||
# for doc, tab in sub_credit.items():
|
||||
# new_pair[doc] = {}
|
||||
# for word, freq in tab.items():
|
||||
# new_pair[doc][word] = freq
|
||||
# print(new_pair)
|
||||
# return HttpResponse(str(main_credit)+str(sub_credit))
|
||||
|
||||
# 若total_credit为空,不进行以下操作,避免err
|
||||
if total_credit:
|
||||
# 暂时解决当该用户当月无任务时,total_credit中不包含该用户的用户名,导致Key Error
|
||||
try:
|
||||
current_user = total_credit[request.user.username]
|
||||
except:
|
||||
current_user = {}
|
||||
|
||||
# 累加该部门各个用户的工作量,计算部门工作量
|
||||
department_cal = {}
|
||||
temp_pre = []
|
||||
depart_pre, depart_real, depart_count = 0, 0, 0
|
||||
for username, value in total_credit.items():
|
||||
print(username)
|
||||
print(value['pre_cal'])
|
||||
depart_pre = depart_pre + value['pre_cal']
|
||||
depart_real = depart_real + value['real_cal']
|
||||
depart_count = depart_count + value['main_count']
|
||||
temp_pre.append(value['pre_cal'])
|
||||
department_cal['pre_cal'] = depart_pre
|
||||
department_cal['real_cal'] = depart_real
|
||||
department_cal['depart_count'] = depart_count
|
||||
department_cal['pre_avg'] = department_cal['pre_cal'] / len(total_credit)
|
||||
department_cal['real_avg'] = department_cal['real_cal'] / len(total_credit)
|
||||
else:
|
||||
department_cal, current_user = {}, {}
|
||||
|
||||
|
||||
# 计算实际工作量、完成质量
|
||||
def cal_method(main_list, sub_list, user_name):
|
||||
# print(main_list, sub_list, user_name)
|
||||
|
||||
# sub_credit = sub_list
|
||||
# main_credit = main_list
|
||||
|
||||
total_data = []
|
||||
season = 1
|
||||
# 分别计算每个季度的工作量、评价
|
||||
for main_credit, sub_credit in zip(main_list, sub_list):
|
||||
# TODO 对于完成质量的核算,先对评价求和,再除以已评价的承办任务数
|
||||
# for i in main_credit:
|
||||
# print(i['main_executor__quality_mark__mark_value__mark_value'])
|
||||
# print(main_credit)
|
||||
quality_dict = {}
|
||||
for i in main_credit:
|
||||
# 先赋值0,避免前端显示个[]
|
||||
quality_dict[i['username']] = []
|
||||
if i['main_executor__quality_mark__mark_value__mark_value'] != None:
|
||||
quality_dict[i['username']].append(i['main_executor__quality_mark__mark_value__mark_value'])
|
||||
|
||||
# TEST
|
||||
for i in sub_credit:
|
||||
# 先赋值0,避免前端显示个[]
|
||||
quality_dict[i['username']] = []
|
||||
if i['sub_executor__quality_mark__mark_value__mark_value'] != None:
|
||||
quality_dict[i['username']].append(i['sub_executor__quality_mark__mark_value__mark_value'])
|
||||
|
||||
|
||||
for key, value in quality_dict.items():
|
||||
if value:
|
||||
quality_dict[key] = sum(value) / len(value)
|
||||
# print(quality_dict)
|
||||
|
||||
main_credit = solve(main_credit, 'username',
|
||||
['main_pre_cal', 'main_real_cal', 'main_count', 'main_executor__evaluate_factor'])
|
||||
main_credit = dict(main_credit)
|
||||
sub_credit = solve(sub_credit, 'username', ['sub_pre_cal', 'sub_real_cal', 'sub_count'])
|
||||
sub_credit = dict(sub_credit)
|
||||
|
||||
# 按用户名合并承办与协办任务字典
|
||||
total_credit = deepcopy(main_credit)
|
||||
for key in sub_credit.keys():
|
||||
if key in total_credit:
|
||||
total_credit[key].update(sub_credit[key])
|
||||
else:
|
||||
total_credit[key] = sub_credit[key]
|
||||
|
||||
# print(total_credit['admin']['sub_pre_cal'])
|
||||
# 根据字典内容,计算总工作量
|
||||
for key, value in total_credit.items():
|
||||
# print(value)
|
||||
value['pre_cal'] = value['sub_pre_cal'] + value['main_pre_cal']
|
||||
value['real_cal'] = value['sub_real_cal'] + value['main_real_cal']
|
||||
value['real_name'] = user_name[key]
|
||||
value['season'] = season
|
||||
|
||||
# 由于不计算协办任务,quality_dict会有空值情况
|
||||
try:
|
||||
value['quality'] = quality_dict[key]
|
||||
except:
|
||||
value['quality'] = 0
|
||||
|
||||
for value in total_credit.values():
|
||||
# print(value)
|
||||
real_cal_season = "real_cal_" + str(season)
|
||||
value[real_cal_season] = value.pop("real_cal")
|
||||
quality_season = "quality_" + str(season)
|
||||
value[quality_season] = value.pop("quality")
|
||||
# print(total_credit)
|
||||
total_data.append(total_credit)
|
||||
# print(season)
|
||||
season += 1
|
||||
|
||||
# new_credit = []
|
||||
# for item in total_credit:
|
||||
# for key, value in item.items():
|
||||
# print(total_data, season)
|
||||
dd = defaultdict(list)
|
||||
for d in total_data: # you can list as many input dicts as you want here
|
||||
for key, value in d.items():
|
||||
dd[key].append(value)
|
||||
# print(dd)
|
||||
return dd
|
||||
user_info = User.objects.filter(department=request.user.department) \
|
||||
.values_list('username', 'real_name')
|
||||
user_name = {}
|
||||
for user in user_info:
|
||||
user_name[user[0]] = user[1]
|
||||
|
||||
work_cal = User.objects.filter(department=request.user.department, main_executor__deadline__year=2021) \
|
||||
.order_by('main_executor__deadline').values('main_executor__todo_topic', 'main_executor__deadline')
|
||||
|
||||
# 以用户表查询,按用户列出所参与承办、协办任务,并在之后按用户名分组合并。
|
||||
main_credit = User.objects.filter(department=request.user.department,
|
||||
main_executor__deadline__year=year) \
|
||||
.annotate(main_count=Count('main_executor')) \
|
||||
.order_by('username') \
|
||||
.values('username', 'main_executor', 'main_executor__predict_work', 'main_executor__real_work',
|
||||
'main_executor__evaluate_factor', 'main_executor__maturity', 'main_executor__quality_mark__mark_value__mark_value', 'main_executor__deadline', 'main_count') # 这里的annotate不知会不会有问题
|
||||
|
||||
sub_count = Todo.objects.filter(sub_executor__department=request.user.department,
|
||||
deadline__year=year) \
|
||||
.annotate(sub_count=Count('sub_executor')).values('id', 'sub_count')
|
||||
sub_credit = User.objects.filter(department=request.user.department,
|
||||
sub_executor__deadline__year=year) \
|
||||
.order_by('username') \
|
||||
.values('username', 'real_name', 'sub_executor', 'sub_executor__predict_work', 'sub_executor__real_work',
|
||||
'sub_executor__evaluate_factor', 'sub_executor__maturity', 'sub_executor__quality_mark__mark_value__mark_value', 'sub_executor__deadline')
|
||||
|
||||
# 构建工作包id对应协办人人数的字典
|
||||
sub_exe_count = {}
|
||||
for i in sub_count:
|
||||
key = i['id']
|
||||
value = i['sub_count']
|
||||
sub_exe_count[key] = value
|
||||
# print(sub_exe_count)
|
||||
|
||||
# 计算每个承办任务的预计、实际工作量、成熟度,并插入字典
|
||||
for i in main_credit:
|
||||
# 将成熟度由百分数转小数,以便其后与其他变量计算 eg. 50% -> 0.5
|
||||
print('ad', i['main_executor__maturity'])
|
||||
# 临时补丁,解决用户将成熟度设置为空的问题,后面的协办任务也改了,记得改回去
|
||||
# TODO 数据库中设置成熟度为非空
|
||||
try:
|
||||
i['main_executor__maturity'] = decimal.Decimal(float(i['main_executor__maturity'].strip('%')) / 100)
|
||||
except:
|
||||
i['main_executor__maturity'] = decimal.Decimal(float(0) / 100)
|
||||
# print(i['main_executor__maturity'])
|
||||
i['main_pre_cal'] = i['main_executor__predict_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
|
||||
i['main_real_cal'] = i['main_executor__real_work'] * i['main_executor__evaluate_factor'] * i['main_executor__maturity']
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
# 将协办任务对应的人数插入,计算每个协办任务的预计、实际工作量,并插入字典
|
||||
for i in sub_credit:
|
||||
sub_todo_id = i['sub_executor']
|
||||
i['sub_exe_count'] = sub_exe_count[sub_todo_id]
|
||||
try:
|
||||
i['sub_executor__maturity'] = decimal.Decimal(float(i['sub_executor__maturity'].strip('%')) / 100)
|
||||
except:
|
||||
i['sub_executor__maturity'] = decimal.Decimal(float(0) / 100)
|
||||
i['sub_pre_cal'] = i['sub_executor__predict_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
|
||||
i['sub_real_cal'] = i['sub_executor__real_work'] * (1 - i['sub_executor__evaluate_factor']) / i['sub_exe_count'] * i['sub_executor__maturity']
|
||||
i['sub_count'] = 1 # 用于帮助累加程序计算每个用户的协办任务数量, sub_exe_count会返回此协办任务的协办人数,在累加时导致计算错误
|
||||
# print(i)
|
||||
# print(str(i['sub_executor']))
|
||||
|
||||
|
||||
|
||||
main_Q1st, main_Q2nd, main_Q3th, main_Q4th = [], [], [], []
|
||||
for i in main_credit:
|
||||
# print(i['main_executor__deadline'].month)
|
||||
deadline_month = i['main_executor__deadline'].month
|
||||
if 1 <= deadline_month <= 3:
|
||||
main_Q1st.append(i)
|
||||
elif 4 <= deadline_month <= 6:
|
||||
main_Q2nd.append(i)
|
||||
elif 7 <= deadline_month <= 9:
|
||||
main_Q3th.append(i)
|
||||
elif 10 <= deadline_month <= 12:
|
||||
main_Q4th.append(i)
|
||||
# print(Q1st, Q2nd, Q3th, Q4th)
|
||||
|
||||
sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th = [], [], [], []
|
||||
for i in sub_credit:
|
||||
# print(i['main_executor__deadline'].month)
|
||||
deadline_month = i['sub_executor__deadline'].month
|
||||
if 1 <= deadline_month <= 3:
|
||||
sub_Q1st.append(i)
|
||||
elif 4 <= deadline_month <= 6:
|
||||
sub_Q2nd.append(i)
|
||||
elif 7 <= deadline_month <= 9:
|
||||
sub_Q3th.append(i)
|
||||
elif 10 <= deadline_month <= 12:
|
||||
sub_Q4th.append(i)
|
||||
|
||||
main_list = [main_Q1st, main_Q2nd, main_Q3th, main_Q4th]
|
||||
sub_list = [sub_Q1st, sub_Q2nd, sub_Q3th, sub_Q4th]
|
||||
stat_result = cal_method(main_list, sub_list, user_name)
|
||||
|
||||
stat = {}
|
||||
for key, value in stat_result.items():
|
||||
stat[key] = {}
|
||||
for j in value:
|
||||
stat[key]['real_name'] = j['real_name']
|
||||
season = str(j['season'])
|
||||
real_cal_season = 'real_cal_' + season
|
||||
quality_season = 'quality_' + season
|
||||
stat[key][real_cal_season] = j[real_cal_season]
|
||||
stat[key][quality_season] = j[quality_season]
|
||||
print(stat_result)
|
||||
|
||||
# return HttpResponse(result.items())
|
||||
# 为页面提供日期信息
|
||||
date = str(year) + '年' + str(month) + '月'
|
||||
|
||||
# return HttpResponse(str(total_credit) + '\n' + str(department_cal))
|
||||
context = {'date': date, 'users_data': total_credit, 'department_cal': department_cal,
|
||||
'current_user': current_user, 'stat': stat}
|
||||
return render(request, 'tasks/index.html', context)
|
||||
|
||||
|
||||
class TodoListView(View):
|
||||
|
@ -23,26 +366,36 @@ class TodoListView(View):
|
|||
my_todo = Todo.objects.filter(main_executor=request.user, deadline__year=year, deadline__month=month)
|
||||
my_sub_todo = Todo.objects.filter(sub_executor=request.user, deadline__year=year, deadline__month=month)
|
||||
date = str(year) + '年' + str(month) + '月'
|
||||
context = {'my_todo': my_todo, 'my_sub_todo': my_sub_todo, 'date': date}
|
||||
current_path = request.get_full_path()
|
||||
context = {'my_todo': my_todo, 'my_sub_todo': my_sub_todo, 'date': date, 'current_path': current_path}
|
||||
return render(request, 'tasks/todolist.html', context)
|
||||
|
||||
|
||||
class GroupTodoList(View):
|
||||
@method_decorator(login_required)
|
||||
def get(self, request, year=timezone.now().year, month=timezone.now().month):
|
||||
group_todo = Todo.objects.filter(main_executor__department=request.user.department, deadline__year=year, deadline__month=month)
|
||||
group_todo = Todo.objects.filter(main_executor__department=request.user.department, deadline__year=year,
|
||||
deadline__month=month).order_by('related_task_id', 'deadline')
|
||||
date = str(year) + '年' + str(month) + '月'
|
||||
context = {'group_todo': group_todo, 'date': date}
|
||||
current_path = request.get_full_path()
|
||||
context = {'group_todo': group_todo, 'date': date, 'current_path': current_path}
|
||||
return render(request, 'tasks/group_todolist.html', context)
|
||||
|
||||
|
||||
class TaskListView(View):
|
||||
@method_decorator(login_required)
|
||||
def get(self, request):
|
||||
tasks = Task.objects.filter().order_by('task_id')
|
||||
context = {'tasks': tasks}
|
||||
def get(self, request, year=timezone.now().year): # TODO 把timezone.now().year写在后面要用year替换的地方是否可以解决
|
||||
tasks = Task.objects.filter(department=request.user.department, deadline__year=year).order_by('task_id')\
|
||||
| Task.objects.filter(department=request.user.department, related_task__deadline__year=year).order_by('task_id')
|
||||
tasks = tasks.distinct()
|
||||
# tasks = Task.objects.filter(Q(department=request.user.department), Q(deadline__year=year) | Q(related_task__deadline__year=year)).order_by('task_id')
|
||||
# 使用‘或’,找出工作包/年度任务的截止日期在今年的年度任务。后面还要做一个筛选,以达到只显示本年度的工作包
|
||||
year_quarter = {'1': [1, year], '2': [2, year], '3': [3, year], '4': [4, year]}
|
||||
|
||||
context = {'tasks': tasks, 'year_quarter': year_quarter}
|
||||
return render(request, 'tasks/tasklist.html', context)
|
||||
|
||||
|
||||
class UserLoginView(View):
|
||||
def get(self, request):
|
||||
return render(request, 'tasks/login.html')
|
||||
|
@ -58,6 +411,7 @@ class UserLoginView(View):
|
|||
else:
|
||||
return redirect('tasks:index')
|
||||
|
||||
|
||||
class UserLogoutView(View):
|
||||
def get(self, request):
|
||||
logout(request)
|
||||
|
@ -74,7 +428,16 @@ class TodoEntryView(View):
|
|||
def post(self, request, pk):
|
||||
todo_detail = Todo.objects.get(id=pk)
|
||||
form = TodoForm(instance=todo_detail, data=request.POST)
|
||||
redirect_to = request.GET.get('next')
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect('tasks:index')
|
||||
# return redirect('tasks:todo_detail', pk=pk)
|
||||
if url_has_allowed_host_and_scheme(redirect_to, None):
|
||||
return redirect(redirect_to)
|
||||
else:
|
||||
return redirect('tasks:todolist')
|
||||
# return redirect('tasks:todo_detail', pk=pk)
|
||||
|
||||
|
||||
class AboutView(View):
|
||||
def get(self, request):
|
||||
return render(request, 'tasks/about.html')
|
||||
|
|
|
@ -23,23 +23,54 @@ class MyUserAdmin(UserAdmin):
|
|||
class MyGroupAdmin(GroupAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class DepartmentAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class TaskPropertyAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'task_property', 'own_department'
|
||||
)
|
||||
|
||||
def get_queryset(self, request):
|
||||
qs = super(TaskPropertyAdmin, self).get_queryset(request)
|
||||
if request.user.is_superuser:
|
||||
return qs
|
||||
else:
|
||||
return qs.filter(own_department=request.user.department)
|
||||
|
||||
|
||||
class QualityMarkAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'mark_name',
|
||||
'mark_value'
|
||||
)
|
||||
def mark_value(self):
|
||||
return self.mark_value
|
||||
mark_value.short_description = 'ss'
|
||||
|
||||
# # 仅显示当前部门的任务属性,除非为超管
|
||||
# def get_queryset(self, request):
|
||||
# qs = super(QualityMarkAdmin, self).get_queryset(request)
|
||||
# if request.user.is_superuser:
|
||||
# return qs
|
||||
# else:
|
||||
# return qs.filter(department=request.user.department)
|
||||
|
||||
|
||||
class MarkValueAdmin(admin.ModelAdmin):
|
||||
# def get_queryset(self, request):
|
||||
# qs = super(MarkValueAdmin, self).get_queryset(request)
|
||||
# if request.user.is_superuser:
|
||||
# return qs
|
||||
# else:
|
||||
# return qs.filter(department=request.user.department)
|
||||
pass
|
||||
|
||||
admin.site.register(models.User, MyUserAdmin)
|
||||
admin.site.register(models.MyGroup, MyGroupAdmin)
|
||||
admin.site.register(models.MarkValue)
|
||||
admin.site.register(models.MarkValue, MarkValueAdmin)
|
||||
admin.site.register(models.Department)
|
||||
admin.site.register(models.QualityMark, QualityMarkAdmin)
|
||||
admin.site.register(models.TaskProperty, TaskPropertyAdmin)
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 13:31
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Department',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=50, verbose_name='部门名称')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '部门',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MarkValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('mark_value', models.DecimalField(decimal_places=2, max_digits=3)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MyGroup',
|
||||
fields=[
|
||||
('group_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='auth.group')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '权限组',
|
||||
'verbose_name_plural': '权限组',
|
||||
},
|
||||
bases=('auth.group',),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.GroupManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TaskProperty',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('task_property', models.CharField(max_length=50, verbose_name='任务属性')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '任务属性',
|
||||
'verbose_name_plural': '任务属性',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='QualityMark',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('mark_name', models.CharField(max_length=10, verbose_name='评分')),
|
||||
('mark_value', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.markvalue')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('real_name', models.CharField(max_length=150, verbose_name='姓名')),
|
||||
('staff_id', models.CharField(max_length=150, verbose_name='工号')),
|
||||
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '用户',
|
||||
'verbose_name_plural': '用户',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 13:32
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='department',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='users.department'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 14:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_auto_20210129_1332'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='department',
|
||||
options={'verbose_name': '部门', 'verbose_name_plural': '部门'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='taskproperty',
|
||||
name='own_department',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='users.department'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -1,19 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-29 14:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0003_auto_20210129_1417'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='taskproperty',
|
||||
name='own_department',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.department', verbose_name='所属部门'),
|
||||
),
|
||||
]
|
|
@ -1,24 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-01-30 20:21
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0004_auto_20210129_1434'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='taskproperty',
|
||||
name='own_department',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.department', verbose_name='所属部门'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='department',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.department'),
|
||||
),
|
||||
]
|
|
@ -1,31 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-02-01 19:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0005_auto_20210130_2021'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='markvalue',
|
||||
options={'verbose_name': '评分值', 'verbose_name_plural': '评分值'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='qualitymark',
|
||||
options={'verbose_name': '评分名称', 'verbose_name_plural': '评分名称'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='markvalue',
|
||||
name='mark_value',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=3, verbose_name='评分值'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='qualitymark',
|
||||
name='mark_name',
|
||||
field=models.CharField(max_length=10, verbose_name='评分名称'),
|
||||
),
|
||||
]
|
|
@ -1,31 +0,0 @@
|
|||
# Generated by Django 3.1.5 on 2021-02-01 22:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0006_auto_20210201_1900'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='markvalue',
|
||||
options={'verbose_name': '评价等级考核系数', 'verbose_name_plural': '评价等级考核系数'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='qualitymark',
|
||||
options={'verbose_name': '评价等级定义', 'verbose_name_plural': '评价等级定义'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='markvalue',
|
||||
name='mark_value',
|
||||
field=models.DecimalField(decimal_places=2, max_digits=3, verbose_name='评价等级考核系数'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='qualitymark',
|
||||
name='mark_name',
|
||||
field=models.CharField(max_length=10, verbose_name='评价等级定义'),
|
||||
),
|
||||
]
|
|
@ -3,10 +3,13 @@ from django.contrib.auth.models import AbstractUser, Group
|
|||
|
||||
|
||||
# Create your models here.
|
||||
from django.db.models import Avg, Sum, F, Value
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
real_name = models.CharField(max_length=150, verbose_name='姓名')
|
||||
staff_id = models.CharField(max_length=150, verbose_name='工号')
|
||||
department = models.ForeignKey('Department', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
department = models.ForeignKey('Department', related_name='member', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '用户'
|
||||
|
@ -15,6 +18,16 @@ class User(AbstractUser):
|
|||
def __str__(self):
|
||||
return self.real_name
|
||||
|
||||
@classmethod
|
||||
def get_total_point(cls):
|
||||
return cls.objects.annotate(total=Sum('main_executor__predict_work'))
|
||||
# def get_total_point(cls):
|
||||
# return cls.objects.aggregate(total=Sum(F('main_executor__predict_work') * F('main_executor__evaluate_factor') + F('sub_executor__predict_work') * F('sub_executor__evaluate_factor')))['total']
|
||||
|
||||
@classmethod
|
||||
def get_predict_work_count(cls):
|
||||
return cls.objects.aggregate(total_predict_work=Sum('main_executor__predict_work'))
|
||||
|
||||
|
||||
class MyGroup(Group):
|
||||
class Meta:
|
||||
|
@ -32,6 +45,10 @@ class Department(models.Model):
|
|||
verbose_name = '部门'
|
||||
verbose_name_plural = '部门'
|
||||
|
||||
@property
|
||||
def get_user_number(self):
|
||||
return self.member.count()
|
||||
|
||||
|
||||
class MarkValue(models.Model):
|
||||
mark_value = models.DecimalField('评价等级考核系数', max_digits=3, decimal_places=2)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
version: "3"
|
||||
services:
|
||||
app:
|
||||
restart: always
|
||||
build: . # '点'代表当前目录
|
||||
command: "python3 manage.py runserver 0.0.0.0:8000"
|
||||
volumes:
|
||||
- .:/code
|
||||
ports:
|
||||
- "8000:8000"
|
|
@ -0,0 +1,7 @@
|
|||
import { defineClientConfig } from '@vuepress/client'
|
||||
|
||||
export default defineClientConfig({
|
||||
enhance({ app, router, siteData }) {},
|
||||
setup() {},
|
||||
rootComponents: [],
|
||||
})
|
|
@ -0,0 +1,37 @@
|
|||
// module.exports = {
|
||||
// lang: 'zh-CN',
|
||||
// title: 'TasksManager',
|
||||
// description: '超好用的任务分配管理系统',
|
||||
// }
|
||||
//
|
||||
import {defaultTheme, defineUserConfig} from 'vuepress'
|
||||
|
||||
export default defineUserConfig({
|
||||
lang: 'zh-CN',
|
||||
title: 'TasksManager',
|
||||
description: '超好用的任务分配管理系统',
|
||||
|
||||
theme: defaultTheme({
|
||||
navbar: [
|
||||
{
|
||||
text: '指南',
|
||||
link: '/guide/',
|
||||
},
|
||||
{
|
||||
text: '部署',
|
||||
link: '/install/',
|
||||
}
|
||||
],
|
||||
sidebar: "auto",
|
||||
repo: 'https://github.com/raiots/TasksManager',
|
||||
repoLabel: '✨Github',
|
||||
docsDir: 'docs',
|
||||
docsBranch: 'master',
|
||||
lastUpdatedText: "📑 最后更新",
|
||||
contributorsText: "💕 参与贡献",
|
||||
editLinkText: "🖊️ 编辑本文",
|
||||
notFound: ["👻 页面不存在"],
|
||||
|
||||
}),
|
||||
})
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
home: true
|
||||
heroText: TasksManager
|
||||
tagline: 超好用的任务分配管理工具❤️
|
||||
actions:
|
||||
- text: 项目简介
|
||||
link: /guide/
|
||||
type: primary
|
||||
- text: 快速部署
|
||||
link: /install/
|
||||
type: secondary
|
||||
|
||||
features:
|
||||
- title: 任务分发
|
||||
details: TasksManager 管理员能够以年度任务为主线,向用户发送待办任务
|
||||
- title: 任务提交
|
||||
details: 用户可以查看自己的待办任务、并提交完成度与完成情况说明,帮助管理员掌握部门工作情况
|
||||
- title: 评价考核
|
||||
details: TasksManager 会对用户完成任务数量与质量进行综合评价,并直观显示于首页
|
||||
---
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
sidebar: auto
|
||||
|
||||
---
|
||||
|
||||
TasksManager 是一个由Django开发的任务分发系统
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
sidebar: auto
|
||||
navbar: True
|
||||
---
|
||||
|
||||
# 部署
|
||||
|
||||
你可以使用两种方式部署 TasksManager,这里推荐使用 Docker compose 的方式进行部署。
|
||||
|
||||
## Docker Compose 部署
|
||||
|
||||
> Docker Compose 是一个用于在使用Compose 文件格式定义的 Docker 上运行多容器应用程序的工具。
|
||||
|
||||
### 安装
|
||||
|
||||
拉取 TaskManager 程序源码
|
||||
|
||||
```shell
|
||||
git clone https://github.com/raiots/TasksManager.git
|
||||
```
|
||||
|
||||
### 启动
|
||||
使用 cd 命令进入程序文件夹后启动程序:
|
||||
|
||||
```shell
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
TasksManager 将会运行在服务器的 8000 端口,在浏览器中打开 http://ip地址:8000 即可访问
|
||||
|
||||
## 使用源码手动部署
|
||||
|
||||
> 程序使用 Python3.8 开发,请提前配置 Python 环境
|
||||
|
||||
### 安装
|
||||
|
||||
使用 git 下载 TasksManager 源码:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/raiots/TasksManager.git
|
||||
cd TasksManager
|
||||
```
|
||||
|
||||
创建并激活 Python 虚拟环境
|
||||
|
||||
```shell
|
||||
python -m venv venv
|
||||
.\venv\Scripts\activate.sh
|
||||
```
|
||||
|
||||
安装 TasksManager 依赖
|
||||
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 启动
|
||||
|
||||
```shell
|
||||
python3 manage.py runserver 0.0.0.0:8000
|
||||
```
|
||||
|
||||
TasksManager 将会运行在服务器的 8000 端口,在浏览器中打开 http://ip地址:8000 即可访问
|
||||
|
||||
## 使用 Nginx 配置反向代理
|
||||
|
||||
```
|
||||
server
|
||||
{
|
||||
listen 443 ssl http2;
|
||||
server_name your.domain.com ;
|
||||
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000; # 转发规则
|
||||
proxy_set_header Host $proxy_host; # 修改转发请求头,让8000端口的应用可以受到真实的请求
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
```
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"vuepress": "^2.0.0-beta.60"
|
||||
},
|
||||
"scripts": {
|
||||
"docs:dev": "vuepress dev docs",
|
||||
"docs:build": "vuepress build docs"
|
||||
}
|
||||
}
|
|
@ -1,7 +1,21 @@
|
|||
asgiref==3.3.1
|
||||
Django==3.1.5
|
||||
django-simpleui==2021.1.1
|
||||
defusedxml==0.7.1
|
||||
diff-match-patch==20200713
|
||||
Django==3.1.12
|
||||
django-import-export==2.5.0
|
||||
django-simpleui==2021.8.1
|
||||
et-xmlfile==1.0.1
|
||||
MarkupPy==1.14
|
||||
numpy==1.20.2
|
||||
odfpy==1.4.1
|
||||
openpyxl==3.0.7
|
||||
pandas==1.2.4
|
||||
python-dateutil==2.8.1
|
||||
pytz==2020.5
|
||||
PyYAML==5.4.1
|
||||
six==1.15.0
|
||||
sqlparse==0.4.1
|
||||
tablib==3.0.0
|
||||
xlrd==2.0.1
|
||||
xlwt==1.3.0
|
||||
django-debug-toolbar~=3.2.2
|
|
@ -0,0 +1,40 @@
|
|||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Press Start 2P';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nYivN04w.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Press Start 2P';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nRivN04w.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Press Start 2P';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nWivN04w.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Press Start 2P';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nbivN04w.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Press Start 2P';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/pressstart2p/v9/e3t4euO8T-267oIAQAu6jDQyK3nVivM.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.7 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -5,11 +5,16 @@
|
|||
BORDER-TOP: 0px solid;
|
||||
/*width:100%;*/
|
||||
overflow:hidden;
|
||||
width:200px;
|
||||
width:160px;
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
|
||||
.table-d-center{
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.col-xs-1-5 {
|
||||
width: 20%;
|
||||
float: left;
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>ABOUT - Raiot</title>
|
||||
<link rel="stylesheet" href="{% static 'tasks/nes.min.css' %}">
|
||||
<style>
|
||||
@charset "utf-8";
|
||||
@import url({% static 'tasks/about_font.css' %});
|
||||
|
||||
@font-face {
|
||||
font-family: 'pixel';
|
||||
src: url('{% static 'tasks/pixel.ttf' %}');
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0 2rem;
|
||||
margin: 0 2rem;
|
||||
}
|
||||
|
||||
#nescss>.container {
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
margin-top: 150px;
|
||||
}
|
||||
|
||||
main p {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
border-bottom: 4px solid #D3D3D3;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
header>.container {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding-top: 1rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
header>.container>.nav-brand {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.nav-brand .brand-logo {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.nav-brand>a {
|
||||
color: #212529;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Main */
|
||||
.main-content {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h2>a {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.topic {
|
||||
margin-bottom: 3rem;
|
||||
font-family: 'pixel';
|
||||
}
|
||||
|
||||
/* Containers */
|
||||
.item.containers>.nes-container {
|
||||
display: inline-block;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
||||
/* Topic */
|
||||
h3.topic-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3.topic-title>i {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: calc(980px - 4rem)) {
|
||||
header>.container {
|
||||
margin: 0 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
body {
|
||||
margin: 2rem 0.5rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header>.container {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.github-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.message-list>.message>.nes-balloon {
|
||||
max-width: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body cz-shotcut-listen="true">
|
||||
<div id="nescss">
|
||||
<header class="">
|
||||
<div class="container">
|
||||
<div class="nav-brand">
|
||||
<a href="#">
|
||||
<h1>STAFF</h1>
|
||||
</a>
|
||||
<p>© 2021 RAIOT</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="container">
|
||||
<main class="main-content">
|
||||
<section class="topic">
|
||||
<h2>
|
||||
<a href="#">#</a>
|
||||
MIT许可证
|
||||
</h2>
|
||||
<p>
|
||||
特此免费授予任何获得副本的人这个软件和相关的文档文件(“软件”)来处理在软件中没有限制,包括但不限于权利使用,复制,
|
||||
修改,合并,发布,分发,再许可和/或销售该软件的副本,并允许软件所属的人员提供这样做,但须符合以下条件:
|
||||
<br>
|
||||
上述版权声明和本许可声明必须包含在内本软件的副本或实质性部分。
|
||||
<br>
|
||||
本软件按“原样”提供,不附有任何形式的明示或暗示保证默示的,包括但不限于对适销性的保证,适用于特定目的和不侵权。在任何
|
||||
情况下,作者或版权持有者对任何索赔,损坏或其他责任均不负任何责任责任,无论是在合同,民事侵权行为或其他方面,与本软件
|
||||
或本软件的使用或其他交易有关或与之有关软件。
|
||||
</p>
|
||||
</section>
|
||||
<section class="topic">
|
||||
<h2>
|
||||
<a href="#">#</a>
|
||||
关于我
|
||||
</h2>
|
||||
<p>
|
||||
Django开发的练习之作,请不要在意混乱的代码(误)
|
||||
</p>
|
||||
</section>
|
||||
<section class="topic">
|
||||
<h2>
|
||||
<a href="#">#</a>
|
||||
免责声明
|
||||
</h2>
|
||||
<p>您当前所访问的站点使用的是下面所提到的开源程序。您能看到这个页面仅代表这个站点使用了本程序,其使用过程中发生的一切问题需要其自行解决,本程序不承担任何责任。</p>
|
||||
<div class="nes-table-responsive">
|
||||
<table class="nes-table is-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>作者</td>
|
||||
<td><a href="#">Raiot.Me</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>维护</td>
|
||||
<td>
|
||||
<a href="#">Raiot.Me</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>鸣谢</td>
|
||||
<td>所有被引用过代码的同学,以及所有提交过PR的同学,这份About界面框架的作者。当然,还有在使用这份程序的你我Ta。</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
<section class="topic">
|
||||
<h2>
|
||||
<a href="#">#</a>
|
||||
Credits
|
||||
</h2>
|
||||
<div class="lists">
|
||||
<ul class="nes-list is-circle">
|
||||
<li>
|
||||
<a href="#" target="_blank" rel="noopener">
|
||||
NES.css
|
||||
</a>
|
||||
- NES-style CSS Framework
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" target="_blank" rel="noopener">Simple-UI</a>
|
||||
- Django后台模板
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" target="_blank" rel="noopener">AdminLTE</a>
|
||||
- Free Bootstrap Admin Template
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -9,7 +9,7 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>任务进度管理系统</title>
|
||||
<title>任务管理工具</title>
|
||||
|
||||
<!-- Google Font: Source Sans Pro -->
|
||||
<link rel="stylesheet" href="{% static 'tasks/dist/css/googlefont.css' %}">
|
||||
|
@ -32,7 +32,7 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
<div class="container-xl">
|
||||
<a href="{% url 'tasks:index' %}" class="navbar-brand">
|
||||
<img src="{% static 'tasks/dist/img/AdminLTELogo.png' %}" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8">
|
||||
<span class="brand-text font-weight-light">任务管理系统</span>
|
||||
<span class="brand-text font-weight-light">任务管理工具</span>
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler order-1" type="button" data-toggle="collapse" data-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||
|
@ -53,10 +53,18 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
<a href="{% url 'tasks:tasklist' %}" class="nav-link">年度任务</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin" class="nav-link">编辑任务</a>
|
||||
<a href="/admin/#tasks/task" class="nav-link">编辑任务</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a id="dropdownSubMenu1" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle">管理域风险分析</a>
|
||||
<ul aria-labelledby="dropdownSubMenu1" class="dropdown-menu border-0 shadow">
|
||||
<li><a href="{% url 'tasks:todolist' %}" class="dropdown-item" style="font-size: 0.9rem"> 风险事件采集 </a></li>
|
||||
<li><a href="{% url 'tasks:group_todolist' %}" class="dropdown-item" style="font-size: 0.9rem">风险事件批量采集</a></li>
|
||||
<li><a href="{% url 'tasks:group_todolist' %}" class="dropdown-item" style="font-size: 0.9rem">可视化分析</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin" class="nav-link">系统配置</a>
|
||||
<a href="/admin/#users/user" class="nav-link">系统配置</a>
|
||||
</li>
|
||||
{# <li class="nav-item dropdown">#}
|
||||
{# <a id="dropdownSubMenu1" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="nav-link dropdown-toggle">统计与分析</a>#}
|
||||
|
@ -189,7 +197,9 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
{# </li>#}
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-tabs">
|
||||
<a>欢迎,{{ user.real_name }}</a>
|
||||
<a>欢迎,</a>
|
||||
<b style="color: cadetblue">{{ user.real_name }}</b>
|
||||
<a>。 本软件为非涉密应用工具,禁止处理秘密级及以上信息!!!</a>
|
||||
</li>
|
||||
<li class="navbar-tabs">
|
||||
<a href="{% url 'tasks:logout' %}"> 登出</a>
|
||||
|
@ -302,10 +312,10 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
<footer class="main-footer">
|
||||
<!-- To the right -->
|
||||
<div class="float-right d-none d-sm-inline">
|
||||
CHRDI Task Manager <a href="#">ver 0.5 - beta</a>
|
||||
CHRDI Task Manager <a href="{% url 'tasks:about' %}">ver 0.6 - beta</a>
|
||||
</div>
|
||||
<!-- Default to the left -->
|
||||
<strong>Made with ❤ by <a href="https://raiot.me">Raintony</a>.</strong>
|
||||
<strong>Made with ❤ by <a href="{% url 'tasks:about' %}">Raiot</a>.</strong>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
|
@ -319,5 +329,8 @@ scratch. This page gets rid of all links and provides the needed markup only.
|
|||
<script src="{% static 'tasks/plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<!-- AdminLTE App -->
|
||||
<script src="{% static 'tasks/dist/js/adminlte.min.js' %}"></script>
|
||||
{% block script %}
|
||||
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -58,18 +58,20 @@
|
|||
<table class="table" style="word-break: break-all">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10px; text-align:center; vertical-align: middle;">#</th>
|
||||
<th style="width: 16px; text-align:center; vertical-align: middle;">序号</th>
|
||||
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="width: 160px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="width: 60px; text-align:center; vertical-align: middle;">任务编号</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">任务来源</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">承办单位</th>
|
||||
<th style="width: 120px;text-align:center; vertical-align: middle;">承/督办人</th>
|
||||
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
|
||||
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 220px; text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
|
||||
{# <th style="width: 40px">Label</th>#}
|
||||
</tr>
|
||||
|
@ -77,12 +79,14 @@
|
|||
<tbody>
|
||||
{% for todo in group_todo %}
|
||||
<tr>
|
||||
<td>{{ todo.id }}</td>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ todo.todo_topic }}</td>
|
||||
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
|
||||
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.task_id }}</td>
|
||||
<td style="text-align: center">{{ todo.task_origin }}</td>
|
||||
<td style="text-align: center">{{ todo.duty_group }}</td>
|
||||
<td style="text-align: center">{{ todo.main_executor.all|join:", " }}</td>
|
||||
<td style="text-align: center">{{ todo.main_executor}}</td>
|
||||
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
|
||||
<td style="text-align:center;">{{ todo.predict_work }}</td>
|
||||
<td style="text-align:center;">{{ todo.evaluate_factor }}</td>
|
||||
|
@ -93,7 +97,7 @@
|
|||
</div>
|
||||
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
|
||||
</td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden;width:100%;">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -1 +1,455 @@
|
|||
{% extends 'tasks/base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block main_content %}
|
||||
<div class="content-wrapper" style="min-height: 1200.88px;">
|
||||
<!-- Content Header (Page header) -->
|
||||
<section class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1>总览 ({{ date }}) </h1>
|
||||
{# <h1><font color="#FF0000">测试版,请勿用于生产环境!!!</font></h1>#}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{# <ol class="breadcrumb float-sm-right">#}
|
||||
{# <li class="breadcrumb-item active"><a href="#">主页</a></li>#}
|
||||
{# </ol>#}
|
||||
<br>
|
||||
{% load taskfilter %}
|
||||
<ul class="pagination float-md-right">
|
||||
<li class="page-item"><a class="page-link" href="/{{ date|last_month }}">«</a></li>
|
||||
<li class="page-item"><a class="page-link"> {{ date|this_month }}月 </a></li>
|
||||
<li class="page-item"><a class="page-link" href="/{{ date|next_month }}">»</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.container-fluid -->
|
||||
</section>
|
||||
|
||||
<!-- Main content -->
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-danger elevation-1"><i class="fas fa-tasks"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">部门总任务数</span>
|
||||
<span class="info-box-number">
|
||||
{{ department_cal.depart_count|floatformat:'0'|default_if_none:'0' }}
|
||||
<small>个</small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">部门预计总工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ department_cal.pre_cal|default_if_none:'0'|floatformat:'1' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">部门实际总工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ department_cal.real_cal|default_if_none:'0'|floatformat:'1' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">部门预计平均工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ department_cal.pre_avg|default_if_none:'0'|floatformat:'1' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">部门实际平均工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ department_cal.real_avg|default_if_none:'0'|floatformat:'1' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
|
||||
<!-- fix for small devices only -->
|
||||
<div class="clearfix hidden-md-up"></div>
|
||||
|
||||
{# <div class="col-12 col-sm-6 col-md-3">#}
|
||||
{# <div class="info-box mb-3">#}
|
||||
{# <span class="info-box-icon bg-success elevation-1"><i class="fas fa-shopping-cart"></i></span>#}
|
||||
{##}
|
||||
{# <div class="info-box-content">#}
|
||||
{# <span class="info-box-text">Sales</span>#}
|
||||
{# <span class="info-box-number">760</span>#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box-content -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.col -->#}
|
||||
{# <div class="col-12 col-sm-6 col-md-3">#}
|
||||
{# <div class="info-box mb-3">#}
|
||||
{# <span class="info-box-icon bg-warning elevation-1"><i class="fas fa-users"></i></span>#}
|
||||
{##}
|
||||
{# <div class="info-box-content">#}
|
||||
{# <span class="info-box-text">New Members</span>#}
|
||||
{# <span class="info-box-number">2,000</span>#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box-content -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box -->#}
|
||||
{# </div>#}
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-info elevation-1"><i class="fas fa-tasks"></i></span>
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text"><b>个人任务数据统计:</b></span>
|
||||
{# <span class="info-box-number">#}
|
||||
{# {% for user in current_user %}{{ user.main_executor_count }}{% endfor %}#}
|
||||
{# <small>个</small>#}
|
||||
{# </span>#}
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="info-box">
|
||||
{# <span class="info-box-icon bg-info elevation-1"><i class="fas fa-tasks"></i></span>#}
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">承办任务数</span>
|
||||
<span class="info-box-number">
|
||||
{{ current_user.main_count }}
|
||||
<small>个</small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
{# <span class="info-box-icon bg-danger elevation-1"><i class="fas fa-tasks"></i></span>#}
|
||||
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">协办任务数</span>
|
||||
<span class="info-box-number">
|
||||
{{ current_user.sub_count }}
|
||||
<small>个</small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">预计工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ current_user.pre_cal|floatformat:'1'|default_if_none:'0' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col">
|
||||
<div class="info-box mb-3">
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">实际工作量</span>
|
||||
<span class="info-box-number">
|
||||
{{ current_user.real_cal|floatformat:'1'|default_if_none:'0' }}
|
||||
<small></small>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /.info-box-content -->
|
||||
</div>
|
||||
<!-- /.info-box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
|
||||
<!-- fix for small devices only -->
|
||||
<div class="clearfix hidden-md-up"></div>
|
||||
|
||||
{# <div class="col-12 col-sm-6 col-md-3">#}
|
||||
{# <div class="info-box mb-3">#}
|
||||
{# <span class="info-box-icon bg-success elevation-1"><i class="fas fa-shopping-cart"></i></span>#}
|
||||
{##}
|
||||
{# <div class="info-box-content">#}
|
||||
{# <span class="info-box-text">Sales</span>#}
|
||||
{# <span class="info-box-number">760</span>#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box-content -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.col -->#}
|
||||
{# <div class="col-12 col-sm-6 col-md-3">#}
|
||||
{# <div class="info-box mb-3">#}
|
||||
{# <span class="info-box-icon bg-warning elevation-1"><i class="fas fa-users"></i></span>#}
|
||||
{##}
|
||||
{# <div class="info-box-content">#}
|
||||
{# <span class="info-box-text">New Members</span>#}
|
||||
{# <span class="info-box-number">2,000</span>#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box-content -->#}
|
||||
{# </div>#}
|
||||
{# <!-- /.info-box -->#}
|
||||
{# </div>#}
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<!-- BAR CHART -->
|
||||
<div class="card card-gray">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">本部门工作统计表</h3>
|
||||
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-card-widget="collapse"><i
|
||||
class="fas fa-minus"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-tool" data-card-widget="remove"><i
|
||||
class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工作统计表格 -->
|
||||
<div class="card-body">
|
||||
<div class="chart">
|
||||
<div class="chartjs-size-monitor">
|
||||
<div class="chartjs-size-monitor-expand">
|
||||
<div class=""></div>
|
||||
</div>
|
||||
<div class="chartjs-size-monitor-shrink">
|
||||
<div class=""></div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="barChart"
|
||||
style="height: 230px; min-height: 230px; display: block; width: 764px;"
|
||||
width="764" height="230" class="chartjs-render-monitor"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
<!-- /.col (MAIN) -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">部门工作统计表</h3>
|
||||
</div>
|
||||
{# TODO 更换为绩效考核表 分季度按人显示工作量及完成质量#}
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<div id="example2_wrapper" class="dataTables_wrapper dt-bootstrap4"><div class="row"><div class="col-sm-12 col-md-6"></div><div class="col-sm-12 col-md-6"></div></div><div class="row"><div class="col-sm-12"><table id="example2" class="table table-bordered table-hover dataTable" role="grid" aria-describedby="example2_info">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th class="sorting_asc" tabindex="0" aria-controls="example2" rowspan="2" colspan="1" aria-sort="ascending" aria-label="Rendering engine: activate to sort column descending">姓名</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">一季度</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">二季度</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">三季度</th>
|
||||
<th class="sorting" tabindex="0" aria-controls="example2" rowspan="1" colspan="2" style="text-align: center">四季度</th>
|
||||
</tr>
|
||||
<tr role="row">
|
||||
<th>实际工作量</th>
|
||||
<th>完成质量</th>
|
||||
<th>实际工作量</th>
|
||||
<th>完成质量</th>
|
||||
<th>实际工作量</th>
|
||||
<th>完成质量</th>
|
||||
<th>实际工作量</th>
|
||||
<th>完成质量</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in stat.values %}
|
||||
<tr role="row" class="odd">
|
||||
<td class="sorting_1">{{ user.real_name }}</td>
|
||||
<td>{{ user.real_cal_1|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.quality_1|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.real_cal_2|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.quality_2|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.real_cal_3|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.quality_3|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.real_cal_4|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
<td>{{ user.quality_4|default_if_none:'0'|floatformat:'1' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{# <tfoot>#}
|
||||
{# <tr><th rowspan="1" colspan="1">Rendering engine</th><th rowspan="1" colspan="1">Browser</th><th rowspan="1" colspan="1">Platform(s)</th><th rowspan="1" colspan="1">Engine version</th><th rowspan="1" colspan="1">CSS grade</th></tr>#}
|
||||
{# </tfoot>#}
|
||||
</table></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
</div><!-- /.container-fluid -->
|
||||
</section>
|
||||
<!-- /.content -->
|
||||
</div>
|
||||
|
||||
{# <div class="content-wrapper">#}
|
||||
{# {{ main_points }}#}
|
||||
{# <br>#}
|
||||
{# {{ sub_points }}#}
|
||||
{# <br>#}
|
||||
{# {{ points }}#}
|
||||
{# {{ points.3.credit }}#}
|
||||
{# {% for point in points.values %}'{{ point }}', {% endfor %}#}
|
||||
{# {% for point in points %}#}
|
||||
{# {{ point }}#}
|
||||
{# {% endfor %}#}
|
||||
{##}
|
||||
{# {% for user in users %}#}
|
||||
{# {{ user }}#}
|
||||
{# {{ user.get_predict_work_count.total_predict_work }}#}
|
||||
{# {{ user.get_total_point }}#}
|
||||
{# {{ user.main_executor.count }}#}
|
||||
{# {% for todo in user.main_executor.all %}#}
|
||||
{# {{ todo }}#}
|
||||
{# {{ todo.points }}#}
|
||||
{# {% endfor %}#}
|
||||
{# </br>#}
|
||||
{# {% endfor %}#}
|
||||
{##}
|
||||
{# </div>#}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<!-- ChartJS -->
|
||||
<script src="{% static 'tasks/plugins/chart.js/Chart.min.js' %}"></script>
|
||||
<script>
|
||||
$(function () {
|
||||
/* ChartJS
|
||||
* -------
|
||||
* Here we will create a few charts using ChartJS
|
||||
*/
|
||||
var areaChartData = {
|
||||
labels : [{% for user in users_data.values %}'{{ user.real_name }}', {% endfor %}],
|
||||
datasets: [
|
||||
{
|
||||
label : '协办任务',
|
||||
backgroundColor : 'rgba(114,140,212,1)',
|
||||
borderColor : 'rgba(114,140,212,1)',
|
||||
pointRadius : false,
|
||||
pointColor : 'rgba(114,140,212,1)',
|
||||
pointStrokeColor : '#c1c7d1',
|
||||
pointHighlightFill : '#fff',
|
||||
pointHighlightStroke: 'rgba(220,220,220,1)',
|
||||
data : [{% for user in users_data.values %}'{{ user.sub_count }}', {% endfor %}]
|
||||
},
|
||||
{
|
||||
label : '承办任务',
|
||||
backgroundColor : 'rgba(23,162,184,0.9)',
|
||||
borderColor : 'rgba(100,98,204,0.8)',
|
||||
pointRadius : false,
|
||||
pointColor : '#3b8bba',
|
||||
pointStrokeColor : 'rgba(60,141,188,1)',
|
||||
pointHighlightFill : '#fff',
|
||||
pointHighlightStroke: 'rgba(60,141,188,1)',
|
||||
data : [{% for user in users_data.values %}'{{ user.main_count }}', {% endfor %}]
|
||||
},
|
||||
{
|
||||
label : '预计工作量',
|
||||
backgroundColor : 'rgba(210, 214, 222,1)',
|
||||
borderColor : 'rgba(210, 214, 222,1)',
|
||||
pointRadius : false,
|
||||
pointColor : '#3b8bba',
|
||||
pointStrokeColor : 'rgba(60,141,188,1)',
|
||||
pointHighlightFill : '#fff',
|
||||
pointHighlightStroke: 'rgba(60,141,188,1)',
|
||||
data : [{% for user in users_data.values %}'{{ user.pre_cal|floatformat:'1' }}', {% endfor %}]
|
||||
},
|
||||
{
|
||||
label : '工作量',
|
||||
backgroundColor : 'rgba(253,199,101,0.9)',
|
||||
borderColor : 'rgba(253,199,101,0.8)',
|
||||
pointRadius : false,
|
||||
pointColor : '#3b8bba',
|
||||
pointStrokeColor : 'rgba(60,141,188,1)',
|
||||
pointHighlightFill : '#fff',
|
||||
pointHighlightStroke: 'rgba(60,141,188,1)',
|
||||
data : [{% for user in users_data.values %}'{{ user.real_cal|floatformat:'1' }}', {% endfor %}]
|
||||
},
|
||||
]
|
||||
}
|
||||
//-------------
|
||||
//- BAR CHART -
|
||||
//-------------
|
||||
var barChartCanvas = $('#barChart').get(0).getContext('2d')
|
||||
var barChartData = jQuery.extend(true, {}, areaChartData)
|
||||
var temp0 = areaChartData.datasets[0]
|
||||
var temp1 = areaChartData.datasets[1]
|
||||
barChartData.datasets[0] = temp1
|
||||
barChartData.datasets[1] = temp0
|
||||
|
||||
var barChartOptions = {
|
||||
responsive : true,
|
||||
maintainAspectRatio : false,
|
||||
datasetFill : false
|
||||
}
|
||||
|
||||
var barChart = new Chart(barChartCanvas, {
|
||||
type: 'bar',
|
||||
data: barChartData,
|
||||
options: barChartOptions
|
||||
})
|
||||
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -5,7 +5,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>AdminLTE 3 | Log in (v2)</title>
|
||||
<title>任务管理工具</title>
|
||||
|
||||
<!-- Google Font: Source Sans Pro -->
|
||||
<link rel="stylesheet" href="{% static 'tasks/dist/css/googlefont.css' %}">
|
||||
|
@ -16,12 +16,12 @@
|
|||
<!-- Theme style -->
|
||||
<link rel="stylesheet" href="{% static 'tasks/dist/css/adminlte.min.css' %}">
|
||||
</head>
|
||||
<body class="hold-transition login-page">
|
||||
<body class="hold-transition login-page" style="background-image: url({% static 'tasks/images/peak_background.svg' %})">
|
||||
<div class="login-box">
|
||||
<!-- /.login-logo -->
|
||||
<div class="card card-outline card-primary">
|
||||
<div class="card-header text-center">
|
||||
<a href="../../index2.html" class="h1"><b>任务</b>管理系统</a>
|
||||
<a href="{% url 'tasks:index' %}" class="h1"><b>任务</b>管理系统</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="login-box-msg">登录以继续</p>
|
||||
|
@ -68,9 +68,9 @@
|
|||
{% csrf_token %}
|
||||
</form>
|
||||
|
||||
<p class="mb-1">
|
||||
<a href="forgot-password.html">无法登录?</a>
|
||||
</p>
|
||||
{# <p class="mb-1">#}
|
||||
{# <a href="#">无法登录?</a>#}
|
||||
{# </p>#}
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
</div>
|
||||
|
|
|
@ -33,13 +33,26 @@
|
|||
<!-- /.card-header -->
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-striped" style="word-break: break-all; overflow: auto" cellspacing="0">
|
||||
<div class="row">
|
||||
<div class="col-12" style="background-color: #f2f2f2">
|
||||
<button type="button" id="unfold" onclick="alert1()" class="btn btn-default float-left" style="display: block;"><i class="far fa-credit-card"></i> 展开</button>
|
||||
<button type="button" id="fold" onclick="alert1()" class="btn btn-default float-left" style="display: none;"><i class="far fa-credit-card"></i> 收起</button>
|
||||
<ul class="pagination float-md-right">
|
||||
{% load taskfilter %}
|
||||
<li class="page-item"><a class="page-link" href="/tasklist/{{ year_quarter.1|last_year }}">«</a></li>
|
||||
<li class="page-item"><a class="page-link"> {{ year_quarter.1|this_year }}年 </a></li>
|
||||
<li class="page-item"><a class="page-link" href="/tasklist/{{ year_quarter.1|next_year }}">»</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<thead>
|
||||
<tr style="background-color: #f2f2f2">
|
||||
<th style="width: 100px; vertical-align: middle;">任务属性</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">任务编号</th>
|
||||
<th style="width: 200px; text-align:center; vertical-align: middle;">任务名称</th>
|
||||
<th style="text-align:center; vertical-align: middle;">任务来源</th>
|
||||
<th style="width: 80px;text-align:center; vertical-align: middle;">目标值</th>
|
||||
<th style="width: 140px; text-align:center; vertical-align: middle;">任务属性</th>
|
||||
<th style="width: 60px; text-align:center; vertical-align: middle;">任务编号</th>
|
||||
<th style="width: 220px; text-align:center; vertical-align: middle;">任务名称</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">任务来源</th>
|
||||
<th style="width: 120px;text-align:center; vertical-align: middle;">目标值</th>
|
||||
<th style="text-align:center; vertical-align: middle; padding: 0" colspan="4">
|
||||
<table class="table" style="margin: 0;" cellpadding="0">
|
||||
<tr style="background-color: #f2f2f2"><th colspan="4">任务显性目标及节点</th></tr>
|
||||
|
@ -62,40 +75,40 @@
|
|||
{# </thead>#}
|
||||
</table>
|
||||
</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">完成日期</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">完成日期</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">责任单位</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">负责人</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">负责人</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">主管领导</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">任务说明</th>
|
||||
<th style="width: 240px; text-align:center; vertical-align: middle;">任务说明</th>
|
||||
{# <th style="width: 40px">Label</th>#}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody style="font-size: 0.9rem">
|
||||
{% for task in tasks %}
|
||||
<tr style="background-color: white">
|
||||
<td>{{ task.task_property }}</td>
|
||||
<td style="text-align:center;">{{ task.task_property }}</td>
|
||||
<td style="text-align:center;">{{ task.task_id }}</td>
|
||||
<td>{{ task.task_topic }}</td>
|
||||
<td style="width: 160px">{{ task.task_origin }}</td>
|
||||
<td style="text-align:center;">{{ task.task_origin }}</td>
|
||||
<td>{{ task.aim_value }}</td>
|
||||
{% load taskfilter %}
|
||||
<td style="background-color: #BFE7A8; padding: 0">
|
||||
<textarea class="text-none-border" style="background-color: #BFE7A8;">{% for todo in task.related_task.all %}{{ todo|quarter_cate:'1' }} {% endfor %}</textarea>
|
||||
<textarea class="text-none-border" style="background-color: #BFE7A8;">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.1|safe }}{% endfor %}</textarea>
|
||||
</td>
|
||||
<td style="background-color: #F5CCB0; padding: 0">
|
||||
<textarea class="text-none-border" style="background-color: #F5CCB0">{% for todo in task.related_task.all %}{{ todo|quarter_cate:'2' }} {% endfor %}</textarea>
|
||||
<textarea class="text-none-border" style="background-color: #F5CCB0">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.2|safe }}{% endfor %}</textarea>
|
||||
</td>
|
||||
<td style="background-color: #F7E4A8; padding: 0">
|
||||
<textarea class="text-none-border" style="background-color: #F7E4A8">{% for todo in task.related_task.all %} {{ todo|quarter_cate:'3' }} {% endfor %}</textarea>
|
||||
<textarea class="text-none-border" style="background-color: #F7E4A8">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.3|safe }}{% endfor %}</textarea>
|
||||
</td>
|
||||
<td style="background-color: #83E1E3; padding: 0">
|
||||
<textarea class="text-none-border" style="background-color: #83E1E3">{% for todo in task.related_task.all %} {{ todo|quarter_cate:'4' }} {% endfor %}</textarea>
|
||||
<textarea class="text-none-border" style="background-color: #83E1E3">{% for todo in task.related_task.all %}{{ todo|quarter_cate:year_quarter.4|safe }}{% endfor %}</textarea>
|
||||
</td>
|
||||
<td>{{ task.deadline | date:"Y年m月" }}</td>
|
||||
<td style="text-align:center;">{{ task.deadline | date:"Y.m" }}</td>
|
||||
<td style="text-align:center;">{{ task.duty_group }}</td>
|
||||
<td style="text-align:center;">{{ task.principal }}</td>
|
||||
<td style="text-align:center;">{{ task.leader }}</td>
|
||||
<td><textarea>{{ task.task_note }}</textarea></td>
|
||||
<td style="text-align:center;">{{ task.leader|default_if_none:"" }}</td>
|
||||
<td><textarea style="overflow:hidden;width:100%;">{{ task.task_note }}</textarea></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -111,4 +124,44 @@
|
|||
<!-- /.content -->
|
||||
</div>
|
||||
<!-- /.content-wrapper -->
|
||||
{% endblock %}
|
||||
<script>
|
||||
function setHeight(element) {
|
||||
$(element).css({'height':'auto','overflow-y':'hidden'}).height(element.scrollHeight);
|
||||
}
|
||||
$('textarea').each(function () {
|
||||
setHeight(this);
|
||||
}).on('input', function () {
|
||||
setHeight(this);
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
|
||||
$('#unfold').click(function (element){
|
||||
var unfold = document.getElementById("unfold");
|
||||
var fold = document.getElementById("fold");
|
||||
unfold.style.display = "none";
|
||||
fold.style.display = "block";
|
||||
$(element).css({'height':'auto','overflow-y':'hidden'}).height(element.scrollHeight);
|
||||
$('textarea').each(function () {
|
||||
setHeight(this);
|
||||
}).on('input', function () {
|
||||
setHeight(this);
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
$('#fold').click(function (element){
|
||||
var unfold = document.getElementById("unfold");
|
||||
var fold = document.getElementById("fold");
|
||||
unfold.style.display = "block";
|
||||
fold.style.display = "none";
|
||||
location.reload();
|
||||
// TODO 暂时无法解决让textarea恢复原本高度的问题,使用刷新代替
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<div class="container">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0"> 我的任务 <small>Example 3.0</small></h1>
|
||||
<h1 class="m-0"> 我的任务 <small></small></h1>
|
||||
</div><!-- /.col -->
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
|
@ -26,26 +26,40 @@
|
|||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header bg-gray">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-text-width"></i>
|
||||
任务详情
|
||||
</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<div class="card-body">
|
||||
<div class="card-body bg-gray-light">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4">工作事项</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.todo_topic }}</dd>
|
||||
<dt class="col-sm-4">完成时间</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.deadline }}</dd>
|
||||
{# <dd class="col-sm-8 offset-sm-4">Donec id elit non mi porta gravida at eget metus.</dd>#}
|
||||
<dt class="col-sm-4">交付物</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.todo_note }}</dd>
|
||||
{# <dd class="col-sm-8 offset-sm-4">Donec id elit non mi porta gravida at eget metus.</dd>#}
|
||||
<dt class="col-sm-4">任务编号</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.task_id }}</dd>
|
||||
<dt class="col-sm-4">任务来源</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.task_origin }}</dd>
|
||||
<dt class="col-sm-4">责任单位</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.duty_group }}</dd>
|
||||
<dt class="col-sm-4">责任人</dt>
|
||||
<dd class="col-sm-8">Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo
|
||||
sit amet risus.
|
||||
</dd>
|
||||
<dt class="col-sm-4">承/督办人</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.main_executor }}</dd>
|
||||
<dt class="col-sm-4">协办人</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.sub_executor.all|join:', ' }}</dd>
|
||||
<dt class="col-sm-4">预计工作量</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.predict_work }}</dd>
|
||||
<dt class="col-sm-4">折算系数</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.evaluate_factor }}</dd>
|
||||
<dt class="col-sm-4">实际工作量</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.real_work }}</dd>
|
||||
<dt class="col-sm-4">成熟度</dt>
|
||||
<dd class="col-sm-8">{{ todo_detail.maturity }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<!-- /.card-body -->
|
||||
|
@ -55,16 +69,40 @@
|
|||
<div class="col-md-6">
|
||||
<!-- general form elements -->
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Quick Example</h3>
|
||||
<div class="card-header bg-gray">
|
||||
<h3 class="card-title">更新工作包</h3>
|
||||
</div>
|
||||
<!-- /.card-header -->
|
||||
<!-- form start -->
|
||||
<form action="" method="post">
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1">成熟度</label>
|
||||
{{ form.maturity }}
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1">成熟度</label>
|
||||
{{ form.maturity }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail1">实际工作量</label>
|
||||
{{ form.real_work }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail">协办人</label>
|
||||
{{ form.sub_executor }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label for="exampleInputEmail">折算系数</label>
|
||||
{{ form.evaluate_factor }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">完成情况说明</label>
|
||||
|
|
|
@ -61,17 +61,17 @@
|
|||
<h3 style="text-align: center; letter-spacing: 30px">我的承办</h3>
|
||||
<HR width="50%" color=#e2e4e6 SIZE=3>
|
||||
<tr>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">序号</th>
|
||||
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
|
||||
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">序号</th>
|
||||
<th style="width: 300px; text-align:center; vertical-align: middle;">工作事项</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="width: 240px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="width: 140px; text-align:center; vertical-align: middle;">承办单位</th>
|
||||
<th style="width: 200px; text-align:center; vertical-align: middle;">协办人</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
|
||||
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 320px; text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
|
||||
<th style="text-align:center; vertical-align: middle;"></th>
|
||||
{# <th style="width: 40px">Label</th>#}
|
||||
|
@ -80,10 +80,10 @@
|
|||
<tbody>
|
||||
{% for todo in my_todo %}
|
||||
<tr>
|
||||
<td style="text-align: center">{{ todo.id }}</td>
|
||||
<td style="text-align: center">{{ forloop.counter }}</td>
|
||||
<td>{{ todo.todo_topic }}</td>
|
||||
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
|
||||
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
|
||||
<td><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.duty_group }}</td>
|
||||
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
|
||||
<td style="text-align:center;">{{ todo.predict_work }}</td>
|
||||
|
@ -95,9 +95,9 @@
|
|||
</div>
|
||||
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
|
||||
</td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden; width: 100%" readonly="readonly">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
|
||||
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}">填报</a> </td>
|
||||
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}?next={{ current_path }}">填报</a> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
@ -112,18 +112,18 @@
|
|||
<h3 style="text-align: center; letter-spacing: 30px">我的协办</h3>
|
||||
<HR width="50%" color=#e2e4e6 SIZE=3>
|
||||
<tr>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">序号</th>
|
||||
<th style="width: 280px; text-align:center; vertical-align: middle;">工作事项</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="text-align:center; vertical-align: middle; width: 120px">承办单位</th>
|
||||
<th style="width: 120px;text-align:center; vertical-align: middle;">承/督办人</th>
|
||||
<th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>
|
||||
<th style="width: 70px; text-align:center; vertical-align: middle;">序号</th>
|
||||
<th style="width: 300px; text-align:center; vertical-align: middle;">工作事项</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">完成时间</th>
|
||||
<th style="width: 240px; text-align:center; vertical-align: middle;">工作要求及交付物</th>
|
||||
<th style="width: 140px; text-align:center; vertical-align: middle;">承办单位</th>
|
||||
<th style="width: 200px;text-align:center; vertical-align: middle;">承/督办人</th>
|
||||
{# <th style="width: 200px;text-align:center; vertical-align: middle;">协办人</th>#}
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量(pre)</th>
|
||||
<th style="width: 60px; text-align:center; vertical-align: middle;">折算系数</th>
|
||||
<th style="width: 80px; text-align:center; vertical-align: middle;">工作量</th>
|
||||
<th style="width: 100px; text-align:center; vertical-align: middle;">成熟度</th>
|
||||
<th style="text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 320px; text-align:center; vertical-align: middle;">完成情况</th>
|
||||
<th style="width: 120px; text-align:center; vertical-align: middle;">完成质量</th>
|
||||
<th style="text-align:center; vertical-align: middle;"></th>
|
||||
{# <th style="width: 40px">Label</th>#}
|
||||
|
@ -132,13 +132,13 @@
|
|||
<tbody>
|
||||
{% for todo in my_sub_todo %}
|
||||
<tr>
|
||||
<td style="text-align: center">{{ todo.id }}</td>
|
||||
<td style="text-align: center">{{ forloop.counter }}</td>
|
||||
<td>{{ todo.todo_topic }}</td>
|
||||
<td style="text-align: center">{{ todo.deadline | date:"m月d日" }}</td>
|
||||
<td style="width: 160px"><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
|
||||
<td><textarea style="width:100%;overflow:hidden">{{ todo.todo_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.duty_group }}</td>
|
||||
<td style="text-align: center">{{ todo.main_executor.all|join:", " }}</td>
|
||||
<td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>
|
||||
<td style="text-align: center">{{ todo.main_executor }}</td>
|
||||
{# <td style="text-align: center">{{ todo.sub_executor.all|join:", " }}</td>#}
|
||||
<td style="text-align:center;">{{ todo.predict_work }}</td>
|
||||
<td style="text-align:center;">{{ todo.evaluate_factor }}</td>
|
||||
<td style="text-align:center;">{{ todo.real_work|default:'' }}</td>
|
||||
|
@ -148,9 +148,9 @@
|
|||
</div>
|
||||
<span class="badge bg-gradient-blue">{{ todo.maturity }}</span>
|
||||
</td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden; width: 300px">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center"><textarea style="overflow:hidden; width:100%" readonly="readonly">{{ todo.complete_note }}</textarea></td>
|
||||
<td style="text-align: center">{{ todo.quality_mark|default:'' }}</td>
|
||||
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}">填报</a> </td>
|
||||
<td><a href="{% url 'tasks:todo_detail' pk=todo.id %}?next={{ current_path }}">填报</a> </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
Loading…
Reference in New Issue