Initial commit
|
@ -0,0 +1,5 @@
|
|||
/.idea/
|
||||
/venv/
|
||||
/packagesdir/
|
||||
/db.sqlite3
|
||||
/identifier.sqlite
|
|
@ -0,0 +1,20 @@
|
|||
# TasksManager
|
||||
### Beta 版本的说明
|
||||
|
||||
Author: [面壁的雨](https://raiot.me)
|
||||
|
||||
某残念的练习之作,不断更新中。
|
||||
|
||||
请体谅~~一大堆Bug以及各种·逻辑混乱的代码~~
|
||||
|
||||
到时候会出一个文档的(或许
|
||||
|
||||
from 2020-7-13 to 2021-2-2
|
||||
|
||||
鬼知道为什么会用这么长时间
|
||||
|
||||
但无论如何,beta版本总算完成啦
|
||||
|
||||
#### 注意!
|
||||
|
||||
* 此版本仅为beta版本,请勿在生产环境使用
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
ASGI config for TasksManager project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TasksManager.settings')
|
||||
|
||||
application = get_asgi_application()
|
|
@ -0,0 +1,152 @@
|
|||
"""
|
||||
Django settings for TasksManager project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.1.5.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
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 = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'simpleui',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'apps.users',
|
||||
'apps.tasks',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'TasksManager.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'TasksManager.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = False
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static',
|
||||
]
|
||||
#STATIC_ROOT = os.path.join(BASE_DIR, "static/")
|
||||
|
||||
# 登录链接与登录后跳转路径
|
||||
LOGIN_URL = '/login'
|
||||
LOGIN_REDIRECT_URL = '/'
|
||||
|
||||
# 自定义用户模型
|
||||
AUTH_USER_MODEL = 'users.User'
|
||||
|
||||
|
||||
# SimpleUI 配置
|
||||
|
||||
# 离线模式
|
||||
SIMPLEUI_STATIC_OFFLINE = True
|
||||
|
||||
# 关闭首页模块以及信息采集模块
|
||||
SIMPLEUI_HOME_INFO = False
|
||||
SIMPLEUI_HOME_QUICK = True
|
||||
SIMPLEUI_HOME_ACTION = True
|
||||
SIMPLEUI_ANALYSIS = False
|
||||
SIMPLEUI_HOME_TITLE = '任务管理系统'
|
||||
# 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
|
|
@ -0,0 +1,26 @@
|
|||
"""TasksManager URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
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'任务进度管理系统'
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for TasksManager project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TasksManager.settings')
|
||||
|
||||
application = get_wsgi_application()
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'apps.tasks.apps.TasksConfig'
|
|
@ -0,0 +1,97 @@
|
|||
import re
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.html import format_html
|
||||
|
||||
from . import models
|
||||
from apps.users.models import TaskProperty
|
||||
|
||||
|
||||
class TaskAdmin(admin.ModelAdmin):
|
||||
|
||||
# def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
# if db_field.name == "related_task":
|
||||
# ori_path = request.path
|
||||
# f_id = re.sub("\D", "", ori_path)
|
||||
# try:
|
||||
# kwargs["queryset"] = models.Task.objects.get(id=f_id).related_task
|
||||
# return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
# except:
|
||||
# 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)
|
||||
|
||||
list_display = (
|
||||
'task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group', 'principal',
|
||||
'leader', 'task_note',
|
||||
)
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': (
|
||||
('task_property', 'task_id', 'task_topic', 'task_origin', 'aim_value', 'deadline', 'duty_group',
|
||||
'principal', 'leader'),
|
||||
'task_note', 'related_task', 'department'),
|
||||
}),
|
||||
)
|
||||
raw_id_fields = ("principal", "leader",)
|
||||
autocomplete_fields = ('related_task',)
|
||||
search_fields = ('related_task',)
|
||||
|
||||
|
||||
class TodoAdmin(admin.ModelAdmin):
|
||||
def sub_executor(self, obj):
|
||||
return ', '.join([a.real_name for a in obj.sub_executor.all()])
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': (
|
||||
'todo_topic', 'todo_note', 'deadline', 'duty_group', 'main_executor', 'sub_executor', 'predict_work',
|
||||
'evaluate_factor',
|
||||
)
|
||||
}),
|
||||
('完成情况', {
|
||||
'fields': (
|
||||
'real_work', 'complete_note', 'quality_mark', 'maturity'
|
||||
)
|
||||
})
|
||||
)
|
||||
list_display = (
|
||||
'todo_topic',
|
||||
'deadline',
|
||||
'task_id',
|
||||
'lined_task',
|
||||
'task_origin',
|
||||
'duty_department',
|
||||
'duty_group',
|
||||
'main_executor',
|
||||
# 'sub_executor',
|
||||
)
|
||||
list_filter = ('deadline', )
|
||||
list_display_links = ('todo_topic', 'deadline', )
|
||||
date_hierarchy = 'deadline'
|
||||
list_per_page = 20
|
||||
raw_id_fields = ("main_executor", "sub_executor")
|
||||
search_fields = ('todo_topic',)
|
||||
# ordering = ('task_id',)
|
||||
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.short_description = '任务编号'
|
||||
lined_task.admin_order_field = 'task__task_topic'
|
||||
lined_task.short_description = '任务名称'
|
||||
|
||||
|
||||
admin.site.register(models.Task, TaskAdmin)
|
||||
admin.site.register(models.Todo, TodoAdmin)
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class TasksConfig(AppConfig):
|
||||
name = 'apps.tasks'
|
||||
verbose_name = '任务管理'
|
|
@ -0,0 +1,14 @@
|
|||
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)
|
||||
|
||||
class TodoForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Todo
|
||||
fields = ['maturity', 'complete_note']
|
||||
labels ={'text': ''}
|
||||
widgets = {'row': '3'}
|
|
@ -0,0 +1,50 @@
|
|||
# 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'],
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,69 @@
|
|||
# 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='任务属性'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# 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='责任单位'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# 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='承办单位'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# 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='责任单位'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
# 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='承/督办人'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,20 @@
|
|||
# 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='质量评价'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
# 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': '工作包'},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,120 @@
|
|||
from django.db import models
|
||||
from apps.users.models import User, MyGroup, QualityMark, TaskProperty, Department
|
||||
|
||||
|
||||
class Todo(models.Model):
|
||||
todo_topic = models.CharField(
|
||||
verbose_name='工作事项',
|
||||
max_length=50
|
||||
)
|
||||
todo_note = models.CharField(
|
||||
verbose_name='工作要求及交付物',
|
||||
blank=True,
|
||||
max_length=100
|
||||
)
|
||||
# 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)
|
||||
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='完成时间')
|
||||
|
||||
def __str__(self):
|
||||
date = str(self.deadline)
|
||||
date = parse_ymd(date)
|
||||
return str(date) + self.todo_topic
|
||||
|
||||
class Meta:
|
||||
ordering = ['deadline']
|
||||
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循环直接查的
|
||||
|
||||
@property
|
||||
def task_id(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.task_id
|
||||
|
||||
def task_origin(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.task_origin
|
||||
def duty_department(self):
|
||||
tasks = Task.objects.filter(related_task=self)
|
||||
for task in tasks:
|
||||
return task.duty_group
|
||||
|
||||
@property
|
||||
def last_month_list(self):
|
||||
return self.deadline
|
||||
|
||||
@property
|
||||
def get_tatal_num(self):
|
||||
return self.objects.all().count()
|
||||
|
||||
|
||||
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_note = models.CharField(
|
||||
verbose_name='任务说明',
|
||||
max_length=100
|
||||
)
|
||||
task_origin = models.CharField(max_length=150, verbose_name='任务来源')
|
||||
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='目标值')
|
||||
# start_date = models.DateField(verbose_name='起始日期')
|
||||
deadline = models.DateField(verbose_name='完成时间')
|
||||
|
||||
def __str__(self):
|
||||
return self.task_topic
|
||||
|
||||
class Meta:
|
||||
ordering = ['deadline']
|
||||
verbose_name = '年度任务'
|
||||
verbose_name_plural = '年度任务'
|
||||
|
||||
|
||||
def parse_ymd(s):
|
||||
year_s, mon_s, day_s = s.split('-')
|
||||
date = mon_s + '月' + day_s + '日 '
|
||||
return date
|
||||
# https://www.cnblogs.com/chichung/p/9905835.html
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
from django import template
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(name='quarter_cate')
|
||||
def quarter_cate(value, quarter):
|
||||
month = value.deadline.strftime('%m')
|
||||
month = int(month)
|
||||
quarter = int(quarter)
|
||||
|
||||
if quarter == 1 and 1 <= month <= 3:
|
||||
return value
|
||||
|
||||
elif quarter == 2 and 4 <= month <= 6:
|
||||
return value
|
||||
|
||||
elif quarter == 3 and 7 <= month <= 9:
|
||||
return value
|
||||
|
||||
elif quarter == 4 and 10 <= month <= 12:
|
||||
return value
|
||||
|
||||
else:
|
||||
return ''
|
||||
|
||||
@register.filter(name='last_month')
|
||||
def last_month(value):
|
||||
curent_date = datetime.strptime(value, '%Y年%m月')
|
||||
last_date = curent_date - relativedelta(months=+1)
|
||||
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月')
|
||||
next_date = curent_date + relativedelta(months=+1)
|
||||
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')
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,15 @@
|
|||
from django.urls import path
|
||||
from apps.tasks import views
|
||||
|
||||
app_name = 'tasks'
|
||||
urlpatterns = [
|
||||
path('', views.IndexView.as_view(), name='index'),
|
||||
path('login/', views.UserLoginView.as_view(), name='login'),
|
||||
path('logout/', views.UserLogoutView.as_view(), name='logout'),
|
||||
path('todolist/', views.TodoListView.as_view(), name='todolist'),
|
||||
path('todolist/<int:year>/<int:month>/', views.TodoListView.as_view(), name='todolist_month'),
|
||||
path('group_todolist/', views.GroupTodoList.as_view(), name='group_todolist'),
|
||||
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'),
|
||||
]
|
|
@ -0,0 +1,80 @@
|
|||
from django.contrib import auth, messages
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
import django.utils.timezone as timezone
|
||||
from django.views import View
|
||||
# Create your views here.
|
||||
from apps.tasks.models import Todo, Task
|
||||
from apps.tasks.forms import TodoForm
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TodoListView(View):
|
||||
@method_decorator(login_required)
|
||||
def get(self, request, year=timezone.now().year, month=timezone.now().month):
|
||||
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}
|
||||
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)
|
||||
date = str(year) + '年' + str(month) + '月'
|
||||
context = {'group_todo': group_todo, 'date': date}
|
||||
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}
|
||||
return render(request, 'tasks/tasklist.html', context)
|
||||
|
||||
class UserLoginView(View):
|
||||
def get(self, request):
|
||||
return render(request, 'tasks/login.html')
|
||||
|
||||
def post(self, request):
|
||||
username = request.POST.get('username')
|
||||
password = request.POST.get('password')
|
||||
user = auth.authenticate(username=username, password=password)
|
||||
if user:
|
||||
auth.login(request, user)
|
||||
messages.success(request, 'hh')
|
||||
return redirect('tasks:index')
|
||||
else:
|
||||
return redirect('tasks:index')
|
||||
|
||||
class UserLogoutView(View):
|
||||
def get(self, request):
|
||||
logout(request)
|
||||
return redirect('tasks:index')
|
||||
|
||||
|
||||
class TodoEntryView(View):
|
||||
def get(self, request, pk):
|
||||
todo_detail = Todo.objects.get(id=pk)
|
||||
form = TodoForm(instance=todo_detail)
|
||||
context = {'todo_detail': todo_detail, 'form': form}
|
||||
return render(request, 'tasks/todo.html', context)
|
||||
|
||||
def post(self, request, pk):
|
||||
todo_detail = Todo.objects.get(id=pk)
|
||||
form = TodoForm(instance=todo_detail, data=request.POST)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect('tasks:index')
|
||||
# return redirect('tasks:todo_detail', pk=pk)
|
|
@ -0,0 +1 @@
|
|||
default_app_config = 'apps.users.apps.UsersConfig'
|
|
@ -0,0 +1,46 @@
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.admin import UserAdmin, GroupAdmin
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class MyUserAdmin(UserAdmin):
|
||||
# @staticmethod
|
||||
# def group_list(self):
|
||||
# return ', '.join([a.name for a in self.groups.all()])
|
||||
# group_list.short_description = '部门/组'
|
||||
|
||||
list_display = ('username', 'real_name', 'staff_id', 'department')
|
||||
fieldsets = (
|
||||
(None, {'fields': ('username', 'password', 'real_name', 'staff_id', 'department')}),
|
||||
('权限', {
|
||||
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups'),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
class MyGroupAdmin(GroupAdmin):
|
||||
pass
|
||||
|
||||
class DepartmentAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
class TaskPropertyAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'task_property', 'own_department'
|
||||
)
|
||||
|
||||
class QualityMarkAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'mark_name',
|
||||
'mark_value'
|
||||
)
|
||||
|
||||
admin.site.register(models.User, MyUserAdmin)
|
||||
admin.site.register(models.MyGroup, MyGroupAdmin)
|
||||
admin.site.register(models.MarkValue)
|
||||
admin.site.register(models.Department)
|
||||
admin.site.register(models.QualityMark, QualityMarkAdmin)
|
||||
admin.site.register(models.TaskProperty, TaskPropertyAdmin)
|
||||
admin.site.unregister(Group)
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'apps.users'
|
||||
verbose_name = '系统配置'
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
# 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()),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
# 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,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# 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='所属部门'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,24 @@
|
|||
# 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'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# 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='评分名称'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# 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='评价等级定义'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,66 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, Group
|
||||
|
||||
|
||||
# Create your models here.
|
||||
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)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '用户'
|
||||
verbose_name_plural = '用户'
|
||||
|
||||
def __str__(self):
|
||||
return self.real_name
|
||||
|
||||
|
||||
class MyGroup(Group):
|
||||
class Meta:
|
||||
verbose_name = '权限组'
|
||||
verbose_name_plural = '权限组'
|
||||
|
||||
|
||||
class Department(models.Model):
|
||||
name = models.CharField(max_length=50, verbose_name='部门名称')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = '部门'
|
||||
verbose_name_plural = '部门'
|
||||
|
||||
|
||||
class MarkValue(models.Model):
|
||||
mark_value = models.DecimalField('评价等级考核系数', max_digits=3, decimal_places=2)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.mark_value)
|
||||
class Meta:
|
||||
verbose_name = '评价等级考核系数'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
class QualityMark(models.Model):
|
||||
mark_name = models.CharField('评价等级定义', max_length=10)
|
||||
mark_value = models.ForeignKey('MarkValue', on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return self.mark_name
|
||||
class Meta:
|
||||
verbose_name = '评价等级定义'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
|
||||
class TaskProperty(models.Model):
|
||||
own_department = models.ForeignKey('Department', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='所属部门')
|
||||
task_property = models.CharField(max_length=50, verbose_name='任务属性')
|
||||
|
||||
class Meta:
|
||||
verbose_name = '任务属性'
|
||||
verbose_name_plural = '任务属性'
|
||||
|
||||
def __str__(self):
|
||||
return self.task_property
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TasksManager.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,7 @@
|
|||
asgiref==3.3.1
|
||||
Django==3.1.5
|
||||
django-simpleui==2021.1.1
|
||||
python-dateutil==2.8.1
|
||||
pytz==2020.5
|
||||
six==1.15.0
|
||||
sqlparse==0.4.1
|
|
@ -0,0 +1,643 @@
|
|||
/*!
|
||||
* AdminLTE v3.1.0-rc
|
||||
* Only Pages
|
||||
* Author: Colorlib
|
||||
* Website: AdminLTE.io <https://adminlte.io>
|
||||
* License: Open source - MIT <https://opensource.org/licenses/MIT>
|
||||
*/
|
||||
.close, .mailbox-attachment-close {
|
||||
float: right;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
color: #000;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.close:hover, .mailbox-attachment-close:hover {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.close:not(:disabled):not(.disabled):hover, .mailbox-attachment-close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus, .mailbox-attachment-close:not(:disabled):not(.disabled):focus {
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
button.close, button.mailbox-attachment-close {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a.close.disabled, a.disabled.mailbox-attachment-close {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mailbox-messages > .table {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mailbox-controls {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.mailbox-controls.with-border {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.mailbox-read-info {
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.125);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mailbox-read-info h3 {
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mailbox-read-info h5 {
|
||||
margin: 0;
|
||||
padding: 5px 0 0;
|
||||
}
|
||||
|
||||
.mailbox-read-time {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.mailbox-read-message {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mailbox-attachments {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.mailbox-attachments li {
|
||||
border: 1px solid #eee;
|
||||
float: left;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 10px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.mailbox-attachment-name {
|
||||
color: #666;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mailbox-attachment-icon,
|
||||
.mailbox-attachment-info,
|
||||
.mailbox-attachment-size {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mailbox-attachment-info {
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mailbox-attachment-size {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mailbox-attachment-size > span {
|
||||
display: inline-block;
|
||||
padding-top: .75rem;
|
||||
}
|
||||
|
||||
.mailbox-attachment-icon {
|
||||
color: #666;
|
||||
font-size: 65px;
|
||||
max-height: 132.5px;
|
||||
padding: 20px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mailbox-attachment-icon.has-img {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mailbox-attachment-icon.has-img > img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.lockscreen {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.lockscreen .lockscreen-name {
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lockscreen-logo {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lockscreen-logo a {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.lockscreen-wrapper {
|
||||
margin: 0 auto;
|
||||
margin-top: 10%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.lockscreen-item {
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
margin: 10px auto 30px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
width: 290px;
|
||||
}
|
||||
|
||||
.lockscreen-image {
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
left: -10px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.lockscreen-image > img {
|
||||
border-radius: 50%;
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.lockscreen-credentials {
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.lockscreen-credentials .form-control {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.lockscreen-credentials .btn {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.lockscreen-footer {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dark-mode .lockscreen-item {
|
||||
background-color: #343a40;
|
||||
}
|
||||
|
||||
.dark-mode .lockscreen-logo a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-mode .lockscreen-credentials .btn {
|
||||
background-color: #343a40;
|
||||
}
|
||||
|
||||
.dark-mode .lockscreen-image {
|
||||
background-color: #6c757d;
|
||||
}
|
||||
|
||||
.login-logo,
|
||||
.register-logo {
|
||||
font-size: 2.1rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: .9rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-logo a,
|
||||
.register-logo a {
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.login-page,
|
||||
.register-page {
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
background-color: #e9ecef;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-box,
|
||||
.register-box {
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.login-box,
|
||||
.register-box {
|
||||
margin-top: .5rem;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.login-box .card,
|
||||
.register-box .card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.login-card-body,
|
||||
.register-card-body {
|
||||
background-color: #fff;
|
||||
border-top: 0;
|
||||
color: #666;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control,
|
||||
.register-card-body .input-group .form-control {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control:focus,
|
||||
.register-card-body .input-group .form-control:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,
|
||||
.login-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text,
|
||||
.register-card-body .input-group .form-control:focus ~ .input-group-prepend .input-group-text,
|
||||
.register-card-body .input-group .form-control:focus ~ .input-group-append .input-group-text {
|
||||
border-color: #80bdff;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control.is-valid:focus,
|
||||
.register-card-body .input-group .form-control.is-valid:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,
|
||||
.login-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text,
|
||||
.register-card-body .input-group .form-control.is-valid ~ .input-group-prepend .input-group-text,
|
||||
.register-card-body .input-group .form-control.is-valid ~ .input-group-append .input-group-text {
|
||||
border-color: #28a745;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control.is-invalid:focus,
|
||||
.register-card-body .input-group .form-control.is-invalid:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text,
|
||||
.register-card-body .input-group .form-control.is-invalid ~ .input-group-append .input-group-text {
|
||||
border-color: #dc3545;
|
||||
}
|
||||
|
||||
.login-card-body .input-group .input-group-text,
|
||||
.register-card-body .input-group .input-group-text {
|
||||
background-color: transparent;
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
border-left: 0;
|
||||
border-top-right-radius: 0.25rem;
|
||||
color: #777;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.login-box-msg,
|
||||
.register-box-msg {
|
||||
margin: 0;
|
||||
padding: 0 20px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.social-auth-links {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.dark-mode .login-card-body,
|
||||
.dark-mode .register-card-body {
|
||||
background-color: #343a40;
|
||||
border-color: #6c757d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-mode .login-logo a,
|
||||
.dark-mode .register-logo a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.error-page {
|
||||
margin: 20px auto 0;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.error-page {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.error-page > .headline {
|
||||
float: left;
|
||||
font-size: 100px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.error-page > .headline {
|
||||
float: none;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.error-page > .error-content {
|
||||
display: block;
|
||||
margin-left: 190px;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.error-page > .error-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.error-page > .error-content > h3 {
|
||||
font-size: 25px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.error-page > .error-content > h3 {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.invoice {
|
||||
background-color: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.invoice-title {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.dark-mode .invoice {
|
||||
background-color: #343a40;
|
||||
}
|
||||
|
||||
.profile-user-img {
|
||||
border: 3px solid #adb5bd;
|
||||
margin: 0 auto;
|
||||
padding: 3px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.profile-username {
|
||||
font-size: 21px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.post {
|
||||
border-bottom: 1px solid #adb5bd;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.post:last-of-type {
|
||||
border-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.post .user-block {
|
||||
margin-bottom: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.post .row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dark-mode .post {
|
||||
color: #fff;
|
||||
border-color: #6c757d;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.product-image-thumbs {
|
||||
-ms-flex-align: stretch;
|
||||
align-items: stretch;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.product-image-thumb {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
|
||||
border-radius: 0.25rem;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
margin-right: 1rem;
|
||||
max-width: 7rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.product-image-thumb img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.product-image-thumb:hover {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.product-share a {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.projects td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.projects .list-inline {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.projects img.table-avatar,
|
||||
.projects .table-avatar img {
|
||||
border-radius: 50%;
|
||||
display: inline;
|
||||
width: 2.5rem;
|
||||
}
|
||||
|
||||
.projects .project-state {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body.iframe-mode .main-sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.iframe-mode .content-wrapper {
|
||||
margin-left: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
body.iframe-mode .main-header,
|
||||
body.iframe-mode .main-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.iframe-mode-fullscreen {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .navbar-nav {
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .navbar-nav .nav-link {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .tab-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .tab-empty {
|
||||
width: 100%;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .tab-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
display: none;
|
||||
background-color: #f4f6f9;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode .tab-loading > div {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.content-wrapper.iframe-mode iframe .content-wrapper {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
body.iframe-mode-fullscreen .content-wrapper.iframe-mode {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin-left: 0 !important;
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
z-index: 1048;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .content {
|
||||
height: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .content .container,
|
||||
.content-wrapper.kanban .content .container-fluid {
|
||||
width: -webkit-max-content;
|
||||
width: -moz-max-content;
|
||||
width: max-content;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-align: stretch;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .content-header + .content {
|
||||
height: calc(100% - ((2 * 15px) + (1.8rem * 1.2)));
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card .card-body {
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card.card-row {
|
||||
width: 340px;
|
||||
display: inline-block;
|
||||
margin: 0 .5rem;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card.card-row:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card.card-row .card-body {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card.card-row .card .card-header {
|
||||
padding: .5rem .75rem;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .card.card-row .card .card-body {
|
||||
padding: .75rem;
|
||||
}
|
||||
|
||||
.content-wrapper.kanban .btn-tool.btn-link {
|
||||
text-decoration: underline;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
/*# sourceMappingURL=adminlte.pages.css.map */
|
|
@ -0,0 +1,251 @@
|
|||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7qsDJT9g.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: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7jsDJT9g.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7rsDJT9g.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7ksDJT9g.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7osDJT9g.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7psDJT9g.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: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDI.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;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmhduz8A.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwkxduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmxduz8A.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlBduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmBduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwmRduz8A.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdu.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;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNa7lqDY.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qPK7lqDY.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNK7lqDY.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qO67lqDY.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qN67lqDY.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qNq7lqDY.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.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;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmhduz8A.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwkxduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmxduz8A.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlBduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmBduz8A.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwmRduz8A.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: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: fallback;
|
||||
src: url(https://fonts.gstatic.com/s/sourcesanspro/v14/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.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;}
|
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 339 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 647 KiB |
After Width: | Height: | Size: 413 KiB |
After Width: | Height: | Size: 362 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 4.9 KiB |