质量清单

This commit is contained in:
shilixia 2021-03-12 09:34:52 +08:00
parent ad4e61fee3
commit 3e7febf4c4
8 changed files with 447 additions and 7 deletions

44
client/src/api/content.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
export function getContentList(query) {
return request({
url: '/supervision/content/',
method: 'get',
params: query
})
}
export function getContent(id) {
return request({
url: `/supervision/content/${id}/`,
method: 'get'
})
}
export function createContent(data) {
return request({
url: '/supervision/content/',
method: 'post',
data
})
}
export function updateContent(id, data) {
return request({
url: `/supervision/content/${id}/`,
method: 'put',
data
})
}
export function deleteContent(id, data) {
return request({
url: `/supervision/content/${id}/`,
method: 'delete',
data
})
}

44
client/src/api/task.js Normal file
View File

@ -0,0 +1,44 @@
import request from '@/utils/request'
export function getTaskList(query) {
return request({
url: '/supervision/task/',
method: 'get',
params: query
})
}
export function getTask(id) {
return request({
url: `/supervision/task/${id}/`,
method: 'get'
})
}
export function createTask(data) {
return request({
url: '/supervision/task/',
method: 'post',
data
})
}
export function updateTask(id, data) {
return request({
url: `/supervision/task/${id}/`,
method: 'put',
data
})
}
export function deleteTask(id, data) {
return request({
url: `/supervision/task/${id}/`,
method: 'delete',
data
})
}

View File

@ -106,6 +106,27 @@ export const asyncRoutes = [
meta: { title: '检验能力', icon: 'table', perms: ['inspection_view'] } meta: { title: '检验能力', icon: 'table', perms: ['inspection_view'] }
}] }]
}, },
{
path: '/supervision',
component: Layout,
redirect: '/supervision/content',
name:'Supervision',
meta: { title: '日常监督', icon: 'table', perms: ['supervision_manage'] },
children: [
{
path: 'content',
name: 'Content',
component: () => import('@/views/supervision/content.vue'),
meta: { title: '资料清单', icon: 'documentation', perms: ['content_manage'] }
},
{
path: 'organization',
name: 'Organization',
component: () => import('@/views/system/organization'),
meta: { title: '上报计划', icon: 'tree', perms: ['org_manage'] }
}]
},
{ {
path: '/system', path: '/system',

View File

@ -0,0 +1,267 @@
<template>
<div class="app-container">
<div style="margin-top: 10px">
<el-button type="primary" icon="el-icon-plus" @click="handleAddContent"
>新增</el-button
>
</div>
<el-table
v-loading="listLoading"
:data="contentList"
style="width: 100%; margin-top: 10px"
border
fit
stripe
highlight-current-row
max-height="600"
>
<el-table-column type="index" width="50" />
<el-table-column align="center" label="名称">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column align="center" label="详情">
<template slot-scope="scope">{{ scope.row.desc }}</template>
</el-table-column>
<el-table-column align="center" label="材料类型">
<template slot-scope="scope">{{ scope.row.type_ }}</template>
</el-table-column>
<el-table-column align="center" label="是否可主动报送">
<template slot-scope="scope"> <el-tag type="success" v-if="scope.row.can_doself"></el-tag>
<el-tag type="danger" v-else></el-tag></template>
</el-table-column>
<el-table-column
align="center"
label="操作"
width="200px"
fixed="right"
>
<template slot-scope="scope">
<el-button
:disabled="!checkPermission(['content_update'])"
type="primary"
size="small"
icon="el-icon-edit"
@click="handleEdit(scope)"
/>
<el-button
:disabled="!checkPermission(['content_delete'])"
type="danger"
size="small"
icon="el-icon-delete"
@click="handleDelete(scope)"
/>
</template>
</el-table-column>
</el-table>
<el-dialog
:visible.sync="dialogVisible"
:title="dialogType === 'edit' ? '编辑用户' : '新增用户'"
>
<el-form
ref="Form"
:model="Content"
label-width="80px"
label-position="right"
:rules="rule1"
>
<el-form-item label="名称" prop="name">
<el-input v-model="Content.name" placeholder="名称" />
</el-form-item>
<el-form-item label="材料类型" prop="type">
<treeselect
v-model="Content.type"
:disable-branch-nodes="true"
placeholder="请选择产品分类"
clearable
:style="{width: '100%'}"
:options="typeOptions"
:multiple="false"
></treeselect>
</el-form-item>
<el-form-item label="详情" prop="desc">
<el-input type="textarea" :rows="4" v-model="Content.desc" placeholder="详情" />
</el-form-item>
<el-form-item label="是否有效" prop="can_doself">
<el-switch v-model="Content.can_doself"></el-switch>
</el-form-item>
</el-form>
<div style="text-align: right">
<el-button type="danger" @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirm('Form')">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
}
.avatar {
width: 100px;
height: 100px;
display: block;
}
</style>
<script>
import {
getContentList,
createContent,
deleteContent,
updateContent
} from "@/api/content";
import { genTree } from "@/utils";
import checkPermission from "@/utils/permission";
import Pagination from "@/components/Pagination"; // secondary package based on el-pagination
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {getDictList, getDictTypeList} from "@/api/dict";
const defaultContent = {
name: "",
desc: "",
can_doself: false
};
export default {
components: { Pagination, Treeselect },
data() {
return {
Content: defaultContent,
typeOptions: [],
listLoading: true,
dialogVisible: false,
dialogType: "new",
rule1: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }]
},
filterOrgText: "",
treeLoding: false,
};
},
computed: {},
watch: {
filterOrgText(val) {
this.$refs.tree.filter(val);
},
},
created() {
this.getList();
this.getTypeAll();
},
methods: {
checkPermission,
filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
},
getList() {
this.listLoading = true;
getContentList().then((response) => {
if (response.data) {
this.contentList = response.data;
}
this.listLoading = false;
});
},
getTypeAll() {
getDictList({type__code:"data_type"}).then(res=>{
this.typeOptions = genTree(res.data)
this.defaultContent.type=this.typeOptions[0].id;
})
},
handleFilter() {
this.listQuery.page = 1;
this.getList();
},
handleAddContent() {
this.Content = Object.assign({}, defaultContent);
this.dialogType = "new";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleEdit(scope) {
this.Content = Object.assign({}, scope.row); // copy obj
this.dialogType = "edit";
this.dialogVisible = true;
this.$nextTick(() => {
this.$refs["Form"].clearValidate();
});
},
handleDelete(scope) {
this.$confirm("确认删除?", "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "error",
})
.then(async () => {
await deleteContent(scope.row.id);
this.getList();
this.$message.success("成功");
})
.catch((err) => {
console.error(err);
});
},
async confirm(form) {
this.$refs[form].validate((valid) => {
if (valid) {
const isEdit = this.dialogType === "edit";
if (isEdit) {
updateContent(this.Content.id, this.Content).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
} else {
createContent(this.Content).then((res) => {
if (res.code >= 200) {
this.getList();
this.dialogVisible = false;
this.$message.success("成功");
}
});
}
} else {
return false;
}
});
}
},
};
</script>

View File

@ -0,0 +1,56 @@
# Generated by Django 3.0.5 on 2021-03-11 08:58
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('system', '0008_auto_20210311_0919'),
('supervision', '0002_remove_content_belong_to'),
]
operations = [
migrations.AddField(
model_name='record',
name='files',
field=models.ManyToManyField(to='system.File', verbose_name='关联文件'),
),
migrations.AddField(
model_name='record',
name='updepart',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='system.Organization', verbose_name='上传部门'),
),
migrations.AddField(
model_name='record',
name='upuser',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL, verbose_name='上传人'),
),
migrations.CreateModel(
name='Task',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('name', models.CharField(max_length=100, verbose_name='名称')),
('is_do', models.BooleanField(default=False, verbose_name='是否自创任务')),
('complete_rate', models.IntegerField(default=0, verbose_name='完成度')),
('belong_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task_belong_to', to='system.Organization', verbose_name='所属部门')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='task_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='record',
name='task',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supervision.Task', verbose_name='关联任务'),
),
]

View File

@ -1,6 +1,6 @@
from django.db import models from django.db import models
from utils.model import BaseModel from utils.model import BaseModel
from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict from apps.system.models import CommonAModel, CommonBModel, Organization, User, Dict, File
# Create your models here. # Create your models here.
class Content(CommonAModel): class Content(CommonAModel):
@ -17,9 +17,11 @@ class Content(CommonAModel):
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
# class Task(CommonBModel): class Task(CommonBModel):
# name = models.CharField('名称', max_length=100) name = models.CharField('名称', max_length=100)
# pass is_do = models.BooleanField('是否自创任务', default=False)
complete_rate = models.IntegerField('完成度', default=0)
class Record(BaseModel): class Record(BaseModel):
@ -27,10 +29,13 @@ class Record(BaseModel):
上报记录 上报记录
""" """
content = models.ForeignKey(Content, verbose_name='材料内容', on_delete=models.DO_NOTHING) content = models.ForeignKey(Content, verbose_name='材料内容', on_delete=models.DO_NOTHING)
# task = models.ForeignKey(Task, verbose_name='关联任务', null=True, blank=True, on_delete=models.SET_NULL) task = models.ForeignKey(Task, verbose_name='关联任务', null=True, blank=True, on_delete=models.SET_NULL)
updepart = models.ForeignKey(Organization, verbose_name='上传部门', null=True, blank=True,on_delete=models.DO_NOTHING)
upuser = models.ForeignKey(User,verbose_name='上传人', null=True, blank=True,on_delete=models.DO_NOTHING)
is_lock = models.BooleanField('是否锁住', default=False) is_lock = models.BooleanField('是否锁住', default=False)
is_yes = models.BooleanField('是否适用', default=True) is_yes = models.BooleanField('是否适用', default=True)
note = models.TextField('说明') note = models.TextField('说明')
files = models.ManyToManyField(File, verbose_name="关联文件")
class Meta: class Meta:
verbose_name = '上报记录' verbose_name = '上报记录'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name

View File

@ -18,4 +18,4 @@ class ContentSerializer(serializers.ModelSerializer):
data = obj.type.name data = obj.type.name
if obj.type.pid: if obj.type.pid:
data = obj.type.pid.name + '/' + data data = obj.type.pid.name + '/' + data
return data return data

View File

@ -25,4 +25,7 @@ class ContentViewSet(CreateModelAMixin, ModelViewSet):
serializer_class = ContentSerializer serializer_class = ContentSerializer
search_fields = ['name', 'desc'] search_fields = ['name', 'desc']
filterset_fields = ['type'] filterset_fields = ['type']
ordering = ['type__sort', 'create_time'] ordering = ['type__sort', 'create_time']
def paginate_queryset(self, queryset):
return None