package.json | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
public/qualityFile.docx | 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/main.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/components/exportDoc.js | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/build/conpanyFunctionConsult/orgStructure/dutyDistributeChart/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
package.json
@@ -25,10 +25,12 @@ "axios": "0.27.2", "docx-preview": "^0.3.5", "docxtemplater": "^3.63.2", "docxtemplater-image-module-free": "^1.1.1", "echarts": "5.4.0", "element-plus": "2.2.27", "file-saver": "2.0.5", "fuse.js": "6.6.2", "html2canvas": "^1.4.1", "js-base64": "^3.7.5", "js-cookie": "3.0.1", "jsencrypt": "3.3.1", @@ -42,11 +44,11 @@ "tinymce": "^5.10.2", "video.js": "^8.12.0", "vue": "3.2.45", "vue-baidu-map-3x": "^1.0.35", "vue-cropper": "1.0.3", "vue-quill-editor": "^3.0.6", "vue-router": "4.1.4", "vue3-json-excel": "^1.0.10-alpha", "vue3-tree-org": "^4.2.2", "wangeditor5-for-vue3": "^0.1.0" }, "devDependencies": { public/qualityFile.docxBinary files differ
src/main.js
@@ -46,7 +46,8 @@ import preReClick from "@/utils/preReClick"; import vue3JsonExcel from 'vue3-json-excel'; import vue3TreeOrg from 'vue3-tree-org'; import "vue3-tree-org/lib/vue3-tree-org.css"; import { Boot } from '@wangeditor/editor' import attachmentModule from '@wangeditor/plugin-upload-attachment' @@ -75,7 +76,7 @@ app.component('ImagePreview', ImagePreview) app.component('RightToolbar', RightToolbar) app.component('Editor', Editor) app.use(vue3TreeOrg) app.use(vue3JsonExcel) app.use(router) app.use(store) src/views/build/conpanyFunctionConsult/digitalFileDep/manageType/qualityManual/components/exportDoc.js
@@ -3,7 +3,7 @@ import Docxtemplater from 'docxtemplater'; import JSZipUtils from 'jszip-utils'; import { saveAs } from 'file-saver'; import ImageModule from 'docxtemplater-image-module-free' // 加载 .docx 模板文件 function loadFile(url, callback) { JSZipUtils.getBinaryContent(url, callback); @@ -70,14 +70,12 @@ function buildList(items) { let listHtml = '<ul style="font-family: 宋体; font-size: 12pt; line-height: 1.5;">'; items.forEach(item => { listHtml += `<li style="margin-bottom: 6pt;">${item.label}`; listHtml += `<li style="margin-bottom: 6pt;">${item.deptName}`; if (item.children && item.children.length > 0) { listHtml += buildList(item.children); } listHtml += '</li>'; }); @@ -89,8 +87,233 @@ return html; } function generateTableXML(clauses, deptList) { const allDeptNames = [...new Set(deptList.map(item => item.deptName))]; // 构建数据映射 const dataMap = {}; deptList.forEach(item => { if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {}; dataMap[item.clauseNum][item.deptName] = item.chooseLab; }); return ` <w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> <w:tblPr> <w:tblW w:w="10000" w:type="pct"/> <!-- 边框设置 --> <w:tblBorders> <w:top w:val="single" w:sz="4" w:space="0" w:color="000000"/> <w:left w:val="single" w:sz="4" w:space="0" w:color="000000"/> <w:bottom w:val="single" w:sz="4" w:space="0" w:color="000000"/> <w:right w:val="single" w:sz="4" w:space="0" w:color="000000"/> <w:insideH w:val="single" w:sz="4" w:space="0" w:color="000000"/> <w:insideV w:val="single" w:sz="4" w:space="0" w:color="000000"/> </w:tblBorders> <w:tblLook w:val="04A0"/> </w:tblPr> <!-- 列宽定义 --> <w:tblGrid> <w:gridCol w:w="1500"/> <!-- 条款号列 --> <w:gridCol w:w="3500"/> <!-- 内容列 --> ${allDeptNames.map(() => '<w:gridCol w:w="2000"/>').join('')} </w:tblGrid> <!-- 表头 --> <w:tr> <w:tc> <w:tcPr><w:tcW w:w="1500" w:type="dxa"/></w:tcPr> <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>条款号</w:t></w:r></w:p> </w:tc> <w:tc> <w:tcPr><w:tcW w:w="3500" w:type="dxa"/></w:tcPr> <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>内容描述</w:t></w:r></w:p> </w:tc> ${allDeptNames.map(dept => ` <w:tc> <w:tcPr><w:tcW w:w="2000" w:type="dxa"/></w:tcPr> <w:p><w:r><w:rPr><w:b/></w:rPr><w:t>${dept}</w:t></w:r></w:p> </w:tc> `).join('')} </w:tr> <!-- 数据行 --> ${clauses.map(clause => ` <w:tr> <w:tc><w:p><w:r><w:t>${clause.clauseNum}</w:t></w:r></w:p></w:tc> <w:tc><w:p><w:r><w:t>${clause.content}</w:t></w:r></w:p></w:tc> ${allDeptNames.map(dept => ` <w:tc> <w:p><w:r><w:t>${dataMap[clause.clauseNum]?.[dept] ?? 0}</w:t></w:r></w:p> </w:tc> `).join('')} </w:tr> `).join('')} </w:tbl> `; } function processTableHtml(html) { if (!html) return ''; const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const table = doc.querySelector('table'); if (!table) return ''; // 提取表格结构 const rows = table.querySelectorAll('tr'); const result = []; // 处理表头 const headers = Array.from(rows[0].querySelectorAll('th')) .map(th => `${th.textContent.trim()}`) .join('\t'); result.push(headers); // 处理数据行 for (let i = 1; i < rows.length; i++) { const cells = rows[i].querySelectorAll('td'); const rowData = Array.from(cells).map(cell => { const indent = cell.style.paddingLeft ? ' '.repeat(parseInt(cell.style.paddingLeft)/4) : ''; const content = cell.textContent.trim(); return indent + content; }).join('\t'); result.push(rowData); } return result.join('\n'); } function generateTableHtml(clauses, deptList) { const allDeptNames = [...new Set(deptList.map(item => item.deptName))]; const dataMap = {}; // 构建数据映射 deptList.forEach(item => { if (!dataMap[item.clauseNum]) dataMap[item.clauseNum] = {}; dataMap[item.clauseNum][item.deptName] = item.chooseLab? (item.chooseLab==1?'●':'○'):'○' }); return ` <table style="width: 100%;border-collapse: collapse;font-family: 'Microsoft YaHei', sans-serif;font-size: 10.5pt;margin-bottom: 12pt;border: 1px solid #ccc"> <thead> <tr style="background-color: #f5f5f5;width: 100%"> <th style="padding: 6pt 8pt;border: 1px solid #ccc;text-align: center;font-weight: bold;min-width: 60pt">条款号</th> <th style="padding: 6pt 8pt;border: 1px solid #cccccc;text-align: left;font-weight: bold">内容描述</th> ${allDeptNames.map(dept => ` <th style="padding: 6pt 8pt;border: 1pt solid #cccccc;text-align: center;font-weight: bold;min-width: 50pt">${dept}</th> `).join('')} </tr> </thead> <tbody> ${clauses.map(clause => ` <tr> <td style="padding: 5pt 8pt; border: 1pt solid #e0e0e0; text-align: center; vertical-align: top">${clause.clauseNum}</td> <td style=" padding: 5pt 8pt; border: 1pt solid #e0e0e0; text-align: left; vertical-align: top">${clause.content}</td> ${allDeptNames.map(dept => ` <td style=" padding: 5pt 8pt; border: 1pt solid #e0e0e0; text-align: center; vertical-align: top"> ${dataMap[clause.clauseNum]?.[dept] ?? '○'} </td> `).join('')} </tr> `).join('')} </tbody> </table> `; } const base64Regex = /^(?:data:)?image\/(png|jpg|jpeg|svg|svg\+xml);base64,/; const validBase64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; function base64Parser(tagValue) { if ( typeof tagValue !== "string" || !base64Regex.test(tagValue) ) { return false; } const stringBase64 = tagValue.replace(base64Regex, ""); if (!validBase64.test(stringBase64)) { throw new Error( "Error parsing base64 data, your data contains invalid characters" ); } // For nodejs, return a Buffer if (typeof Buffer !== "undefined" && Buffer.from) { return Buffer.from(stringBase64, "base64"); } // For browsers, return a string (of binary content) : const binaryString = window.atob(stringBase64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { const ascii = binaryString.charCodeAt(i); bytes[i] = ascii; } return bytes.buffer; } const imageOptions = { getImage(tagValue) { return base64Parser(tagValue); }, getSize(img, tagValue, tagName, context) { return [600, 600]; }, }; const base64DataURLToArrayBuffer = (dataURL) => { // 返回包含 ArrayBuffer 和原始 base64 字符串的对象 const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/; if (!base64Regex.test(dataURL)) { return { buffer: null, base64: dataURL }; } const stringBase64 = dataURL.replace(base64Regex, ""); let binaryString = window.atob(stringBase64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return { buffer: bytes.buffer, // 图片模块需要的 ArrayBuffer base64: stringBase64 // 保留原始 base64 字符串(不带前缀) }; }; // 生成并下载 Word 文档 export function generateWordDocument(templatePath, data, name) { // 处理部门表格数据 // if (data.clauses && data.duties) { // const tableHtml = generateTableHtml(data.clauses, data.duties); // data.departmentsTable = processTableHtml(tableHtml); // } // 生成表格XML // if (data.clauses && data.duties) { // data.tableXML = generateTableXML(data.clauses, data.duties); // } // 处理富文本字段(如果有) if (data.summaries && typeof data.summaries === 'string') { data.summaries = processRichText(data.summaries); @@ -103,6 +326,10 @@ if (data.deptList && Array.isArray(data.deptList)) { data.departmentsHtml = processRichText(convertTreeToHtml(data.deptList)); } if (data.orgChart && typeof data.orgChart !== 'string') { console.warn("orgChart 不是字符串,可能被意外转换:", data.orgChart); delete data.orgChart; // 避免传递无效数据 } loadFile(templatePath, function (error, content) { if (error) { @@ -112,9 +339,11 @@ try { // 加载模板文件内容到 PizZip const zip = new PizZip(content); const imageModule = new ImageModule(imageOptions); const doc = new Docxtemplater(zip, { paragraphLoop: true, linebreaks: true, modules: [imageModule] }); // 设置模板中的占位符数据 @@ -123,6 +352,11 @@ // 渲染文档 doc.render(); // 替换占位符 // let xml = zip.files['word/document.xml'].asText(); // xml = xml.replace('<!-- TABLE_PLACEHOLDER -->', data.tableXML); // zip.file('word/document.xml', xml); // 生成最终的文档 Blob const fileWord = doc.getZip().generate({ type: 'blob', 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> src/views/build/conpanyFunctionConsult/orgStructure/dutyDistributeChart/index.vue
@@ -140,7 +140,10 @@ {clauseNum: '9.1.3', content: '分析和评价'}, {clauseNum: '9.2', content: '内部审核'}, {clauseNum: '9.3', content: '管理评审'}, {clauseNum: '10', content: '持续改进'} {clauseNum: '10', content: '持续改进'}, {clauseNum: '10.1', content: '总则'}, {clauseNum: '10.2', content: '不合格和纠正措施'}, {clauseNum: '10.3', content: '持续改进'} ], form: { companyId: null,