From 28ac8082ce22a769c7c487616007f36a112b72cc Mon Sep 17 00:00:00 2001 From: 祖安之光 <11848914+light-of-zuan@user.noreply.gitee.com> Date: 星期二, 08 七月 2025 10:32:10 +0800 Subject: [PATCH] 修改新增 --- src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue | 273 ++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 203 insertions(+), 70 deletions(-) diff --git a/src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue b/src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue index 0ba9970..2194f8e 100644 --- a/src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue +++ b/src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue @@ -42,8 +42,17 @@ </template> </el-table-column> </el-table> -<!-- <org-tree :data="companyInfo.deptList" />--> -<!-- <button @click="exportOrgChart">导出组织架构图</button>--> + + <div class="orgTreeBox" id="org-tree-container"> + <vue3-tree-org + :data="deptList" + :horizontal="false" + :props="treeProps" + :toolBar="tools" + :label-style="labelStyle" + center + /> + </div> <pagination v-show="total > 0" :total="total" @@ -69,6 +78,7 @@ getStandardDetail, getStandardQuality } from "@/api/standardSys/standardSys"; +import {getDepart, getDistribution} from "@/api/orgStructure/depart"; const userStore = useUserStore() const { proxy } = getCurrentInstance(); @@ -82,6 +92,27 @@ }, total: 0, dataList: [], + deptList: { + id: 0, + deptName: "总经理", + children:[ + { + id: '0-1', + deptName: "管理者代表", + children: [] + } + ] + }, + treeProps: { + label: 'deptName' + }, + tools: { + scale: false, restore: false, expand: false, zoom: false, fullscreen: false + }, + labelStyle: { + border: '1px solid #ccc', + background: 'rgba(0,0,0,0)' + }, companyList: [], isAdmin: false, companyInfo: { @@ -99,8 +130,8 @@ {clauseNum: '4.4', content: '质量管理体系及其过程',manage: false,represent: true}, {clauseNum: '5', content: '领导作用'}, {clauseNum: '5.1', content: '领导作用和承诺',manage: true,represent: false}, - {clauseNum: '5.2', content: '方针',manage: true,represent: false}, - {clauseNum: '5.3', content: '组织内的角色、职责和权限',manage: true,represent: false}, + {clauseNum: '5.2', content: '质量方针',manage: true,represent: false}, + {clauseNum: '5.3', content: '组织的岗位、职责和权限',manage: true,represent: false}, {clauseNum: '6', content: ''}, {clauseNum: '6.1', content: '应对风险和机遇的措施',manage: true,represent: false}, {clauseNum: '6.2', content: '质量目标及其实现的策划',manage: false,represent: true}, @@ -112,11 +143,57 @@ {clauseNum: '7.1.3', content: '基础设施',manage: false,represent: true}, {clauseNum: '7.1.4', content: '过程运行环境',manage: false,represent: true}, {clauseNum: '7.1.5', content: '监视和测量资源',manage: false,represent: true}, - {clauseNum: '7.1.6', content: '组织的知识',manage: false,represent: true} + {clauseNum: '7.1.6', content: '组织的知识',manage: false,represent: true}, + {clauseNum: '7.2', content: '能力'}, + {clauseNum: '7.3', content: '意识'}, + {clauseNum: '7.4', content: '沟通'}, + {clauseNum: '7.5', content: '成文信息'}, + {clauseNum: '7.6', content: '质量信息'}, + {clauseNum: '8', content: '运行'}, + {clauseNum: '8.1', content: '运行策划和控制'}, + {clauseNum: '8.2', content: '产品和服务的要求'}, + {clauseNum: '8.2.1', content: '顾客沟通'}, + {clauseNum: '8.2.2', content: '与产品和服务有关的要求的确定'}, + {clauseNum: '8.2.3', content: '与产品和服务有关的要求的评审'}, + {clauseNum: '8.2.4', content: '产品和服务要求的更改'}, + {clauseNum: '8.3', content: '产品和服务的设计和开发'}, + {clauseNum: '8.3.1', content: '总则'}, + {clauseNum: '8.3.2', content: '设计和开发策划'}, + {clauseNum: '8.3.3', content: '设计和开发输入'}, + {clauseNum: '8.3.4', content: '设计和开发控制'}, + {clauseNum: '8.3.5', content: '设计和开发输出'}, + {clauseNum: '8.3.6', content: '设计和开发更改'}, + {clauseNum: '8.3.7', content: '新产品试制'}, + {clauseNum: '8.3.8', content: '设计和开发的试验控制'}, + {clauseNum: '8.4', content: '外部提供过程、产品和服务的控制'}, + {clauseNum: '8.4.1', content: '总则'}, + {clauseNum: '8.4.2', content: '控制类型和程度'}, + {clauseNum: '8.4.3', content: '提供给外部供方的信息'}, + {clauseNum: '8.5', content: '生产和服务提供'}, + {clauseNum: '8.5.1', content: '生产和服务提供的控制'}, + {clauseNum: '8.5.2', content: '标识和可追溯性'}, + {clauseNum: '8.5.3', content: '顾客或外部供方的财产'}, + {clauseNum: '8.5.4', content: '防护'}, + {clauseNum: '8.5.5', content: '交付后的活动'}, + {clauseNum: '8.5.6', content: '更改控制'}, + {clauseNum: '8.5.7', content: '关键过程'}, + {clauseNum: '8.6', content: '产品和服务的放行'}, + {clauseNum: '8.7', content: '不合格输出的控制'}, + {clauseNum: '9', content: '绩效评价'}, + {clauseNum: '9.1', content: '监视、测量、分析和评价'}, + {clauseNum: '9.1.1', content: '总则'}, + {clauseNum: '9.1.2', content: '顾客满意'}, + {clauseNum: '9.1.3', content: '分析和评价'}, + {clauseNum: '9.2', content: '内部审核'}, + {clauseNum: '9.3', content: '管理评审'}, + {clauseNum: '10', content: '持续改进'}, + {clauseNum: '10.1', content: '总则'}, + {clauseNum: '10.2', content: '不合格和纠正措施'}, + {clauseNum: '10.3', content: '持续改进'} ] }); -const { queryParams, total, dataList,companyList, isAdmin, companyInfo } = toRefs(data); +const { queryParams, total, dataList,deptList,treeProps,tools,labelStyle,companyList, isAdmin, companyInfo, caluseList } = toRefs(data); const userInfo = ref() onMounted(async ()=>{ if(userStore.roles.includes('admin')){ @@ -133,26 +210,6 @@ }) -const exportOrgChart=()=> { - const element = document.querySelector('.org-tree'); // 获取组织架构图的容器元素 - html2canvas(element).then(canvas => { - // 创建一个图片元素 - let img = new Image(); - img.src = canvas.toDataURL('image/png'); - - // 创建并触发下载 - img.onload = () => { - let w = img.width; - let h = img.height; - let canvas2 = document.createElement('canvas'); - let ctx = canvas2.getContext('2d'); - canvas2.width = w; - canvas2.height = h; - ctx.drawImage(img, 0, 0, w, h); - saveAs(canvas2.toDataURL('image/png'), '组织架构图.png'); - }; - }); -} const getList = async () => { loading.value = true @@ -166,6 +223,34 @@ loading.value = false } +const getDeptData = async (val) => { + await getDeptList(val.companyId) + const res = await getDistribution({companyId: val.companyId}) + if(res.code == 200){ + for(let item of data.caluseList){ + const sameNum = data.companyInfo.duties.filter(i=>i.clauseNum == item.clauseNum).map(j=> { + return { + deptId: j.deptId, + chooseLab: j.chooseLab + } + } + ) + for(let i of sameNum){ + item[i.deptId] = i.chooseLab? (i.chooseLab==1?'●':'○'):'○' + } + } + }else{ + ElMessage.warning(res.message) + } +} +const getDeptList = async (id) => { + const res = await getDepart({pageNum: 1, pageSize: 999, companyId: id}) + if(res.code == 200){ + data.deptList.children[0].children = res.data + }else{ + ElMessage.warning(res.message) + } +} const addFile = async ()=>{ let params={} if(data.queryParams.companyId){ @@ -200,72 +285,110 @@ } } -const getInfo = async () => { +const getInfo = async (val) => { loading.value = true; - if(!data.queryParams.companyId){ - ElMessage.warning('请选择企业') - } - const res = await getStandardDetail({companyId: data.queryParams.companyId}); + const res = await getStandardDetail({companyId: val.companyId}); if(res.code === 200){ - data.companyInfo.summaries = res.data.companySummaries[0].companySummary - data.companyInfo.policies = res.data.companyQualityPolicies[0].policy - data.companyInfo.deptList = res.data.treeSelects - data.companyInfo.duties = transformDuties(res.data.sysFunctionalDistributions) + if(!res.data || (res.data.companyIndustryTemplates.length == 0 && res.data.companyQualityPolicies.length == 0 && res.data.companySummaries.length == 0 && res.data.sysFunctionalDistributions + .length == 0 && res.data.treeSelects.length == 0)){ + loading.value = false; + return Promise.reject(new Error('该企业暂无质量数据')); + } + data.companyInfo.summaries = res.data.companySummaries ? res.data.companySummaries[0]?.companySummary : [] + data.companyInfo.policies = res.data.companyQualityPolicies ? res.data.companyQualityPolicies[0]?.policy : [] + data.companyInfo.deptList = res.data.treeSelects ? res.data.treeSelects : [] + // data.companyInfo.clauses = data.caluseList + // data.companyInfo.duties = res.data.sysFunctionalDistributions + + const duties = transToTableData(res.data.sysFunctionalDistributions) + data.companyInfo.allDeptNames = duties.allDeptNames + data.companyInfo.clauses = duties.clauses data.companyInfo.temps = res.data.companyIndustryTemplates?.map((item,index)=>{ return { index: index + 1, templateName: item.templateName } - }) + }) || [] + // const imageBase64 = await exportTableToImage() + // data.companyInfo.tableImage = { + // data: imageBase64.split(",")[1], // 去掉 data:image/png;base64, 前缀 + // type: "png", + // } }else{ ElMessage.warning(res.message) } loading.value = false; - } -const transformDuties=(duties)=>{ - let tableData = [] - for(let item of duties){ - const index = tableData.findIndex(i=>i.deptId == item.deptId) - if(index == -1){ - const obj = { - deptName: item.deptName, - deptId: item.deptId - } - obj[item.clauseNum] = item.chooseLab==1?'●':'○' - tableData.push(obj) - }else{ - tableData[index][item.clauseNum] = item.chooseLab==1?'●':'○' - } - } - console.log(tableData,'table1') - tableData = tableData.map(j=>{ - for(let i of data.caluseList){ - if(!j.hasOwnProperty(i.clauseNum)){ - j[i.clauseNum] = '○' - } - } - return j - }) - console.log(tableData,'table2') - return tableData +const transToTableData=(duties)=>{ + // 步骤1:获取所有唯一的部门和条款编号 + const allDeptNames = [...new Set(duties.map(item => item.deptName))]; + const allClauseNums = [...new Set([ + ...data.caluseList.map(c => c.clauseNum), + ...duties.map(d => d.clauseNum) + ])]; + +// 步骤2:为每个条款生成完整的部门数据(缺失数据默认 chooseLab: 0) + const processedClauses = allClauseNums.map(clauseNum => { + const clauseContent = data.caluseList.find(c => c.clauseNum === clauseNum)?.content || ""; + + // 为当前条款生成所有部门的数据(确保每个部门都有值) + const deptValues = allDeptNames.map(deptName => { + const matchedDept = duties.find( + item => item.clauseNum === clauseNum && item.deptName === deptName + ); + return matchedDept ? (matchedDept.chooseLab==1?'●':'○' ): '○'; + + }); + + return { + clauseNum, + content: clauseContent, + deptValues // 数组形式,例如 [0, 1, 0, 0] + }; + }); + +// 最终数据结构 + return { + clauses: processedClauses, + allDeptNames // 用于生成表头 + }; } -const initFile = async (name) => { - await getInfo() - const templatePath = '/qualityFile.docx' +const initFile = async (val) => { try { + await getInfo(val) + await getDeptData(val) + // 2. 等待DOM更新完成 + await nextTick(); + + // 3. 捕获组织结构图图片 + const orgChartImage = await captureElementToImage('org-tree-container'); + data.companyInfo.orgChart = orgChartImage + const templatePath = '/qualityFile.docx' await generateWordDocument( templatePath, data.companyInfo, - name+'质量手册.docx' + val.companyName+'质量手册.docx' ); - ElMessage.success('手册导出成功!'); + ElMessage.success('手册导出成功!') } catch (error) { - console.error('导出失败:', error); - ElMessage.warning('手册导出失败'); + console.error('操作失败:', error); + ElMessage.warning(error.message || '操作失败'); } +} + +const captureElementToImage = async (elementId) => { + const html2canvas = (await import('html2canvas')).default; + const element = document.getElementById(elementId); + if (!element) throw new Error('找不到组织结构图容器'); + + return await html2canvas(element, { + scale: 4, + useCORS: true, + allowTaint: true, + backgroundColor: 'rgba(0,0,0,0)' // 确保背景是白色 + }).then(canvas => canvas.toDataURL('image/png')); } const openDialog = (type, value) => { @@ -303,3 +426,13 @@ } </script> +<style lang="scss"> + .orgTreeBox{ + width: 700px; + height: 700px; + position: absolute; /* 或 fixed */ + left: -9999px; /* 移出可视区域 */ + pointer-events: none; /* 禁止交互 */ + z-index: -1; + } +</style> \ No newline at end of file -- Gitblit v1.9.2