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