diff --git a/hb_client/.env.production b/hb_client/.env.production index a6efc10..9305698 100644 --- a/hb_client/.env.production +++ b/hb_client/.env.production @@ -2,6 +2,7 @@ ENV = 'production' # base api +#VUE_APP_BASE_API = 'http://192.168.1.250/api' VUE_APP_BASE_API = 'http://49.232.14.174:2222/api' #VUE_APP_BASE_API = 'http://127.0.0.1:8000/api' diff --git a/hb_client/package.json b/hb_client/package.json index 8e3aa61..c181bcf 100644 --- a/hb_client/package.json +++ b/hb_client/package.json @@ -29,7 +29,11 @@ "element-ui": "^2.15.5", "file-saver": "^2.0.2", "fuse.js": "^6.4.6", + "html-docx-js": "^0.3.1", + "html-docx-js-typescript": "^0.1.5", + "html2canvas": "^1.4.1", "js-cookie": "^3.0.0", + "jspdf": "^2.5.1", "mammoth": "^1.4.19", "normalize.css": "^8.0.1", "nprogress": "0.2.0", diff --git a/hb_client/src/api/qm.js b/hb_client/src/api/qm.js index 32ddc26..d18d4e2 100644 --- a/hb_client/src/api/qm.js +++ b/hb_client/src/api/qm.js @@ -65,6 +65,13 @@ export function getTestRecord(query) { params: query }) } +//检测记录模板 +export function getTestRecordExport(id) { + return request({ + url: `/qm/testrecord/${id}/export/`, + method: 'get' + }) +} //检测记录 export function getTestRecordItem(id) { return request({ diff --git a/hb_client/src/api/user.js b/hb_client/src/api/user.js index df7dac1..8343375 100644 --- a/hb_client/src/api/user.js +++ b/hb_client/src/api/user.js @@ -8,6 +8,14 @@ export function login(data) { }) } +export function refreshToken(data) { + return request({ + url: '/auth/token/refresh/', + method: 'post', + data + }) +} + export function logout() { return request({ url: '/auth/token/black/', diff --git a/hb_client/src/components/customForm/index.vue b/hb_client/src/components/customForm/index.vue index ad62842..66ab915 100644 --- a/hb_client/src/components/customForm/index.vue +++ b/hb_client/src/components/customForm/index.vue @@ -3,7 +3,7 @@ @@ -32,8 +32,8 @@ required > - + @@ -158,8 +158,18 @@ 检查合格 检查不合格 + + + + + + + + + + @@ -169,65 +179,75 @@ - diff --git a/hb_client/src/layout/index.vue b/hb_client/src/layout/index.vue index ef11a7b..bc167a8 100644 --- a/hb_client/src/layout/index.vue +++ b/hb_client/src/layout/index.vue @@ -25,7 +25,7 @@ import { Navbar, Sidebar, AppMain,TagsView } from './components' import ResizeMixin from './mixin/ResizeHandler' import { mapGetters } from 'vuex' -import { getToken } from '@/utils/auth' // get token from cookie +import { getToken,getRefresh } from '@/utils/auth' // get token from cookie export default { name: 'Layout', components: { @@ -64,12 +64,12 @@ export default { }, mounted() { let hasToken = getToken(); - /*debugger; - console.log(hasToken);*/ if (hasToken) { + this.refreshCountData(); this.$store.dispatch("user/getCount", {}); + }else{ + this.$router.push({name:'login'}) } - // this.refreshCountData(); }, methods: { refreshCountData(){ @@ -80,12 +80,11 @@ export default { this.$store.dispatch("user/getCount", {}); } },0) - },5000) + },50000) }, handleClickOutside() { this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) }, - gotoTicketPage(){ let path = this.$route.path; if(path==='/workflow/ticket'){ diff --git a/hb_client/src/router/index.js b/hb_client/src/router/index.js index d921826..87a1375 100644 --- a/hb_client/src/router/index.js +++ b/hb_client/src/router/index.js @@ -113,7 +113,7 @@ export const asyncRoutes = [ path: 'material/:id', name: 'MaterialDO', component: () => import('@/views/mtm/materialdo.vue'), - meta: { title: '绑定检查表', perms: ['vendor_manage'] }, + meta: { title: '绑定检查表' }, hidden: true } , @@ -121,8 +121,21 @@ export const asyncRoutes = [ path: 'materialDetail/:id', name: 'MaterialDetail', component: () => import('@/views/mtm/materialDetail.vue'), - meta: { title: '物料详情', perms: ['vendor_manage'] }, + meta: { title: '物料详情' }, hidden: true + } + + ,{ + path: 'wproductList', + name: 'wproductList', + component: () => import('@/views/mtm/wproductList'), + meta: { title: '半成品列表', icon: 'material', perms: ['mtm_wproduct'] } + }, + { + path: 'productList', + name: 'productList', + component: () => import('@/views/mtm/productList'), + meta: { title: '成品列表', icon: 'material', perms: ['mtm_product'] } }, { path: 'process', @@ -134,7 +147,7 @@ export const asyncRoutes = [ path: 'step/:id', name: 'Step', component: () => import('@/views/mtm/step.vue'), - meta: { title: '子工序', perms: ['vendor_manage'] }, + meta: { title: '子工序'}, hidden: true } , @@ -142,7 +155,7 @@ export const asyncRoutes = [ path: 'stepdo/:id', name: 'StepDo', component: () => import('@/views/mtm/stepdo.vue'), - meta: { title: '子工序查看', perms: ['vendor_manage'] }, + meta: { title: '子工序查看'}, hidden: true }, { @@ -151,18 +164,7 @@ export const asyncRoutes = [ component: () => import('@/views/mtm/productprocess'), meta: { title: '产品管理', icon: 'product', perms: ['mtm_productprocess'] } } - ,{ - path: 'productList', - name: 'productList', - component: () => import('@/views/mtm/productList'), - meta: { title: '成品列表', icon: 'material', perms: ['mtm_material'] } - } - ,{ - path: 'wproductList', - name: 'wproductList', - component: () => import('@/views/mtm/wproductList'), - meta: { title: '半成品列表', icon: 'material', perms: ['mtm_material'] } - } + ] }, { @@ -183,7 +185,7 @@ export const asyncRoutes = [ path: 'work/:id', name: 'work', component: () => import('@/views/pm/work'), - meta: { title: '生产子计划', perms: ['pm_plan'] }, + meta: { title: '生产子计划'}, hidden: true }, { @@ -199,24 +201,24 @@ export const asyncRoutes = [ component: () => import('@/views/pm/management'), meta: { title: '生产任务管理', icon: 'productionTask', perms: ['pm_testitem'] } }, - + { path: 'plandetails/:id', name: 'plandetails', component: () => import('@/views/pm/plandetails'), - meta: { title: '生产任务详情', perms: ['pm_testitem'] }, + meta: { title: '生产任务详情'}, hidden: true } , - + { path: 'processcard/:id', name: 'processcard', component: () => import('@/views/pm/processcard'), - meta: { title: '流程卡', perms: ['vendor_manage'] }, + meta: { title: '流程卡' }, hidden: true } - + ] }, { @@ -226,7 +228,7 @@ export const asyncRoutes = [ name: 'wpm', meta: { title: '生产执行', icon: 'execute', perms: ['wpm_manage'] }, children: [ - { + { path: 'worktask', name: 'worktask', component: () => import('@/views/wpm/worktask'), @@ -261,9 +263,8 @@ export const asyncRoutes = [ path: 'firstCheck', name: 'firstCheck', component: () => import('@/views/wpm/firstCheck'), - meta: { title: '首件确认', icon: 'finishedCheck', perms: ['wpm_firstCheck'] ,noCache: true} + meta: { title: '首件确认', icon: 'finishedCheck', perms: ['wpm_firstCheck']} } - ] }, { @@ -317,10 +318,10 @@ export const asyncRoutes = [ path: 'contractdetail/:id', name: 'contractdetail', component: () => import('@/views/sam/contractdetail'), - meta: { title: '合同详情', perms: ['sam_contract'] }, + meta: { title: '合同详情'}, hidden: true } - + , { path: 'order', @@ -328,13 +329,13 @@ export const asyncRoutes = [ component: () => import('@/views/sam/order'), meta: { title: '订单管理', icon: 'order', perms: ['sam_order'] } } - + , { path: 'orderdetail/:id', name: 'orderdetail', component: () => import('@/views/sam/orderdetail'), - meta: { title: '订单详情', perms: ['sam_order'] }, + meta: { title: '订单详情'}, hidden: true }, { @@ -348,7 +349,7 @@ export const asyncRoutes = [ path: 'salesdetail/:id', name: 'salesdetail', component: () => import('@/views/sam/salesdetail'), - meta: { title: '销售详情', perms: ['sam_sales'] }, + meta: { title: '销售详情'}, hidden: true } ] @@ -384,7 +385,7 @@ export const asyncRoutes = [ path: 'taskdetails/:id', name: 'taskdetails', component: () => import('@/views/qm/taskdetails'), - meta: { title: '过程检验详情', perms: ['qm_processtest'] }, + meta: { title: '过程检验详情'}, hidden: true } , @@ -392,7 +393,7 @@ export const asyncRoutes = [ path: 'wproduct/:id', name: 'wproduct', component: () => import('@/views/qm/wproduct'), - meta: { title: '工序玻璃', perms: ['vendor_manage'] }, + meta: { title: '工序玻璃' }, hidden: true } , @@ -400,7 +401,7 @@ export const asyncRoutes = [ path: 'taskrecordfrom/:id', name: 'taskrecordfrom', component: () => import('@/views/qm/taskrecordfrom'), - meta: { title: '检验记录', perms: ['vendor_manage'] }, + meta: { title: '检验记录'}, hidden: true } , @@ -416,7 +417,7 @@ export const asyncRoutes = [ component: () => import('@/views/qm/unproduct'), meta: { title: '不合格品', icon: 'unqualified', perms: ['qm_unproduct'] } } - + ] }, { @@ -424,19 +425,19 @@ export const asyncRoutes = [ component: Layout, redirect: '/personnel/user', name: 'personnel', - meta: { title: '人员管理', icon: 'user', perms: ['personnel_set'] }, + meta: { title: '人员管理', icon: 'user', perms: ['employee_manage'] }, children: [ { path: 'user', name: 'user', component: () => import('@/views/personnel/user'), - meta: { title: '人员列表', icon: 'userList', perms: ['personnel_user'] } + meta: { title: '人员列表', icon: 'userList', perms: ['employee_userList'] } }, { path: 'attendance', name: 'attendance', component: () => import('@/views/personnel/attendance'), - meta: { title: '考勤列表', icon: 'attendance', perms: ['personnel_attendance'] } + meta: { title: '考勤列表', icon: 'attendance', perms: ['employee_attendance'] } } ,{ path: 'userupdate/:id', name: 'userupdate', @@ -444,7 +445,7 @@ export const asyncRoutes = [ meta: { title: '人员信息详情', icon: 'home' }, hidden: true }, - + ] }, { @@ -464,7 +465,7 @@ export const asyncRoutes = [ path: 'inventory/:id', name: 'inventory', component: () => import('@/views/inm/inventory'), - meta: { title: '仓库物料', perms: ['vendor_manage'] }, + meta: { title: '仓库物料' }, hidden: true } , @@ -484,7 +485,7 @@ export const asyncRoutes = [ path: 'fifodetail/:id', name: 'fifodetail', component: () => import('@/views/inm/fifodetail'), - meta: { title: '仓库物料', perms: ['vendor_manage'] }, + meta: { title: '仓库物料'}, hidden: true }, { @@ -499,7 +500,7 @@ export const asyncRoutes = [ component: () => import('@/views/inm/product'), meta: { title: '成品库', icon: 'finishedProduct', perms: ['inm_product'] } }, - + ] }, { @@ -507,25 +508,25 @@ export const asyncRoutes = [ component: Layout, redirect: '/procurement/vendor', name: 'procurement', - meta: { title: '采购管理', icon: 'purchase', perms: ['procurement_manage'] }, + meta: { title: '采购管理', icon: 'purchase', perms: ['pum_manage'] }, children: [ { path: 'vendor', name: 'vendor', component: () => import('@/views/procurement/vendor'), - meta: { title: '供应商', icon: 'supplier', perms: ['procurement_vendor'] } + meta: { title: '供应商', icon: 'supplier', perms: ['pum_vendor'] } }, { path: 'puorder', name: 'puorder', component: () => import('@/views/procurement/puorder'), - meta: { title: '采购订单', icon: 'purchaseOrder', perms: ['procurement_puorder'] } + meta: { title: '采购订单', icon: 'purchaseOrder', perms: ['pum_puorder'] } }, { path: 'puorderitem/:id', name: 'puorderitem', component: () => import('@/views/procurement/puorderitem'), - meta: { title: '采购订单项', perms: ['procurement_puorder'] }, + meta: { title: '采购订单项'}, hidden: true } ] @@ -555,18 +556,18 @@ export const asyncRoutes = [ component: () => import('@/views/statistics/personStatistics'), meta: { title: '人员统计', icon: 'personStatistics', perms: ['statistics_personStatistics'] } }, - { - path: 'costStatistics', - name: 'costStatistics', - component: () => import('@/views/statistics/costStatistics'), - meta: { title: '成本统计', icon: 'costStatistics', perms: ['workflow_index'] } - }, - { - path: 'testStatistics', - name: 'testStatistics', - component: () => import('@/views/statistics/testStatistics'), - meta: { title: '检验统计', icon: 'testStatistics', perms: ['workflow_index'] } - }, + /* { + path: 'costStatistics', + name: 'costStatistics', + component: () => import('@/views/statistics/costStatistics'), + meta: { title: '成本统计', icon: 'costStatistics', perms: ['workflow_index'] } + }, + { + path: 'testStatistics', + name: 'testStatistics', + component: () => import('@/views/statistics/testStatistics'), + meta: { title: '检验统计', icon: 'testStatistics', perms: ['workflow_index'] } + },*/ ] }, @@ -648,7 +649,7 @@ export const asyncRoutes = [ meta: { title: '用户管理', icon: 'home', perms: ['user_manage'] } } , - + { path: 'organization', name: 'Organization', @@ -677,7 +678,7 @@ export const asyncRoutes = [ path: 'file', name: 'File', component: () => import('@/views/system/file'), - meta: { title: '文件库', icon: 'home', perms: ['file_room'] } + meta: { title: '文件库', icon: 'home', perms: ['system_file'] } }, { path: 'task', diff --git a/hb_client/src/store/getters.js b/hb_client/src/store/getters.js index 3ff47b0..82d3611 100644 --- a/hb_client/src/store/getters.js +++ b/hb_client/src/store/getters.js @@ -2,6 +2,7 @@ const getters = { sidebar: state => state.app.sidebar, device: state => state.app.device, token: state => state.user.token, + refresh: state => state.user.refresh, avatar: state => state.user.avatar, name: state => state.user.name, perms: state => state.user.perms, diff --git a/hb_client/src/store/modules/user.js b/hb_client/src/store/modules/user.js index 6458db4..5c72760 100644 --- a/hb_client/src/store/modules/user.js +++ b/hb_client/src/store/modules/user.js @@ -1,12 +1,14 @@ import { login, logout, getInfo } from '@/api/user' import { getCount } from '@/api/workflow' -import { getToken, setToken, removeToken } from '@/utils/auth' +import { refreshToken } from '@/api/user' +import { getToken, setToken, removeToken} from '@/utils/auth' import { resetRouter } from '@/router' const getDefaultState = () => { return { token: getToken(), name: '', + refresh: '', avatar: '', count: {}, perms: [] @@ -22,6 +24,9 @@ const mutations = { SET_TOKEN: (state, token) => { state.token = token }, + SET_REFRESH: (state, refresh) => { + state.refresh = refresh + }, SET_NAME: (state, name) => { state.name = name }, @@ -44,9 +49,10 @@ const actions = { login({ username: username.trim(), password: password }).then(response => { const { data } = response; commit('SET_TOKEN', data.access); + commit('SET_REFRESH', data.refresh); + sessionStorage.setItem('refresh',data.refresh); setToken(data.access); resolve() - }).catch(error => { reject(error) }) @@ -63,14 +69,11 @@ const actions = { this.$router.push({name:'login',params:{}}); reject('验证失败,重新登陆.'); } - const { perms, name, avatar } = data; - // perms must be a non-empty array if (!perms || perms.length <= 0) { reject('没有任何权限!') } - commit('SET_PERMS', perms); commit('SET_NAME', name); commit('SET_AVATAR', avatar); @@ -118,6 +121,20 @@ const actions = { }) }) } + , + refreshToken({ commit },ref) { + return new Promise((resolve, reject) => { + refreshToken({refresh:ref}).then((res) => { + const { data } = res; + removeToken(); + commit('SET_TOKEN', data.access); + setToken(data.access); + resolve() + }).catch(error => { + reject(error) + }) + }) + } } export default { diff --git a/hb_client/src/utils/auth.js b/hb_client/src/utils/auth.js index 392db62..b63b921 100644 --- a/hb_client/src/utils/auth.js +++ b/hb_client/src/utils/auth.js @@ -1,6 +1,6 @@ import Cookies from 'js-cookie' -const TokenKey = 'token' +const TokenKey = 'token'; export function getToken() { return Cookies.get(TokenKey) @@ -14,12 +14,3 @@ export function removeToken() { return Cookies.remove(TokenKey) } -// export function refreshToken() { -// let token = getToken() -// let data = {"token": token} -// return request({ -// url: '/token/refresh/', -// method: 'post', -// data -// }) -// } diff --git a/hb_client/src/utils/htmlToPdf.js b/hb_client/src/utils/htmlToPdf.js new file mode 100644 index 0000000..11b33ce --- /dev/null +++ b/hb_client/src/utils/htmlToPdf.js @@ -0,0 +1,98 @@ +// 导出页面为PDF格式 +import html2canvas from 'html2canvas' +import JsPDF from 'jspdf' +/*export default{ + install (Vue, options) { + Vue.prototype.getPdf = function () { + var title = this.htmlTitle; + html2Canvas(document.querySelector('#pdfDom'), { + allowTaint: true + }).then(function (canvas) { + let contentWidth = canvas.width; + let contentHeight = canvas.height; + let pageHeight = contentWidth / 592.28 * 841.89; + let leftHeight = contentHeight; + let position = 0; + let imgWidth = 595.28; + let imgHeight = 592.28 / contentWidth * contentHeight; + let pageData = canvas.toDataURL('image/jpeg', 1.0); + let PDF = new JsPDF('', 'pt', 'a4'); + if (leftHeight < pageHeight) { + PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight) + } else { + while (leftHeight > 0) { + PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight); + leftHeight -= pageHeight; + position -= 841.89; + if (leftHeight > 0) { + PDF.addPage() + } + } + } + PDF.save(title + '.pdf') + } + ) + } + } +}*/ +function downloadPDF(ele, pdfName){ + let eleW = ele.offsetWidth;// 获得该容器的宽 + let eleH = ele.offsetHeight;// 获得该容器的高 + let eleOffsetTop = ele.offsetTop; // 获得该容器到文档顶部的距离 + let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离 + var canvas = document.createElement("canvas"); + var abs = 0; + let win_in = document.documentElement.clientWidth || document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条) + let win_out = window.innerWidth; // 获得当前窗口的宽度(包含滚动条) + if(win_out>win_in){ + abs = (win_out - win_in)/2; // 获得滚动条宽度的一半 + } + canvas.width = eleW * 2; // 将画布宽&&高放大两倍 + canvas.height = eleH * 2; + var context = canvas.getContext("2d"); + context.scale(2, 2); + context.translate(-eleOffsetLeft -abs, -eleOffsetTop); + // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此 + // translate的时候,要把这个差值去掉 + html2canvas( ele, { + dpi: 300, + // allowTaint: true, //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的 + useCORS:true //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。 + } ).then( (canvas)=>{ + var contentWidth = canvas.width; + var contentHeight = canvas.height; + //一页pdf显示html页面生成的canvas高度; + var pageHeight = contentWidth / 592.28 * 841.89; + //未生成pdf的html页面高度 + var leftHeight = contentHeight; + //页面偏移 + var position = 0; + //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高 + var imgWidth = 595.28; + var imgHeight = 595.28/contentWidth * contentHeight; + var pageData = canvas.toDataURL('image/jpeg', 1.0); + var pdf = new JsPDF('', 'pt', 'a4'); + //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89) + //当内容未超过pdf一页显示的范围,无需分页 + if (leftHeight < pageHeight) { + //在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示; + pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight); + // pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight); + } else { // 分页 + while(leftHeight > 0) { + pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight); + leftHeight -= pageHeight; + position -= 841.89; + //避免添加空白页 + if(leftHeight > 0) { + pdf.addPage(); + } + } + } + //可动态生成 + pdf.save(pdfName); + }) +} +export default { + downloadPDF +} diff --git a/hb_client/src/utils/request.js b/hb_client/src/utils/request.js index 6183457..f046f8b 100644 --- a/hb_client/src/utils/request.js +++ b/hb_client/src/utils/request.js @@ -1,8 +1,11 @@ import axios from 'axios' import { MessageBox, Message } from 'element-ui' import store from '@/store' -import { getToken } from '@/utils/auth' - +import { refreshToken } from '@/api/user' +import { getToken,setToken,removeToken } from '@/utils/auth' +let isRefreshing = false; +//重试队列 +let requests = []; // create an axios instance const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url @@ -56,7 +59,7 @@ service.interceptors.response.use( * You can also judge the status by HTTP Status Code */ response => { - const res = response.data + const res = response.data; if(res.code>=200 && res.code<400){ return res } @@ -68,24 +71,60 @@ service.interceptors.response.use( duration: 3 * 1000 }) }else{ - MessageBox.confirm('认证失败,请重新登陆.', '确认退出', { + if (!isRefreshing) { + isRefreshing = true; + //调用刷新token的接口 + return refreshToken({refresh: sessionStorage.getItem('refresh')}).then(res => { + const token = res.data.access; + // 替换token + removeToken(); + setToken(token); + response.headers.Authorization = 'Bearer ' + token; + // token 刷新后将数组的方法重新执行 + requests.forEach((cb) => cb(token)); + requests = []; // 重新请求完清空 + return service(response.config) + }).catch(err => { + //跳到登录页 + removeToken(); + store.dispatch('user/logout').then(() => { + location.reload() + }); + return Promise.reject(err) + }).finally(() => { + isRefreshing = false + }) + }else { + // 返回未执行 resolve 的 Promise + return new Promise(resolve => { + // 用函数形式将 resolve 存入,等待刷新后再执行 + requests.push(token => { + response.headers.Authorization = 'Bearer ' + token; + resolve(service(response.config)) + }) + }) + } + /* MessageBox.confirm('认证失败,请重新登陆.', '确认退出', { confirmButtonText: '重新登陆', cancelButtonText: '取消', type: 'warning' }).then(() => { - store.dispatch('user/resetToken').then(() => { + store.dispatch('user/logout').then(() => { location.reload() }) - }) + })*/ } - } else if (res.code >= 400) { - Message({ - message: res.msg || '请求出错', - type: 'error', - duration: 3 * 1000 - }) - return Promise.reject(new Error(res.msg || '请求出错')) + if(res.msg.indexOf('请调整位置')>-1){ + return; + }else{ + Message({ + message: res.msg || '请求出错', + type: 'error', + duration: 3 * 1000 + }) + return Promise.reject(new Error(res.msg || '请求出错')) + } } }, error => { diff --git a/hb_client/src/views/bigScreen/centerLeft1.vue b/hb_client/src/views/bigScreen/centerLeft1.vue index 9b84107..08d39ec 100644 --- a/hb_client/src/views/bigScreen/centerLeft1.vue +++ b/hb_client/src/views/bigScreen/centerLeft1.vue @@ -46,11 +46,12 @@ return { options: {}, cdata: { - xData: ["冷加工", "热弯", "化学钢化"], + xData: ["冷加工", "热弯","物理钢化", "化学钢化"], seriesData: [ - {value: 25, name: "冷加工"}, - {value: 20, name: "热弯"}, - {value: 30, name: "化学钢化"} + {value: 20, name: "冷加工"}, + {value: 25, name: "热弯"}, + {value: 30, name: "物理钢化"}, + {value: 35, name: "化学钢化"} ] } } diff --git a/hb_client/src/views/bigScreen/centerRight1.vue b/hb_client/src/views/bigScreen/centerRight1.vue index 2b5b409..f8ce660 100644 --- a/hb_client/src/views/bigScreen/centerRight1.vue +++ b/hb_client/src/views/bigScreen/centerRight1.vue @@ -9,9 +9,12 @@ 人员到岗情况 - - + + + + + diff --git a/hb_client/src/views/bigScreen/index.vue b/hb_client/src/views/bigScreen/index.vue index 8a71ee7..8104f81 100644 --- a/hb_client/src/views/bigScreen/index.vue +++ b/hb_client/src/views/bigScreen/index.vue @@ -135,6 +135,7 @@ export default { mixins: [drawMixin], + inject:['reload'], data() { return { timing: null, @@ -419,7 +420,7 @@ ], limitedTwo: false, configArticle: { - header: ['标题', '置顶', '作者', '发布时间'], + header: ['标题', '置顶', '发布时间'], data: [ ['郑成功', '2022-03-05'], ['冯宝宝', '2022-03-05'] @@ -437,6 +438,7 @@ category:[], planData:[], realData:[], + timer:null, } }, components: { @@ -476,6 +478,9 @@ that.getOneData();//车间一 that.getTwoData();//车间二 that.getArticle(); + that.timer = window.setInterval(() => { + that.reload(); + },3600000) }, beforeDestroy() { clearInterval(this.timing) @@ -483,8 +488,8 @@ methods: { timeFn() { this.timing = setInterval(() => { - this.dateDay = formatTimeBigScreen(new Date(), 'HH: mm: ss') - this.dateYear = formatTimeBigScreen(new Date(), 'yyyy-MM-dd') + this.dateDay = formatTimeBigScreen(new Date(), 'HH: mm: ss'); + this.dateYear = formatTimeBigScreen(new Date(), 'yyyy-MM-dd'); this.dateWeek = this.weekday[new Date().getDay()] }, 1000) }, @@ -527,8 +532,19 @@ that.titleItem[3].number.number[0] = respo.data.count_selled; that.titleItem[4].number.number[0] = respo.data.count_notok; let mtestCount = respo.data.count_mtestok + respo.data.count_mtestnotok; - that.titleItem[5].number.number[0] = Math.floor((respo.data.count_mtestok / mtestCount) * 100); - that.rate[0].tips = Math.floor((respo.data.count_ok / respo.data.count) * 100); + if(respo.data.count_mtestok===0){ + that.titleItem[5].number.number[0] = 0; + that.rate[0].tips = 0; + }else if(mtestCount===0){ + that.titleItem[5].number.number[0] = 100; + }else{ + that.titleItem[5].number.number[0] = Math.floor((respo.data.count_mtestok / mtestCount) * 100); + } + if(respo.data.count===0){ + that.rate[0].tips = 100; + }else{ + that.rate[0].tips = Math.floor((respo.data.count_ok / respo.data.count) * 100); + } //工序生产进度 getProcessNow({type: 'big_screen'}).then(res => { if (res.data) { @@ -653,6 +669,7 @@ that.listUser = false; getEmployee({page: 0,type:'big_screen',show_atwork:true}).then((response) => { if (response.data) { + // debugger; let list = response.data; let data = []; list.forEach(item => { @@ -660,10 +677,11 @@ obj.push(item.name); // obj.push(item.dept_.name); if (item.is_atwork) { - obj.push("已到岗") + obj.push("已到岗") } else { - obj.push("未到岗") + obj.push("未到岗") } + // obj.push(item.dept_.name); data.push(obj) }); that.userConfig.data = data; @@ -688,7 +706,7 @@ } else { obj.push('已置顶'); } - obj.push(item.author); + // obj.push(item.author); obj.push(item.create_time); data.push(obj) }); @@ -703,6 +721,8 @@ that.limitedPlan = false; getPlanGantt({type: 'big_screen'}).then(res => { if (res.code === 200) { + that.planData = []; + that.realData = []; let list = res.data.results; list.forEach(item=>{ that.category.push(item.number); @@ -715,6 +735,9 @@ } }) }, + }, + destroyed() { + clearInterval(this.timer) } } diff --git a/hb_client/src/views/dashboard/index.vue b/hb_client/src/views/dashboard/index.vue index 738d691..480d859 100644 --- a/hb_client/src/views/dashboard/index.vue +++ b/hb_client/src/views/dashboard/index.vue @@ -604,7 +604,6 @@ page: val, show_atwork:true, page_size: that.userPageSize, - fields: 'id,name,dept_name,is_atwork' }).then((response) => { if (response.data) { that.userList = response.data.results; diff --git a/hb_client/src/views/em/equipment.vue b/hb_client/src/views/em/equipment.vue index a0e596a..e4e1ae1 100644 --- a/hb_client/src/views/em/equipment.vue +++ b/hb_client/src/views/em/equipment.vue @@ -47,21 +47,21 @@ v-el-height-adaptive-table="{bottomOffset: 42}" > - + - + - + - + - + - + - + - + {{ state_[scope.row.state] }} @@ -77,16 +77,16 @@ - + {{ scope.row.parameter }} - + {{ scope.row.keeper_.username }} - + {{ scope.row.place }} - + diff --git a/hb_client/src/views/em/record.vue b/hb_client/src/views/em/record.vue index 5dcd17c..52367a8 100644 --- a/hb_client/src/views/em/record.vue +++ b/hb_client/src/views/em/record.vue @@ -78,13 +78,13 @@ width="120px" > - 编辑 - + --> - - + + {{ scope.row.create_by_.name }} @@ -80,7 +80,7 @@ 已审核 - + {{ item.batch }} {{ item.material_.name }}{{ item.material_.name }} @@ -346,10 +346,12 @@ label="批次" :prop="'details.' + index + '.batch'" > - + - + + + + + + + {{ob.number}} + + + + + + + diff --git a/hb_client/src/views/inm/fifodetail.vue b/hb_client/src/views/inm/fifodetail.vue index ee67ebc..acd4a30 100644 --- a/hb_client/src/views/inm/fifodetail.vue +++ b/hb_client/src/views/inm/fifodetail.vue @@ -53,8 +53,9 @@ v-if="this.$route.params.pu_order != null" > - 不合格 - 合格 + 合格 + 未复验 + 不合格 @@ -86,7 +87,7 @@ 复验记录 @@ -462,7 +463,7 @@ {{item.field_name}}: - + diff --git a/hb_client/src/views/login/index.vue b/hb_client/src/views/login/index.vue index 392e3ac..96c4bb9 100644 --- a/hb_client/src/views/login/index.vue +++ b/hb_client/src/views/login/index.vue @@ -14,6 +14,13 @@ :rules="loginRules" > + + auto-complete="off" + > + + + + > + + - + 登录 + 登录 + @@ -75,9 +93,11 @@ diff --git a/hb_client/src/views/mtm/materialdo.vue b/hb_client/src/views/mtm/materialdo.vue index 6a63650..b669b27 100644 --- a/hb_client/src/views/mtm/materialdo.vue +++ b/hb_client/src/views/mtm/materialdo.vue @@ -1,6 +1,14 @@ + + + {{item.number}} + {{materialTypes[item.type]}} + {{item.name}} + {{item.specification}} + + @@ -113,6 +121,20 @@ + + + 上传模板 + + @@ -132,7 +154,8 @@ :title="tableForm.name" > - + @@ -519,23 +542,21 @@ import {upFile} from "@/api/file"; import {genTree} from "@/utils"; 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 Treeselect from '@riophae/vue-treeselect'; + import '@riophae/vue-treeselect/dist/vue-treeselect.css'; const defaultstep = { name: "", number: "", }; - const defaultrecordform = {enabled: false}; - const defaultfield = {}; - let preDrawAry = []; export default { - components: {Pagination, vueJsonEditor, Treeselect, customForm, faceLogin}, + components: {Pagination,customForm, faceLogin}, data() { return { + item:{}, step: defaultstep, stepList: [], + dialogFieldList: [], upHeaders: upHeaders(), upUrl: upUrl(), fileList: [], @@ -575,7 +596,7 @@ }, listQueryrecordform: { page: 1, - page_size: 20, + page_size: 99, }, recordformList: { count: 0, @@ -673,10 +694,19 @@ ], typeOptions_:{ 10 : "生产记录表", - 20 : "工序检查表", - 30 : "入厂检验表", - 40 : "成品检验表", - 50 : "首件检查表", + 20 : "工序检查表",//wp p + 30 : "入厂检验表",//wuliao + 40 : "成品检验表",//p + 50 : "首件检查表",//wp + }, + materialTypes: { + "1": '成品', + "2": '半成品', + "3": '主要原料', + "4": '辅助材料', + "5": '加工工具', + "6": '辅助工装', + }, canvas: null, ctx: null, @@ -693,15 +723,21 @@ judgeList: [], limitedPhoto: false, isDisabled: true, + type:null, }; }, computed: {}, watch: {}, - created() { - this.material = this.$route.params.id; - this.recordformLists(); - }, mounted(){ + this.item = JSON.parse(sessionStorage.getItem('materialItem')) ; + let type = sessionStorage.getItem('materialType') ; + if(type.indexOf(','>0)){ + this.type = type.split(','); + }else{ + this.type = parseInt(type); + } + this.material = this.item.id; + this.recordformLists(); getrecordformList({page:0}).then((response) => { if (response.data) { this.formList = response.data; @@ -709,6 +745,19 @@ }); }, methods: { + /* handlePreview(file) { + if ("url" in file) { + window.open(file.url); + } else { + window.open(file.response.data.path); + } + },*/ + handleUpSuccess(res) { + this.recordform.export_template = res.data.path; + }, + handleRemove() { + this.recordform.export_template = ''; + }, formFunc(value) { this.dialogVisibleForm = value; }, @@ -795,7 +844,7 @@ } }, checkValue() { - this.field.field_key = this.field.field_key.replace(/[^a-zA-Z]/g, ''); + this.field.field_key = this.field.field_key.replace(/[^0-9a-zA-Z]/g, ''); }, //添加字段选项 addDomain() { @@ -805,16 +854,23 @@ removeDomain(index) { this.field_choice.splice(index, 1); }, - handleLook(scope) { + async handleLook(scope) { + debugger; let that = this; + this.dialogFieldList=[]; that.tableForm = Object.assign({}, scope.row); // copy obj that.formID = that.tableForm.id; that.listQueryfield.form = that.formID; getrffieldList(that.listQueryfield).then((response) => { if (response.data) { - this.hasPicture = false; that.fieldList = response.data; - let list = response.data.results; + } + }); + await getrffieldList({page:0,form:that.formID}).then((response) => { + if (response.data) { + this.hasPicture = false; + let list = response.data; + this.dialogFieldList= response.data; let arr = list.filter(item => { return item.field_type === 'draw' }); @@ -837,12 +893,8 @@ that.$set(that.checkForm, key, '') } that.dialogVisibleForm = true; - /* setTimeout(function () { - that.canvasInit(); - }, 500);*/ } }); - that.fieldLists(); }, checkPermission, handleCurrentChange(row) { @@ -851,13 +903,32 @@ this.fieldLists1(); }, recordformLists() { - this.listQueryrecordform.material = this.material; - // this.listQueryrecordform.type = 2; - getrecordformList(this.listQueryrecordform).then((response) => { - if (response.data) { - this.recordformList = response.data; - } - }); + debugger; + let that = this; + that.listQueryrecordform.material = that.material; + if(that.type===10){ + debugger; + that.listQueryrecordform.type = 10; + getrecordformList(that.listQueryrecordform).then((response) => { + if (response.data) { + that.recordformList = response.data; + } + }); + }else{ + debugger; + that.listQueryrecordform.type = that.type[0]; + getrecordformList(that.listQueryrecordform).then((response) => { + if (response.data) { + that.recordformList = response.data; + that.listQueryrecordform.type = that.type[1]; + getrecordformList(that.listQueryrecordform).then((response) => { + if (response.data) { + that.recordformList.results = that.recordformList.results.concat(response.data.results); + } + }); + } + }); + } }, fieldLists() { let that = this; @@ -927,6 +998,14 @@ this.recordform = Object.assign({}, scope.row); // copy obj this.dialogType = "edit"; this.dialogVisible = true; + if (this.recordform.export_template) { + this.fileList = [ + { + name: this.recordform.export_template, + url: this.recordform.export_template, + }, + ]; + } this.$nextTick(() => { this.$refs["Forms"].clearValidate(); }); @@ -979,6 +1058,7 @@ obj.name=this.recordform.name; obj.type=this.recordform.type; obj.enabled=this.recordform.enabled; + obj.export_template=this.recordform.export_template?this.recordform.export_template:''; if (isEdit) { obj.form=this.recordform.form?this.recordform.form:null; updaterecordform(this.recordform.id, obj).then( diff --git a/hb_client/src/views/mtm/process.vue b/hb_client/src/views/mtm/process.vue index a32c419..b958077 100644 --- a/hb_client/src/views/mtm/process.vue +++ b/hb_client/src/views/mtm/process.vue @@ -14,9 +14,9 @@ border fit stripe - max-height="620" height="100" - v-el-height-adaptive-table="{bottomOffset: 10}" + highlight-current-row + v-el-height-adaptive-table="{bottomOffset: 50}" > diff --git a/hb_client/src/views/mtm/productList.vue b/hb_client/src/views/mtm/productList.vue index 9b2e82b..d208cbd 100644 --- a/hb_client/src/views/mtm/productList.vue +++ b/hb_client/src/views/mtm/productList.vue @@ -216,8 +216,7 @@ }, { value: '套', label: '套' - }, - { + }, { value: '个', label: '个' }, { @@ -226,6 +225,27 @@ }, { value: '瓶', label: '瓶' + }, { + value: 'g', + label: 'g' + }, { + value: 'kg', + label: 'kg' + }, { + value: '卷', + label: '卷' + }, { + value: '张', + label: '张' + }, { + value: '支', + label: '支' + }, { + value: '袋', + label: '袋' + },{ + value: '米', + label: '米' }, ], listQuery: { @@ -268,7 +288,21 @@ }, //检查表 handlebind(scope) { - this.$router.push({name: "MaterialDO", params: {id: scope.row.id},}) + let materialItem = sessionStorage.getItem('materialItem'); + let materialType = sessionStorage.getItem('materialType'); + if(materialItem){ + sessionStorage.removeItem('materialItem'); + sessionStorage.setItem('materialItem',JSON.stringify(scope.row)); + }else{ + sessionStorage.setItem('materialItem',JSON.stringify(scope.row)); + } + if(materialType){ + sessionStorage.removeItem('materialType'); + sessionStorage.setItem('materialType','20,40'); + }else{ + sessionStorage.setItem('materialType','20,40'); + } + this.$router.push({name: "MaterialDO", params: {id: scope.row.id}}) }, handleFilter() { this.listQuery.page = 1; diff --git a/hb_client/src/views/mtm/productprocess.vue b/hb_client/src/views/mtm/productprocess.vue index 431e799..073a693 100644 --- a/hb_client/src/views/mtm/productprocess.vue +++ b/hb_client/src/views/mtm/productprocess.vue @@ -4,7 +4,7 @@ - 产品列表 @@ -14,33 +14,26 @@ border fit stripe + height="100" + @current-change="handleCurrentChange" highlight-current-row - height="660px" - @current-change="handleCurrentChange"> - + v-el-height-adaptive-table="{bottomOffset: 10}" + > - - {{ scope.row.number }} + {{ scope.row.number }} {{ scope.row.name }} - {{ scope.row.specification }} - - - - - - - + 流程分解 - + 新增 - + @@ -181,6 +175,9 @@ {{ scope.row.material_.unit }} + + {{ scope.row.material_.specification }} + {{ scope.row.count }} @@ -259,6 +256,7 @@ border fit stripe + :height="halfHeight" > @@ -359,6 +357,7 @@ fit stripe style="width: 100%" + :height="halfHeight" > @@ -447,6 +446,7 @@ border fit stripe + :height="halfHeight" > @@ -458,6 +458,12 @@ 不检验 + + + 是 + 否 + + {{ scope.row.remark }} @@ -500,12 +506,6 @@ label-width="100px" label-position="right" > - - - 检验 - 不检验 - - + + + 检验 + 不检验 + + + + + 是 + 否 + + @@ -536,6 +548,7 @@ 新增 - 子工序列表 新增子工序 + >新增子工序 + - + {{ scope.row.name }} @@ -27,18 +28,19 @@ - {{item.name}} + + {{item.name}} + - - {{ type_[scope.row.type] }} + + {{ type_[scope.row.type] }} - - - 编辑 + 编辑 + 删除 + 删除 + @@ -73,40 +75,36 @@ :rules="rule1" > - + - + - - + + :value="item.value" + > - + - - - + + + :value="item.value" + > - - - - 取消 @@ -116,15 +114,20 @@ - - + + - 过程记录表 - 新增 + + 新增 + - - - {{ scope.row.name }} + + + + {{ scope.row.material_.name }} - - - 启用 - 禁用 - + + + 启用 + 禁用 + - + {{ scope.row.number }} 查看 + >查看 + 编辑 + >编辑 + 删除 + >删除 + @@ -182,31 +186,54 @@ label-width="80px" label-position="right" > - - + - - + + + + + - - - - - - + + :value="item.value" + > + + + + + + + + + + + + - - - 取消 @@ -214,84 +241,104 @@ + :title="tableForm.name" + > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + {{item.help_text}} + + + + {{item.help_text}} + + + + {{item.help_text}} + + + + + {{item.help_text}} + + + + + {{item.help_text}} + + + + + + + {{item.help_text}} + + + + + + + {{item.help_text}} + + + + + + + + + - - - - - - + + - 记录字段 - 新增 + + 新增 + - - + {{ options_[scope.row.field_type] }} - {{ scope.row.field_name }} - {{ scope.row.field_key }} - {{ scope.row.field_choice }} - - + + {{ scope.row.help_text }} + - 编辑 + 编辑 + 删除 + 删除 + - - - + + - + - + - + - + + + + 添加 - + - + + + + + + + - + @@ -399,63 +467,73 @@ \ No newline at end of file + diff --git a/hb_client/src/views/pm/management.vue b/hb_client/src/views/pm/management.vue index c672637..b62b4b9 100644 --- a/hb_client/src/views/pm/management.vue +++ b/hb_client/src/views/pm/management.vue @@ -33,7 +33,7 @@ fit stripe style="width: 100%" - height="300" + :height="domHeight" @row-click="tableRowClick" > @@ -96,7 +96,7 @@ @pagination="getplanList" /> - + 甘特图 diff --git a/hb_client/src/views/pm/plan.vue b/hb_client/src/views/pm/plan.vue index 908f79c..da9c19f 100644 --- a/hb_client/src/views/pm/plan.vue +++ b/hb_client/src/views/pm/plan.vue @@ -29,49 +29,50 @@ - + - + {{ scope.row.order_.number }} - + {{scope.row.order_.contract_.number }} - + {{ scope.row.product_.name }} - + {{scope.row.product_.specification}} - + {{ scope.row.product_.unit }} - + - + {{ state_[scope.row.state] }} - + - + - + {{scope.row.order_.delivery_date}} - + 否 是 @@ -87,7 +88,7 @@ > @@ -100,6 +101,7 @@ > 启用 + - 查看子计划 + 查看 @@ -141,11 +142,12 @@ @@ -235,6 +237,9 @@ > + + + diff --git a/hb_client/src/views/pm/plandetails.vue b/hb_client/src/views/pm/plandetails.vue index c41a538..5a7dbb8 100644 --- a/hb_client/src/views/pm/plandetails.vue +++ b/hb_client/src/views/pm/plandetails.vue @@ -30,15 +30,10 @@ - {{ - actstate_[scope.row.act_state] - }} - + {{actstate_[scope.row.act_state]}} - - {{scope.row.step_.name}} - + {{scope.row.step_.name}} @@ -58,7 +53,6 @@ > 生成流程卡 @@ -106,7 +100,7 @@ {{item.field_name}}: - + diff --git a/hb_client/src/views/pm/resources.vue b/hb_client/src/views/pm/resources.vue index d0d9363..701d248 100644 --- a/hb_client/src/views/pm/resources.vue +++ b/hb_client/src/views/pm/resources.vue @@ -21,66 +21,38 @@ stripe highlight-current-row height="100" - v-el-height-adaptive-table="{ bottomOffset: 25 }" + v-el-height-adaptive-table="{ bottomOffset: 44 }" > - {{ scope.row.number }} - - {{ - scope.row.product_.name - }} - + {{scope.row.product_.name }} - {{ scope.row.count }} - {{ - scope.row.planed_count - }} - + {{scope.row.planed_count }} - - {{ - scope.row.product_.specification - }} - + {{scope.row.product_.specification }} - {{ - scope.row.customer_.name - }} - + {{scope.row.customer_.name}} - {{ - scope.row.contract_.number - }} - + {{scope.row.contract_.number }} - {{ - scope.row.contract_.name - }} - + {{scope.row.contract_.name }} - {{ - scope.row.delivery_date - }} - + {{scope.row.delivery_date }} - {{ - scope.row.create_time - }} - + {{scope.row.create_time }} @@ -96,7 +68,6 @@ - @@ -194,10 +165,9 @@ fit stripe style="width: 100%" - height="280" + :height="domHeight" > - {{ scope.row.name }} @@ -268,6 +238,7 @@ components: {Pagination}, data() { return { + domHeight:null, orderplan: defaulteorderplan, orderList: { count: 0, @@ -397,5 +368,11 @@ }); }, }, + mounted() { + let hei = document.getElementsByClassName('app-main')[0].clientHeight;// + let domHeight = this.domHeight = (hei - 116) / 2; + document.getElementById('orderList').style.height = domHeight + 'px'; + document.getElementById('productionPlan').style.height = domHeight + 'px'; + } }; diff --git a/hb_client/src/views/pm/work.vue b/hb_client/src/views/pm/work.vue index 2b16536..3993d40 100644 --- a/hb_client/src/views/pm/work.vue +++ b/hb_client/src/views/pm/work.vue @@ -1,100 +1,97 @@ - - 生产任务列表 - - - - + 生产任务列表 + + - - - - {{scope.row.number}} - - + + + {{scope.row.number}} + + {{ scope.row.product_.name }} - + {{ scope.row.product_.specification }} - - {{ scope.row.subproduction_.name }} + + {{ scope.row.subproduction_.name }} - - - - {{ scope.row.process_.name }} + + {{ scope.row.process_.name }} - - {{ scope.row.process_.number }} + + {{ scope.row.process_.number }} - - + + {{item.name}} - - + :value="item.number" + > + {{item.name}} + - - {{ scope.row.workshop_.name }} + + {{ scope.row.workshop_.name }} - + {{ scope.row.count }} - + {{ scope.row.start_date }} - + {{ scope.row.end_date }} - + {{ state_[scope.row.state] }} - - - + {{ scope.row.create_time }} - 修改日期 - + 下达 - - + 查看详情 - + 查看详情 + - - - - 取消 确认 - - - - - - - - + + {{ scope.row.count }} - + {{ scope.row.count_real }} {{ scope.row.subproduction_plan_.number }} - + {{ scope.row.material_.name }} - + {{ scope.row.material_.number }} - + {{ scope.row.material_.specification }} - + {{ scope.row.material_.unit }} - - - - - - - + + {{ scope.row.count }} - + {{ scope.row.count_real }} {{ scope.row.subproduction_plan_.number }} - + {{ scope.row.material_.name }} - + {{ scope.row.material_.number }} - + {{ scope.row.material_.specification }} - + {{ scope.row.material_.unit }} - 取消 - - \ No newline at end of file + }) + .catch((err) => { + console.error(err); + }); + } + }, + }; + + diff --git a/hb_client/src/views/procurement/puorder.vue b/hb_client/src/views/procurement/puorder.vue index 47ae8bd..bc77435 100644 --- a/hb_client/src/views/procurement/puorder.vue +++ b/hb_client/src/views/procurement/puorder.vue @@ -43,7 +43,6 @@ fit stripe highlight-current-row - max-height="700" height="100" v-el-height-adaptive-table="{ bottomOffset: 43 }" > @@ -73,7 +72,7 @@ 订单项 diff --git a/hb_client/src/views/procurement/puorderitem.vue b/hb_client/src/views/procurement/puorderitem.vue index 3a8b9d3..e52e53a 100644 --- a/hb_client/src/views/procurement/puorderitem.vue +++ b/hb_client/src/views/procurement/puorderitem.vue @@ -2,9 +2,13 @@ - 新增采购订单项 + 新增采购订单项 + @@ -19,32 +23,33 @@ height="100" v-el-height-adaptive-table="{ bottomOffset: 43 }" > - - + + {{ scope.row.material_.name }} - + {{ scope.row.material_.specification }} - - + + {{ scope.row.count }} - + {{ scope.row.delivered_count }} - + {{ scope.row.delivery_date }} - 删除 + 删除 + @@ -65,10 +70,10 @@ :rules="rule1" > - + - - + + 取消 @@ -97,101 +105,101 @@ diff --git a/hb_client/src/views/procurement/vendor.vue b/hb_client/src/views/procurement/vendor.vue index e945868..c13b362 100644 --- a/hb_client/src/views/procurement/vendor.vue +++ b/hb_client/src/views/procurement/vendor.vue @@ -43,9 +43,8 @@ fit stripe highlight-current-row - max-height="700" height="100" - v-el-height-adaptive-table="{bottomOffset: 50}" + v-el-height-adaptive-table="{bottomOffset: 42}" > @@ -56,8 +55,8 @@ - - + diff --git a/hb_client/src/views/qm/admissiontest.vue b/hb_client/src/views/qm/admissiontest.vue index 46c9ff7..86bc47a 100644 --- a/hb_client/src/views/qm/admissiontest.vue +++ b/hb_client/src/views/qm/admissiontest.vue @@ -4,14 +4,13 @@ @@ -57,7 +56,7 @@ {{scope.row.create_time}} - @@ -69,7 +68,7 @@ 复验记录 - + --> @@ -127,7 +126,7 @@ stripe highlight-current-row height="620" - v-el-height-adaptive-table="{bottomOffset: 40}" + v-el-height-adaptive-table="{bottomOffset: 42}" > @@ -164,7 +163,7 @@ {{ scope.row.create_time }} - @@ -185,7 +184,7 @@ 复验记录 - + --> @@ -235,7 +234,7 @@ {{ scope.row.create_time }} - @@ -256,7 +255,7 @@ 复验记录 - + --> import {getfifodetailList} from "@/api/inm"; import checkPermission from "@/utils/permission"; - import {createTestrecord} from "@/api/inm"; + // import {createTestrecord} from "@/api/inm"; import Pagination from "@/components/Pagination"; // secondary package based on el-pagination export default { diff --git a/hb_client/src/views/qm/product.vue b/hb_client/src/views/qm/product.vue index 3c943fa..1884153 100644 --- a/hb_client/src/views/qm/product.vue +++ b/hb_client/src/views/qm/product.vue @@ -8,7 +8,6 @@ fit stripe highlight-current-row - max-height="700" height="100" v-el-height-adaptive-table="{bottomOffset: 42}" > diff --git a/hb_client/src/views/qm/producttest.vue b/hb_client/src/views/qm/producttest.vue index 10c9e74..2c25c36 100644 --- a/hb_client/src/views/qm/producttest.vue +++ b/hb_client/src/views/qm/producttest.vue @@ -7,7 +7,8 @@ fit stripe highlight-current-row - max-height="600" + height="100" + v-el-height-adaptive-table="{bottomOffset: 42}" > @@ -111,6 +112,10 @@ @click="handleRecordDetail(scope)" >查看 + 导出 + @@ -145,7 +150,7 @@ {{item.field_name}}: - + @@ -154,11 +159,11 @@ diff --git a/hb_client/src/views/qm/taskdetails.vue b/hb_client/src/views/qm/taskdetails.vue index 117c79e..41f1107 100644 --- a/hb_client/src/views/qm/taskdetails.vue +++ b/hb_client/src/views/qm/taskdetails.vue @@ -99,7 +99,6 @@ > 查看 @@ -137,7 +136,6 @@ > 查看 diff --git a/hb_client/src/views/qm/taskrecordfrom.vue b/hb_client/src/views/qm/taskrecordfrom.vue index 9b9c359..9a19d09 100644 --- a/hb_client/src/views/qm/taskrecordfrom.vue +++ b/hb_client/src/views/qm/taskrecordfrom.vue @@ -48,6 +48,7 @@ 查看 + 导出 @@ -79,7 +80,7 @@ {{item.field_name}}: - + @@ -90,8 +91,7 @@ diff --git a/hb_client/src/views/qm/unproduct.vue b/hb_client/src/views/qm/unproduct.vue index b303293..f0736f7 100644 --- a/hb_client/src/views/qm/unproduct.vue +++ b/hb_client/src/views/qm/unproduct.vue @@ -4,12 +4,14 @@ @@ -70,8 +72,8 @@ fit stripe highlight-current-row - height="620" - v-el-height-adaptive-table="{ bottomOffset: 40 }" + height="100" + v-el-height-adaptive-table="{bottomOffset: 42}" > @@ -129,14 +131,13 @@ @@ -274,7 +275,7 @@ {{item.field_name}}: - + @@ -390,7 +391,7 @@ //不合格玻璃审理单查看 handledetailbhg(scope) { - this.$router.push({name: "ticketDetail", params: {ticketId: scope.row.ticket}}) + this.$router.push({name: "ticketDetail", params: {ticketId: scope.row.ticket,workflow:scope.row.workflow}}) }, //入场检验不合格 diff --git a/hb_client/src/views/sam/contractdetail.vue b/hb_client/src/views/sam/contractdetail.vue index 59e557a..d25b472 100644 --- a/hb_client/src/views/sam/contractdetail.vue +++ b/hb_client/src/views/sam/contractdetail.vue @@ -1,19 +1,19 @@ - - {{contractdetail.name}} - {{contractdetail.number}} - {{contractdetail.amount}} - {{contractdetail.customer_.name}} - - - - - 关联订单 - - {{contractdetail.name}} + {{contractdetail.number}} + {{contractdetail.amount}} + {{contractdetail.customer_.name}} + + + + + + 关联订单 + + - - - + + {{ scope.row.number }} - + {{ scope.row.customer_.name }} - - + {{ scope.row.product_.name }} - + {{ scope.row.product_.specification }} - + {{ scope.row.count }} - + {{ scope.row.delivery_date }} - + {{ scope.row.delivered_count }} - - + {{ scope.row.create_time }} - - - - 详情 - - + 详情 + - + diff --git a/hb_client/src/views/sam/order.vue b/hb_client/src/views/sam/order.vue index 59f7b14..7ed366a 100644 --- a/hb_client/src/views/sam/order.vue +++ b/hb_client/src/views/sam/order.vue @@ -128,7 +128,7 @@ :rules="rule1" > - + @@ -224,8 +224,9 @@ dialogVisible: false, dialogType: "new", rule1: { - product: [{required: true, message: "请输入", trigger: "blur"}], - delivery_date: [{required: true, message: "请输入", trigger: "blur"}], + number: [{required: true, message: "请输入订单编号", trigger: "blur"}], + product: [{required: true, message: "请选择所需产品", trigger: "blur"}], + delivery_date: [{required: true, message: "请选择交货日期", trigger: "blur"}], }, }; }, diff --git a/hb_client/src/views/sam/salesdetail.vue b/hb_client/src/views/sam/salesdetail.vue index c643e72..4f6db9c 100644 --- a/hb_client/src/views/sam/salesdetail.vue +++ b/hb_client/src/views/sam/salesdetail.vue @@ -3,30 +3,30 @@ - {{ salesdetail.customer_.name }} + {{ salesdetail.customer_.name }} + - {{ salesdetail.product_.name }} + {{ salesdetail.product_.name }} + - {{ salesdetail.product_.specification }} - + {{ salesdetail.product_.specification }} + - {{ salesdetail.order_.contract_.name }} + {{ salesdetail.order_.contract_.name }} + - {{ salesdetail.order_.contract_.number }} - + {{ salesdetail.order_.contract_.number }} + + {{ salesdetail.order_.number }} - - 物流单 + + 物流单 + 上传物流信息 @@ -40,38 +40,36 @@ highlight-current-row style="width: 100%" > - + {{ scope.row.number }} - - {{ - scope.row.iproduct_.material_.name - }} + + {{scope.row.iproduct_.material_.name }} + - {{ - scope.row.iproduct_.batch - }} + + {{scope.row.iproduct_.batch }} + - {{ - scope.row.iproduct_.warehouse_.name - }} + + {{scope.row.iproduct_.warehouse_.name }} + 未军检 - 已军检 + 已军检 + - 不合格 - 合格 + 合格 + {{ scope.row.packnum }} @@ -82,60 +80,59 @@ 装箱 - + 装箱 + 备注 + 备注 + 删除 + 删除 + - - - - - 上传文件 - - - - - 取消 - 确认 - - + + + + 上传文件 + + + + + 取消 + 确认 + + - + - - + {{ scope.row.name }} @@ -183,22 +179,22 @@ - {{ - scope.row.material_.count - }} + + {{scope.row.material_.count}} + - {{ - scope.row.material_.count_safe - }} + + {{scope.row.material_.count_safe}} + - 取消 + + 取消 + 确认 @@ -215,13 +211,13 @@ label-position="right" > - + - 取消 + + 取消 + 确认 @@ -229,63 +225,63 @@ diff --git a/hb_client/src/views/workflow/customfield.vue b/hb_client/src/views/workflow/customfield.vue index 8013b84..7f321d7 100644 --- a/hb_client/src/views/workflow/customfield.vue +++ b/hb_client/src/views/workflow/customfield.vue @@ -39,20 +39,17 @@ width="220px" > - 编辑 - + 编辑 + 删除 - + 删除 + diff --git a/hb_client/src/views/workflow/index.vue b/hb_client/src/views/workflow/index.vue index cc750d8..4c7605f 100644 --- a/hb_client/src/views/workflow/index.vue +++ b/hb_client/src/views/workflow/index.vue @@ -119,7 +119,7 @@ - + @@ -356,7 +356,7 @@ var g = new dagreD3.graphlib.Graph().setGraph({ rankdir: 'DL', nodesep: 100, - edgesep: 10,//两条线之间的距离 + edgesep: 50,//两条线之间的距离 ranksep: 50,//节点之间的距离 marginx: 160, marginy: 20, diff --git a/hb_client/src/views/workflow/transitions.vue b/hb_client/src/views/workflow/transitions.vue index d0827f6..70e0c84 100644 --- a/hb_client/src/views/workflow/transitions.vue +++ b/hb_client/src/views/workflow/transitions.vue @@ -1,23 +1,26 @@ - - - 新增 - + + + 新增 + + - + {{ scope.row.name }} - + {{ scope.row.timer }} @@ -31,32 +34,27 @@ {{scope.row.destination_state_.name}} - + {{ scope.row.create_time }} - - + + 编辑 + 编辑 - 删除 + 删除 + - - + - - + + - - + - - + + - - - - + + + + - - - + + - 取消 @@ -128,28 +123,35 @@ diff --git a/hb_client/src/views/wpm/firstCheck.vue b/hb_client/src/views/wpm/firstCheck.vue index cf8c402..38c74a0 100644 --- a/hb_client/src/views/wpm/firstCheck.vue +++ b/hb_client/src/views/wpm/firstCheck.vue @@ -16,9 +16,10 @@ :data="subPlanList" fit style="width: 100%" - height="100" stripe border + height="100" + highlight-current-row v-el-height-adaptive-table="{bottomOffset: 50}" > @@ -70,7 +71,7 @@ 首件检验 @@ -90,6 +91,13 @@ > 查看 + + 导出 + @@ -215,12 +223,19 @@ + 审核人员确认 + + 审核人员确认 + + + + @@ -231,6 +246,7 @@ import faceLogin from '@/components/faceLogin/review.vue'; import {getProcessList,getrecordformList} from "@/api/mtm"; import {getsubplanList} from "@/api/wpm"; + import {getTestRecordExport} from "@/api/qm"; import {firstTestInit,firstAudit} from "@/api/pm"; import {getTestRecordItem,putTestRecordItem,subTestRecordItem} from "@/api/qm"; @@ -256,6 +272,11 @@ enabled:true, material:null }, + checkParams:{ + id:null, + is_testok:true, + record_data:null, + }, planId:null, leader:null, recordId: null, @@ -272,6 +293,7 @@ reviewVisible:false, recordVisible:false, is_midtesting:false, + limitedUserCheck:false, is_testok:false, formName:'首件确认检查表', update_time:'', @@ -296,6 +318,8 @@ changeIndex(item,index) { this.activeIndex = index; this.listQuery.process = item.id; + this.subPlanList = []; + this.count = 0; this.getTableData(); }, @@ -341,6 +365,7 @@ //首件审批 handleSelectclick(scope,index){ let that = this; + debugger; this.reviewVisible = true; that.planId = scope.row.id; that.leader_1 = scope.row.leader_1_?scope.row.leader_1_.name:null; @@ -445,27 +470,56 @@ }); }, - //提交首件检查 + //提交首件检查:需要人脸识别进行身份验证 recordSubmit(value) { let that = this; - let id = value.id; let params = {}; - params.record_data = value.record_data; + params.id = value.id; params.is_testok = value.is_testok; - putTestRecordItem(id, params).then((res) => { - if (res.code >= 200) { - subTestRecordItem(id, params).then((res) => { + params.record_data = value.record_data; + that.checkParams = params; + that.limitedUserCheck = true; + }, + + //人脸识别获取人员信息后 + checkSubmit(data){ + let that =this; + let id = that.checkParams.id; + let params = new Object(); + params.is_testok = that.checkParams.is_testok; + params.record_data = that.checkParams.record_data; + params.token = data.token; + let text = '确定以操作员'+data.name+'身份提交?'; + if(data.token!==''&&data.token!==null&&data.token!==undefined) { + this.$confirm(text, '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + putTestRecordItem(id, params).then((res) => { if (res.code >= 200) { - that.recordVisible = false; - that.getTableData(); + subTestRecordItem(id, params).then((res) => { + debugger; + that.limitedUserCheck = false; + if (res.code >= 200) { + that.recordVisible = false; + that.getTableData(); + }else{ + that.$message.error(res.msg) + } + }).catch(()=>{ + that.limitedUserCheck = false; + }); + } else { + that.$message.error(res.msg) } + }).catch((err) => { + that.$message.error(err); }); - } else { - that.$message.error(res.msg) - } - }).catch((err) => { - that.$message.error(err); - }); + }).catch(() => { + that.limitedUserCheck = false; + }); + } }, //再次点击首件检验 @@ -550,6 +604,17 @@ }) } }, + handleExportClick(scope){ + let exportFormId = scope.row.id; + getTestRecordExport(exportFormId).then(res=>{ + if(res.code===200){ + let link = document.createElement('a'); + link.href = res.data.path; + document.body.appendChild(link); + link.click(); + } + }) + }, }, mounted() { this.getProcessList(); diff --git a/hb_client/src/views/wpm/need.vue b/hb_client/src/views/wpm/need.vue index 27b5bfa..e637c94 100644 --- a/hb_client/src/views/wpm/need.vue +++ b/hb_client/src/views/wpm/need.vue @@ -61,6 +61,8 @@ > {{ scope.row.step_.name }} + + @@ -176,6 +177,8 @@ > {{ scope.row.step_.name }} + + {{ scope.row.step_.name }} - + + {{scope.row.update_by_.username}} + {{scope.row.create_by_.username}} + + + @@ -360,19 +368,24 @@ {{ scope.row.subproduction_plan_.number }} - + + {{scope.row.update_by_.username}} + {{scope.row.create_by_.username}} + + + 报废 @@ -426,6 +439,8 @@ > {{ scope.row.step_.name }} + + - + 删除 @@ -732,13 +749,21 @@ - + {{item.field_name}}: - + + 导出 + + + 审核人员确认 + + + + diff --git a/hb_client/src/views/wpm/worktask.vue b/hb_client/src/views/wpm/worktask.vue index 73501b6..a304125 100644 --- a/hb_client/src/views/wpm/worktask.vue +++ b/hb_client/src/views/wpm/worktask.vue @@ -1,238 +1,247 @@ - - - + + - - - - - - {{scope.row.plan_product_.name}} - - - - - {{scope.row.product_.specification}} - - - - - {{scope.row.product_.name}} - - - - - {{scope.row.subproduction_.name}} - - - - - {{scope.row.workshop_.name}} - - - - - - {{ item.name }} - - - - - - - - - - {{ state_[scope.row.state] }} - - - - - 未领料 - 已领料 - - - - - {{ scope.row.count }}/{{ scope.row.count_real }}/{{scope.row.count_ok}} - - - - - - 领料 - - - 继续领料 - - - 领半成品 - - - - - - - - - - 半成品 - - - {{ item.name }} - - - 报废 - - - + + + + + {{scope.row.plan_product_.name}} + + + + + {{scope.row.product_.specification}} + + + + + {{scope.row.product_.name}} + + + + + {{scope.row.subproduction_.name}} + + + + + {{scope.row.workshop_.name}} + + + + + - 显示全部 - - - + + + + + + + + + {{ state_[scope.row.state] }} + + + + + {{ state_[scope.row.production_plan_.state] }} + + + + + 未领料 + 已领料 + + + + + {{ scope.row.count }}/{{ scope.row.count_real }}/{{scope.row.count_ok}} + + + + + + 领料 + + + 继续领料 + + + 领半成品 + + + + + + + + + + + + + + 半成品 + + + {{ item.name }} + + + 报废 + + + - - - - {{ scope.row.number }}> - - {{scope.row.subproduction_plan_.number}} - - - - - - - {{scope.row.material_.name}} - - - - - {{scope.row.step_.name}} - - - - - {{actstate_[scope.row.act_state]}} - - - - - {{ ng_sign_[scope.row.ng_sign] }} - - - - - - - - - - - 车间物料 - - - - - - {{scope.row.subproduction_plan_.number }} - - - - - {{scope.row.material_.name}} - - - - - {{scope.row.material_.unit}} - - - - - - - - - - - - + 显示全部 + + + + + + + {{ scope.row.number }}> + + {{scope.row.subproduction_plan_.number}} + + + + + + + {{scope.row.material_.name}} + + + + + {{scope.row.step_.name}} + + + + + {{actstate_[scope.row.act_state]}} + + + + + {{ ng_sign_[scope.row.ng_sign] }} + + + + + + + + + + + 车间物料 + + + + + + {{scope.row.subproduction_plan_.number }} + + + + + {{scope.row.material_.name}} + + + + + {{scope.row.material_.unit}} + + + + + + + + + + + { + let that = this; + that.process = tab.name; + that.listQuery.process = tab.name; + that.steps = []; + getsubplanList(that.listQuery).then((response) => { if (response.data) { - this.subproductionplanList = response.data; + that.subproductionplanList = response.data; } }); - //子工序列表 - getStepLists(tab.name).then((response) => { if (response.data) { - this.steps = response.data; + that.steps = response.data; } }); //车间物料表 - this.getwmaterialLists(); + that.getwmaterialLists(); // 半成品表 - this.getwproductLists(); + that.getwproductLists(); }, //车间物料 @@ -900,7 +909,6 @@ //工序对应的子计划,弹出对应的车间物料 handleCurrentChange(row) { // this.steps = row.steps; //调出子工序 - this.subproduction_plan = row.id; //子计划Id this.getwproductLists(); getwmaterialList({ @@ -915,9 +923,29 @@ }, //大工序工序渲染 getProcessList() { + let that = this; getProcessList({page: 0}).then((response) => { if (response.data) { - this.processOption = response.data; + that.processOption = response.data; + that.activeName = response.data[0].id; + that.process = response.data[0].id; + that.listQuery.process = response.data[0].id; + that.steps = []; + getsubplanList(that.listQuery).then((response) => { + if (response.data) { + that.subproductionplanList = response.data; + } + }); + //子工序列表 + getStepLists(response.data[0].id).then((response) => { + if (response.data) { + that.steps = response.data; + } + }); + //车间物料表 + that.getwmaterialLists(); + // 半成品表 + that.getwproductLists(); } }); }, @@ -1013,9 +1041,7 @@ createPick(this.pickData).then((res) => { if (res.code >= 200) { this.dialogVisiblenw = false; - this.$message.success("领料成功!"); - this.listQuery.process = this.process; getsubplanList(this.listQuery).then((response) => { @@ -1174,5 +1200,11 @@ }); }, }, + mounted() { + let H = document.getElementsByClassName('app-main')[0].clientHeight; + let h = H/2-85; + this.tableHeight = h+'px'; + this.secondTableHeight = h-36+'px'; + } }; diff --git a/hb_server/apps/develop/views.py b/hb_server/apps/develop/views.py index 96ed853..6364b66 100644 --- a/hb_server/apps/develop/views.py +++ b/hb_server/apps/develop/views.py @@ -25,12 +25,14 @@ class CleanDataView(APIView): """ Order.objects.all().delete(soft=False) ProductionPlan.objects.all().delete(soft=False) - FIFO.objects.all().delete(soft=False) + FIFO.objects.all().delete() Material.objects.filter(type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).update(count=0) MaterialBatch.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() Inventory.objects.filter(material__type__in=[Material.MA_TYPE_GOOD, Material.MA_TYPE_HALFGOOD]).delete() Ticket.objects.all().delete(soft=False) Operation.objects.all().delete() + from apps.pum.models import PuOrder + PuOrder.objects.all().delete(soft=False) return Response() diff --git a/hb_server/apps/hrm/serializers.py b/hb_server/apps/hrm/serializers.py index 56a91ae..a79ca9c 100644 --- a/hb_server/apps/hrm/serializers.py +++ b/hb_server/apps/hrm/serializers.py @@ -20,10 +20,15 @@ class EmployeeNotWorkRemarkSerializer(ModelSerializer): fields = ['not_work_remark'] class FaceLoginSerializer(serializers.Serializer): base64 = serializers.CharField() + tolerance = serializers.FloatField(required=False, default=0.36) +class FaceLoginPathSerializer(serializers.Serializer): + path = serializers.CharField() + tolerance = serializers.FloatField(required=False, default=0.36) class FaceClockCreateSerializer(serializers.Serializer): base64 = serializers.CharField() + tolerance = serializers.FloatField(required=False, default=0.36) class ClockRecordListSerializer(serializers.ModelSerializer): create_by_ = UserSimpleSerializer(source='create_by', read_only=True) diff --git a/hb_server/apps/hrm/services.py b/hb_server/apps/hrm/services.py index eca4639..3bb097e 100644 --- a/hb_server/apps/hrm/services.py +++ b/hb_server/apps/hrm/services.py @@ -10,7 +10,40 @@ from django.core.cache import cache class HRMService: @classmethod - def face_compare_from_base64(cls, base64_data): + def face_compare_from_path(cls, path, tolerance=0.36): + filepath = settings.BASE_DIR +path + try: + unknown_picture = face_recognition.load_image_file(filepath) + unknown_face_encoding = face_recognition.face_encodings(unknown_picture, num_jitters=2)[0] + # os.remove(filepath) + except: + # os.remove(filepath) + return None, '识别失败,请调整位置' + + # 匹配人脸库 + face_datas = cache.get('face_datas') + if face_datas is None: + update_all_user_facedata_cache() + face_datas = cache.get('face_datas') + face_users = cache.get('face_users') + results = face_recognition.compare_faces(face_datas, + unknown_face_encoding, tolerance=tolerance) + user_index = cls.get_user_index(results) + user_index_len = len(user_index) + if user_index_len == 1: + user = User.objects.get(id=face_users[user_index[0]]) + return user, '' + elif user_index_len == 0: + return None, '人脸未匹配,请调整位置' + else: + user_ids = [] + for i in user_index: + user_ids.append(face_users[i]) + user_name_str = ','.join(list(User.objects.filter(id__in=user_ids).values_list('name', flat=True))) + return None, '匹配多张人脸:' + user_name_str + + @classmethod + def face_compare_from_base64(cls, base64_data, tolerance=0.36): filename = str(uuid.uuid4()) filepath = settings.BASE_DIR +'/temp/' + filename +'.png' with open(filepath, 'wb') as f: @@ -30,13 +63,34 @@ class HRMService: face_datas = cache.get('face_datas') face_users = cache.get('face_users') results = face_recognition.compare_faces(face_datas, - unknown_face_encoding, tolerance=0.45) + unknown_face_encoding, tolerance=tolerance) + user_index = cls.get_user_index(results) + user_index_len = len(user_index) + if user_index_len == 1: + user = User.objects.get(id=face_users[user_index[0]]) + return user, '' + elif user_index_len == 0: + return None, '人脸未匹配,请调整位置' + else: + user_ids = [] + for i in user_index: + user_ids.append(face_users[i]) + user_name_str = ','.join(list(User.objects.filter(id__in=user_ids).values_list('name', flat=True))) + return None, '匹配多张人脸:' + user_name_str + + + @classmethod + def get_user_index(cls, results): + """ + 返回user_index列表 + """ + true_num = 0 + user_index = [] for index, value in enumerate(results): if value: - # 识别成功 - user = User.objects.get(id=face_users[index]) - return user, '' - return None, '人脸未匹配,请调整位置' + true_num = true_num + 1 + user_index.append(index) + return user_index @classmethod def get_facedata_from_img(cls, img_path): diff --git a/hb_server/apps/hrm/tasks.py b/hb_server/apps/hrm/tasks.py index b365f58..7cc65a3 100644 --- a/hb_server/apps/hrm/tasks.py +++ b/hb_server/apps/hrm/tasks.py @@ -18,7 +18,7 @@ def update_all_user_facedata_cache(): 更新人脸数据缓存 """ facedata_queyset = Employee.objects.filter(face_data__isnull=False, - user__is_active=True).values('user', 'face_data') + user__is_active=True, user__is_deleted = False).values('user', 'face_data') face_users = [] face_datas = [] for i in facedata_queyset: diff --git a/hb_server/apps/hrm/urls.py b/hb_server/apps/hrm/urls.py index b0ced0c..b740eda 100644 --- a/hb_server/apps/hrm/urls.py +++ b/hb_server/apps/hrm/urls.py @@ -1,6 +1,6 @@ from rest_framework import urlpatterns -from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin, NotWorkRemarkViewSet +from apps.hrm.views import ClockRecordViewSet, EmployeeViewSet, FaceLogin, FacePathLogin, NotWorkRemarkViewSet from django.urls import path, include from rest_framework.routers import DefaultRouter @@ -10,6 +10,7 @@ router.register('clock_record', ClockRecordViewSet, basename='clock_record') router.register('not_work_remark', NotWorkRemarkViewSet, basename='not_work_reamrk') urlpatterns = [ path('facelogin/', FaceLogin.as_view()), + path('facelogin_path/', FacePathLogin.as_view()), path('', include(router.urls)), ] diff --git a/hb_server/apps/hrm/views.py b/hb_server/apps/hrm/views.py index c9121b0..303e25d 100644 --- a/hb_server/apps/hrm/views.py +++ b/hb_server/apps/hrm/views.py @@ -10,7 +10,7 @@ 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, NotWorkRemark -from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginSerializer, NotWorkRemarkListSerializer +from apps.hrm.serializers import ClockRecordListSerializer, EmployeeNotWorkRemarkSerializer, EmployeeSerializer, FaceClockCreateSerializer, FaceLoginPathSerializer, FaceLoginSerializer, NotWorkRemarkListSerializer @@ -106,12 +106,12 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): def create(self, request, *args, **kwargs): now = timezone.now() now_local = timezone.localtime() - if 8<=now_local.hour<=17: + if 6<=now_local.hour<=17: base64_data = base64.urlsafe_b64decode(tran64( request.data.get('base64').replace(' ', '+'))) - user, msg = HRMService.face_compare_from_base64(base64_data) + user, msg = HRMService.face_compare_from_base64(base64_data, request.data.get('tolerance', 0.36)) if user: - ins, created = ClockRecord.objects.get_or_create( + ClockRecord.objects.get_or_create( create_by = user, create_time__hour__range = [8,18], create_time__year=now_local.year, create_time__month=now_local.month, create_time__day=now_local.day, @@ -120,9 +120,9 @@ class ClockRecordViewSet(CreateModelMixin, ListModelMixin, GenericViewSet): 'create_by':user, 'create_time':now }) - if not created: - ins.update_time = now - ins.save() + # if not created: + # ins.update_time = now + # ins.save() # 设为在岗 Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now) return Response(UserSimpleSerializer(instance=user).data) @@ -160,7 +160,45 @@ class FaceLogin(CreateAPIView): 人脸识别登录 """ base64_data = base64.urlsafe_b64decode(tran64(request.data.get('base64').replace(' ', '+'))) - user, msg = HRMService.face_compare_from_base64(base64_data) + user, msg = HRMService.face_compare_from_base64(base64_data, request.data.get('tolerance', 0.36)) + if user: + refresh = RefreshToken.for_user(user) + # 可设为在岗 + now = timezone.now() + now_local = timezone.localtime() + if 8<=now_local.hour<=17: + ins, created = ClockRecord.objects.get_or_create( + create_by = user, create_time__hour__range = [8,18], + create_time__year=now_local.year, create_time__month=now_local.month, + create_time__day=now_local.day, + defaults={ + 'type':ClockRecord.ClOCK_WORK1, + 'create_by':user, + 'create_time':now + }) + # 设为在岗 + if created: + Employee.objects.filter(user=user).update(is_atwork=True, last_check_time=now) + + return Response({ + 'refresh': str(refresh), + 'access': str(refresh.access_token), + 'username':user.username, + 'name':user.name + }) + return Response(msg, status=status.HTTP_400_BAD_REQUEST) + +class FacePathLogin(CreateAPIView): + authentication_classes = [] + permission_classes = [] + serializer_class = FaceLoginPathSerializer + + + def create(self, request, *args, **kwargs): + """ + 人脸识别登录-文件地址 + """ + user, msg = HRMService.face_compare_from_path(request.data.get('path'), request.data.get('tolerance', 0.36)) if user: refresh = RefreshToken.for_user(user) # 可设为在岗 diff --git a/hb_server/apps/inm/views.py b/hb_server/apps/inm/views.py index c528834..1b7a03c 100644 --- a/hb_server/apps/inm/views.py +++ b/hb_server/apps/inm/views.py @@ -82,8 +82,7 @@ class FIFOItemViewSet(ListModelMixin, CreateModelMixin, DestroyModelMixin, Updat """ 出入库记录详情表 """ - perms_map = {'get': '*', 'post':'fifoitem_create', - 'put':'fifoitem_update', 'delete':'fifoitem_delete'} + perms_map = {'*':'*'} queryset = FIFOItem.objects.select_related('material', 'fifo').prefetch_related('files').all() serializer_class = FIFOItemSerializer filterset_fields = ['material', 'fifo', diff --git a/hb_server/apps/mtm/migrations/0050_auto_20220419_1109.py b/hb_server/apps/mtm/migrations/0050_auto_20220419_1109.py new file mode 100644 index 0000000..344566a --- /dev/null +++ b/hb_server/apps/mtm/migrations/0050_auto_20220419_1109.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.9 on 2022-04-19 03:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0049_auto_20220222_0944'), + ] + + operations = [ + migrations.AddField( + model_name='recordform', + name='export_template', + field=models.CharField(blank=True, max_length=200, null=True, verbose_name='导出模板'), + ), + migrations.AddField( + model_name='recordformfield', + name='span', + field=models.PositiveSmallIntegerField(default=12, verbose_name='span值'), + ), + ] diff --git a/hb_server/apps/mtm/migrations/0051_material_file.py b/hb_server/apps/mtm/migrations/0051_material_file.py new file mode 100644 index 0000000..027f417 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0051_material_file.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.9 on 2022-04-27 02:36 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('system', '0004_auto_20220318_1705'), + ('mtm', '0050_auto_20220419_1109'), + ] + + operations = [ + migrations.AddField( + model_name='material', + name='file', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='system.file', verbose_name='文件'), + ), + ] diff --git a/hb_server/apps/mtm/migrations/0052_material_brand.py b/hb_server/apps/mtm/migrations/0052_material_brand.py new file mode 100644 index 0000000..3c27b29 --- /dev/null +++ b/hb_server/apps/mtm/migrations/0052_material_brand.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-06-06 05:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0051_material_file'), + ] + + operations = [ + migrations.AddField( + model_name='material', + name='brand', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='牌号'), + ), + ] diff --git a/hb_server/apps/mtm/migrations/0053_alter_material_unit.py b/hb_server/apps/mtm/migrations/0053_alter_material_unit.py new file mode 100644 index 0000000..ef671ae --- /dev/null +++ b/hb_server/apps/mtm/migrations/0053_alter_material_unit.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-06-09 00:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0052_material_brand'), + ] + + operations = [ + migrations.AlterField( + model_name='material', + name='unit', + field=models.CharField(default='块', max_length=10, verbose_name='基准计量单位'), + ), + ] diff --git a/hb_server/apps/mtm/migrations/0054_usedstep_reuse_form.py b/hb_server/apps/mtm/migrations/0054_usedstep_reuse_form.py new file mode 100644 index 0000000..1b803dc --- /dev/null +++ b/hb_server/apps/mtm/migrations/0054_usedstep_reuse_form.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-06-27 05:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mtm', '0053_alter_material_unit'), + ] + + operations = [ + migrations.AddField( + model_name='usedstep', + name='reuse_form', + field=models.BooleanField(default=True, verbose_name='复用上表'), + ), + ] diff --git a/hb_server/apps/mtm/models.py b/hb_server/apps/mtm/models.py index 9ea0abd..80ab3b3 100644 --- a/hb_server/apps/mtm/models.py +++ b/hb_server/apps/mtm/models.py @@ -36,13 +36,15 @@ class Material(CommonAModel): ) name = models.CharField('物料名称', max_length=100) number = models.CharField('编号', max_length=100, unique=True) + brand = models.CharField('牌号', max_length=100, null=True, blank=True) specification = models.CharField('型号', max_length=100, null=True, blank=True) type = models.PositiveSmallIntegerField('物料类型', choices= type_choices, default=1) sort_str = models.CharField('排序字符', max_length=100, null=True, blank=True) - unit = models.CharField('基准计量单位', choices=unit_choices, default='块', max_length=10) + unit = models.CharField('基准计量单位', default='块', max_length=10) count = models.PositiveIntegerField('物料库存总数', default=0) count_safe = models.PositiveIntegerField('安全库存总数', null=True, blank=True) piece_count = models.PositiveSmallIntegerField('单片玻璃数量', null=True, blank=True) + file = models.ForeignKey(File, verbose_name='文件', on_delete=models.SET_NULL, null=True, blank=True) class Meta: verbose_name = '物料表' verbose_name_plural = verbose_name @@ -139,6 +141,7 @@ class RecordForm(CommonAModel): material = models.ForeignKey(Material, verbose_name='关联物料', on_delete=models.CASCADE, null=True, blank=True) enabled = models.BooleanField('是否启用', default=False) number = models.CharField('编号', null=True, blank=True, max_length=32) + export_template = models.CharField('导出模板', max_length=200, null=True, blank=True) class Meta: verbose_name = '记录表格' @@ -212,7 +215,7 @@ class RecordFormField(CommonAModel): parent = models.ForeignKey('self', verbose_name='父', on_delete=models.CASCADE, null=True, blank=True) draw_template = models.CharField('绘图模板', max_length=200, null=True, blank=True) - + span = models.PositiveSmallIntegerField('span值', default=12) class Meta: verbose_name = '记录表格字段' @@ -263,6 +266,7 @@ class UsedStep(CommonADModel): """ step = models.ForeignKey(Step, verbose_name='子工序', on_delete=models.CASCADE, related_name='usedstep') need_test = models.BooleanField('工序内检验', default=False) + reuse_form = models.BooleanField('复用上表', default=True) remark = models.TextField('生产备注', null=True, blank=True) subproduction = models.ForeignKey(SubProduction, verbose_name='关联生产分解', on_delete=models.CASCADE, related_name='usedstep_subproduction') diff --git a/hb_server/apps/mtm/serializers.py b/hb_server/apps/mtm/serializers.py index 942ae3b..ec43e99 100644 --- a/hb_server/apps/mtm/serializers.py +++ b/hb_server/apps/mtm/serializers.py @@ -8,12 +8,14 @@ from apps.system.serializers import FileSimpleSerializer, OrganizationSimpleSeri class MaterialSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): + file_ = FileSimpleSerializer(source='file', read_only=True) class Meta: model = Material fields = '__all__' class MaterialDetailSerializer(serializers.ModelSerializer): processes_ = serializers.SerializerMethodField() + file_ = FileSimpleSerializer(source='file', read_only=True) class Meta: model = Material fields = '__all__' @@ -44,7 +46,7 @@ class PackItemUpdateSerializer(serializers.ModelSerializer): class MaterialSimpleSerializer(serializers.ModelSerializer): class Meta: model = Material - fields = ['id', 'name', 'number', 'unit','specification', 'type', 'count', 'count_safe'] + fields = ['id', 'name', 'number', 'unit','specification', 'type', 'count', 'count_safe', 'brand'] class PackItemDetailSerializer(serializers.ModelSerializer): material_ = MaterialSimpleSerializer(source='material', read_only=True) @@ -161,7 +163,7 @@ class UsedStepCreateSerializer(serializers.ModelSerializer): """ class Meta: model = UsedStep - fields = ['step', 'subproduction', 'remark', 'need_test'] + fields = ['step', 'subproduction', 'remark', 'need_test', 'reuse_form'] class UsedStepUpdateSerializer(serializers.ModelSerializer): """ @@ -169,7 +171,7 @@ class UsedStepUpdateSerializer(serializers.ModelSerializer): """ class Meta: model = UsedStep - fields = ['remark', 'need_test'] + fields = ['remark', 'need_test', 'reuse_form'] class UsedStepListSerializer(serializers.ModelSerializer): """ @@ -209,10 +211,10 @@ class RecordFormSerializer(serializers.ModelSerializer): class RecordFormCreateSerializer(serializers.ModelSerializer): form = serializers.PrimaryKeyRelatedField( - queryset=RecordForm.objects.all(), label="复制表ID", required=False) + queryset=RecordForm.objects.all(), label="复制表ID", required=False, write_only=True) class Meta: model = RecordForm - fields = ['name', 'type', 'step', 'material', 'number', 'enabled', 'form'] + fields = ['name', 'type', 'step', 'material', 'number', 'enabled', 'form', 'export_template'] # def validate(self, attrs): @@ -225,7 +227,7 @@ class RecordFormCreateSerializer(serializers.ModelSerializer): class RecordFormUpdateSerializer(serializers.ModelSerializer): class Meta: model = RecordForm - fields = ['name', 'type', 'number', 'enabled'] + fields = ['name', 'type', 'number', 'enabled', 'export_template', 'material'] # def validate(self, attrs): # if attrs['enabled']: @@ -278,7 +280,7 @@ class RecordFormFieldCreateSerializer(serializers.ModelSerializer): class RecordFormFieldUpdateSerializer(serializers.ModelSerializer): class Meta: model = RecordFormField - exclude = ['field_key'] + fields = '__all__' class RecordFormFieldSimpleSerializer(serializers.ModelSerializer): class Meta: diff --git a/hb_server/apps/mtm/views.py b/hb_server/apps/mtm/views.py index 2862a39..7ab01d0 100644 --- a/hb_server/apps/mtm/views.py +++ b/hb_server/apps/mtm/views.py @@ -19,7 +19,7 @@ class MaterialViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): """ perms_map = {'get': '*', 'post': 'material_create', 'put': 'material_update', 'delete': 'material_delete'} - queryset = Material.objects.all() + queryset = Material.objects.select_related('file').all() serializer_class = MaterialSerializer search_fields = ['name', 'number'] filterset_class = MaterialFilterSet @@ -35,8 +35,8 @@ class PackItemViewSet(CreateUpdateModelAMixin, ModelViewSet): """ 装箱项目-增删改查 """ - perms_map = {'get': '*', 'post': 'packitem_create', - 'put': 'packitem_update', 'delete': 'packitem_delete'} + perms_map = {'get': '*', 'post': 'material_update', + 'put': 'material_update', 'delete': 'material_update'} queryset = PackItem.objects.all() serializer_class = PackItemSerializer search_fields = ['name', 'number'] @@ -64,7 +64,7 @@ class ProcessViewSet(PageOrNot, CreateUpdateModelAMixin, ModelViewSet): ordering_fields = ['number'] ordering = ['number'] - @action(methods=['get'], detail=True, perms_map={'get':'process_update'}, pagination_class=None, serializer_class=StepDetailSerializer) + @action(methods=['get'], detail=True, perms_map={'get':'*'}, pagination_class=None, serializer_class=StepDetailSerializer) def steps(self, request, pk=None): """ 工序下的子工序 @@ -166,7 +166,7 @@ class UsedStepViewSet(OptimizationMixin, CreateModelMixin, DestroyModelMixin, Li 'put':'subproduction_update', 'delete':'subproduction_update'} queryset = UsedStep.objects.all() filterset_fields = ['subproduction', 'step'] - ordering = ['step__sort', '-step__create_time'] + ordering = ['step__number', 'step__create_time'] def get_serializer_class(self): if self.action =='create': @@ -179,12 +179,12 @@ class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet """ 记录表格增删改查 """ - perms_map = {'get':'*', 'post':'recordform_create', - 'put':'recordform_update', 'delete':'recordform_delete'} + perms_map = {'get':'*', 'post':'*', + 'put':'*', 'delete':'*'} queryset = RecordForm.objects.all() filterset_fields = ['step', 'type', 'material', 'number', 'enabled'] search_fields = ['name'] - ordering='id' + ordering=['id'] def get_serializer_class(self): @@ -216,10 +216,10 @@ class RecordFormViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelViewSet instance.save() if form: for i in RecordFormField.objects.filter(form=form, is_deleted=False): - i.pk = None - i.form = instance - i.parent = None - i.save() + i.pk = None + i.form = instance + i.parent = None + i.save() return Response() @@ -229,13 +229,13 @@ class RecordFormFieldViewSet(OptimizationMixin, CreateUpdateModelAMixin, ModelVi """ 表格字段表 增删改查 """ - perms_map = {'get':'*', 'post':'recordform_update', - 'put':'recordform_update', 'delete':'recordform_update'} + perms_map = {'get':'*', 'post':'*', + 'put':'*', 'delete':'*'} queryset = RecordFormField.objects.all() filterset_fields = ['field_type', 'form'] search_fields = ['field_name', 'field_key'] - ordering = 'sort' - ordering_fields = ['sort', 'id'] + ordering = ['sort', 'create_time'] + ordering_fields = ['sort', 'create_time'] def get_serializer_class(self): if self.action =='create': diff --git a/hb_server/apps/pm/serializers.py b/hb_server/apps/pm/serializers.py index 0d653fd..7351932 100644 --- a/hb_server/apps/pm/serializers.py +++ b/hb_server/apps/pm/serializers.py @@ -2,7 +2,6 @@ from apps.mtm.models import RecordForm from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from rest_framework import serializers from apps.qm.models import TestRecord -from apps.qm.serializers import TestRecordShortSerializer from apps.sam.serializers import OrderSerializer, OrderSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, RecordFormSimpleSerializer, SubProductionSimpleSerializer from apps.system.serializers import OrganizationSimpleSerializer, UserSimpleSerializer @@ -12,7 +11,7 @@ from utils.mixins import DynamicFieldsSerializerMixin class ProductionPlanCreateFromOrderSerializer(serializers.ModelSerializer): class Meta: model = ProductionPlan - fields = ['order', 'count', 'start_date', 'end_date'] + fields = ['order', 'count', 'start_date', 'end_date', 'number'] class ProductionPlanSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): order_ = OrderSimpleSerializer(source='order', read_only=True) @@ -21,6 +20,11 @@ class ProductionPlanSerializer(DynamicFieldsSerializerMixin, serializers.ModelSe model = ProductionPlan fields ='__all__' +class ProductionPlanSimpleSerializer(serializers.ModelSerializer): + class Meta: + model = ProductionPlan + fields = ['number', 'state'] + class ResourceCalSerializer(serializers.Serializer): id = serializers.IntegerField(label='产品ID') count = serializers.IntegerField(label='生产数量') @@ -37,6 +41,7 @@ class ResourceCalListSerializer(serializers.ListSerializer): class SubProductionPlanListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): workshop_ = OrganizationSimpleSerializer(source='workshop', read_only=True) + production_plan_ = ProductionPlanSimpleSerializer(source='production_plan', read_only=True) process_ = ProcessSimpleSerializer(source='process', read_only=True) subproduction_ = SubProductionSimpleSerializer(source='subproduction', read_only=True) product_ = MaterialSimpleSerializer(source='product', read_only=True) @@ -44,19 +49,20 @@ class SubProductionPlanListSerializer(DynamicFieldsSerializerMixin, serializers. leader_1_ = UserSimpleSerializer(source='leader_1', read_only=True) leader_2_ = UserSimpleSerializer(source='leader_2', read_only=True) leader_3_ = UserSimpleSerializer(source='leader_3', read_only=True) - first_test_ = TestRecordShortSerializer(source='first_test', read_only=True) - # first_test_ = serializers.SerializerMethodField() + first_test_ = serializers.SerializerMethodField() class Meta: model=SubProductionPlan fields = '__all__' def get_plan_product_(self, obj): return MaterialSimpleSerializer(instance=obj.production_plan.product).data + + def get_first_test_(self, obj): + tr = obj.first_test + if obj.first_test: + return {'id':tr.id, 'is_submited':tr.is_submited} + return None - # def get_first_test_(self, obj): - # if obj.first_test: - # return TestRecordShortSerializer(instance=obj.first_test).data - # return None class SubProductionPlanUpdateSerializer(serializers.ModelSerializer): diff --git a/hb_server/apps/pm/views.py b/hb_server/apps/pm/views.py index 0cd7d44..fa23f72 100644 --- a/hb_server/apps/pm/views.py +++ b/hb_server/apps/pm/views.py @@ -71,7 +71,7 @@ class ProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, CreateModel pass else: raise APIException('排产数量错误') - instance = serializer.save(create_by=request.user, product=order.product, number='JH'+ranstr(7)) + instance = serializer.save(create_by=request.user, product=order.product) updateOrderPlanedCount(instance.order) return Response() @@ -150,7 +150,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo """ 子生产计划-列表/修改 """ - perms_map = {'get': '*', 'put':'subplan_update'} + perms_map = {'get': '*', 'put':'*'} queryset = SubProductionPlan.objects.select_related('process', 'workshop', 'subproduction', 'product', 'production_plan__product', 'leader_1', 'leader_2', 'leader_3', 'first_test') @@ -176,7 +176,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo serializer = SubProductionProgressSerializer(instance=obj.progress_subplan, many=True) return Response(serializer.data) - @action(methods=['post'], detail=True, perms_map={'post':'subplan_issue'}, serializer_class=serializers.Serializer) + @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) @transaction.atomic def issue(self, request, pk=None): """ @@ -193,7 +193,7 @@ class SubProductionPlanViewSet(CreateUpdateModelAMixin, ListModelMixin, UpdateMo return Response() raise APIException('计划状态有误') - @action(methods=['post'], detail=True, perms_map={'post':'subplan_start'}, serializer_class=serializers.Serializer) + @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=serializers.Serializer) def start(self, request, pk=None): """ 开始生产 diff --git a/hb_server/apps/qm/export.py b/hb_server/apps/qm/export.py new file mode 100644 index 0000000..e34ce99 --- /dev/null +++ b/hb_server/apps/qm/export.py @@ -0,0 +1,40 @@ +from docxtpl import DocxTemplate, InlineImage +from apps.qm.models import TestRecord +from apps.qm.serializers import TestRecordDetailSerializer +from server.settings import BASE_DIR +from utils.tools import ranstr +from docx.shared import Mm +from apps.mtm.models import RecordFormField + + + +def exprot_test_record(tr:TestRecord, tm:str): + data = TestRecordDetailSerializer(instance=tr).data + doc = DocxTemplate(BASE_DIR + tm) + edata = {} + edata['formName'] = data['form_']['name'] + edata['glassSpec'] = data['wproduct_']['material_']['specification'] if data['wproduct_'] else '' + edata['glassNum'] = data['wproduct_']['number'] if data['wproduct_'] else '' + edata['testDate'] = data['update_time'][0:11] if data['update_time'] else data['create_time'][0:11] + tester_s = data['update_by_']['signature'] if data['update_by_'] else data['create_by_']['signature'] + if tester_s: # 签名 + edata['tester'] = InlineImage(doc, BASE_DIR + tester_s, height=Mm(10)) + + for i in data['record_data']: + if i['field_type'] == str(RecordFormField.FIELD_DRAW): + edata[i['field_key']] = { + 's': InlineImage(doc, BASE_DIR + i['field_value'], width=Mm(40)), + 'm': InlineImage(doc, BASE_DIR + i['field_value'], width=Mm(60)), + 'l': InlineImage(doc, BASE_DIR + i['field_value'], width=Mm(80)), + } + else: + edata[i['field_key']] = i['origin_value'] if 'origin_value' in i else '' + if i['field_value'] not in ['', None]: + edata[i['field_key']] = i['field_value'] + # 开始生成word + doc.render(edata) + filename = edata['formName'] + '_' + ranstr(6) + path = '/media/export/' + filename + '.docx' + filepath = BASE_DIR + path + doc.save(filepath) + return path \ No newline at end of file diff --git a/hb_server/apps/qm/migrations/0029_alter_testrecord_number.py b/hb_server/apps/qm/migrations/0029_alter_testrecord_number.py new file mode 100644 index 0000000..7a05324 --- /dev/null +++ b/hb_server/apps/qm/migrations/0029_alter_testrecord_number.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.9 on 2022-06-13 01:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('qm', '0028_alter_testrecord_fifo_item'), + ] + + operations = [ + migrations.AlterField( + model_name='testrecord', + name='number', + field=models.CharField(blank=True, max_length=20, null=True, verbose_name='玻璃编号'), + ), + ] diff --git a/hb_server/apps/qm/models.py b/hb_server/apps/qm/models.py index 3e2bc54..4a042b2 100644 --- a/hb_server/apps/qm/models.py +++ b/hb_server/apps/qm/models.py @@ -72,6 +72,7 @@ class TestRecord(CommonADModel): is_submited = models.BooleanField('是否提交', default=False) is_midtesting = models.BooleanField('是否子工序检验中', default=False) remark = models.TextField('备注', default='') + number = models.CharField('玻璃编号', max_length=20, null=True, blank=True) class TestRecordItem(CommonADModel): diff --git a/hb_server/apps/qm/serializers.py b/hb_server/apps/qm/serializers.py index 93d5a76..48ab58d 100644 --- a/hb_server/apps/qm/serializers.py +++ b/hb_server/apps/qm/serializers.py @@ -1,7 +1,8 @@ from rest_framework import serializers from apps.mtm.models import RecordForm, RecordFormField from apps.mtm.serializers import RecordFormFieldSerializer, RecordFormSimpleSerializer -from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer +from apps.system.serializers import FileSimpleSerializer, UserSimpleSerializer, UserStandardSerializer +from apps.wpm.serializers import WProductBaseSerializer from .models import Standard, TestItem, TestRecord, TestRecordItem from django.db import transaction @@ -97,8 +98,9 @@ class TestRecordDetailSerializer(serializers.ModelSerializer): # record_data = TestRecordItemSerializer(source='item_test_record', read_only=True, many=True) record_data = serializers.SerializerMethodField() origin_test_ = TestRecordDetailBaseSerializer(source='origin_test', read_only=True) - create_by_ = UserSimpleSerializer(source='create_by', read_only=True) - update_by_ = UserSimpleSerializer(source='update_by', read_only=True) + create_by_ = UserStandardSerializer(source='create_by', read_only=True) + update_by_ = UserStandardSerializer(source='update_by', read_only=True) + wproduct_ = WProductBaseSerializer(source='wproduct', read_only=True) class Meta: model = TestRecord fields = '__all__' @@ -127,7 +129,7 @@ class TestRecordUpdateSerializer(serializers.ModelSerializer): record_data = TestRecordItemUpdatexSerializer(many=True, write_only=True) class Meta: model = TestRecord - fields = ['is_testok', 'record_data'] + fields = ['is_testok', 'record_data', 'number', 'remark'] def update(self, instance, validated_data): # 获取更新人 diff --git a/hb_server/apps/qm/views.py b/hb_server/apps/qm/views.py index 304d954..c63f9f7 100644 --- a/hb_server/apps/qm/views.py +++ b/hb_server/apps/qm/views.py @@ -1,5 +1,7 @@ from rest_framework import exceptions, serializers from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin +from apps.mtm.models import RecordFormField +from apps.qm.export import exprot_test_record from apps.qm.serializers import StandardCreateUpdateSerializer, StandardSerializer, TestItemCreateUpdateSerializer, TestItemSerializer, TestRecordCreateSerializer, TestRecordDetailSerializer, TestRecordListSerializer, TestRecordUpdateSerializer from apps.qm.models import Standard, TestItem, TestRecord, TestRecordItem from django.shortcuts import render @@ -13,6 +15,7 @@ from rest_framework.decorators import action from apps.wpm.models import WProduct from apps.wpm.services import WpmService +from rest_framework.exceptions import ParseError # Create your views here. class StandardViewSet(CreateUpdateModelAMixin, ModelViewSet): """ @@ -92,11 +95,25 @@ class TestRecordViewSet(ListModelMixin, UpdateModelMixin, RetrieveModelMixin, De raise exceptions.APIException('存在未填写项目:'+ ','.join(list(items_not.values_list('form_field__field_name', flat=True)))) with transaction.atomic(): obj.is_submited=True + obj.update_by = request.user obj.save() if obj.wproduct: WpmService.update_wproduct_by_test(obj, request.user) # 这里已经做了日志记录和进度计算 return Response() + @action(methods=['get'], detail=True, perms_map={'get':'*'}, serializer_class=serializers.Serializer) + def export(self, request, pk=None): + """ + 按模板导出 + """ + instance = self.get_object() + # tm = instance.form.export_template + tm = '/media/default/tmp.docx' + if tm: + path = exprot_test_record(tr=instance, tm=tm) + return Response({'path':path}) + raise ParseError('未配置导出模板') + # def create(self, request, *args, **kwargs): # serializer = self.get_serializer(data=request.data) # serializer.is_valid(raise_exception=True) diff --git a/hb_server/apps/sam/serializers.py b/hb_server/apps/sam/serializers.py index 31d2ab0..2fd2928 100644 --- a/hb_server/apps/sam/serializers.py +++ b/hb_server/apps/sam/serializers.py @@ -43,10 +43,10 @@ class ContractCreateUpdateSerializer(serializers.ModelSerializer): class OrderCreateUpdateSerializer(serializers.ModelSerializer): class Meta: model = Order - fields = ['customer', 'contract', 'product', 'count', 'delivery_date', 'need_mtest'] + fields = ['customer', 'contract', 'product', 'count', 'delivery_date', 'need_mtest', 'number'] def create(self, validated_data): - validated_data['number'] = 'DD' + ranstr(7) + # validated_data['number'] = 'DD' + ranstr(7) return super().create(validated_data) class OrderSerializer(DynamicFieldsSerializerMixin, serializers.ModelSerializer): diff --git a/hb_server/apps/srm/views.py b/hb_server/apps/srm/views.py index f92d6b3..2fe296e 100644 --- a/hb_server/apps/srm/views.py +++ b/hb_server/apps/srm/views.py @@ -130,7 +130,7 @@ class AtWorkCountView(CreateAPIView): """ 到岗天数统计 """ - perms_map = {'get':'*'} + perms_map = {'post':'*'} serializer_class = AtWorkCountSerializer def create(self, request, *args, **kwargs): diff --git a/hb_server/apps/system/permission.py b/hb_server/apps/system/permission.py index 397d906..4a7810d 100644 --- a/hb_server/apps/system/permission.py +++ b/hb_server/apps/system/permission.py @@ -8,7 +8,7 @@ def get_permission_list(user): """ 获取权限列表,可用redis存取 """ - perms_list = ['visitor'] + perms_list = [] if user.is_superuser: perms_list = ['admin'] else: @@ -17,8 +17,12 @@ def get_permission_list(user): if roles: for i in roles: perms = perms | i.perms.all() - perms_list = perms.values_list('method', flat=True) - perms_list = list(set(perms_list)) + perms_list_l = perms.values_list('method', flat=True) + for i in perms_list_l: + if i and i not in perms_list: + perms_list.append(i) + if len(perms_list) == 0: + perms_list = ['visitor'] cache.set(user.username + '__perms', perms_list, 60*60) return perms_list @@ -35,6 +39,7 @@ class RbacPermission(BasePermission): :param view: :return: """ + perms = ['visitor'] if not request.user: if request.META.get('HTTP_AUTHORIZATION', None) == 'big_screen': perms = ['visitor'] diff --git a/hb_server/apps/system/serializers.py b/hb_server/apps/system/serializers.py index 45d8c29..fb2ba3d 100644 --- a/hb_server/apps/system/serializers.py +++ b/hb_server/apps/system/serializers.py @@ -52,7 +52,7 @@ class PTaskSerializer(serializers.ModelSerializer): class FileSimpleSerializer(serializers.ModelSerializer): class Meta: - model =File + model = File fields = ['id', 'name', 'file', 'path'] class FileSerializer(serializers.ModelSerializer): @@ -126,6 +126,16 @@ class UserSimpleSerializer(serializers.ModelSerializer): model = User fields = ['id', 'username', 'name'] +class UserStandardSerializer(serializers.ModelSerializer): + signature= serializers.SerializerMethodField() + class Meta: + model = User + fields = ['id', 'username', 'name', 'signature'] + + def get_signature(self, obj): + if hasattr(obj, 'employee_user'): + return obj.employee_user.signature + return None # class UserStandardSerializer(serializers.ModelSerializer): # dept_name = serializers.StringRelatedField(source='dept') # class Meta: diff --git a/hb_server/apps/wpm/models.py b/hb_server/apps/wpm/models.py index 085a20d..45c5fcb 100644 --- a/hb_server/apps/wpm/models.py +++ b/hb_server/apps/wpm/models.py @@ -136,6 +136,13 @@ class WProduct(CommonAModel): """ return self.test_wproduct.filter(type=TestRecord.TEST_PROCESS, is_submited=True).order_by('-id').first() + @property + def last_wp_test(self): + """ + 最后提交的本产品本工序自检 + """ + return self.test_wproduct.filter(is_submited=True, type=TestRecord.TEST_PROCESS, subproduction_plan=self.subproduction_plan).order_by('-id').first() + class WprouctTicket(CommonAModel): """ diff --git a/hb_server/apps/wpm/serializers.py b/hb_server/apps/wpm/serializers.py index 79b4234..8479f45 100644 --- a/hb_server/apps/wpm/serializers.py +++ b/hb_server/apps/wpm/serializers.py @@ -8,11 +8,11 @@ from apps.inm.serializers import WareHouseSimpleSerializer from apps.inm.services import InmService from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial from apps.mtm.serializers import MaterialSimpleSerializer, ProcessSimpleSerializer, RecordFormSimpleSerializer, StepSimpleSerializer - -from apps.pm.models import SubProductionPlan, SubProductionProgress +from django.db.models.aggregates import Sum, Count +from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from apps.pm.serializers import SubproductionPlanSimpleSerializer +from apps.pm.serializers import ProductionPlanSerializer, ProductionPlanSimpleSerializer, SubproductionPlanSimpleSerializer from apps.qm.models import TestRecord, TestRecordItem from apps.sam.serializers import OrderSimpleSerializer from apps.system.models import User @@ -168,6 +168,8 @@ class WProductListSerializer(DynamicFieldsSerializerMixin, serializers.ModelSeri children = serializers.SerializerMethodField() to_order_ = OrderSimpleSerializer(source='to_order', read_only=True) order_ = serializers.SerializerMethodField() + create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + update_by_ = UserSimpleSerializer(source='update_by', read_only=True) class Meta: model = WProduct fields = '__all__' @@ -242,6 +244,8 @@ class WProductDetailSerializer(serializers.ModelSerializer): children = serializers.SerializerMethodField() to_order_ = OrderSimpleSerializer(source='to_order', read_only=True) order_ = OrderSimpleSerializer(source='subproduction_plan__production_plan__order', read_only=True) + create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + update_by_ = UserSimpleSerializer(source='update_by', read_only=True) class Meta: model = WProduct fields = '__all__' @@ -254,18 +258,22 @@ class WProductDetailSerializer(serializers.ModelSerializer): class OperationDetailSerializer(serializers.ModelSerializer): create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + update_by_ = UserSimpleSerializer(source='update_by', read_only=True) step_ = StepSimpleSerializer(source='step', read_only=True) class Meta: model = Operation fields = '__all__' class OperationListSerializer(serializers.ModelSerializer): + create_by_ = UserSimpleSerializer(source='create_by', read_only=True) + update_by_ = UserSimpleSerializer(source='update_by', read_only=True) step_ = StepSimpleSerializer(source='step', read_only=True) + out_detail = serializers.SerializerMethodField() # wproduct_ = serializers.SerializerMethodField() - count_work = serializers.SerializerMethodField() - equip_ = serializers.SerializerMethodField() - record_ = serializers.SerializerMethodField() + # count_work = serializers.SerializerMethodField() + # equip_ = serializers.SerializerMethodField() + # record_ = serializers.SerializerMethodField() class Meta: model = Operation fields = '__all__' @@ -273,8 +281,31 @@ class OperationListSerializer(serializers.ModelSerializer): # def get_wproduct_(self, obj): # return WProduct.objects.filter(ow_wproduct__operation=obj).values('id', 'number') + def get_out_detail(self, obj): + rets = [] + if obj.step.type == Step.STEP_TYPE_NOM: + qs = OperationWproduct.objects.filter(operation=obj).values( + 'subproduction_plan__production_plan', + 'subproduction_plan__production_plan__product' + ).annotate(count=Count('wproduct')) + else: + qs = OperationMaterial.objects.filter(operation=obj, + type=SubprodctionMaterial.SUB_MA_TYPE_OUT, subproduction_progress__is_main=True).values( + 'subproduction_plan__production_plan', + 'subproduction_plan__production_plan__product' + ).annotate(count=Sum('count')) + for i in qs: + ret = {} + ret['plan'] = ProductionPlanSimpleSerializer(instance=ProductionPlan.objects.get( + id= i['subproduction_plan__production_plan'])).data + ret['product'] = MaterialSimpleSerializer(instance=Material.objects.get( + id=i['subproduction_plan__production_plan__product'])).data + ret['count'] = i['count'] + rets.append(ret) + return rets + def get_count_work(self, obj): - from django.db.models.aggregates import Sum + count_work = 0 if obj.step.type == Step.STEP_TYPE_NOM: count_work = OperationWproduct.objects.filter(operation=obj).count() diff --git a/hb_server/apps/wpm/services.py b/hb_server/apps/wpm/services.py index 4440122..1587c55 100644 --- a/hb_server/apps/wpm/services.py +++ b/hb_server/apps/wpm/services.py @@ -2,28 +2,54 @@ from django.utils import timezone from typing import List from django.db.models.expressions import F +from apps.mtm.serializers import UsedStepListSerializer from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress -from apps.mtm.models import Material, Step, SubprodctionMaterial +from apps.mtm.models import Material, Step, SubprodctionMaterial, UsedStep from apps.qm.models import TestRecord from apps.system.models import User from apps.wf.models import State, TicketFlow, Transition from apps.wpm.models import Operation, OperationMaterial, WProduct, WproductFlow, WprouctTicket from utils.tools import ranstr +from rest_framework.exceptions import ParseError class WpmService(object): @classmethod - def get_next_step(cls, subproduction_plan:SubProductionPlan, nowstep:Step): + def get_step_info(cls, subproduction_plan:SubProductionPlan, nowstep:Step): """ - 获取下一步骤 + 返回下一步骤, 当前步骤是否需要检验, 当前步骤是否需要复用表 """ - steps_list = subproduction_plan.steps - stepIds = [i['id'] for i in steps_list] - pindex = stepIds.index(nowstep.id) - need_test = steps_list[pindex].get('need_test', False) - if pindex + 1 < len(stepIds): - return Step.objects.get(pk=stepIds[pindex+1]), need_test - else: - return nowstep, need_test + used_steps = UsedStep.objects.filter(subproduction=subproduction_plan.subproduction).order_by('step__number') + if nowstep is None: + try: + nowstep = used_steps[0].step + except: + pass + for index, i in enumerate(used_steps): + if i.step == nowstep: + try: + used_step = used_steps[index+1] + return used_step.step, i.need_test, i.reuse_form + except: + return nowstep, i.need_test, i.reuse_form + raise ParseError('获取步骤信息失败') + + # steps_list = subproduction_plan.steps + # stepIds = [i['id'] for i in steps_list] + # pindex = stepIds.index(nowstep.id) + # if get_next: + # if pindex + 1 < len(stepIds): + # pindex = pindex + 1 + # need_test = steps_list[pindex].get('need_test', False) + # reuse_form = steps_list[pindex].get('reuse_form', True) + # return Step.objects.get(pk=stepIds[pindex]), need_test, reuse_form + # else: + # need_test = steps_list[pindex].get('need_test', False) + # reuse_form = steps_list[pindex].get('reuse_form', True) + # return nowstep, need_test, reuse_form + # else: + # need_test = steps_list[pindex].get('need_test', False) + # reuse_form = steps_list[pindex].get('reuse_form', True) + # return @classmethod def get_subplans_queryset_from_wproducts(cls, wproducts:List): @@ -49,7 +75,6 @@ class WpmService(object): """ is_testok = test.is_testok wproduct = test.wproduct - test_i = None if is_testok: if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: # 复检 wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT @@ -61,7 +86,6 @@ class WpmService(object): elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and \ test.is_midtesting is True: wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT - test_i = test elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and wproduct.material.type == Material.MA_TYPE_GOOD: # 成品检验 wproduct.act_state = WProduct.WPR_ACT_STATE_TOFINALTEST @@ -70,7 +94,10 @@ class WpmService(object): else: wproduct.act_state = WProduct.WPR_ACT_STATE_OK if wproduct.number is None: # 产生半成品编号 - wproduct.number = 'WP'+ranstr(7) + if test.number: + wproduct.number = test.number + else: + raise ParseError('请提供玻璃编号') # 去除ng_sign if wproduct.ng_sign: @@ -108,7 +135,7 @@ class WpmService(object): wproduct.update_by = user wproduct.update_time = timezone.now() - wproduct.test = test_i + wproduct.test = None wproduct.last_test_result = is_testok wproduct.save() # 添加日志 diff --git a/hb_server/apps/wpm/views.py b/hb_server/apps/wpm/views.py index b8234d0..a72ae13 100644 --- a/hb_server/apps/wpm/views.py +++ b/hb_server/apps/wpm/views.py @@ -4,14 +4,14 @@ from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, \ from rest_framework.viewsets import GenericViewSet from apps.inm.models import FIFO, FIFOItem, FIFOItemProduct from apps.inm.services import InmService -from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc +from apps.mtm.models import Material, RecordForm, RecordFormField, Step, SubprodctionMaterial, TechDoc, UsedStep from apps.mtm.serializers import RecordFormDetailSerializer, SubprodctionMaterialListSerializer, TechDocListSerializer from apps.pm.models import ProductionPlan, SubProductionPlan, SubProductionProgress from apps.pm.serializers import SubProductionPlanListSerializer, SubProductionProgressSerializer from apps.qm.models import TestRecord, TestRecordItem from apps.qm.serializers import TestRecordDetailSerializer -from apps.system.mixins import CreateUpdateModelAMixin +from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin from rest_framework.decorators import action from apps.wf.models import Workflow from apps.wpm.filters import CuttingFilterSet, OperationRecordFilterSet, WMaterialFilterSet, WProductFilterSet @@ -36,7 +36,7 @@ from apps.wpm.services import WpmService from django.utils import timezone from rest_framework import status from django.db.models import Count, Q - +from rest_framework.exceptions import ParseError from utils.tools import ranstr @@ -49,11 +49,7 @@ class WPlanViewSet(ListModelMixin, GenericViewSet): """ perms_map = {'get': '*'} queryset = SubProductionPlan.objects.select_related( - 'process', 'workshop', 'subproduction', 'product').filter( - production_plan__state__in =[ - ProductionPlan.PLAN_STATE_WORKING, ProductionPlan.PLAN_STATE_ASSGINED - ] - ) + 'process', 'workshop', 'subproduction', 'product', 'production_plan').exclude(state=SubProductionPlan.SUBPLAN_STATE_PLANING) search_fields = [] serializer_class = SubProductionPlanListSerializer filterset_fields = ['production_plan', @@ -160,7 +156,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): perms_map = {'get': '*'} queryset = WProduct.objects.select_related('step', 'material', 'subproduction_plan', 'warehouse', 'subproduction_plan__production_plan__order', - 'to_order').prefetch_related('wp_child') + 'to_order', 'update_by', 'create_by').prefetch_related('wp_child') serializer_class = WProductListSerializer filterset_class = WProductFilterSet search_fields = ['number', 'material__name', 'subproduction_plan__number'] @@ -189,14 +185,14 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): vdata = serializer.validated_data wproduct = vdata['wproduct'] form = vdata['form'] - if wproduct.test: - raise exceptions.APIException('存在进行中检验') - # 根据情况创建一条检验记录 if wproduct.act_state not in [WProduct.WPR_ACT_STATE_TOTEST, WProduct.WPR_ACT_STATE_TORETEST, WProduct.WPR_ACT_STATE_TOFINALTEST, WProduct.WPR_ACT_STATE_TOCOMBTEST]: raise exceptions.APIException('该产品当前状态不可检验') + if wproduct.test: + raise exceptions.APIException('存在进行中检验') + savedict = dict( create_by=request.user, wproduct=wproduct, @@ -206,7 +202,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): step=wproduct.step, form=form) if wproduct.act_state == WProduct.WPR_ACT_STATE_TORETEST: - # 查找最近一条检验记录 + # 查找最近一条工序检验记录 trs = wproduct.last_process_test savedict['origin_test'] = trs if not trs: @@ -216,9 +212,11 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): savedict['type'] = TestRecord.TEST_FINAL elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOCOMBTEST: savedict['type'] = TestRecord.TEST_COMB - elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST and\ - wproduct.step != wproduct.pre_step: # 如果是工序内检验 - savedict['is_midtesting'] = True + elif wproduct.act_state == WProduct.WPR_ACT_STATE_TOTEST: + _, need_test, _ = WpmService.get_step_info(wproduct.subproduction_plan, wproduct.pre_step) + if need_test and wproduct.step != wproduct.pre_step: + savedict['is_midtesting'] = True + # if UsedStep.objects.filter(subproduction=wproduct.subproduction_plan.subproduction).first().need_test: tr = TestRecord.objects.create(**savedict) # 更新wproduct wproduct.test = tr @@ -439,7 +437,7 @@ class WProductViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): WpmService.add_wproduct_flow_log(instance=obj, change_str=change_str) return Response() - @action(methods=['get'], detail=True, perms_map={'get': 'wp_card'}) + @action(methods=['get'], detail=True, perms_map={'get': '*'}) def card(self, request, pk=None): """ 流程卡 @@ -530,19 +528,16 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd """ perms_map = {'get': '*', 'post':'operation_create', 'put':'operation_update', 'delete':'operation_delete'} - queryset = Operation.objects.select_related('step').prefetch_related( + queryset = Operation.objects.select_related('step', 'create_by', 'update_by').prefetch_related( 'ow_operation', 'oe_operation', 'or_operation').all() serializer_class = OperationListSerializer filterset_fields = ['step', 'step__process', 'is_submited'] ordering_fields = ['id'] ordering = ['-id'] - def get_queryset(self): - return self.queryset.filter(create_by=self.request.user) - def get_serializer_class(self): if self.action == 'retrieve': - return OperationDetailSerializer + return OperationListSerializer elif self.action == 'create': return OperationCreateSerializer elif self.action == 'update': @@ -608,6 +603,11 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd # 查询需要填写的自定义表格 forms = RecordForm.objects.filter( step=step, type=RecordForm.RF_TYPE_DO, enabled=True) + # 根据产品不同进行筛选 这里其实是有问题的,应该是以产出物归属产品来查表,但操作刚创建是没有的,先这样吧 + if 'wproducts' in vdata and splans: + xforms = forms.filter(material=splans[0].production_plan.product) + if xforms.exists(): + forms = xforms for i in forms: opr = OperationRecord() opr.operation = op @@ -637,7 +637,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd opm.material = i.material opm.type = SubprodctionMaterial.SUB_MA_TYPE_TOOL opm.save() - return Response() + return Response(OperationListSerializer(instance=op).data) @action(methods=['post'], detail=True, perms_map={'post': 'operation_submit'}, serializer_class=serializers.Serializer) @transaction.atomic @@ -700,24 +700,32 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd wsp = i.subproduction_plan # 获取下一步子工序 - newstep, needTest = WpmService.get_next_step(wsp, step) + newstep, needTest, reuseForm = WpmService.get_step_info(wsp, step) wp.step = newstep wp.pre_step = step wp.material = wsp.product if step == newstep: wp.act_state = WProduct.WPR_ACT_STATE_TOTEST - if wp.test:# 如果有正在进行的工序中检验 - wp.test.is_midtesting = False - wp.test.is_submited = False - wp.test.save() + last_test = wp.last_wp_test + if last_test and reuseForm: + last_test.is_midtesting = False + last_test.is_submited = False + last_test.save() + wp.test = last_test + wp.save() else: wp.act_state = WProduct.WPR_ACT_STATE_DOWAIT - if needTest: + if needTest: #子工序若需要检验 + print(needTest, reuseForm) wp.act_state = WProduct.WPR_ACT_STATE_TOTEST - if wp.test:# 如果有正在进行的工序中检验 - wp.test.is_submited = False - wp.test.save() + last_test = wp.last_wp_test + if last_test and reuseForm: + last_test.is_midtesting = True + last_test.is_submited = False + last_test.save() + wp.test = last_test + wp.save() wp.operation = None wp.update_by = request.user @@ -734,7 +742,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd raise exceptions.APIException('请选择物料产出') for i in omos: if i.subproduction_progress.is_main: - newstep, _ = WpmService.get_next_step( + newstep, _, _ = WpmService.get_step_info( i.subproduction_plan, step) wpr = dict(material=i.material, step=newstep, act_state=WProduct.WPR_ACT_STATE_DOWAIT, remark='', @@ -755,7 +763,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd if oms_w.count!=1: raise exceptions.APIException('产出数量应为1') # 校验单片数量是否正确, 暂时未写 - newstep, needTest = WpmService.get_next_step( + newstep, needTest, reuseForm = WpmService.get_step_info( oms_w.subproduction_plan, step) wproduct = WProduct() wproduct.material = oms_w.material @@ -763,14 +771,24 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd wproduct.subproduction_plan = oms_w.subproduction_plan if step == newstep: wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST - if wproduct.test:# 如果有正在进行的工序中检验 - wproduct.test.is_midtesting = False - wproduct.test.is_submited = False - wproduct.test.save() + last_test = wproduct.last_wp_test + if last_test and reuseForm: + last_test.is_midtesting = False + last_test.is_submited = False + last_test.save() + wproduct.test = last_test + wproduct.save() else: wproduct.act_state = WProduct.WPR_ACT_STATE_DOWAIT if needTest: wproduct.act_state = WProduct.WPR_ACT_STATE_TOTEST + last_test = wproduct.last_wp_test + if last_test and reuseForm: + last_test.is_midtesting = True + last_test.is_submited = False + last_test.save() + wproduct.test = last_test + wproduct.save() # 更新子计划进度 WpmService.update_subproduction_progress_main( @@ -787,6 +805,7 @@ class OperationViewSet(ListModelMixin, RetrieveModelMixin, CreateModelMixin, Upd else: raise exceptions.APIException('产出物料未填写或填写错误') op.is_submited = True + op.update_by = request.user op.save() # 如果是冷加工 @@ -800,7 +819,7 @@ class OperationWproductViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMix 操作使用的半成品 """ perms_map = {'get': '*', 'post':'operation_update', - 'put':'operation_update', 'delete':'operation_delete'} + 'put':'operation_update', 'delete':'operation_update'} queryset = OperationWproduct.objects.select_related( 'subproduction_plan', 'material').all() serializer_class = OperationWproductListSerializer @@ -831,7 +850,7 @@ class OperationEquipViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin, 操作使用的设备 """ perms_map = {'get': '*', 'post':'operation_update', - 'put':'operation_update', 'delete':'operation_delete'} + 'put':'operation_update', 'delete':'operation_update'} queryset = OperationEquip.objects.select_related( 'operation', 'equip').all() serializer_class = OperationEquipListSerializer @@ -864,7 +883,7 @@ class OperationRecordViewSet(ListModelMixin, DestroyModelMixin, UpdateModelMixin 操作使用的自定义表格 """ perms_map = {'get': '*', 'post':'operation_update', - 'put':'operation_update', 'delete':'operation_delete'} + 'put':'operation_update', 'delete':'operation_update'} queryset = OperationRecord.objects.select_related( 'operation', 'form').all() serializer_class = OperationRecordListSerializer @@ -908,7 +927,7 @@ class OperationMaterialInputViewSet(ListModelMixin, CreateModelMixin, DestroyMod """ 消耗物料 """ - perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_delete'} + perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_update'} queryset = OperationMaterial.objects.select_related( 'operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_IN) serializer_class = OperationMaterialListSerializer @@ -960,7 +979,7 @@ class OperationMaterialOutputViewSet(ListModelMixin, CreateModelMixin, DestroyMo """ 产出物料 """ - perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_delete'} + perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_update'} queryset = OperationMaterial.objects.select_related( 'operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_OUT) serializer_class = OperationMaterialListSerializer @@ -997,7 +1016,7 @@ class OperationMaterialToolViewSet(ListModelMixin, CreateModelMixin, DestroyMode """ 工具工装 """ - perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_delete'} + perms_map = {'get': '*', 'post':'operation_update', 'delete':'operation_update'} queryset = OperationMaterial.objects.select_related( 'operation', 'subproduction_plan').filter(type=SubprodctionMaterial.SUB_MA_TYPE_TOOL) serializer_class = OperationMaterialListSerializer diff --git a/hb_server/media/default/cqm.jpg b/hb_server/media/default/cqm.jpg new file mode 100644 index 0000000..4ed059c Binary files /dev/null and b/hb_server/media/default/cqm.jpg differ diff --git a/hb_server/media/default/cqm2.jpg b/hb_server/media/default/cqm2.jpg new file mode 100644 index 0000000..3c1bfca Binary files /dev/null and b/hb_server/media/default/cqm2.jpg differ diff --git a/hb_server/media/default/lx.jpg b/hb_server/media/default/lx.jpg new file mode 100644 index 0000000..e070812 Binary files /dev/null and b/hb_server/media/default/lx.jpg differ diff --git a/hb_server/media/default/sj.jpg b/hb_server/media/default/sj.jpg new file mode 100644 index 0000000..7bbfd11 Binary files /dev/null and b/hb_server/media/default/sj.jpg differ diff --git a/hb_server/media/default/slx.png b/hb_server/media/default/slx.png new file mode 100644 index 0000000..5a5c994 Binary files /dev/null and b/hb_server/media/default/slx.png differ diff --git a/hb_server/media/default/temp.docx b/hb_server/media/default/temp.docx new file mode 100644 index 0000000..4127f63 Binary files /dev/null and b/hb_server/media/default/temp.docx differ diff --git a/hb_server/media/default/test.png b/hb_server/media/default/test.png new file mode 100644 index 0000000..5455e93 Binary files /dev/null and b/hb_server/media/default/test.png differ diff --git a/hb_server/media/default/test1.png b/hb_server/media/default/test1.png new file mode 100644 index 0000000..45221ed Binary files /dev/null and b/hb_server/media/default/test1.png differ diff --git a/hb_server/media/default/tmp.docx b/hb_server/media/default/tmp.docx new file mode 100644 index 0000000..c3e21ad Binary files /dev/null and b/hb_server/media/default/tmp.docx differ diff --git a/hb_server/media/default/wc.jpg b/hb_server/media/default/wc.jpg new file mode 100644 index 0000000..f5e07ad Binary files /dev/null and b/hb_server/media/default/wc.jpg differ diff --git a/hb_server/server/settings.py b/hb_server/server/settings.py index 32a09e1..2ddf3b1 100644 --- a/hb_server/server/settings.py +++ b/hb_server/server/settings.py @@ -195,16 +195,19 @@ AUTHENTICATION_BACKENDS = ( ) # 缓存配置,有需要可更改为redis -# CACHES = { -# "default": { -# "BACKEND": "django_redis.cache.RedisCache", -# "LOCATION": "redis://127.0.0.1:6379/0", -# "OPTIONS": { -# "CLIENT_CLASS": "django_redis.client.DefaultClient", -# "PICKLE_VERSION": -1 -# } -# } -# } +CACHES = { + # "default": { + # "BACKEND": "django_redis.cache.RedisCache", + # "LOCATION": "redis://127.0.0.1:6379/0", + # "OPTIONS": { + # "CLIENT_CLASS": "django_redis.client.DefaultClient", + # } + # } + 'default': { + 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', + 'LOCATION': os.path.join(BASE_DIR, 'temp'), + } +} # celery配置,celery正常运行必须安装redis CELERY_BROKER_URL = "redis://127.0.0.1:6379/1" # 任务存储