ceshi
This commit is contained in:
commit
60f726cda6
|
@ -28,6 +28,7 @@
|
|||
"file-saver": "^2.0.2",
|
||||
"fuse.js": "^6.4.6",
|
||||
"js-cookie": "^3.0.0",
|
||||
"mammoth": "^1.4.19",
|
||||
"normalize.css": "^8.0.1",
|
||||
"nprogress": "0.2.0",
|
||||
"path-to-regexp": "^6.2.0",
|
||||
|
@ -37,7 +38,7 @@
|
|||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.5.2",
|
||||
"vuex": "^3.6.2",
|
||||
"webpack-dev-server": "^4.2.0",
|
||||
"webpack-dev-server": "^4.7.3",
|
||||
"xlsx": "^0.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import request from '@/utils/request'
|
||||
export function faceLogin(data) {
|
||||
return request({
|
||||
url: '/hrm/facelogin/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
export function clockRecord(data) {
|
||||
return request({
|
||||
url: '/hrm/clock_record/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
|
@ -2,7 +2,7 @@ import request from '@/utils/request'
|
|||
|
||||
export function login(data) {
|
||||
return request({
|
||||
url: '/token/',
|
||||
url: '/auth/token/',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
|
@ -10,7 +10,7 @@ export function login(data) {
|
|||
|
||||
export function logout() {
|
||||
return request({
|
||||
url: '/token/black/',
|
||||
url: '/auth/token/black/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -299,6 +299,18 @@
|
|||
}
|
||||
} ,
|
||||
},
|
||||
watch: {
|
||||
'$route.path': function (newVal) {
|
||||
if(newVal==='/dashboard'){
|
||||
this.$refs.leftMenu.handlerSelect(this.list[0]);
|
||||
this.$refs.chart.scrollTo({
|
||||
top: 0,
|
||||
left: this.list[0].left - 30,
|
||||
behavior: "smooth"
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
windowWidth:0,
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {faceLogin} from "@/api/testModel";
|
||||
import { getTickets } from "@/api/workflow";
|
||||
import { clockRecord } from '@/api/hrm'
|
||||
import tracking from '@/assets/tracking/build/tracking-min.js';
|
||||
import '@/assets/tracking/build/data/face-min.js';
|
||||
|
||||
|
@ -62,7 +61,7 @@
|
|||
// 避免在新的浏览器中使用它,因为它正在被弃用。
|
||||
_this.video.src = window.URL.createObjectURL(stream)
|
||||
}
|
||||
_this.video.onloadedmetadata = function (e) {
|
||||
_this.video.onloadedmetadata = function () {
|
||||
_this.video.play();
|
||||
};
|
||||
_this.init();
|
||||
|
@ -117,33 +116,12 @@
|
|||
let base64Img = canvas.toDataURL('image/jpeg');
|
||||
let img = base64Img.split(',')[1];
|
||||
let imgData = {base64:img};
|
||||
faceLogin(imgData).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
/* debugger;
|
||||
console.log(res.data);
|
||||
getTickets( {token:res.data.access,page:1,page_size:10}).then((resp)=>{
|
||||
if(resp.code>=200){
|
||||
debugger;
|
||||
console.log(resp.data);
|
||||
}
|
||||
})*/
|
||||
clockRecord(imgData).then((res) => {
|
||||
if (res.code === 200&&res.data.id) {
|
||||
this.$message.success(res.data.name+'签到成功!');
|
||||
setTimeout(()=>{
|
||||
that.uploadLock = false;
|
||||
this.$message.success(res.data.username+'签到成功!');
|
||||
/*that.$confirm("是否提交?", "提示", {
|
||||
confirmButtonText: "确认",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
// that.$emit('func',false);
|
||||
// 请求接口成功以后打开锁
|
||||
that.uploadLock = false;
|
||||
}).catch((err) => {
|
||||
that.$message.success(err);
|
||||
// that.$emit('func',false);
|
||||
// 请求接口成功以后打开锁
|
||||
that.uploadLock = false;
|
||||
});*/
|
||||
// this.$message.success("成功");
|
||||
},3000)
|
||||
}else{
|
||||
// 打开锁
|
||||
that.uploadLock = false;
|
||||
|
|
|
@ -50,7 +50,7 @@ export const constantRoutes = [
|
|||
path: 'dashboard',
|
||||
name: 'Dashboard',
|
||||
component: () => import('@/views/dashboard/index'),
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||
meta: { title: '首页', icon: 'dashboard', affix: true,keepAlive: false }
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
<span class="totalCount">{{contractTotalCurrent}}</span>
|
||||
<span>个</span>
|
||||
</div>
|
||||
<!--<div style="height: 35px;line-height: 35px;font-size: 16px;color: #333333">
|
||||
<span>本年合同总数:</span>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="cardsWrap" @click="toDetail('2')">
|
||||
|
@ -141,59 +144,134 @@
|
|||
</el-row>
|
||||
</div>
|
||||
<el-row :gutter="5">
|
||||
<el-col :span="12">
|
||||
<el-col :span="8">
|
||||
<div class="dashboardBottomRow">
|
||||
<div class="dashboardCardHand">
|
||||
<div class="CardTitleWrap">
|
||||
<span class="verticalLine"></span><span class="dashboardCardTitle">库存列表</span>
|
||||
<span class="stockMore" @click="stockMore">更多>></span>
|
||||
<span class="verticalLine"></span><span class="dashboardCardTitle">生产设备</span>
|
||||
</div>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
:current-page.sync="stockPage"
|
||||
:page-size="stockPageSize"
|
||||
small
|
||||
:pager-count="pageCounts"
|
||||
:current-page.sync="equipmentPage"
|
||||
:page-size="equipmentPageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="stockTotal"
|
||||
@size-change="handleStockSizeChange"
|
||||
@current-change="handleStockCurrentChange"
|
||||
:total="equipmentTotal"
|
||||
@current-change="handleEquipmentCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboardCardPadding">
|
||||
<el-table
|
||||
v-loading="listLoadingStock"
|
||||
:data="stockList"
|
||||
small
|
||||
v-loading="listLoadingEm"
|
||||
:data="equipmentList"
|
||||
fit stripe
|
||||
size="mini"
|
||||
:height="cardTabelHeight"
|
||||
pager-count="3"
|
||||
style="border-top: 1px solid #f5f5f5;"
|
||||
>
|
||||
<el-table-column label="物料编号" prop="material_">
|
||||
<template slot-scope="scope">{{ scope.row.material_.number }}</template>
|
||||
<el-table-column label="序号" type="index" width="50">
|
||||
</el-table-column>
|
||||
<el-table-column label="物料名称" prop="material_" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.material_.name }}</template>
|
||||
<el-table-column label="设备编号" prop="number">
|
||||
</el-table-column>
|
||||
<el-table-column label="规格型号" prop="material_" show-overflow-tooltip>
|
||||
<template slot-scope="scope">{{ scope.row.material_.specification }}</template>
|
||||
<el-table-column label="设备名称" prop="name" show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column label="物料类型" prop="material_">
|
||||
<template slot-scope="scope">{{options[scope.row.material_.type]}}</template>
|
||||
<el-table-column label="设备状态" prop="material_">
|
||||
<!--type=1生产设备-->
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.type===1">
|
||||
<el-tag v-if="scope.row.state===40" type="danger">
|
||||
禁用
|
||||
</el-tag>
|
||||
<el-tag v-else type="success">
|
||||
合格
|
||||
</el-tag>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-tag v-if="scope.row.state===10" type="success">
|
||||
{{ state_[scope.row.state] }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.state===20">
|
||||
{{ state_[scope.row.state] }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.state===30" type="warning">
|
||||
{{ state_[scope.row.state] }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">
|
||||
{{ state_[scope.row.state] }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" prop="material_">
|
||||
<template slot-scope="scope">{{scope.row.material_.unit}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="库存" prop="count">
|
||||
</el-table-column>
|
||||
<el-table-column label="仓库" prop="warehouse_">
|
||||
<template slot-scope="scope">{{ scope.row.warehouse_.name }}</template>
|
||||
<el-table-column label="下次校准日期" prop="model">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.next_check_date" :type="setClass(scope.row.next_check_date)">
|
||||
{{scope.row.next_check_date}}
|
||||
</el-tag>
|
||||
<div v-else></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-col :span="8">
|
||||
<div class="dashboardBottomRow">
|
||||
<div class="dashboardCardHand">
|
||||
<div class="CardTitleWrap">
|
||||
<span class="verticalLine"></span><span class="dashboardCardTitle">人员到岗</span>
|
||||
</div>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
small
|
||||
:pager-count="pageCounts"
|
||||
:current-page.sync="userPage"
|
||||
:page-size="userPageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="userTotal"
|
||||
@current-change="handleUserCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="dashboardCardPadding"
|
||||
@mouseenter="stopScroll()"
|
||||
@mouseleave="toScroll()"
|
||||
>
|
||||
<el-table
|
||||
v-loading="listLoadingUser"
|
||||
:data="userList"
|
||||
fit stripe
|
||||
size="mini"
|
||||
:height="cardTabelHeight"
|
||||
style="border-top: 1px solid #f5f5f5;"
|
||||
>
|
||||
<el-table-column label="id" prop="id" width="50">
|
||||
</el-table-column>
|
||||
<el-table-column label="人员名称" prop="name" show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column label="到岗情况" prop="dept_name">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-if="scope.row.is_atwork" type="success">
|
||||
到岗
|
||||
</el-tag>
|
||||
<el-tag v-else type="danger">
|
||||
未到
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="部门" prop="dept_name">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="dashboardBottomRow">
|
||||
<div class="dashboardCardHand">
|
||||
<div class="CardTitleWrap">
|
||||
|
@ -201,12 +279,14 @@
|
|||
</div>
|
||||
<div class="block">
|
||||
<el-pagination
|
||||
:current-page.sync="remindPage"
|
||||
:page-size="remindPageSize"
|
||||
small
|
||||
:pager-count="pageCounts"
|
||||
:current-page.sync="warningPage"
|
||||
:page-size="warningPageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="remindTotal"
|
||||
@size-change="handleRemindSizeChange"
|
||||
@current-change="handleRemindCurrentChange"
|
||||
:total="warningTotal"
|
||||
@size-change="handleWarningSizeChange"
|
||||
@current-change="handleWarningCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
</div>
|
||||
|
@ -215,38 +295,31 @@
|
|||
<el-tabs type="card" :style="{height:cardTabelHeight+'px'}" v-model="activeName"
|
||||
@tab-click="activeNameClick">
|
||||
<el-tab-pane label="库存警告" name="库存警告">
|
||||
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()" @mouseleave="Up()">
|
||||
<li v-for="item in list" :key="item.id" class="listItem">
|
||||
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
|
||||
@mouseleave="Up()">
|
||||
<li class="listItem" v-for="item in warningList" :key="item.id">
|
||||
<div class="itemText">
|
||||
<span>{{item.name}}</span><span style="float: right">2021-12-30</span>
|
||||
<span>{{item.name}}({{item.unit}})</span><span>剩余{{item.count}},低于安全库存{{item.count_safe}}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="临近交货" name="临近交货">
|
||||
<ul class="lists">
|
||||
<li class="listItem">
|
||||
<div class="itemText">
|
||||
<span>某某批货临近交货日期</span><span>2021-12-20</span>
|
||||
</div>
|
||||
</li>
|
||||
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
|
||||
@mouseleave="Up()">
|
||||
<li class="listItem" v-for="item in warningList" :key="item.id">
|
||||
<div class="itemText">
|
||||
<span>{{item}}</span><span>2021-12-20</span>
|
||||
<span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}到交货日期</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="过期提醒" name="过期提醒">
|
||||
<ul class="lists">
|
||||
<li class="listItem">
|
||||
<div class="itemText">
|
||||
<span>某某批货临近过期</span><span>2021-11-20</span>
|
||||
</div>
|
||||
</li>
|
||||
<ul :style="{height:cardTabelHeight-47+'px'}" class="lists" :class="{anim:animate}" @mouseenter="Stop()"
|
||||
@mouseleave="Up()">
|
||||
<li class="listItem" v-for="item in warningList" :key="item.id">
|
||||
<div class="itemText">
|
||||
<span>{{item}}</span><span>2021-12-20</span>
|
||||
<span>{{item.name}}({{item.number}})</span><span>{{item.delivery_date}}到期</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -262,15 +335,14 @@
|
|||
<script>
|
||||
import echarts from 'echarts'
|
||||
import {mapGetters} from 'vuex';
|
||||
import {getPlanGantt} from "@/api/srm";
|
||||
import {getProcessYield} from "@/api/srm";
|
||||
import {getUserList} from "@/api/user";
|
||||
import {getMaterialList} from "@/api/mtm";
|
||||
import {getInventoryList} from "@/api/inm";
|
||||
import {getProductionplanList} from "@/api/pm";
|
||||
import {getmaterialbatchList} from "@/api/inm";
|
||||
import gantt from "@/components/Gantt/dashGantt";
|
||||
import {getpEquipmentList} from "@/api/equipment";
|
||||
import {getProcessYield ,getPlanGantt} from "@/api/srm";
|
||||
import {getContractList , getOrderList} from "@/api/sam";
|
||||
|
||||
export default {
|
||||
components: {gantt},
|
||||
name: 'Dashboard',
|
||||
|
@ -278,27 +350,32 @@
|
|||
return {
|
||||
animate: false,
|
||||
intNum: null,
|
||||
intNumUser: null,
|
||||
animateUser: false,
|
||||
chartColumn: null,
|
||||
week: null,
|
||||
currentTime: null,
|
||||
currentYear: null,
|
||||
currentMonth: null,
|
||||
currentDay: null,
|
||||
stockPage: 1,
|
||||
stockPageSize: 10,
|
||||
stockTotal: 100,
|
||||
remindPage: 1,
|
||||
remindPageSize: 20,
|
||||
remindTotal: 100,
|
||||
pageCounts: 5,
|
||||
userPage: 1,
|
||||
userPageSize: 10,
|
||||
userTotal: 0,
|
||||
equipmentPage: 1,
|
||||
equipmentPageSize: 10,
|
||||
equipmentTotal: 0,
|
||||
warningPage: 1,
|
||||
warningPageSize: 10,
|
||||
warningTotal: 0,
|
||||
tableIndex: null,
|
||||
chartIndex: null,
|
||||
tableDate: '2021-12',
|
||||
chartDate: [],
|
||||
proList: [],
|
||||
planList: [],
|
||||
stockList: [],
|
||||
remindList: [],
|
||||
userList: [],
|
||||
warningList: [],
|
||||
equipmentList: [],
|
||||
list: [
|
||||
{id: 1, name: 'HIehd91', card: '3337', sco: 'REF-32'},
|
||||
{id: 2, name: 'HIehd92', card: '3337', sco: 'REF-32'},
|
||||
|
@ -308,6 +385,12 @@
|
|||
{id: 6, name: 'HIehd96', card: '3337', sco: 'REF-32'},
|
||||
{id: 7, name: 'HIehd97', card: '3337', sco: 'REF-32'},
|
||||
],
|
||||
state_: {
|
||||
10: '完好',
|
||||
20: '限用',
|
||||
30: '在修',
|
||||
40: '禁用',
|
||||
},
|
||||
options: {
|
||||
"1": '成品',
|
||||
"2": '半成品',
|
||||
|
@ -330,7 +413,8 @@
|
|||
rderTotalCount: null,//累计交付产品总数
|
||||
derTotalCount: null,//累计不合格产品总数
|
||||
listLoadingPlan: false,
|
||||
listLoadingStock: false,
|
||||
listLoadingEm: false,
|
||||
listLoadingUser: false,
|
||||
cardTabelHeight: null,
|
||||
ganttHeight: 0,
|
||||
}
|
||||
|
@ -342,19 +426,48 @@
|
|||
'count'
|
||||
])
|
||||
},
|
||||
watch: {
|
||||
'$route.path': function (newVal) {
|
||||
if(newVal==='/dashboard') {
|
||||
this.getUserList();//用户列表
|
||||
this.getEquipmentList();//设备列表
|
||||
this.getGanttData();//甘特图数据
|
||||
this.getStatisticsData();//统计数据
|
||||
this.getNoticeData();//提醒列表
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setClass:function(check_date) {
|
||||
let obj = {};
|
||||
if(check_date!=null){
|
||||
let dat = new Date();
|
||||
let time = dat.getTime();
|
||||
let check = new Date(check_date).getTime();
|
||||
let timeDiffer = (check-time)/1000/60/60/24;
|
||||
if (4>timeDiffer&&timeDiffer>0) {
|
||||
obj = 'warning';
|
||||
}else if (timeDiffer<0) {
|
||||
obj = "danger";
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
},
|
||||
getNoticeData() {
|
||||
this.ScrollUp();
|
||||
},
|
||||
ScrollUp() {
|
||||
this.intNum = setInterval(() => {
|
||||
this.animate = true;
|
||||
let that = this;
|
||||
that.intNum = setInterval(() => {
|
||||
if(that.warningList.length>3){
|
||||
that.animate = true;
|
||||
setTimeout(() => {
|
||||
this.list.push(this.list[0]);
|
||||
this.list.shift();
|
||||
this.animate = false;
|
||||
}, 500)
|
||||
}, 1000);
|
||||
that.warningList.push(that.warningList[0]);
|
||||
that.warningList.shift();
|
||||
that.animate = false;
|
||||
}, 3000)
|
||||
}
|
||||
}, 3000);
|
||||
},
|
||||
//鼠标移上去停止
|
||||
Stop() {
|
||||
|
@ -433,12 +546,85 @@
|
|||
});
|
||||
|
||||
//获取库存警告
|
||||
getMaterialList({tag: 'low_inm'}).then((response) => {
|
||||
getMaterialList({page: 1, page_size:that.warningPageSize,tag: 'low_inm'}).then((response) => {
|
||||
if (response.data) {
|
||||
debugger;
|
||||
that.warningList = response.data.results;
|
||||
that.warningTotal = response.data.count;
|
||||
}
|
||||
});
|
||||
},
|
||||
//设备列表
|
||||
getEquipmentList() {
|
||||
let that = this;
|
||||
this.listLoadingEm = true;
|
||||
getpEquipmentList({page: that.equipmentPage, page_size: that.equipmentPageSize}).then((response) => {
|
||||
if (response.data) {
|
||||
that.equipmentList = response.data.results;
|
||||
that.equipmentTotal = response.data.count;
|
||||
}
|
||||
that.listLoadingEm = false;
|
||||
});
|
||||
},
|
||||
//设备分页跳转
|
||||
handleEquipmentCurrentChange(val) {
|
||||
let that = this;
|
||||
that.listLoadingEm = true;
|
||||
that.equipmentPage = val;
|
||||
getpEquipmentList({page: val, page_size: that.equipmentPageSize}).then((response) => {
|
||||
if (response.data) {
|
||||
that.equipmentList = response.data.results;
|
||||
that.equipmentTotal = response.data.count;
|
||||
}
|
||||
that.listLoadingEm = false;
|
||||
});
|
||||
},
|
||||
//用户列表
|
||||
getUserList() {
|
||||
let that = this;
|
||||
that.listLoadingUser = true;
|
||||
getUserList({page: that.userPage, page_size: that.userPageSize, fields: 'id,name,dept_name,is_atwork'}).then((response) => {
|
||||
if (response.data) {
|
||||
that.userList = response.data.results;
|
||||
that.userTotal = response.data.count;
|
||||
that.userScroll();
|
||||
}
|
||||
that.listLoadingUser = false;
|
||||
});
|
||||
},
|
||||
//用户分页跳转
|
||||
handleUserCurrentChange(val) {
|
||||
let that = this;
|
||||
that.listLoadingUser = true;
|
||||
that.userPage = val;
|
||||
getUserList({page: val, page_size: that.userPageSize, fields: 'id,name,dept_name,is_atwork'}).then((response) => {
|
||||
if (response.data) {
|
||||
that.userList = response.data.results;
|
||||
that.userTotal = response.data.count;
|
||||
that.userScroll();
|
||||
}
|
||||
that.listLoadingUser = false;
|
||||
});
|
||||
},
|
||||
userScroll() {
|
||||
let that = this;
|
||||
that.intNumUser = setInterval(() => {
|
||||
that.animateUser = true;
|
||||
setTimeout(() => {
|
||||
that.userList.push(that.userList[0]);
|
||||
that.userList.shift();
|
||||
that.animateUser = false;
|
||||
}, 3000)
|
||||
}, 3000);
|
||||
},
|
||||
//鼠标移上去停止
|
||||
stopScroll() {
|
||||
clearInterval(this.intNumUser);
|
||||
},
|
||||
toScroll() {
|
||||
this.userScroll();
|
||||
},
|
||||
//去往工作流页面
|
||||
gotoTicketPage() {
|
||||
let path = this.$route.path;
|
||||
if (path === '/workflow/ticket') {
|
||||
|
@ -448,7 +634,7 @@
|
|||
}
|
||||
},
|
||||
//任务排期列表
|
||||
getPlanList() {
|
||||
/*getPlanList() {
|
||||
let that = this;
|
||||
this.listLoadingPlan = true;
|
||||
getProductionplanList({page: 0}).then((response) => {
|
||||
|
@ -457,9 +643,9 @@
|
|||
}
|
||||
this.listLoadingPlan = false;
|
||||
});
|
||||
},
|
||||
},*/
|
||||
//库存列表
|
||||
getStockList() {
|
||||
/*getStockList() {
|
||||
let that = this;
|
||||
this.listLoadingStock = true;
|
||||
getInventoryList({page: this.stockPage, page_size: this.stockPageSize}).then((response) => {
|
||||
|
@ -470,6 +656,17 @@
|
|||
this.listLoadingStock = false;
|
||||
});
|
||||
},
|
||||
//更多库存
|
||||
stockMore() {
|
||||
this.$router.push({name: 'warehouse', params: {}})
|
||||
},
|
||||
//库存pageSize改变
|
||||
// @size-change="handleStockSizeChange"
|
||||
handleEquipmentSizeChange(val) {
|
||||
this.stockPageSize = val;
|
||||
this.stockPage = 1;
|
||||
},
|
||||
*/
|
||||
//图标渲染
|
||||
drawChart() {
|
||||
let that = this;
|
||||
|
@ -583,9 +780,7 @@
|
|||
}]
|
||||
});
|
||||
},
|
||||
stockMore() {
|
||||
this.$router.push({name: 'warehouse', params: {}})
|
||||
},
|
||||
|
||||
toDetail(index) {
|
||||
if (index === '1') {
|
||||
this.$router.push({name: 'contract', params: {page: 1, page_size: 20}})
|
||||
|
@ -594,9 +789,9 @@
|
|||
} else if (index === '3') {
|
||||
this.$router.push({name: 'management', params: {page: 1, page_size: 20}})
|
||||
} else if (index === '4') {
|
||||
this.$router.push({name: 'product', params: {page: 1, page_size: 20, material__type: 1}})
|
||||
this.$router.push({name: 'product'})
|
||||
} else if (index === '5') {
|
||||
this.$router.push({name: 'product', params: {page: 1, page_size: 20, material__type: 1}})
|
||||
this.$router.push({name: 'unproduct'})
|
||||
}
|
||||
},
|
||||
//便捷查询按钮
|
||||
|
@ -650,7 +845,6 @@
|
|||
this.drawChart();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
//根据时间和类型获取数据
|
||||
},
|
||||
|
@ -683,55 +877,41 @@
|
|||
});
|
||||
}
|
||||
},
|
||||
//库存
|
||||
handleStockSizeChange(val) {
|
||||
this.stockPageSize = val;
|
||||
this.stockPage = 1;
|
||||
},
|
||||
//库存
|
||||
handleStockCurrentChange(val) {
|
||||
let that = this;
|
||||
this.listLoading = true;
|
||||
this.stockPage = val;
|
||||
getInventoryList({page: val, page_size: this.stockPageSize}).then((response) => {
|
||||
if (response.data) {
|
||||
that.stockList = response.data.results;
|
||||
that.stockTotal = response.data.count;
|
||||
}
|
||||
this.listLoading = false;
|
||||
});
|
||||
},
|
||||
//提示
|
||||
activeNameClick(tab) {
|
||||
debugger;
|
||||
debugger;
|
||||
let that = this;
|
||||
that.warningPage = 1;
|
||||
that.warningList = [];
|
||||
if (tab.label === '库存警告') {
|
||||
getMaterialList({page: 0, tag: 'low_inm'}).then((response) => {
|
||||
getMaterialList({page: 1, page_size:that.warningPageSize, tag: 'low_inm'}).then((response) => {
|
||||
if (response.data) {
|
||||
that.warningList = response.data;
|
||||
that.remindTotal = response.data.length;
|
||||
that.warningList = response.data.results;
|
||||
that.warningTotal = response.data.count;
|
||||
}
|
||||
});
|
||||
} else if (tab.label === '临近交货') {
|
||||
getmaterialbatchList({page: 0, tag: 'expired'}).then((response) => {
|
||||
getOrderList({page: 1, page_size:that.warningPageSize,tag:'near_delivery'}).then((response) => {
|
||||
if (response.data) {
|
||||
that.warningList = response.data;
|
||||
that.remindTotal = response.data.length;
|
||||
that.warningList = response.data.results;
|
||||
that.warningTotal = response.data.count;
|
||||
}
|
||||
});
|
||||
} else if (tab.label === '过期提醒') {
|
||||
getmaterialbatchList({page: 0, tag: 'expired'}).then((response) => {
|
||||
getmaterialbatchList({page: 1, page_size:that.warningPageSize, tag: 'expired'}).then((response) => {
|
||||
if (response.data) {
|
||||
that.warningList = response.data;
|
||||
that.remindTotal = response.data.length;
|
||||
that.warningList = response.data.results;
|
||||
that.warningTotal = response.data.count;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleRemindSizeChange(val) {
|
||||
this.remindPageSize = val;
|
||||
this.remindPage = 1;
|
||||
handleWarningSizeChange(val) {
|
||||
this.warningPageSize = val;
|
||||
this.warningPage = 1;
|
||||
},
|
||||
handleRemindCurrentChange(val) {
|
||||
handleWarningCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`);
|
||||
},
|
||||
getGanttData() {
|
||||
|
@ -807,23 +987,21 @@
|
|||
})
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let hei = document.getElementsByClassName('app-main')[0].clientHeight;
|
||||
let heig = document.getElementsByClassName('dashboardTopCard')[0].clientHeight;
|
||||
this.cardTabelHeight = ((hei - heig - 130) / 2);
|
||||
this.ganttHeight = this.cardTabelHeight - 50;
|
||||
document.getElementById('chartColumn').style.height = this.cardTabelHeight - 35 + 'px';
|
||||
this.getPlanList();
|
||||
this.getStockList();
|
||||
this.getGanttData();
|
||||
this.getStatisticsData();
|
||||
this.getNoticeData();
|
||||
let domHeight = ((hei - heig - 140) / 2);
|
||||
this.cardTabelHeight = domHeight-35;
|
||||
this.ganttHeight = domHeight - 10;
|
||||
document.getElementById('chartColumn').style.height = domHeight + 'px';
|
||||
this.getUserList();//用户列表
|
||||
this.getEquipmentList();//设备列表
|
||||
this.getGanttData();//甘特图数据
|
||||
this.getStatisticsData();//统计数据
|
||||
this.getNoticeData();//提醒列表
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-card.is-always-shadow {
|
||||
height: auto !important;
|
||||
|
@ -867,6 +1045,7 @@
|
|||
.cardsWrap:nth-child(1) {
|
||||
.svgIconWrap {
|
||||
background: #e9f3ff;
|
||||
|
||||
.svgIcon {
|
||||
color: #409EFF;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "largeScreen"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -91,6 +91,14 @@
|
|||
<el-table-column label="检查类型">
|
||||
<template slot-scope="scope">{{ checkTypes[scope.row.type] }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人">
|
||||
<template slot-scope="scope">{{ scope.row.create_by_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{scope.row.update_time.substring(0,scope.row.update_time.length-3)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否提交">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.is_submited">已提交</span>
|
||||
|
@ -116,12 +124,30 @@
|
|||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-row>
|
||||
<el-col v-for="item in fieldList" :key="item.id" :span="12">
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作人:</span>
|
||||
<span>{{create_by_}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作时间:</span>
|
||||
<span>{{update_time}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-for="item in fieldList" :key="item.id" :span="12">
|
||||
<div class="items" v-if="item.field_type!=='draw'&&item.field_value!==null&&item.field_value!==''">
|
||||
<span class="itemLabel">{{item.field_name}}:</span>
|
||||
<span>{{item.field_value}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-for="item in fieldList" :key="item.id" :span="24">
|
||||
<div class="items" v-if="item.field_type==='draw'" style="height: 400px">
|
||||
<span class="itemLabel">{{item.field_name}}:</span>
|
||||
<img style="width: 45%;vertical-align: text-top;" :src="'http://47.95.0.242:2222'+item.field_value"/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</el-card>
|
||||
|
@ -171,6 +197,8 @@ export default {
|
|||
mtestform: {
|
||||
is_mtestok: false,
|
||||
},
|
||||
create_by_: '',
|
||||
update_time: '',
|
||||
wproduct: null,
|
||||
recordList: [],
|
||||
fieldList: [],
|
||||
|
@ -239,6 +267,8 @@ export default {
|
|||
handleRecordDetail(scope) {
|
||||
let that = this;
|
||||
that.fieldList = [];
|
||||
that.create_by_ = scope.row.create_by_.name;
|
||||
that.update_time = scope.row.update_time;
|
||||
getTestRecordItem(scope.row.id).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
debugger;
|
||||
|
|
|
@ -29,6 +29,16 @@
|
|||
{{checkTypes[scope.row.type]}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_by_.name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{scope.row.update_time.substring(0,scope.row.update_time.length-3)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否提交">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.is_submited">已提交</span>
|
||||
|
@ -48,8 +58,20 @@
|
|||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作人:</span>
|
||||
<span>{{create_by_}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作时间:</span>
|
||||
<span>{{update_time}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-for="item in fieldList" :key="item.id" :span="12">
|
||||
<div class="items" v-if="item.field_type!=='draw'">
|
||||
<div class="items" v-if="item.field_type!=='draw'&&item.field_value!==null&&item.field_value!==''">
|
||||
<span class="itemLabel">{{item.field_name}}:</span>
|
||||
<span>{{item.field_value}}</span>
|
||||
</div>
|
||||
|
@ -89,6 +111,8 @@
|
|||
50: '已完成',
|
||||
60: '军检完成'
|
||||
},
|
||||
create_by_:'',
|
||||
update_time:'',
|
||||
recordVisible: false,
|
||||
customfieldList: [],
|
||||
recordform: null,
|
||||
|
@ -140,6 +164,8 @@
|
|||
handleRecordDetail(scope) {
|
||||
let that = this;
|
||||
that.fieldList = [];
|
||||
that.create_by_ = scope.row.create_by_.name;
|
||||
that.update_time = scope.row.update_time;
|
||||
getTestRecordItem(scope.row.id).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
debugger;
|
||||
|
|
|
@ -474,6 +474,14 @@
|
|||
<el-table-column label="检查类型">
|
||||
<template slot-scope="scope">{{ checkTypes[scope.row.type] }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人">
|
||||
<template slot-scope="scope">{{ scope.row.create_by_.name }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作时间">
|
||||
<template slot-scope="scope">
|
||||
<span>{{scope.row.update_time.substring(0,scope.row.update_time.length-3)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否提交">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.is_submited">已提交</span>
|
||||
|
@ -620,8 +628,20 @@
|
|||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作人:</span>
|
||||
<span>{{create_by_}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="items">
|
||||
<span class="itemLabel">操作时间:</span>
|
||||
<span>{{update_time}}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col v-for="item in fieldList" :key="item.id" :span="12">
|
||||
<div class="items" v-if="item.field_type!=='draw'">
|
||||
<div class="items" v-if="item.field_type!=='draw'&&item.field_value!==null&&item.field_value!==''">
|
||||
<span class="itemLabel">{{item.field_name}}:</span>
|
||||
<span>{{item.field_value}}</span>
|
||||
</div>
|
||||
|
@ -693,6 +713,8 @@
|
|||
page: 1,
|
||||
page_size: 20,
|
||||
},
|
||||
create_by_: '',
|
||||
update_time: '',
|
||||
formLabelWidth: '',
|
||||
formLabelWidthL: '',
|
||||
actstate_: {
|
||||
|
@ -1109,6 +1131,8 @@
|
|||
handleRecordDetail(scope) {
|
||||
let that = this;
|
||||
that.fieldList = [];
|
||||
that.create_by_ = scope.row.create_by_.name;
|
||||
that.update_time = scope.row.update_time;
|
||||
getTestRecordItem(scope.row.id).then((res) => {
|
||||
if (res.code >= 200) {
|
||||
that.recordFinishedVisible = true;
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
from django.contrib import admin
|
||||
from rest_framework_simplejwt.views import (TokenObtainPairView,
|
||||
TokenRefreshView)
|
||||
# Register your models here.
|
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthConfig(AppConfig):
|
||||
name = 'apps.auth1'
|
||||
verbose_name = "认证"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -0,0 +1,5 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
class LoginSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(label="用户名")
|
||||
password = serializers.CharField(label="密码")
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,12 @@
|
|||
from django.urls import path
|
||||
from rest_framework_simplejwt.views import (TokenObtainPairView,
|
||||
TokenRefreshView)
|
||||
|
||||
from apps.auth1.views import LoginView, LogoutView, TokenBlackView
|
||||
urlpatterns = [
|
||||
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('token/black/', TokenBlackView.as_view(), name='token_black'),
|
||||
path('login/', LoginView.as_view(), name='api_login'),
|
||||
path('logout/', LogoutView.as_view(), name='api_logout')
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
from django.shortcuts import render
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from apps.auth1.serializers import LoginSerializer
|
||||
# Create your views here.
|
||||
|
||||
class TokenBlackView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request, *args, **kwargs): # 可将token加入黑名单
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
class LoginView(CreateAPIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
serializer_class = LoginSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
"""
|
||||
账户密码登录
|
||||
"""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
vdata = serializer.validated_data
|
||||
user = authenticate(username = vdata.get('username'),
|
||||
password = vdata.get('password'))
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
return Response()
|
||||
return Response('登录失败', status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
class LogoutView(APIView):
|
||||
authentication_classes = []
|
||||
permission_classes = []
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""
|
||||
退出登录
|
||||
"""
|
||||
logout(request)
|
||||
return Response()
|
|
@ -2,7 +2,7 @@ from django.db.models import base
|
|||
from rest_framework import urlpatterns
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg
|
||||
from apps.develop.views import CleanDataView, UpdateCuttingView, UpdateEquipState, UpdateFIFOItem, UpdateLastTestResult, UpdateSpg
|
||||
|
||||
urlpatterns = [
|
||||
path('cleandata/', CleanDataView.as_view()),
|
||||
|
@ -10,6 +10,7 @@ urlpatterns = [
|
|||
path('update_last_result/', UpdateLastTestResult.as_view()),
|
||||
path('update_last_result/', UpdateLastTestResult.as_view()),
|
||||
path('update_fifoitem/', UpdateFIFOItem.as_view()),
|
||||
path('update_spg/', UpdateSpg.as_view())
|
||||
path('update_spg/', UpdateSpg.as_view()),
|
||||
path('update_equip_state/', UpdateEquipState.as_view())
|
||||
]
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from apps.sam.models import Order
|
|||
from apps.wf.models import Ticket
|
||||
from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow
|
||||
from apps.wpm.services import WpmServies
|
||||
from apps.em.tasks import update_equip_state_by_next_check_date
|
||||
# Create your views here.
|
||||
|
||||
class CleanDataView(APIView):
|
||||
|
@ -91,3 +92,11 @@ class UpdateSpg(APIView):
|
|||
for i in SubProductionPlan.objects.filter(subproduction__process__id=1):
|
||||
WpmServies.update_subproduction_progress_main(sp=i)
|
||||
return Response()
|
||||
|
||||
|
||||
class UpdateEquipState(APIView):
|
||||
permission_classes = [IsAdminUser]
|
||||
|
||||
def post(self, request, format=None):
|
||||
update_equip_state_by_next_check_date()
|
||||
return Response()
|
|
@ -0,0 +1,13 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from celery import shared_task
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.em.models import Equipment
|
||||
|
||||
|
||||
@shared_task
|
||||
def update_equip_state_by_next_check_date():
|
||||
Equipment.objects.filter(next_check_date__lt=timezone.now()).update(
|
||||
state = Equipment.EQUIP_STATE_DISABLE
|
||||
)
|
|
@ -6,20 +6,9 @@ from apps.system.serializers import UserListSerializer, UserSimpleSerializer
|
|||
from django.db.models.query import Prefetch
|
||||
|
||||
class EmployeeSerializer(ModelSerializer):
|
||||
# user_ = UserListSerializer(source='user', read_only=True)
|
||||
class Meta:
|
||||
model = Employee
|
||||
fields = '__all__'
|
||||
# @staticmethod
|
||||
# def setup_eager_loading(queryset):
|
||||
# """ Perform necessary eager loading of data. """
|
||||
# queryset = queryset.select_related('user', 'user__dept')
|
||||
# # queryset = queryset.prefetch_related('user','user__dept')
|
||||
# queryset = queryset.prefetch_related(
|
||||
# Prefetch('user_',
|
||||
# queryset=User.objects.filter(employee_user__isnull=True))
|
||||
# )
|
||||
# return queryset
|
||||
exclude = ['face_data']
|
||||
|
||||
class FaceLoginSerializer(serializers.Serializer):
|
||||
base64 = serializers.CharField()
|
||||
|
|
|
@ -3,7 +3,9 @@ import uuid
|
|||
import face_recognition
|
||||
import os
|
||||
from apps.hrm.models import Employee
|
||||
from apps.hrm.tasks import update_all_user_facedata_cache
|
||||
from apps.system.models import User
|
||||
from django.core.cache import cache
|
||||
|
||||
class HRMService:
|
||||
|
||||
|
@ -22,18 +24,25 @@ class HRMService:
|
|||
return None, '头像解码失败'
|
||||
|
||||
# 匹配人脸库
|
||||
user_faces = Employee.objects.filter(face_data__isnull=False,
|
||||
user__is_active=True).values('user', 'face_data')
|
||||
user_l = []
|
||||
face_l = []
|
||||
for i in user_faces:
|
||||
user_l.append(i['user'])
|
||||
face_l.append(i['face_data'])
|
||||
face_datas = cache.get('face_datas')
|
||||
face_users = cache.get('face_users')
|
||||
if face_datas is None:
|
||||
update_all_user_facedata_cache()
|
||||
|
||||
results = face_recognition.compare_faces(face_l, unknown_face_encoding, tolerance=0.5)
|
||||
results = face_recognition.compare_faces(face_datas, unknown_face_encoding, tolerance=0.5)
|
||||
for index, value in enumerate(results):
|
||||
if value:
|
||||
# 识别成功
|
||||
user = User.objects.get(id=user_l[index])
|
||||
user = User.objects.get(id=face_users[index])
|
||||
return user, ''
|
||||
return None, '识别失败'
|
||||
|
||||
def get_facedata_from_img(cls, img_rpath):
|
||||
try:
|
||||
photo_path = settings.BASE_DIR + img_rpath
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
face_data_list = my_face_encoding.tolist()
|
||||
return face_data_list
|
||||
except:
|
||||
return None
|
|
@ -2,6 +2,10 @@ from django.db.models.signals import post_save
|
|||
from apps.system.models import User
|
||||
from django.dispatch import receiver
|
||||
from apps.hrm.models import Employee
|
||||
from django.conf import settings
|
||||
import face_recognition
|
||||
import logging
|
||||
logger = logging.getLogger('log')
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def createEmployee(sender, instance, created, **kwargs):
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from celery import shared_task
|
||||
from apps.hrm.models import Employee
|
||||
from apps.system.models import User
|
||||
from django.core.cache import cache
|
||||
|
||||
|
||||
@shared_task
|
||||
def x():
|
||||
print('ok')
|
||||
def update_all_user_not_atwork():
|
||||
"""
|
||||
将所有员工设为非在岗状态
|
||||
"""
|
||||
User.objects.all().update(is_atwork=False)
|
||||
|
||||
@shared_task
|
||||
def update_all_user_facedata_cache():
|
||||
"""
|
||||
更新人脸数据缓存
|
||||
"""
|
||||
facedata_queyset = Employee.objects.filter(face_data__isnull=False,
|
||||
user__is_active=True).values('user', 'face_data')
|
||||
face_users = []
|
||||
face_datas = []
|
||||
for i in facedata_queyset:
|
||||
face_users.append(i['user'])
|
||||
face_datas.append(i['face_data'])
|
||||
cache.set('face_users', face_users, timeout=None)
|
||||
cache.set('face_datas', face_datas, timeout=None)
|
||||
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from functools import update_wrapper
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
from rest_framework.response import Response
|
||||
|
@ -5,13 +6,13 @@ from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
|||
from rest_framework.mixins import UpdateModelMixin, RetrieveModelMixin, CreateModelMixin, ListModelMixin
|
||||
from apps.hrm.filters import ClockRecordFilterSet
|
||||
from apps.hrm.services import HRMService
|
||||
from apps.hrm.tasks import update_all_user_facedata_cache
|
||||
from apps.system.mixins import CreateUpdateModelAMixin, OptimizationMixin
|
||||
from apps.hrm.models import ClockRecord, Employee
|
||||
from apps.hrm.serializers import ClockRecordListSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer
|
||||
import face_recognition
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
import logging
|
||||
|
||||
|
||||
|
||||
from rest_framework.generics import CreateAPIView
|
||||
from rest_framework import status
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
@ -19,20 +20,8 @@ from rest_framework import exceptions
|
|||
from apps.system.models import User
|
||||
from apps.system.serializers import UserSimpleSerializer
|
||||
from rest_framework.permissions import AllowAny
|
||||
logger = logging.getLogger('log')
|
||||
|
||||
|
||||
def load_face_data(username:int, path:str):
|
||||
"""
|
||||
将某用户face_encoding加载进缓存
|
||||
"""
|
||||
face_datas = cache.get_or_set('face_datas', {}, timeout=None)
|
||||
photo_path = settings.BASE_DIR + path
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
face_datas[username] = my_face_encoding
|
||||
cache.set('face_datas', face_datas, timeout=None)
|
||||
return my_face_encoding
|
||||
|
||||
# Create your views here.
|
||||
class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMixin, RetrieveModelMixin, GenericViewSet):
|
||||
|
@ -44,16 +33,23 @@ class EmployeeViewSet(CreateUpdateModelAMixin, OptimizationMixin, UpdateModelMix
|
|||
serializer_class = EmployeeSerializer
|
||||
ordering = ['-pk']
|
||||
|
||||
def perform_update(self, serializer):
|
||||
instance = serializer.save(update_by = self.request.user)
|
||||
try:
|
||||
photo_path = settings.BASE_DIR + instance.photo
|
||||
picture_of_me = face_recognition.load_image_file(photo_path)
|
||||
my_face_encoding = face_recognition.face_encodings(picture_of_me)[0]
|
||||
instance.face_data = my_face_encoding.tolist()
|
||||
instance.save()
|
||||
except:
|
||||
logger.error('人脸识别出错')
|
||||
def update(self, request, *args, **kwargs):
|
||||
partial = kwargs.pop('partial', False)
|
||||
instance = self.get_object()
|
||||
data = request.data
|
||||
serializer = self.get_serializer(instance, data=data, partial=partial)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
photo = data.get('photo', None)
|
||||
if instance.photo != photo:
|
||||
f_l = HRMService.get_facedata_from_img(photo)
|
||||
if f_l:
|
||||
serializer.save(update_by=request.user, face_data = f_l)
|
||||
# 更新人脸缓存
|
||||
update_all_user_facedata_cache.delay()
|
||||
return Response()
|
||||
return Response('头像识别失败', status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer.save(update_by=request.user)
|
||||
return Response()
|
||||
|
||||
|
||||
class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
|
||||
|
|
|
@ -15,7 +15,8 @@ class MbFilterSet(filters.FilterSet):
|
|||
|
||||
def filter_tag(self, queryset, name, value):
|
||||
if value == 'expired':
|
||||
queryset = queryset.exclude(expiration_date=None).filter(expiration_date__lte=timezone.now())
|
||||
queryset = queryset.exclude(expiration_date=None).filter(
|
||||
expiration_date__lte=timezone.now())
|
||||
return queryset
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ class MaterialBatchSerializer(serializers.ModelSerializer):
|
|||
class IProductListSerializer(serializers.ModelSerializer):
|
||||
material_ = MaterialSimpleSerializer(source='material', read_only=True)
|
||||
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
|
||||
is_mtested = serializers.BooleanField(source='wproduct.is_mtested', read_only=True)
|
||||
is_mtestok = serializers.BooleanField(source='wproduct.is_mtestok', read_only=True)
|
||||
remark_mtest = serializers.CharField(source='wproduct.remark_mtest', read_only=True)
|
||||
|
||||
|
|
|
@ -1,22 +1,36 @@
|
|||
from django_filters import rest_framework as filters
|
||||
|
||||
from apps.sam.models import Order
|
||||
from django.db.models import F
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class OrderFilterSet(filters.FilterSet):
|
||||
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
|
||||
create_time_end = filters.DateFilter(field_name="create_time", lookup_expr='lte')
|
||||
material = filters.NumberFilter(method='filter_material')
|
||||
tag = filters.CharFilter(method='filter_tag')
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ['product', 'contract', 'customer', 'create_time_start', 'create_time_end']
|
||||
fields = ['product', 'contract', 'customer', 'create_time_start',
|
||||
'create_time_end', 'tag']
|
||||
|
||||
def filter_material(self, queryset, name, value):
|
||||
"""
|
||||
按物料筛选
|
||||
"""
|
||||
return queryset.filter(plan_order__subplan_plan__progress_subplan__material__id=value).distinct()
|
||||
return queryset.filter(
|
||||
plan_order__subplan_plan__progress_subplan__material__id=value).distinct()
|
||||
|
||||
def filter_tag(self, queryset, name, value):
|
||||
if value == 'near_delivery':
|
||||
day7_after = datetime.now() + timedelta(days=7)
|
||||
queryset = queryset.filter(delivered_count__lt=F('count'),
|
||||
delivery_date__lte = datetime.date(day7_after))
|
||||
elif value == 'out_delivery':
|
||||
queryset = queryset.filter(delivered_count__lt=F('count'),
|
||||
delivery_date__gt = datetime.date(datetime.now()))
|
||||
return queryset
|
||||
|
||||
class ContractFilterSet(filters.FilterSet):
|
||||
create_time_start = filters.DateFilter(field_name="create_time", lookup_expr='gte')
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.9 on 2022-01-24 05:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sam', '0010_auto_20211208_1408'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='order',
|
||||
name='need_mtest',
|
||||
field=models.BooleanField(default=False, verbose_name='是否需要军检'),
|
||||
),
|
||||
]
|
|
@ -66,6 +66,7 @@ class Order(CommonAModel):
|
|||
planed_count = models.PositiveIntegerField('已排数量', default=0)
|
||||
delivered_count = models.PositiveIntegerField('已交货数量', default=0)
|
||||
delivery_date = models.DateField('交货日期')
|
||||
need_mtest = models.BooleanField('是否需要军检', default=False)
|
||||
class Meta:
|
||||
verbose_name = '订单信息'
|
||||
verbose_name_plural = verbose_name
|
||||
|
|
|
@ -63,7 +63,7 @@ class OrderSimpleSerializer(serializers.ModelSerializer):
|
|||
customer_ = CustomerSimpleSerializer(source='customer', read_only=True)
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = '__all__'
|
||||
fields = ['id', 'number', 'contract_', 'customer_', 'need_mtest']
|
||||
|
||||
class SaleCreateSerializer(serializers.ModelSerializer):
|
||||
iproducts = serializers.PrimaryKeyRelatedField(queryset=IProduct.objects.all(), many=True)
|
||||
|
|
|
@ -136,13 +136,6 @@ class SaleViewSet(CreateUpdateCustomMixin, ListModelMixin, RetrieveModelMixin, C
|
|||
fifo.inout_date = timezone.now()
|
||||
fifo.create_by = request.user
|
||||
fifo.save()
|
||||
# 出库条目 暂时不校验是否军检
|
||||
# spds = SaleProduct.objects.filter(sale=obj)
|
||||
# for i in spds:
|
||||
# if i.is_mtested and i.is_mtestok:
|
||||
# pass
|
||||
# else:
|
||||
# raise exceptions.APIException('存在未军检产品')
|
||||
# 创建出库条目
|
||||
ips = IProduct.objects.filter(sale_iproduct__sale=obj)
|
||||
items = ips.values('warehouse', 'material', 'batch').annotate(total=Count('id'))
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from .models import User
|
||||
from utils.mixins import DynamicFieldsFilterMixin
|
||||
|
||||
|
||||
class UserFilter(filters.FilterSet):
|
||||
class UserFilter(DynamicFieldsFilterMixin, filters.FilterSet):
|
||||
name = filters.CharFilter(field_name='name', lookup_expr='contains')
|
||||
class Meta:
|
||||
model = User
|
||||
fields = {
|
||||
'name': ['exact', 'contains'],
|
||||
'is_active': ['exact'],
|
||||
}
|
||||
fields = ['name', 'is_active', 'is_atwork']
|
|
@ -5,6 +5,7 @@ from rest_framework import serializers
|
|||
|
||||
from .models import (Dict, DictType, File, Organization, Permission, Position,
|
||||
Role, User)
|
||||
from utils.mixins import DynamicFieldsSerializerMixin
|
||||
|
||||
class IntervalSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
@ -132,7 +133,7 @@ class UserSimpleSerializer(serializers.ModelSerializer):
|
|||
# fields = ['id', 'username', 'name', 'is_active', 'dept_name', 'dept']
|
||||
|
||||
|
||||
class UserListSerializer(serializers.ModelSerializer):
|
||||
class UserListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
|
||||
"""
|
||||
用户列表序列化
|
||||
"""
|
||||
|
|
|
@ -48,12 +48,6 @@ class TaskList(APIView):
|
|||
tasks = list(sorted(name for name in celery_app.tasks if not name.startswith('celery.')))
|
||||
return Response(tasks)
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = []
|
||||
|
||||
def get(self, request, *args, **kwargs): # 可将token加入黑名单
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
class PTaskViewSet(OptimizationMixin, ModelViewSet):
|
||||
perms_map = {'get': '*', 'post': 'ptask_create',
|
||||
'put': 'ptask_update', 'delete': 'ptask_delete'}
|
||||
|
|
|
@ -2,6 +2,7 @@ from django_filters import rest_framework as filters
|
|||
from apps.mtm.models import Material, Step
|
||||
|
||||
from apps.wpm.services import WpmServies
|
||||
from utils.mixins import DynamicFieldsFilterMixin
|
||||
from .models import Operation, OperationMaterial, OperationRecord, WMaterial, WProduct
|
||||
|
||||
|
||||
|
@ -28,11 +29,15 @@ class WMaterialFilterSet(filters.FilterSet):
|
|||
return queryset
|
||||
|
||||
|
||||
class WProductFilterSet(filters.FilterSet):
|
||||
class WProductFilterSet(DynamicFieldsFilterMixin, filters.FilterSet):
|
||||
tag = filters.CharFilter(method='filter_tag')
|
||||
production_plan = filters.NumberFilter(
|
||||
field_name='subproduction_plan__production_plan')
|
||||
def filter_fields(self, queryset, name, value):
|
||||
return queryset
|
||||
|
||||
def filter_omit(self, queryset, name, value):
|
||||
return queryset
|
||||
class Meta:
|
||||
model = WProduct
|
||||
fields = ['step', 'subproduction_plan', 'material',
|
||||
|
|
|
@ -120,7 +120,6 @@ class WProduct(CommonAModel):
|
|||
on_delete=models.SET_NULL, null=True, blank=True, related_name='wp_ticket')
|
||||
|
||||
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
|
||||
is_mtested = models.BooleanField('是否军检', default=False)
|
||||
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
|
||||
remark_mtest = models.TextField('军检备注', null=True, blank=True)
|
||||
last_test_result = models.BooleanField('最后一次检验结果', null=True, blank=True)
|
||||
|
@ -194,7 +193,6 @@ class WproductFlow(CommonAModel):
|
|||
ticket = models.ForeignKey('wf.ticket', verbose_name='当前工单',
|
||||
on_delete=models.SET_NULL, null=True, blank=True)
|
||||
to_order = models.ForeignKey('sam.order', verbose_name='指派的订单', null=True, blank=True, on_delete = models.CASCADE)
|
||||
is_mtested = models.BooleanField('是否军检', default=False)
|
||||
is_mtestok = models.BooleanField('是否军检合格', null=True, blank=True)
|
||||
remark_mtest = models.TextField('军检备注', null=True, blank=True)
|
||||
last_test_result = models.BooleanField('最后一次检验结果', null=True, blank=True)
|
||||
|
|
|
@ -20,6 +20,7 @@ from apps.system.serializers import UserSimpleSerializer
|
|||
from apps.wpm.models import Operation, OperationEquip, OperationMaterial, OperationWproduct, Pick, WMaterial, WProduct, OperationRecord, OperationRecordItem, WprouctTicket
|
||||
from django.db import transaction
|
||||
from apps.sam.models import Order
|
||||
from utils.mixins import DynamicFieldsSerializerMixin
|
||||
|
||||
class PickHalfSerializer(serializers.Serializer):
|
||||
id = serializers.PrimaryKeyRelatedField(queryset=SubProductionProgress.objects.all(), label='子计划进度ID')
|
||||
|
@ -147,7 +148,7 @@ class WProductBaseSerializer(serializers.ModelSerializer):
|
|||
model = WProduct
|
||||
fields = '__all__'
|
||||
|
||||
class WProductListSerializer(serializers.ModelSerializer):
|
||||
class WProductListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer):
|
||||
"""
|
||||
半成品列表
|
||||
"""
|
||||
|
@ -157,6 +158,7 @@ class WProductListSerializer(serializers.ModelSerializer):
|
|||
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
|
||||
children = serializers.SerializerMethodField()
|
||||
to_order_ = OrderSimpleSerializer(source='to_order', read_only=True)
|
||||
order_ = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = WProduct
|
||||
fields = '__all__'
|
||||
|
@ -167,6 +169,11 @@ class WProductListSerializer(serializers.ModelSerializer):
|
|||
return WProductBaseSerializer(instance=wps, many=True).data
|
||||
return []
|
||||
|
||||
def get_order_(self, obj):
|
||||
order = Order.objects.select_related('contract', 'customer').filter(
|
||||
plan_order__subplan_plan__wproduct_subplan=obj).first()
|
||||
return OrderSimpleSerializer(instance=order).data
|
||||
|
||||
class WProductCardBaseSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
产品流程序列化
|
||||
|
@ -224,6 +231,8 @@ class WProductDetailSerializer(serializers.ModelSerializer):
|
|||
subproduction_plan_ = SubproductionPlanSimpleSerializer(source='subproduction_plan', read_only=True)
|
||||
warehouse_ = WareHouseSimpleSerializer(source='warehouse', read_only=True)
|
||||
children = serializers.SerializerMethodField()
|
||||
to_order_ = OrderSimpleSerializer(source='to_order', read_only=True)
|
||||
order_ = OrderSimpleSerializer(source='subproduction_plan__production_plan__order', read_only=True)
|
||||
class Meta:
|
||||
model = WProduct
|
||||
fields = '__all__'
|
||||
|
@ -231,7 +240,6 @@ class WProductDetailSerializer(serializers.ModelSerializer):
|
|||
def get_children(self, obj):
|
||||
wps = WProduct.objects.filter(child=obj)
|
||||
if wps.exists():
|
||||
print(wps)
|
||||
return WProductBaseSerializer(instance=wps, many=True).data
|
||||
return []
|
||||
|
||||
|
|
|
@ -148,7 +148,8 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
|||
"""
|
||||
perms_map = {'*': '*'}
|
||||
queryset = WProduct.objects.select_related('step', 'material',
|
||||
'subproduction_plan', 'warehouse', 'to_order').prefetch_related('wproduct_child')
|
||||
'subproduction_plan', 'warehouse', 'subproduction_plan__production_plan__order',
|
||||
'to_order').prefetch_related('wproduct_child')
|
||||
serializer_class = WProductListSerializer
|
||||
filterset_class = WProductFilterSet
|
||||
search_fields = ['number']
|
||||
|
@ -398,12 +399,11 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
|
|||
军检
|
||||
"""
|
||||
obj = self.get_object()
|
||||
if obj.is_mtested:
|
||||
if obj.is_mtestok is None:
|
||||
raise exceptions.APIException('已进行军检')
|
||||
if obj.material.type != Material.MA_TYPE_GOOD:
|
||||
raise exceptions.APIException('军检必须是成品')
|
||||
obj.remark_mtest = request.data.get('remark_mtest', None)
|
||||
obj.is_mtested = True
|
||||
is_mtestok = request.data.get('is_mtestok')
|
||||
obj.is_mtestok = is_mtestok
|
||||
if is_mtestok:
|
||||
|
|
|
@ -48,6 +48,7 @@ INSTALLED_APPS = [
|
|||
'simple_history',
|
||||
'apps.system',
|
||||
'apps.monitor',
|
||||
'apps.auth1',
|
||||
'apps.pum',
|
||||
'apps.em',
|
||||
'apps.hrm',
|
||||
|
@ -194,7 +195,7 @@ AUTHENTICATION_BACKENDS = (
|
|||
# CACHES = {
|
||||
# "default": {
|
||||
# "BACKEND": "django_redis.cache.RedisCache",
|
||||
# "LOCATION": "redis://redis:6379/1",
|
||||
# "LOCATION": "redis://127.0.0.1:6379/0",
|
||||
# "OPTIONS": {
|
||||
# "CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
# "PICKLE_VERSION": -1
|
||||
|
@ -203,7 +204,7 @@ AUTHENTICATION_BACKENDS = (
|
|||
# }
|
||||
|
||||
# celery配置,celery正常运行必须安装redis
|
||||
CELERY_BROKER_URL = "redis://redis:6379/0" # 任务存储
|
||||
CELERY_BROKER_URL = "redis://127.0.0.1:6379/1" # 任务存储
|
||||
CELERYD_MAX_TASKS_PER_CHILD = 100 # 每个worker最多执行300个任务就会被销毁,可防止内存泄露
|
||||
CELERY_TIMEZONE = 'Asia/Shanghai' # 设置时区
|
||||
CELERY_ENABLE_UTC = True # 启动时区设置
|
||||
|
|
|
@ -13,7 +13,7 @@ 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 apps.system.views import FileViewSet, LogoutView
|
||||
from apps.system.views import FileViewSet
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
|
@ -23,8 +23,6 @@ from drf_yasg import openapi
|
|||
from drf_yasg.views import get_schema_view
|
||||
from rest_framework import routers
|
||||
from rest_framework.documentation import include_docs_urls
|
||||
from rest_framework_simplejwt.views import (TokenObtainPairView,
|
||||
TokenRefreshView)
|
||||
from django.views.generic import TemplateView
|
||||
from utils.view import GenSignature, UpdateDevelop
|
||||
|
||||
|
@ -53,12 +51,10 @@ urlpatterns = [
|
|||
path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
|
||||
# api
|
||||
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
|
||||
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
|
||||
path('api/token/black/', LogoutView.as_view(), name='token_black'),
|
||||
path('api/file/', include(router.urls)),
|
||||
path('api/system/', include('apps.system.urls')),
|
||||
path('api/monitor/', include('apps.monitor.urls')),
|
||||
path('api/auth/', include('apps.auth1.urls')),
|
||||
path('api/pum/', include('apps.pum.urls')),
|
||||
path('api/em/', include('apps.em.urls')),
|
||||
path('api/hrm/', include('apps.hrm.urls')),
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
Mixin to dynamically select only a subset of fields per DRF resource.
|
||||
"""
|
||||
# import warnings
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
# from django.conf import settings
|
||||
class DynamicFieldsFilterMixin(object):
|
||||
fields = filters.CharFilter(method='filter_fields')
|
||||
omit = filters.CharFilter(method='filter_omit')
|
||||
def filter_fields(self, queryset, name, value):
|
||||
return queryset
|
||||
|
||||
def filter_omit(self, queryset, name, value):
|
||||
return queryset
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
fields = super(DynamicFieldsFilterMixin, self).fields
|
||||
fields.extend(['fields', 'omit'])
|
||||
return fields
|
||||
|
||||
class DynamicFieldsSerializerMixin(object):
|
||||
"""
|
||||
A serializer mixin that takes an additional `fields` argument that controls
|
||||
which fields should be displayed.
|
||||
"""
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
"""
|
||||
Filters the fields according to the `fields` query parameter.
|
||||
A blank `fields` parameter (?fields) will remove all fields. Not
|
||||
passing `fields` will pass all fields individual fields are comma
|
||||
separated (?fields=id,name,url,email).
|
||||
"""
|
||||
fields = super(DynamicFieldsSerializerMixin, self).fields
|
||||
|
||||
if not hasattr(self, '_context'):
|
||||
# We are being called before a request cycle
|
||||
return fields
|
||||
|
||||
# Only filter if this is the root serializer, or if the parent is the
|
||||
# root serializer with many=True
|
||||
is_root = self.root == self
|
||||
parent_is_list_root = self.parent == self.root and getattr(self.parent, 'many', False)
|
||||
if not (is_root or parent_is_list_root):
|
||||
return fields
|
||||
|
||||
try:
|
||||
request = self.context['request']
|
||||
except KeyError:
|
||||
# conf = getattr(settings, 'DRF_DYNAMIC_FIELDS', {})
|
||||
# if not conf.get('SUPPRESS_CONTEXT_WARNING', False) is True:
|
||||
# warnings.warn('Context does not have access to request. '
|
||||
# 'See README for more information.')
|
||||
return fields
|
||||
|
||||
# NOTE: drf test framework builds a request object where the query
|
||||
# parameters are found under the GET attribute.
|
||||
params = getattr(
|
||||
request, 'query_params', getattr(request, 'GET', None)
|
||||
)
|
||||
# if params is None:
|
||||
# warnings.warn('Request object does not contain query paramters')
|
||||
|
||||
try:
|
||||
filter_fields = params.get('fields', None).split(',')
|
||||
except AttributeError:
|
||||
filter_fields = None
|
||||
|
||||
try:
|
||||
omit_fields = params.get('omit', None).split(',')
|
||||
except AttributeError:
|
||||
omit_fields = []
|
||||
|
||||
# Drop any fields that are not specified in the `fields` argument.
|
||||
existing = set(fields.keys())
|
||||
if filter_fields is None:
|
||||
# no fields param given, don't filter.
|
||||
allowed = existing
|
||||
else:
|
||||
allowed = set(filter(None, filter_fields))
|
||||
|
||||
# omit fields in the `omit` argument.
|
||||
omitted = set(filter(None, omit_fields))
|
||||
|
||||
for field in existing:
|
||||
|
||||
if field not in allowed:
|
||||
fields.pop(field, None)
|
||||
|
||||
if field in omitted:
|
||||
fields.pop(field, None)
|
||||
|
||||
return fields
|
Loading…
Reference in New Issue