修改下载报告
Showing
5 changed files
with
758 additions
and
245 deletions
| ... | @@ -149,6 +149,9 @@ export const dataSourceTypeList = [{ | ... | @@ -149,6 +149,9 @@ export const dataSourceTypeList = [{ |
| 149 | }, { | 149 | }, { |
| 150 | value: 2, | 150 | value: 2, |
| 151 | label: '文件导入' | 151 | label: '文件导入' |
| 152 | }, { | ||
| 153 | value: 3, | ||
| 154 | label: '文件夹' | ||
| 152 | }]; | 155 | }]; |
| 153 | 156 | ||
| 154 | /** 获取数据库选择列表 */ | 157 | /** 获取数据库选择列表 */ |
| ... | @@ -340,4 +343,11 @@ export const exportAnonReport = (params) => request({ | ... | @@ -340,4 +343,11 @@ export const exportAnonReport = (params) => request({ |
| 340 | url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/anon-task/download-report?taskGuid=${params.taskGuid}&taskExecGuid=${params.execGuid}`, | 343 | url: `${import.meta.env.VITE_APP_DIGITAL_CONTRACT_URL}/anon-task/download-report?taskGuid=${params.taskGuid}&taskExecGuid=${params.execGuid}`, |
| 341 | method: 'post', | 344 | method: 'post', |
| 342 | responseType: 'blob' | 345 | responseType: 'blob' |
| 346 | }) | ||
| 347 | |||
| 348 | export const htmlToWord = (params) => request({ | ||
| 349 | url: `${import.meta.env.VITE_APP_ANONYMIZATION_BASEURL}/anon-task/download/html-to-word`, | ||
| 350 | method: 'postJsonD', | ||
| 351 | data: params, | ||
| 352 | responseType: 'blob' | ||
| 343 | }) | 353 | }) |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -8,12 +8,14 @@ import { | ... | @@ -8,12 +8,14 @@ import { |
| 8 | getAnonAnalyzePageData, | 8 | getAnonAnalyzePageData, |
| 9 | getAnonAnalyzeResult, | 9 | getAnonAnalyzeResult, |
| 10 | getAnonTaskDetail, | 10 | getAnonTaskDetail, |
| 11 | htmlToWord, | ||
| 11 | } from '@/api/modules/dataAnonymization'; | 12 | } from '@/api/modules/dataAnonymization'; |
| 12 | import { changeNum, download } from '@/utils/common'; | 13 | import { changeNum, download } from '@/utils/common'; |
| 13 | import { ElMessage } from 'element-plus'; | 14 | import { ElMessage } from 'element-plus'; |
| 14 | import anonResultAnalysis from './components/anonResultAnalysis.vue'; | 15 | import anonResultAnalysis from './components/anonResultAnalysis.vue'; |
| 15 | import { commonPageConfig } from '@/utils/enum'; | 16 | import { commonPageConfig } from '@/utils/enum'; |
| 16 | import { calcColumnWidth } from '@/utils'; | 17 | import { calcColumnWidth } from '@/utils'; |
| 18 | import html2canvas from 'html2canvas'; | ||
| 17 | 19 | ||
| 18 | const route = useRoute(); | 20 | const route = useRoute(); |
| 19 | const router = useRouter(); | 21 | const router = useRouter(); |
| ... | @@ -52,6 +54,8 @@ const originResultTableFieldColumn = ref({}); | ... | @@ -52,6 +54,8 @@ const originResultTableFieldColumn = ref({}); |
| 52 | 54 | ||
| 53 | /** 结果分析中的字段表格数据 */ | 55 | /** 结果分析中的字段表格数据 */ |
| 54 | const resultData: any = ref([]); | 56 | const resultData: any = ref([]); |
| 57 | /** 全部未分页的数据,下载word时使用。 */ | ||
| 58 | const fullResultData: any = ref([]); | ||
| 55 | 59 | ||
| 56 | /** 结果分析中的字段信息 */ | 60 | /** 结果分析中的字段信息 */ |
| 57 | const analysisResultTableFields: any = ref([]); | 61 | const analysisResultTableFields: any = ref([]); |
| ... | @@ -100,52 +104,118 @@ watch( | ... | @@ -100,52 +104,118 @@ watch( |
| 100 | } | 104 | } |
| 101 | ); | 105 | ); |
| 102 | 106 | ||
| 103 | const getAnalysisResultPageData = () => { | 107 | const getAnalysisResultPageData = (isFull = false) => { |
| 104 | analysisResultLoading.value = true; | 108 | analysisResultLoading.value = true; |
| 105 | getAnonAnalyzePageData({ | 109 | getAnonAnalyzePageData({ |
| 106 | pageIndex: pageInfo.value.curr, | 110 | pageIndex: pageInfo.value.curr, |
| 107 | pageSize: pageInfo.value.limit, | 111 | pageSize: isFull ? -1 : pageInfo.value.limit, |
| 108 | taskExecGuid: taskExecGuid.value, | 112 | taskExecGuid: taskExecGuid.value, |
| 109 | }).then((res: any) => { | 113 | }).then((res: any) => { |
| 110 | analysisResultLoading.value = false; | 114 | analysisResultLoading.value = false; |
| 111 | if (res?.code == proxy.$passCode) { | 115 | if (res?.code == proxy.$passCode) { |
| 112 | pageInfo.value.rows = | 116 | if (isFull) { |
| 117 | fullResultData.value = []; | ||
| 118 | res.data?.records?.forEach(d => { | ||
| 119 | let obj = {}; | ||
| 120 | analysisResultTableFields.value.forEach(t => { | ||
| 121 | obj[t.enName] = d.fieldValue?.[t.enName]; | ||
| 122 | }); | ||
| 123 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | ||
| 124 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | ||
| 125 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 126 | fullResultData.value.push(obj); | ||
| 127 | }); | ||
| 128 | resultData.value = fullResultData.value.slice(0, pageInfo.value.limit); | ||
| 129 | pageInfo.value.rows = fullResultData.value.length; | ||
| 130 | } else { | ||
| 113 | resultData.value = []; | 131 | resultData.value = []; |
| 114 | res.data?.records?.forEach(d => { | 132 | res.data?.records?.forEach(d => { |
| 115 | let obj = {}; | 133 | let obj = {}; |
| 116 | analysisResultTableFields.value.forEach(t => { | 134 | analysisResultTableFields.value.forEach(t => { |
| 117 | obj[t.enName] = d.fieldValue?.[t.enName]; | 135 | obj[t.enName] = d.fieldValue?.[t.enName]; |
| 136 | }); | ||
| 137 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | ||
| 138 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | ||
| 139 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 140 | resultData.value.push(obj); | ||
| 118 | }); | 141 | }); |
| 119 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | 142 | pageInfo.value.rows = res.data?.totalRows ?? 0; |
| 120 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | 143 | } |
| 121 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 122 | resultData.value.push(obj); | ||
| 123 | }); | ||
| 124 | pageInfo.value.rows = res.data?.totalRows ?? 0; | ||
| 125 | } else { | 144 | } else { |
| 126 | proxy.$ElMessage.error(res.msg); | 145 | proxy.$ElMessage.error(res.msg); |
| 127 | } | 146 | } |
| 128 | }) | 147 | }) |
| 129 | } | 148 | } |
| 130 | 149 | ||
| 150 | const isWordStyle = ref(false); | ||
| 151 | |||
| 131 | /** 下载评估报告 */ | 152 | /** 下载评估报告 */ |
| 132 | const transfer = () => { | 153 | const transfer = () => { |
| 154 | isWordStyle.value = true; | ||
| 155 | } | ||
| 156 | |||
| 157 | const domClone: any = ref(null); | ||
| 158 | |||
| 159 | const resultReportRef = ref(); | ||
| 160 | |||
| 161 | const convertHtml2Img = (dom, domClone) => { | ||
| 162 | const element = <HTMLElement>dom.querySelector('.kpi-content') | ||
| 163 | if (!element) { | ||
| 164 | return Promise.resolve(); | ||
| 165 | } | ||
| 166 | return html2canvas(element, { | ||
| 167 | allowTaint: true, | ||
| 168 | useCORS: true, | ||
| 169 | scale: 2, | ||
| 170 | }).then((canvas: any) => { | ||
| 171 | document.documentElement.scrollTop = 0; | ||
| 172 | document.body.scrollTop = 0; | ||
| 173 | element.parentNode && ((<HTMLElement>element.parentNode).scrollTop = 0); | ||
| 174 | let url = canvas.toDataURL('image/jpeg'); | ||
| 175 | let img = document.createElement('img'); | ||
| 176 | if (url) { | ||
| 177 | // img.src = url.split(',')[1]; | ||
| 178 | img.src = url; | ||
| 179 | img.width = 620; | ||
| 180 | img.height = 265; | ||
| 181 | img.crossOrigin = 'Anonymous'; | ||
| 182 | } | ||
| 183 | const copyElement = <HTMLElement>domClone.querySelector('.kpi-content') | ||
| 184 | copyElement.parentNode?.replaceChild(img, copyElement); | ||
| 185 | }) | ||
| 186 | } | ||
| 187 | |||
| 188 | const loadingText = ref(''); | ||
| 189 | |||
| 190 | const getHTML = (reportResultContent) => { | ||
| 191 | let html = reportResultContent; | ||
| 192 | html = html.replace(/"/g, "'"); | ||
| 193 | return html; | ||
| 194 | }; | ||
| 195 | |||
| 196 | const downloadWord = () => { | ||
| 133 | if (downPromise.value) { | 197 | if (downPromise.value) { |
| 134 | return; | 198 | return; |
| 135 | } | 199 | } |
| 136 | downPromise.value = exportAnonReport({ | 200 | let dom = domClone.value || (domClone.value = document.createElement('div')); |
| 137 | taskGuid: route.query.guid, | 201 | let report = resultReportRef.value?.report; |
| 138 | execGuid: taskExecGuid.value | 202 | dom.innerHTML = report?.innerHTML; |
| 139 | }).then((res: any) => { | 203 | resultDataLoading.value = true; |
| 140 | downPromise.value = null; | 204 | loadingText.value = '报告正在下载中,请勿关闭浏览器...'; |
| 141 | if (res && !res.msg) { | 205 | downPromise.value = convertHtml2Img(report, dom).then(() => { |
| 142 | download(res, (route.query.taskName || oldAnonTaskValueInfo.value.taskName) + '_匿名化评估报告.docx', 'word') | 206 | htmlToWord({ html: encodeURIComponent(`<div>${getHTML(dom.innerHTML)}</div>`) }).then((res: any) => { |
| 143 | } else { | 207 | downPromise.value = null |
| 144 | res?.msg && ElMessage.error(res?.msg); | 208 | loadingText.value = ''; |
| 145 | } | 209 | resultDataLoading.value = false; |
| 210 | if (res && !res.msg) { | ||
| 211 | download(res, (route.query.taskName || oldAnonTaskValueInfo.value.taskName) + '_匿名化评估报告.docx', 'word') | ||
| 212 | } else { | ||
| 213 | res?.msg && ElMessage.error(res?.msg); | ||
| 214 | } | ||
| 215 | }) | ||
| 146 | }).catch(() => { | 216 | }).catch(() => { |
| 147 | downPromise.value = null; | 217 | downPromise.value = null; |
| 148 | }) | 218 | }); |
| 149 | } | 219 | } |
| 150 | 220 | ||
| 151 | onMounted(() => { | 221 | onMounted(() => { |
| ... | @@ -160,12 +230,12 @@ onMounted(() => { | ... | @@ -160,12 +230,12 @@ onMounted(() => { |
| 160 | onBeforeMount(() => { | 230 | onBeforeMount(() => { |
| 161 | resultDataLoading.value = true; | 231 | resultDataLoading.value = true; |
| 162 | getAnonAnalyzeResult(taskExecGuid.value).then((res: any) => { | 232 | getAnonAnalyzeResult(taskExecGuid.value).then((res: any) => { |
| 163 | resultDataLoading.value = false; | 233 | resultDataLoading.value = false; |
| 164 | if (res?.code == proxy.$passCode) { | 234 | if (res?.code == proxy.$passCode) { |
| 165 | analysisResultInfo.value = res.data || {}; | 235 | analysisResultInfo.value = res.data || {}; |
| 166 | analysisResultTableFields.value = res.data?.column || []; | 236 | analysisResultTableFields.value = res.data?.column || []; |
| 167 | pageInfo.value.curr = 1; | 237 | pageInfo.value.curr = 1; |
| 168 | getAnalysisResultPageData(); | 238 | getAnalysisResultPageData(true); |
| 169 | } else { | 239 | } else { |
| 170 | res?.msg && proxy.$ElMessage.error(res.msg); | 240 | res?.msg && proxy.$ElMessage.error(res.msg); |
| 171 | } | 241 | } |
| ... | @@ -182,13 +252,24 @@ onBeforeMount(() => { | ... | @@ -182,13 +252,24 @@ onBeforeMount(() => { |
| 182 | </script> | 252 | </script> |
| 183 | 253 | ||
| 184 | <template> | 254 | <template> |
| 185 | <div class="table_tool_wrap" v-loading="resultDataLoading" ref="containerRef"> | 255 | <div class="table_tool_wrap" v-loading="resultDataLoading" ref="containerRef" :element-loading-text="loadingText"> |
| 186 | <el-button style="margin-bottom: 8px;" type="primary" @click="transfer" v-preReClick>下载评估报告</el-button> | 256 | <el-button v-show="!isWordStyle" style="margin-bottom: 8px;" type="primary" @click="transfer" |
| 187 | <anonResultAnalysis :show-title="true" :analysis-result-info="analysisResultInfo" | 257 | v-preReClick>生成Word评估报告</el-button> |
| 188 | :analysis-result-loading="analysisResultLoading" :analysis-result-table-fields="analysisResultTableFields" | 258 | <div v-show="isWordStyle" style="margin-bottom: 8px;"> |
| 189 | :old-anon-task-value-info="oldAnonTaskValueInfo" :container-width="containerWidth" | 259 | <el-button @click="isWordStyle = false">返回</el-button> |
| 190 | :origin-result-table-field-column="originResultTableFieldColumn" :page-info="pageInfo" :result-data="resultData" | 260 | <el-button type="primary" @click="downloadWord">下载评估报告</el-button> |
| 191 | @page-change="pageChange"></anonResultAnalysis> | 261 | </div> |
| 262 | <anonResultAnalysis ref="resultReportRef" :show-title="true" :analysis-result-info="analysisResultInfo" | ||
| 263 | :isWordStyle="isWordStyle" :style="isWordStyle ? { | ||
| 264 | height: 'calc(100% - 36px)', | ||
| 265 | 'overflow-y': 'auto', | ||
| 266 | 'margin-right': '-16px', | ||
| 267 | 'padding-right': '16px', | ||
| 268 | width: 'auto' | ||
| 269 | } : null" :analysis-result-loading="analysisResultLoading" | ||
| 270 | :analysis-result-table-fields="analysisResultTableFields" :old-anon-task-value-info="oldAnonTaskValueInfo" | ||
| 271 | :container-width="containerWidth" :origin-result-table-field-column="originResultTableFieldColumn" | ||
| 272 | :page-info="pageInfo" :result-data="resultData" :fullResultData="fullResultData" @page-change="pageChange"></anonResultAnalysis> | ||
| 192 | </div> | 273 | </div> |
| 193 | </template> | 274 | </template> |
| 194 | 275 | ... | ... |
| ... | @@ -17,7 +17,8 @@ | ... | @@ -17,7 +17,8 @@ |
| 17 | formId="model-select-edit" col="col3 custom-form" @select-change="handleDataSelectFormSelectChange" | 17 | formId="model-select-edit" col="col3 custom-form" @select-change="handleDataSelectFormSelectChange" |
| 18 | @uploadFileChange="uploadFileChange" @checkboxChange="handleDataSelectFormCheckboxChange" /> | 18 | @uploadFileChange="uploadFileChange" @checkboxChange="handleDataSelectFormCheckboxChange" /> |
| 19 | </ContentWrap> | 19 | </ContentWrap> |
| 20 | <ContentWrap id="id-previewData" title="数据抽样预览" description="" style="margin-top: 16px;"> | 20 | <ContentWrap v-show="formRef?.formInline?.dataSource != 3" id="id-previewData" title="数据抽样预览" description="" |
| 21 | style="margin-top: 16px;"> | ||
| 21 | <!-- 选择抽样预览的表单设置 --> | 22 | <!-- 选择抽样预览的表单设置 --> |
| 22 | <Form ref="dataSimpleFormRef" :itemList="dataSimpleFormItems" :rules="dataSimpleFormRules" | 23 | <Form ref="dataSimpleFormRef" :itemList="dataSimpleFormItems" :rules="dataSimpleFormRules" |
| 23 | formId="data-simple-edit" col="col3 fixwidth-form" @switch-change="handleDataSimpleFormSwitchChange" | 24 | formId="data-simple-edit" col="col3 fixwidth-form" @switch-change="handleDataSimpleFormSwitchChange" |
| ... | @@ -47,6 +48,51 @@ | ... | @@ -47,6 +48,51 @@ |
| 47 | </div> | 48 | </div> |
| 48 | </div> | 49 | </div> |
| 49 | </ContentWrap> | 50 | </ContentWrap> |
| 51 | <ContentWrap v-show="formRef?.formInline?.dataSource == 3" id="id-folder" title="提取文件" description="" | ||
| 52 | style="margin-top: 16px;"> | ||
| 53 | <div class="folder-main"> | ||
| 54 | <el-upload ref="uploadRef" class="upload-file" action="#" :file-list="folderList" :limit="1" directory | ||
| 55 | :http-request="(file) => hanlderUploadFolder(file)"> | ||
| 56 | <template #trigger> | ||
| 57 | <el-button :icon="Upload" class="mr8">上传文件</el-button> | ||
| 58 | </template> | ||
| 59 | </el-upload> | ||
| 60 | <!-- 正在解析的状态 --> | ||
| 61 | <div class="folder-progress"> | ||
| 62 | <div class="folder-title">正在解析</div> | ||
| 63 | <el-progress :percentage="30" :stroke-width="12" striped striped-flow :show-text="false" :duration="8" /> | ||
| 64 | <div style="display: flex;"><span class="cnt">{{ '共' + changeNum(100, 0) + '个文件, 已解析30%' }}</span></div> | ||
| 65 | </div> | ||
| 66 | <!-- 解析失败的状态 --> | ||
| 67 | <div class="folder-progress"> | ||
| 68 | <div class="folder-title"> | ||
| 69 | <el-icon class="title-icon fail"> | ||
| 70 | <CircleCloseFilled /> | ||
| 71 | </el-icon><span>解析失败</span> | ||
| 72 | </div> | ||
| 73 | <el-progress :percentage="30" :stroke-width="12" color="#F30000" :show-text="false" /> | ||
| 74 | <div style="display: flex;"><span class="cnt">{{ '已成功解析' + changeNum(100, 0) + '个文件, 失败' + changeNum(30, | ||
| 75 | 0) + | ||
| 76 | '个文件' }}</span></div> | ||
| 77 | <div style="text-align: center;"> | ||
| 78 | <el-button @click="reAnalyzing">重试</el-button> | ||
| 79 | </div> | ||
| 80 | </div> | ||
| 81 | <!-- 解析成功状态 --> | ||
| 82 | <div class="folder-progress"> | ||
| 83 | <div class="folder-title"> | ||
| 84 | <el-icon class="title-icon success"> | ||
| 85 | <svg-icon name="icon-success" /> | ||
| 86 | </el-icon><span>解析成功</span> | ||
| 87 | </div> | ||
| 88 | <el-progress :percentage="30" :stroke-width="12" color="#F30000" :show-text="false" /> | ||
| 89 | <div style="display: flex;"><span class="cnt">{{ '已成功解析' + changeNum(100, 0) + '个文件, 失败' + changeNum(30, | ||
| 90 | 0) + | ||
| 91 | '个文件' }}</span></div> | ||
| 92 | </div> | ||
| 93 | </div> | ||
| 94 | |||
| 95 | </ContentWrap> | ||
| 50 | </div> | 96 | </div> |
| 51 | <!-- 第二步 配置匿名化方案,单独抽取vue组件页面 --> | 97 | <!-- 第二步 配置匿名化方案,单独抽取vue组件页面 --> |
| 52 | <anonTaskStepTwo ref="anonTaskStepTwoRef" v-show="step == 1" :anonTaskRules="detailInfo.anonTaskRules" | 98 | <anonTaskStepTwo ref="anonTaskStepTwoRef" v-show="step == 1" :anonTaskRules="detailInfo.anonTaskRules" |
| ... | @@ -70,13 +116,19 @@ | ... | @@ -70,13 +116,19 @@ |
| 70 | <div v-show="analysisResultInfo.errorMsg" class="error-desc">{{ '【' + analysisResultInfo.errorMsg + '】' }} | 116 | <div v-show="analysisResultInfo.errorMsg" class="error-desc">{{ '【' + analysisResultInfo.errorMsg + '】' }} |
| 71 | </div> | 117 | </div> |
| 72 | </div> | 118 | </div> |
| 73 | <anonResultAnalysis v-show="isExecEnd && analysisResultInfo.status == 'Y'" :analysis-result-info="analysisResultInfo" | 119 | <anonResultAnalysis v-show="isExecEnd && analysisResultInfo.status == 'Y'" ref="resultReportRef" v-loading="downloadLoading" |
| 74 | :analysis-result-loading="analysisResultLoading" :analysis-result-table-fields="analysisResultTableFields" :old-anon-task-value-info="oldAnonTaskValueInfo" | 120 | :analysis-result-info="analysisResultInfo" :is-word-style="isWordStyle" :element-loading-text="loadingText" |
| 75 | :container-width="containerWidth" :origin-result-table-field-column="originResultTableFieldColumn" :page-info="pageInfo" | 121 | :analysis-result-loading="analysisResultLoading" :analysis-result-table-fields="analysisResultTableFields" |
| 76 | :result-data="resultData" @page-change="pageChange"></anonResultAnalysis> | 122 | :old-anon-task-value-info="oldAnonTaskValueInfo" :container-width="containerWidth" |
| 123 | :origin-result-table-field-column="originResultTableFieldColumn" :page-info="pageInfo" | ||
| 124 | :result-data="resultData" :fullResultData="fullResultData" @page-change="pageChange"></anonResultAnalysis> | ||
| 77 | <template #header> | 125 | <template #header> |
| 78 | <el-button v-show="isExecEnd && analysisResultInfo.status == 'Y'" type="primary" v-loading="!!downPromise" | 126 | <el-button v-show="isExecEnd && analysisResultInfo.status == 'Y' && !isWordStyle" type="primary" |
| 79 | @click="transfer">下载评估报告</el-button> | 127 | v-loading="!!downPromise" @click="transfer">生成Word评估报告</el-button> |
| 128 | <div v-show="isWordStyle"> | ||
| 129 | <el-button @click="isWordStyle = false">返回</el-button> | ||
| 130 | <el-button type="primary" @click="downloadWord">下载评估报告</el-button> | ||
| 131 | </div> | ||
| 80 | </template> | 132 | </template> |
| 81 | </ContentWrap> | 133 | </ContentWrap> |
| 82 | </div> | 134 | </div> |
| ... | @@ -105,7 +157,9 @@ | ... | @@ -105,7 +157,9 @@ |
| 105 | <el-button v-show="formRef?.formInline?.handleType != '02'" type="primary" | 157 | <el-button v-show="formRef?.formInline?.handleType != '02'" type="primary" |
| 106 | :disabled="analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')" | 158 | :disabled="analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')" |
| 107 | @click="changeStep(4)">下一步</el-button> | 159 | @click="changeStep(4)">下一步</el-button> |
| 108 | <el-button type="primary" v-show="formRef?.formInline?.handleType == '02'" :disabled="analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')" v-preReClick @click="closeTask">关闭</el-button> | 160 | <el-button type="primary" v-show="formRef?.formInline?.handleType == '02'" |
| 161 | :disabled="analysisResultInfo.status == 'R' || (isExecEnd && analysisResultInfo.status == 'E')" v-preReClick | ||
| 162 | @click="closeTask">关闭</el-button> | ||
| 109 | </template> | 163 | </template> |
| 110 | <template v-else> | 164 | <template v-else> |
| 111 | <el-button @click="changeStep(3)">上一步</el-button> | 165 | <el-button @click="changeStep(3)">上一步</el-button> |
| ... | @@ -131,6 +185,7 @@ import { | ... | @@ -131,6 +185,7 @@ import { |
| 131 | updateAnonTask, | 185 | updateAnonTask, |
| 132 | exportAnonExecData, | 186 | exportAnonExecData, |
| 133 | exportAnonReport, | 187 | exportAnonReport, |
| 188 | htmlToWord, | ||
| 134 | } from '@/api/modules/dataAnonymization'; | 189 | } from '@/api/modules/dataAnonymization'; |
| 135 | import { | 190 | import { |
| 136 | parseAndDecodeUrl, | 191 | parseAndDecodeUrl, |
| ... | @@ -154,8 +209,9 @@ import anonResultView from './anonResultView.vue'; | ... | @@ -154,8 +209,9 @@ import anonResultView from './anonResultView.vue'; |
| 154 | import useDataAnonymizationStore from "@/store/modules/dataAnonymization"; | 209 | import useDataAnonymizationStore from "@/store/modules/dataAnonymization"; |
| 155 | import { RefreshRight, CircleCloseFilled, Right } from "@element-plus/icons-vue"; | 210 | import { RefreshRight, CircleCloseFilled, Right } from "@element-plus/icons-vue"; |
| 156 | import { commonPageConfig } from '@/components/PageNav'; | 211 | import { commonPageConfig } from '@/components/PageNav'; |
| 157 | import { QuestionFilled } from "@element-plus/icons-vue"; | 212 | import { Upload } from "@element-plus/icons-vue"; |
| 158 | import anonResultAnalysis from './components/anonResultAnalysis.vue'; | 213 | import anonResultAnalysis from './components/anonResultAnalysis.vue'; |
| 214 | import html2canvas from 'html2canvas'; | ||
| 159 | 215 | ||
| 160 | const anonymizationStore = useDataAnonymizationStore(); | 216 | const anonymizationStore = useDataAnonymizationStore(); |
| 161 | const { proxy } = getCurrentInstance() as any; | 217 | const { proxy } = getCurrentInstance() as any; |
| ... | @@ -191,7 +247,7 @@ const reportStepsInfo = ref({ | ... | @@ -191,7 +247,7 @@ const reportStepsInfo = ref({ |
| 191 | step: step.value, | 247 | step: step.value, |
| 192 | list: [ | 248 | list: [ |
| 193 | { title: '数据输入', value: 1 }, | 249 | { title: '数据输入', value: 1 }, |
| 194 | // { title: '配置匿名化方案', value: 2 }, | 250 | // { title: '配置匿名化方案', value: 2 }, |
| 195 | { title: '匿名结果分析', value: 2 }, | 251 | { title: '匿名结果分析', value: 2 }, |
| 196 | // { title: '结果输出', value: 4 } | 252 | // { title: '结果输出', value: 4 } |
| 197 | ] | 253 | ] |
| ... | @@ -845,6 +901,19 @@ const uploadFileChange = (file) => { | ... | @@ -845,6 +901,19 @@ const uploadFileChange = (file) => { |
| 845 | parseFileData(fileRaw); | 901 | parseFileData(fileRaw); |
| 846 | } | 902 | } |
| 847 | 903 | ||
| 904 | /*** ----------------------- 解析扫描文件 ------------------------------ */ | ||
| 905 | const folderList: any = ref([]); | ||
| 906 | |||
| 907 | const hanlderUploadFolder = (file) => { | ||
| 908 | debugger | ||
| 909 | return Promise.resolve(); | ||
| 910 | } | ||
| 911 | |||
| 912 | /** 重试 */ | ||
| 913 | const reAnalyzing = () => { | ||
| 914 | |||
| 915 | } | ||
| 916 | |||
| 848 | /** 第二步的配置组件引用。 */ | 917 | /** 第二步的配置组件引用。 */ |
| 849 | const anonTaskStepTwoRef = ref(); | 918 | const anonTaskStepTwoRef = ref(); |
| 850 | 919 | ||
| ... | @@ -1259,7 +1328,7 @@ const processStepThreeResultView = (isRefresh = false) => { | ... | @@ -1259,7 +1328,7 @@ const processStepThreeResultView = (isRefresh = false) => { |
| 1259 | refreshTimer.value = null; | 1328 | refreshTimer.value = null; |
| 1260 | analysisResultTableFields.value = res.data?.column || []; | 1329 | analysisResultTableFields.value = res.data?.column || []; |
| 1261 | pageInfo.value.curr = 1; | 1330 | pageInfo.value.curr = 1; |
| 1262 | getAnalysisResultPageData(); | 1331 | getAnalysisResultPageData(true); |
| 1263 | } else if (analysisResultInfo.value.status == 'E') { | 1332 | } else if (analysisResultInfo.value.status == 'E') { |
| 1264 | isExecEnd.value = true | 1333 | isExecEnd.value = true |
| 1265 | refreshTimer.value && clearInterval(refreshTimer.value); | 1334 | refreshTimer.value && clearInterval(refreshTimer.value); |
| ... | @@ -1302,6 +1371,9 @@ const originResultTableFieldColumn = ref({}); | ... | @@ -1302,6 +1371,9 @@ const originResultTableFieldColumn = ref({}); |
| 1302 | /** 结果分析中的字段表格数据 */ | 1371 | /** 结果分析中的字段表格数据 */ |
| 1303 | const resultData: any = ref([]); | 1372 | const resultData: any = ref([]); |
| 1304 | 1373 | ||
| 1374 | /** 不分页的全部数据 */ | ||
| 1375 | const fullResultData: any = ref([]); | ||
| 1376 | |||
| 1305 | /** 结果分析中的字段信息 */ | 1377 | /** 结果分析中的字段信息 */ |
| 1306 | const analysisResultTableFields: any = ref([]); | 1378 | const analysisResultTableFields: any = ref([]); |
| 1307 | 1379 | ||
| ... | @@ -1329,28 +1401,43 @@ watch( | ... | @@ -1329,28 +1401,43 @@ watch( |
| 1329 | } | 1401 | } |
| 1330 | ); | 1402 | ); |
| 1331 | 1403 | ||
| 1332 | const getAnalysisResultPageData = () => { | 1404 | const getAnalysisResultPageData = (isFull = false) => { |
| 1333 | analysisResultLoading.value = true; | 1405 | analysisResultLoading.value = true; |
| 1334 | getAnonAnalyzePageData({ | 1406 | getAnonAnalyzePageData({ |
| 1335 | pageIndex: pageInfo.value.curr, | 1407 | pageIndex: pageInfo.value.curr, |
| 1336 | pageSize: pageInfo.value.limit, | 1408 | pageSize: isFull ? -1 : pageInfo.value.limit, |
| 1337 | taskExecGuid: taskExecGuid.value, | 1409 | taskExecGuid: taskExecGuid.value, |
| 1338 | }).then((res: any) => { | 1410 | }).then((res: any) => { |
| 1339 | analysisResultLoading.value = false; | 1411 | analysisResultLoading.value = false; |
| 1340 | if (res?.code == proxy.$passCode) { | 1412 | if (res?.code == proxy.$passCode) { |
| 1341 | pageInfo.value.rows = | 1413 | if (isFull) { |
| 1414 | fullResultData.value = []; | ||
| 1415 | res.data?.records?.forEach(d => { | ||
| 1416 | let obj = {}; | ||
| 1417 | analysisResultTableFields.value.forEach(t => { | ||
| 1418 | obj[t.enName] = d.fieldValue?.[t.enName]; | ||
| 1419 | }); | ||
| 1420 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | ||
| 1421 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | ||
| 1422 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 1423 | fullResultData.value.push(obj); | ||
| 1424 | }); | ||
| 1425 | resultData.value = fullResultData.value.slice(0, pageInfo.value.limit); | ||
| 1426 | pageInfo.value.rows = fullResultData.value.length; | ||
| 1427 | } else { | ||
| 1342 | resultData.value = []; | 1428 | resultData.value = []; |
| 1343 | res.data?.records?.forEach(d => { | 1429 | res.data?.records?.forEach(d => { |
| 1344 | let obj = {}; | 1430 | let obj = {}; |
| 1345 | analysisResultTableFields.value.forEach(t => { | 1431 | analysisResultTableFields.value.forEach(t => { |
| 1346 | obj[t.enName] = d.fieldValue?.[t.enName]; | 1432 | obj[t.enName] = d.fieldValue?.[t.enName]; |
| 1433 | }); | ||
| 1434 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | ||
| 1435 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | ||
| 1436 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 1437 | resultData.value.push(obj); | ||
| 1347 | }); | 1438 | }); |
| 1348 | obj['equivalenceClassNum'] = changeNum(d.equivalenceClassNum || 0, 0); | 1439 | pageInfo.value.rows = res.data?.totalRows ?? 0; |
| 1349 | obj['reIdentifyRisk'] = changeNum(d.reIdentifyRisk || 0, 2); | 1440 | } |
| 1350 | obj['isGtThreshold'] = d.isGtThreshold; | ||
| 1351 | resultData.value.push(obj); | ||
| 1352 | }); | ||
| 1353 | pageInfo.value.rows = res.data?.totalRows ?? 0; | ||
| 1354 | } else { | 1441 | } else { |
| 1355 | proxy.$ElMessage.error(res.msg); | 1442 | proxy.$ElMessage.error(res.msg); |
| 1356 | } | 1443 | } |
| ... | @@ -1359,24 +1446,76 @@ const getAnalysisResultPageData = () => { | ... | @@ -1359,24 +1446,76 @@ const getAnalysisResultPageData = () => { |
| 1359 | 1446 | ||
| 1360 | const downPromise: any = ref() | 1447 | const downPromise: any = ref() |
| 1361 | 1448 | ||
| 1449 | const isWordStyle = ref(false); | ||
| 1362 | /** 下载评估报告 */ | 1450 | /** 下载评估报告 */ |
| 1363 | const transfer = () => { | 1451 | const transfer = () => { |
| 1452 | isWordStyle.value = true; | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | const domClone: any = ref(null); | ||
| 1456 | |||
| 1457 | const resultReportRef = ref(); | ||
| 1458 | |||
| 1459 | const convertHtml2Img = (dom, domClone) => { | ||
| 1460 | const element = <HTMLElement>dom.querySelector('.kpi-content') | ||
| 1461 | if (!element) { | ||
| 1462 | return Promise.resolve(); | ||
| 1463 | } | ||
| 1464 | return html2canvas(element, { | ||
| 1465 | allowTaint: true, | ||
| 1466 | useCORS: true, | ||
| 1467 | scale: 2, | ||
| 1468 | }).then((canvas: any) => { | ||
| 1469 | document.documentElement.scrollTop = 0; | ||
| 1470 | document.body.scrollTop = 0; | ||
| 1471 | element.parentNode && ((<HTMLElement>element.parentNode).scrollTop = 0); | ||
| 1472 | let url = canvas.toDataURL('image/jpeg'); | ||
| 1473 | let img = document.createElement('img'); | ||
| 1474 | if (url) { | ||
| 1475 | // img.src = url.split(',')[1]; | ||
| 1476 | img.src = url; | ||
| 1477 | img.width = 620; | ||
| 1478 | img.height = 265; | ||
| 1479 | img.crossOrigin = 'Anonymous'; | ||
| 1480 | } | ||
| 1481 | const copyElement = <HTMLElement>domClone.querySelector('.kpi-content') | ||
| 1482 | copyElement.parentNode?.replaceChild(img, copyElement); | ||
| 1483 | }) | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | const loadingText = ref(''); | ||
| 1487 | |||
| 1488 | const downloadLoading = ref(false); | ||
| 1489 | |||
| 1490 | const getHTML = (reportResultContent) => { | ||
| 1491 | let html = reportResultContent; | ||
| 1492 | html = html.replace(/"/g, "'"); | ||
| 1493 | return html; | ||
| 1494 | }; | ||
| 1495 | |||
| 1496 | const downloadWord = () => { | ||
| 1364 | if (downPromise.value) { | 1497 | if (downPromise.value) { |
| 1365 | return; | 1498 | return; |
| 1366 | } | 1499 | } |
| 1367 | downPromise.value = exportAnonReport({ | 1500 | let dom = domClone.value || (domClone.value = document.createElement('div')); |
| 1368 | taskGuid: route.query.guid, | 1501 | let report = resultReportRef.value?.report; |
| 1369 | execGuid: taskExecGuid.value | 1502 | dom.innerHTML = report?.innerHTML; |
| 1370 | }).then((res: any) => { | 1503 | loadingText.value = '报告正在下载中,请勿关闭浏览器...'; |
| 1371 | downPromise.value = null; | 1504 | downloadLoading.value = true; |
| 1372 | if (res && !res.msg) { | 1505 | downPromise.value = convertHtml2Img(report, dom).then(() => { |
| 1373 | download(res, (route.query.taskName || oldAnonTaskValueInfo.value.taskName) + '_匿名化评估报告.docx', 'word') | 1506 | htmlToWord({ html: encodeURIComponent(`<div>${getHTML(dom.innerHTML)}</div>`) }).then((res: any) => { |
| 1374 | } else { | 1507 | downPromise.value = null |
| 1375 | res?.msg && ElMessage.error(res?.msg); | 1508 | loadingText.value = ''; |
| 1376 | } | 1509 | downloadLoading.value = false; |
| 1510 | if (res && !res.msg) { | ||
| 1511 | download(res, (route.query.taskName || oldAnonTaskValueInfo.value.taskName) + '_匿名化评估报告.docx', 'word') | ||
| 1512 | } else { | ||
| 1513 | res?.msg && ElMessage.error(res?.msg); | ||
| 1514 | } | ||
| 1515 | }) | ||
| 1377 | }).catch(() => { | 1516 | }).catch(() => { |
| 1378 | downPromise.value = null; | 1517 | downPromise.value = null; |
| 1379 | }) | 1518 | }); |
| 1380 | } | 1519 | } |
| 1381 | 1520 | ||
| 1382 | onUnmounted(() => { | 1521 | onUnmounted(() => { |
| ... | @@ -1704,4 +1843,57 @@ onUnmounted(() => { | ... | @@ -1704,4 +1843,57 @@ onUnmounted(() => { |
| 1704 | justify-content: space-between; | 1843 | justify-content: space-between; |
| 1705 | } | 1844 | } |
| 1706 | } | 1845 | } |
| 1846 | |||
| 1847 | .folder-main { | ||
| 1848 | height: 170px; | ||
| 1849 | display: flex; | ||
| 1850 | align-items: center; | ||
| 1851 | justify-content: center; | ||
| 1852 | flex-direction: column; | ||
| 1853 | |||
| 1854 | .folder-title { | ||
| 1855 | font-size: 14px; | ||
| 1856 | color: #212121; | ||
| 1857 | font-weight: 600; | ||
| 1858 | margin-bottom: 16px; | ||
| 1859 | text-align: center; | ||
| 1860 | line-height: 21px; | ||
| 1861 | display: flex; | ||
| 1862 | justify-content: center; | ||
| 1863 | } | ||
| 1864 | |||
| 1865 | :deep(.el-icon) { | ||
| 1866 | margin-right: 8px; | ||
| 1867 | width: 20px; | ||
| 1868 | height: 20px; | ||
| 1869 | |||
| 1870 | &.fail { | ||
| 1871 | color: #E63E33; | ||
| 1872 | } | ||
| 1873 | |||
| 1874 | &.success { | ||
| 1875 | color: #4FA55D; | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | svg { | ||
| 1879 | width: 100%; | ||
| 1880 | height: 100%; | ||
| 1881 | } | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | .folder-progress { | ||
| 1885 | width: 360px; | ||
| 1886 | |||
| 1887 | .cnt { | ||
| 1888 | font-size: 14px; | ||
| 1889 | color: #212121; | ||
| 1890 | line-height: 21px; | ||
| 1891 | margin-top: 8px; | ||
| 1892 | } | ||
| 1893 | |||
| 1894 | .el-button { | ||
| 1895 | margin-top: 12px; | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | } | ||
| 1707 | </style> | 1899 | </style> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | <div class="analysis-result-main"> | 2 | <div style="display: flex;justify-content: center;width: 100%;"> |
| 3 | <div v-if="showTitle" class="result-title">匿名结果分析</div> | 3 | <div v-show="!isWordStyle" class="analysis-result-main"> |
| 4 | <div class="kpi-content" v-show="Object.keys(analysisResultInfo).length > 0"> | 4 | <div v-if="showTitle" class="result-title">匿名结果分析</div> |
| 5 | <div class="border-content"> | 5 | <div class="kpi-content" v-show="Object.keys(analysisResultInfo).length > 0"> |
| 6 | <div class="text">去标识化效果评估结果</div> | 6 | <div class="border-content"> |
| 7 | <span class="number score-color">{{ analysisResultInfo.rating + '级' }}</span> | 7 | <div class="text">去标识化效果评估结果</div> |
| 8 | </div> | 8 | <span class="number score-color">{{ analysisResultInfo.rating + '级' }}</span> |
| 9 | <div class="border-content"> | 9 | </div> |
| 10 | <span class="text">重标识风险最大值<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | 10 | <div class="border-content"> |
| 11 | <template #content> | 11 | <span class="text">重标识风险最大值<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> |
| 12 | <div style="max-width: 236px;"> | 12 | <template #content> |
| 13 | 所有等价类的重标识风险最大值 | 13 | <div style="max-width: 236px;"> |
| 14 | </div> | 14 | 所有等价类的重标识风险最大值 |
| 15 | </template> | 15 | </div> |
| 16 | <el-icon style="margin-left: 2px;margin-top: 2px;"> | 16 | </template> |
| 17 | <QuestionFilled /> | 17 | <el-icon style="margin-left: 2px;margin-top: 2px;"> |
| 18 | </el-icon> | 18 | <QuestionFilled /> |
| 19 | </el-tooltip></span> | 19 | </el-icon> |
| 20 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRb != null ? | 20 | </el-tooltip></span> |
| 21 | (analysisResultInfo.reIdentifyRiskRb || 0) : '--' | 21 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRb != null ? |
| 22 | }}</span> | 22 | (analysisResultInfo.reIdentifyRiskRb || 0) : '--' |
| 23 | </div> | 23 | }}</span> |
| 24 | <div class="border-content"> | 24 | </div> |
| 25 | <span class="text">重标识风险平均值<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | 25 | <div class="border-content"> |
| 26 | <template #content> | 26 | <span class="text">重标识风险平均值<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> |
| 27 | <div style="max-width: 236px;"> | 27 | <template #content> |
| 28 | 所有等价类的重标识风险平均值 | 28 | <div style="max-width: 236px;"> |
| 29 | </div> | 29 | 所有等价类的重标识风险平均值 |
| 30 | </template> | 30 | </div> |
| 31 | <el-icon style="margin-left: 2px;margin-top: 2px;"> | 31 | </template> |
| 32 | <QuestionFilled /> | 32 | <el-icon style="margin-left: 2px;margin-top: 2px;"> |
| 33 | </el-icon> | 33 | <QuestionFilled /> |
| 34 | </el-tooltip></span> | 34 | </el-icon> |
| 35 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRc != null ? | 35 | </el-tooltip></span> |
| 36 | (analysisResultInfo.reIdentifyRiskRc || 0) : '--' }}</span> | 36 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRc != null ? |
| 37 | </div> | 37 | (analysisResultInfo.reIdentifyRiskRc || 0) : '--' }}</span> |
| 38 | <div class="border-content"> | 38 | </div> |
| 39 | <span class="text">环境重标识攻击概率<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | 39 | <div class="border-content"> |
| 40 | <template #content> | 40 | <span class="text">环境重标识攻击概率<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> |
| 41 | <div style="max-width: 236px;"> | 41 | <template #content> |
| 42 | 完全公开共享数据发布,攻击者对数据集进行环境重标识攻击的概率为1。领地公开共享与受控公开共享数据发布,环境重标识攻击概率为内部故意攻击概率、数据集包含熟人概率和数据泄露概率三者的最大值。 | 42 | <div style="max-width: 236px;"> |
| 43 | </div> | 43 | 完全公开共享数据发布,攻击者对数据集进行环境重标识攻击的概率为1。领地公开共享与受控公开共享数据发布,环境重标识攻击概率为内部故意攻击概率、数据集包含熟人概率和数据泄露概率三者的最大值。 |
| 44 | </template> | 44 | </div> |
| 45 | <el-icon style="margin-left: 2px;margin-top: 2px;"> | 45 | </template> |
| 46 | <QuestionFilled /> | 46 | <el-icon style="margin-left: 2px;margin-top: 2px;"> |
| 47 | </el-icon> | 47 | <QuestionFilled /> |
| 48 | </el-tooltip></span> | 48 | </el-icon> |
| 49 | <span class="number">{{ analysisResultInfo.envReAttackPr != null ? | 49 | </el-tooltip></span> |
| 50 | (analysisResultInfo.envReAttackPr || 0) : '--' }}</span> | 50 | <span class="number">{{ analysisResultInfo.envReAttackPr != null ? |
| 51 | </div> | 51 | (analysisResultInfo.envReAttackPr || 0) : '--' }}</span> |
| 52 | <div class="border-content"> | 52 | </div> |
| 53 | <span class="text">等价类门限风险<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | 53 | <div class="border-content"> |
| 54 | <template #content> | 54 | <span class="text">等价类门限风险<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> |
| 55 | <div style="max-width: 236px;"> | 55 | <template #content> |
| 56 | 完全公开共享数据发布,门限阈值取值 1/20;受控公开共享数据发布,门限阈值取值 1/5;领地公开共享数据发布,门限阈值取值 | 56 | <div style="max-width: 236px;"> |
| 57 | 1/3;等价类门限风险为:等价类的重标识风险大于门限阈值为1,小于等于为0,求和后除以等价类个数。 | 57 | 完全公开共享数据发布,门限阈值取值 1/20;受控公开共享数据发布,门限阈值取值 1/5;领地公开共享数据发布,门限阈值取值 |
| 58 | </div> | 58 | 1/3;等价类门限风险为:等价类的重标识风险大于门限阈值为1,小于等于为0,求和后除以等价类个数。 |
| 59 | </template> | 59 | </div> |
| 60 | <el-icon style="margin-left: 2px;margin-top: 2px;"> | 60 | </template> |
| 61 | <QuestionFilled /> | 61 | <el-icon style="margin-left: 2px;margin-top: 2px;"> |
| 62 | </el-icon> | 62 | <QuestionFilled /> |
| 63 | </el-tooltip></span> | 63 | </el-icon> |
| 64 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRa != null ? | 64 | </el-tooltip></span> |
| 65 | (analysisResultInfo.reIdentifyRiskRa || 0) : '--' }}</span> | 65 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRa != null ? |
| 66 | </div> | 66 | (analysisResultInfo.reIdentifyRiskRa || 0) : '--' }}</span> |
| 67 | <div class="border-content"> | 67 | </div> |
| 68 | <span class="text">重标识风险总体风险<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> | 68 | <div class="border-content"> |
| 69 | <template #content> | 69 | <span class="text">重标识风险总体风险<el-tooltip placement="top" effect="light" popper-class="table_tooltip"> |
| 70 | <div style="max-width: 236px;"> | 70 | <template #content> |
| 71 | 完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。 | 71 | <div style="max-width: 236px;"> |
| 72 | 受控公开共享和领地公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。 | 72 | 完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。 |
| 73 | <!-- {{ oldAnonTaskValueInfo.dataSharingTypeCode == '01' ? '完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。' | 73 | 受控公开共享和领地公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。 |
| 74 | <!-- {{ oldAnonTaskValueInfo.dataSharingTypeCode == '01' ? '完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。' | ||
| 74 | : `${oldAnonTaskValueInfo.dataSharingTypeCode == '02' ? '受控公开共享' : '领地公开共享'},当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。` }} --> | 75 | : `${oldAnonTaskValueInfo.dataSharingTypeCode == '02' ? '受控公开共享' : '领地公开共享'},当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。` }} --> |
| 75 | </div> | 76 | </div> |
| 76 | </template> | 77 | </template> |
| 77 | <el-icon style="margin-left: 2px;margin-top: 2px;"> | 78 | <el-icon style="margin-left: 2px;margin-top: 2px;"> |
| 78 | <QuestionFilled /> | 79 | <QuestionFilled /> |
| 79 | </el-icon> | 80 | </el-icon> |
| 80 | </el-tooltip></span> | 81 | </el-tooltip></span> |
| 81 | <span class="number">{{ analysisResultInfo.reIdentifyOverallRisk != null ? | 82 | <span class="number">{{ analysisResultInfo.reIdentifyOverallRisk != null ? |
| 82 | (analysisResultInfo.reIdentifyOverallRisk || 0) : '--' }}</span> | 83 | (analysisResultInfo.reIdentifyOverallRisk || 0) : '--' }}</span> |
| 83 | </div> | 84 | </div> |
| 84 | <div class="border-content"> | 85 | <div class="border-content"> |
| 85 | <span class="text">重标识可接受风险阈值</span> | 86 | <span class="text">重标识可接受风险阈值</span> |
| 86 | <span class="number">{{ oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold == null ? 0.05 : | 87 | <span class="number">{{ oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold == null ? 0.05 : |
| 87 | (oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold || 0) }}</span> | 88 | (oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold || 0) }}</span> |
| 89 | </div> | ||
| 88 | </div> | 90 | </div> |
| 89 | </div> | 91 | <div class="result-title">重标识风险表</div> |
| 90 | <div class="result-title">重标识风险表</div> | 92 | <el-table ref="tableRef" v-show="analysisResultTableFields.length" :data="resultData" |
| 91 | <el-table ref="tableRef" v-show="analysisResultTableFields.length" :data="resultData" | 93 | v-loading="analysisResultLoading" :highlight-current-row="true" stripe border tooltip-effect="light" |
| 92 | v-loading="analysisResultLoading" :highlight-current-row="true" stripe border tooltip-effect="light" height="100%" | 94 | height="100%" row-key="guid" :style="{ width: '100%', height: '280px' }"> |
| 93 | row-key="guid" :style="{ width: '100%', height: '280px' }"> | 95 | <el-table-column label="等价类" type="index" width="68px" align="center" show-overflow-tooltip> |
| 94 | <el-table-column label="等价类" type="index" width="68px" align="center" show-overflow-tooltip> | 96 | <template #default="scope"> |
| 95 | <template #default="scope"> | 97 | <span>{{ |
| 96 | <span>{{ | 98 | pageInfo.curr !== undefined |
| 97 | pageInfo.curr !== undefined | 99 | ? (pageInfo.curr - 1) * pageInfo.limit + scope.$index + 1 |
| 98 | ? (pageInfo.curr - 1) * pageInfo.limit + scope.$index + 1 | 100 | : scope.$index + 1 |
| 99 | : scope.$index + 1 | 101 | }}</span> |
| 100 | }}</span> | 102 | </template> |
| 101 | </template> | ||
| 102 | </el-table-column> | ||
| 103 | <template v-for="(item, index) in (analysisResultTableFields || [])"> | ||
| 104 | <el-table-column :label="item.chName" :width="item.dataType === 'datetime' | ||
| 105 | ? TableColumnWidth.DATETIME | ||
| 106 | : item.dataType === 'date' | ||
| 107 | ? TableColumnWidth.DATE | ||
| 108 | : originResultTableFieldColumn[item.enName] | ||
| 109 | " :align="getTextAlign(item)" :header-align="getTextAlign(item)" | ||
| 110 | :formatter="(row) => formatterPreviewDate(row, item)" :show-overflow-tooltip="true"> | ||
| 111 | </el-table-column> | 103 | </el-table-column> |
| 112 | </template> | 104 | <template v-for="(item, index) in (analysisResultTableFields || [])"> |
| 113 | <el-table-column label="等价类大小" prop="equivalenceClassNum" width="98" align="right" fixed="right" | 105 | <el-table-column :label="item.chName" :width="item.dataType === 'datetime' |
| 114 | show-overflow-tooltip></el-table-column> | 106 | ? TableColumnWidth.DATETIME |
| 115 | <el-table-column label="重标识风险" prop="reIdentifyRisk" width="96" align="right" fixed="right" | 107 | : item.dataType === 'date' |
| 116 | show-overflow-tooltip></el-table-column> | 108 | ? TableColumnWidth.DATE |
| 117 | <el-table-column label="判断重风险是否大于门限阈值" prop="isGtThreshold" width="130" align="left" fixed="right" | 109 | : originResultTableFieldColumn[item.enName] |
| 118 | show-overflow-tooltip></el-table-column> | 110 | " :align="getTextAlign(item)" :header-align="getTextAlign(item)" |
| 119 | </el-table> | 111 | :formatter="(row) => formatterPreviewDate(row, item)" :show-overflow-tooltip="true"> |
| 120 | <div v-show="!analysisResultTableFields.length" class="empty-content"> | ||
| 121 | <img src="../../../assets/images/empty-data.png" :style="{ width: '168px', height: '96px' }" /> | ||
| 122 | <div class="empty-text">暂无数据</div> | ||
| 123 | </div> | ||
| 124 | <div v-show="analysisResultTableFields.length" class="result-table-desc">门限阈值的取值:完全公开共享数据发布,取值 | ||
| 125 | 1/20;受控公开共享数据发布,取值 | ||
| 126 | 1/5;领地公开共享数据发布,取值 1/3</div> | ||
| 127 | <PageNav v-show="analysisResultTableFields.length" :class="[pageInfo.type]" :pageInfo="pageInfo" | ||
| 128 | @pageChange="pageChange" /> | ||
| 129 | <div class="row-two-main" style="margin-top: 12px;"> | ||
| 130 | <div class="table-one"> | ||
| 131 | <div class="result-title">对抗性测试关键变量</div> | ||
| 132 | <el-table ref="varTableRef" :data="analysisResultInfo?.adversarialTest || []" :highlight-current-row="true" | ||
| 133 | stripe border tooltip-effect="light" height="100%" row-key="guid" | ||
| 134 | :style="{ height: oldAnonTaskValueInfo.dataSharingTypeCode != '01' ? (containerWidth > 1397 ? '378px' : (containerWidth < 1048 ? '414px' : '396px')) : 'auto' }"> | ||
| 135 | <el-table-column label="序号" type="index" width="56px" align="center" show-overflow-tooltip> | ||
| 136 | </el-table-column> | 112 | </el-table-column> |
| 137 | <el-table-column label="数据项" prop="chName" width="150px" align="left" show-overflow-tooltip></el-table-column> | 113 | </template> |
| 138 | <el-table-column label="唯一性分值" prop="uniqueScore" width="110px" align="right" | 114 | <el-table-column label="等价类大小" prop="equivalenceClassNum" width="98" align="right" fixed="right" |
| 139 | show-overflow-tooltip></el-table-column> | 115 | show-overflow-tooltip></el-table-column> |
| 140 | <el-table-column label="影响力分值" prop="influenceScore" width="110" align="right" | 116 | <el-table-column label="重标识风险" prop="reIdentifyRisk" width="96" align="right" fixed="right" |
| 141 | show-overflow-tooltip></el-table-column> | 117 | show-overflow-tooltip></el-table-column> |
| 142 | <el-table-column label="数据属性标识度分值" prop="dataAttrIdentScore" width="160" align="right" | 118 | <el-table-column label="判断重风险是否大于门限阈值" prop="isGtThreshold" width="130" align="left" fixed="right" |
| 143 | show-overflow-tooltip></el-table-column> | 119 | show-overflow-tooltip></el-table-column> |
| 144 | </el-table> | 120 | </el-table> |
| 121 | <div v-show="!analysisResultTableFields.length" class="empty-content"> | ||
| 122 | <img src="../../../assets/images/empty-data.png" :style="{ width: '168px', height: '96px' }" /> | ||
| 123 | <div class="empty-text">暂无数据</div> | ||
| 145 | </div> | 124 | </div> |
| 146 | <div class="table-two" v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'"> | 125 | <div v-show="analysisResultTableFields.length" class="result-table-desc">门限阈值的取值:完全公开共享数据发布,取值 |
| 147 | <div class="result-title">内部故意攻击概率</div> | 126 | 1/20;受控公开共享数据发布,取值 |
| 148 | <div class="desc" style="margin-bottom: 4px;color: #999;line-height: 18px;"> | 127 | 1/5;领地公开共享数据发布,取值 1/3</div> |
| 149 | 重标识数据的动机和能力为低,从重标识攻击可能性分析表可得出在内部攻击方面,重标识攻击概率的取值为0.05。</div> | 128 | <PageNav v-show="analysisResultTableFields.length" :class="[pageInfo.type]" :pageInfo="pageInfo" |
| 150 | <el-table ref="innerTableRef" :data="innerResultData" :highlight-current-row="true" stripe border | 129 | @pageChange="pageChange" /> |
| 151 | tooltip-effect="light" height="100%" row-key="guid" :style="{ height: '356px' }" | 130 | <div class="row-two-main" style="margin-top: 12px;"> |
| 152 | :span-method="arrayInnerSpanMethod" :cell-class-name="handleInnerCellClass"> | 131 | <div class="table-one"> |
| 153 | <!-- <el-table-column label="序号" type="index" width="56px" align="center" show-overflow-tooltip> | 132 | <div class="result-title">对抗性测试关键变量</div> |
| 133 | <el-table ref="varTableRef" :data="analysisResultInfo?.adversarialTest || []" :highlight-current-row="true" | ||
| 134 | stripe border tooltip-effect="light" height="100%" row-key="guid" | ||
| 135 | :style="{ height: oldAnonTaskValueInfo.dataSharingTypeCode != '01' ? (containerWidth > 1397 ? '378px' : (containerWidth < 1048 ? '414px' : '396px')) : 'auto' }"> | ||
| 136 | <el-table-column label="序号" type="index" width="56px" align="center" show-overflow-tooltip> | ||
| 137 | </el-table-column> | ||
| 138 | <el-table-column label="数据项" prop="chName" width="150px" align="left" | ||
| 139 | show-overflow-tooltip></el-table-column> | ||
| 140 | <el-table-column label="唯一性分值" prop="uniqueScore" width="110px" align="right" | ||
| 141 | show-overflow-tooltip></el-table-column> | ||
| 142 | <el-table-column label="影响力分值" prop="influenceScore" width="110" align="right" | ||
| 143 | show-overflow-tooltip></el-table-column> | ||
| 144 | <el-table-column label="数据属性标识度分值" prop="dataAttrIdentScore" width="160" align="right" | ||
| 145 | show-overflow-tooltip></el-table-column> | ||
| 146 | </el-table> | ||
| 147 | </div> | ||
| 148 | <div class="table-two" v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'"> | ||
| 149 | <div class="result-title">内部故意攻击概率</div> | ||
| 150 | <div class="desc" style="margin-bottom: 4px;color: #999;line-height: 18px;"> | ||
| 151 | 重标识数据的动机和能力为低,从重标识攻击可能性分析表可得出在内部攻击方面,重标识攻击概率的取值为0.05。</div> | ||
| 152 | <el-table ref="innerTableRef" :data="innerResultData" :highlight-current-row="true" stripe border | ||
| 153 | tooltip-effect="light" height="100%" row-key="guid" :style="{ height: '356px' }" | ||
| 154 | :span-method="arrayInnerSpanMethod" :cell-class-name="handleInnerCellClass"> | ||
| 155 | <!-- <el-table-column label="序号" type="index" width="56px" align="center" show-overflow-tooltip> | ||
| 154 | </el-table-column> --> | 156 | </el-table-column> --> |
| 155 | <el-table-column label="风险减缓控制水平" prop="level" width="150px" align="left" | 157 | <el-table-column label="风险减缓控制水平" prop="level" width="150px" align="left" |
| 156 | show-overflow-tooltip></el-table-column> | 158 | show-overflow-tooltip></el-table-column> |
| 157 | <el-table-column label="动机和能力" prop="competencyLevel" width="140px" align="left" | 159 | <el-table-column label="动机和能力" prop="competencyLevel" width="140px" align="left" |
| 158 | show-overflow-tooltip></el-table-column> | 160 | show-overflow-tooltip></el-table-column> |
| 159 | <el-table-column label="重标识攻击概率" prop="value" width="140" align="right" show-overflow-tooltip> | 161 | <el-table-column label="重标识攻击概率" prop="value" width="140" align="right" show-overflow-tooltip> |
| 160 | <template #default="scope"> | 162 | <template #default="scope"> |
| 161 | <span>{{ scope.row.value }}</span> | 163 | <span>{{ scope.row.value }}</span> |
| 162 | </template> | 164 | </template> |
| 163 | </el-table-column> | 165 | </el-table-column> |
| 164 | </el-table> | 166 | </el-table> |
| 167 | </div> | ||
| 165 | </div> | 168 | </div> |
| 166 | </div> | 169 | <div class="row-two-main" v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'"> |
| 167 | <div class="row-two-main" v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'"> | 170 | <div class="table-one border"> |
| 168 | <div class="table-one border"> | 171 | <div class="result-title">数据集包含熟人概率</div> |
| 169 | <div class="result-title">数据集包含熟人概率</div> | 172 | <div class="result-title-h1">pr=1-(1-p)^m</div> |
| 170 | <div class="result-title-h1">pr=1-(1-p)^m</div> | 173 | <div class="result-title-desc"> |
| 171 | <div class="result-title-desc"> | 174 | {{ ` 数据集包含熟人概率可通过以上公式计算,${new |
| 172 | {{ ` 数据集包含熟人概率可通过以上公式计算,${new | 175 | Date().getFullYear()}年我国最新的人口统计为${analysisResultInfo?.allPerson || |
| 173 | Date().getFullYear()}年我国最新的人口统计为${analysisResultInfo?.allPerson || | 176 | 0}亿人,其中该数据集的容量为${analysisResultInfo?.dataSetNum || 0}万, |
| 174 | 0}亿人,其中该数据集的容量为${analysisResultInfo?.dataSetNum || 0}万, | 177 | 占总人口的比例为${analysisResultInfo.patientPopulationRate || |
| 175 | 占总人口的比例为${analysisResultInfo.patientPopulationRate || | 178 | 0}%,m值取推荐值150,数据集包含熟人的概率为${analysisResultInfo.randomAcquaintancePr || 0}。` }} |
| 176 | 0}%,m值取推荐值150,数据集包含熟人的概率为${analysisResultInfo.randomAcquaintancePr || 0}。` }} | 179 | </div> |
| 180 | </div> | ||
| 181 | <div class="table-two border"> | ||
| 182 | <div class="result-title">数据泄露概率</div> | ||
| 183 | <div class="result-title-desc"> | ||
| 184 | <div>对于安全和隐私控制能力评估为低的情况,推荐将数据泄露概率设定为0.55。</div> | ||
| 185 | <div>对于安全和隐私控制能力评估为中的情况,推荐将数据泄露概率设定为0.27。</div> | ||
| 186 | <div>对于安全和隐私控制能力评估为高的情况,推荐将数据泄露概率设定为 0.14。</div> | ||
| 187 | <div>{{ `数据接收方的安全和隐私控制能力为高,按照推荐值将数据泄露概率设定为${analysisResultInfo.dataBreachPr || 0}。` }}</div> | ||
| 188 | </div> | ||
| 177 | </div> | 189 | </div> |
| 178 | </div> | 190 | </div> |
| 179 | <div class="table-two border"> | 191 | </div> |
| 180 | <div class="result-title">数据泄露概率</div> | 192 | <div v-show="isWordStyle" class="analysis-result-main report-main" ref="report"> |
| 181 | <div class="result-title-desc"> | 193 | <div |
| 182 | <div>对于安全和隐私控制能力评估为低的情况,推荐将数据泄露概率设定为0.55。</div> | 194 | style="font-family: simsun;height: 40px;display: block;line-height: 32px;font-size: 18px;color: #212121;font-weight: 700;text-align: center;margin-top: 10px;margin-bottom: 10px;">匿名化效果评估指标</div> |
| 183 | <div>对于安全和隐私控制能力评估为中的情况,推荐将数据泄露概率设定为0.27。</div> | 195 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;white-space: pre-wrap;color: #212121;">{{ ' 依据GB/T 42460-2023《信息安全技术个人信息去标识化效果评估指南》附录D,对去标识化以后的数据集基于K匿名模型进行去标识化效果的评估。' }}</p> |
| 184 | <div>对于安全和隐私控制能力评估为高的情况,推荐将数据泄露概率设定为 0.14。</div> | 196 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;white-space: pre-wrap;color: #212121;"> |
| 185 | <div>{{ `数据接收方的安全和隐私控制能力为高,按照推荐值将数据泄露概率设定为${analysisResultInfo.dataBreachPr || 0}。` }}</div> | 197 | {{ ` 总体方案为先计算数据集每行记录、整体数据集的重标识风险,进而计算环境重标识攻击概率,最后再结合环境重标识攻击概率计算整个数据集的重标识整体风险。去标识化效果评估结果为${analysisResultInfo.rating + '级'}。具体指标如下:` }}</p> |
| 198 | <div style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px;">匿名化结果分析</div> | ||
| 199 | <div class="kpi-content" v-show="Object.keys(analysisResultInfo).length > 0"> | ||
| 200 | <div class="border-content"> | ||
| 201 | <div class="text" style="color: #212121;">去标识化效果评估结果</div> | ||
| 202 | <span class="number score-color">{{ analysisResultInfo.rating + '级' }}</span> | ||
| 203 | </div> | ||
| 204 | <div class="border-content"> | ||
| 205 | <span class="text" style="color: #212121;">重标识风险最大值</span> | ||
| 206 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRb != null ? | ||
| 207 | (analysisResultInfo.reIdentifyRiskRb || 0) : '--' | ||
| 208 | }}</span> | ||
| 209 | </div> | ||
| 210 | <div class="border-content"> | ||
| 211 | <span class="text" style="color: #212121;">重标识风险平均值</span> | ||
| 212 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRc != null ? | ||
| 213 | (analysisResultInfo.reIdentifyRiskRc || 0) : '--' }}</span> | ||
| 214 | </div> | ||
| 215 | <div class="border-content"> | ||
| 216 | <span class="text" style="color: #212121;">环境重标识攻击概率</span> | ||
| 217 | <span class="number">{{ analysisResultInfo.envReAttackPr != null ? | ||
| 218 | (analysisResultInfo.envReAttackPr || 0) : '--' }}</span> | ||
| 219 | </div> | ||
| 220 | <div class="border-content"> | ||
| 221 | <span class="text" style="color: #212121;">等价类门限风险</span> | ||
| 222 | <span class="number">{{ analysisResultInfo.reIdentifyRiskRa != null ? | ||
| 223 | (analysisResultInfo.reIdentifyRiskRa || 0) : '--' }}</span> | ||
| 186 | </div> | 224 | </div> |
| 225 | <div class="border-content"> | ||
| 226 | <span class="text" style="color: #212121;">重标识风险总体风险</span> | ||
| 227 | <span class="number">{{ analysisResultInfo.reIdentifyOverallRisk != null ? | ||
| 228 | (analysisResultInfo.reIdentifyOverallRisk || 0) : '--' }}</span> | ||
| 229 | </div> | ||
| 230 | <div class="border-content"> | ||
| 231 | <span class="text" style="color: #212121;">重标识可接受风险阈值</span> | ||
| 232 | <span class="number">{{ oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold == null ? 0.05 : | ||
| 233 | (oldAnonTaskValueInfo.anonPrivacyMode?.riskThreshold || 0) }}</span> | ||
| 234 | </div> | ||
| 235 | </div> | ||
| 236 | <div style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px 4px;">重标识风险表</div> | ||
| 237 | <div style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;margin-bottom: 4px;white-space: pre-wrap;">{{ ' 门限阈值的取值:完全公开共享数据发布,取值 1/20;受控公开共享数据发布,取值 1/5;领地公开共享数据发布,取值 1/3。' }}</div> | ||
| 238 | <table border="0" cellspacing="0" | ||
| 239 | style="width:100%;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;color: #212121"> | ||
| 240 | <thead> | ||
| 241 | <tr> | ||
| 242 | <th v-for="(item, index) in resultTableFields" :key="index" style="border: 0.5px solid #d9d9d9"> | ||
| 243 | <span>{{ item.chName }}</span> | ||
| 244 | </th> | ||
| 245 | </tr> | ||
| 246 | </thead> | ||
| 247 | <tbody> | ||
| 248 | <tr v-for="(recordItem, j) in fullResultData" :key="j"> | ||
| 249 | <td v-for="(columnItem, i) in resultTableFields" :key="i" | ||
| 250 | :style="{border: '0.5px solid #d9d9d9', 'text-align': <any>(getTextAlign(columnItem) ?? 'left') }"> | ||
| 251 | <span :style="{ 'word-break': 'break-all' }"> | ||
| 252 | {{ columnItem.enName == 'index' ? (j + 1) : formatterPreviewDate(recordItem, columnItem) }} | ||
| 253 | </span> | ||
| 254 | </td> | ||
| 255 | </tr> | ||
| 256 | </tbody> | ||
| 257 | </table> | ||
| 258 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 259 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px 4px;">内部故意攻击概率</div> | ||
| 260 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 261 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;margin-bottom: 4px;white-space: pre-wrap;">{{ ' 重标识数据的动机和能力为低,从重标识攻击可能性分析表可得出在内部攻击方面,重标识攻击概率的取值为0.05。'}}</div> | ||
| 262 | <table v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" border="0" cellspacing="0" | ||
| 263 | style="width:100%;word-break: break-all;margin: 0 auto;text-align: center;border-collapse: collapse;color: #212121;"> | ||
| 264 | <thead> | ||
| 265 | <tr> | ||
| 266 | <th v-for="(item, index) in innerResultFields" :key="index" style="border: 0.5px solid #d9d9d9"> | ||
| 267 | <span>{{ item.label }}</span> | ||
| 268 | </th> | ||
| 269 | </tr> | ||
| 270 | </thead> | ||
| 271 | <tbody> | ||
| 272 | <tr v-for="(recordItem, j) in innerResultData" :key="j"> | ||
| 273 | <template v-for="(columnItem, i) in innerResultFields" :key="i"> | ||
| 274 | <td :rowspan="i == 0 ? 3 : 1" v-show="!(j % 3 != 0 && i == 0)" | ||
| 275 | :style="{ border: '0.5px solid #d9d9d9', 'text-align': <any>(columnItem.align ?? 'left') }"> | ||
| 276 | <span :style="{ 'word-break': 'break-all' }"> | ||
| 277 | {{ recordItem[columnItem.field] }} | ||
| 278 | </span> | ||
| 279 | </td> | ||
| 280 | </template> | ||
| 281 | </tr> | ||
| 282 | </tbody> | ||
| 283 | </table> | ||
| 284 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 285 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px;">数据集包含熟人概率</div> | ||
| 286 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 287 | style="font-family: simsun;text-align: center; display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700">pr=1-(1-p)^m</div> | ||
| 288 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 289 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;white-space: pre-wrap;"> | ||
| 290 | {{ ` 数据集包含熟人概率可通过以上公式计算,${new Date().getFullYear()}年我国最新的人口统计为${analysisResultInfo?.allPerson || | ||
| 291 | 0}亿人,其中该数据集的容量为${analysisResultInfo?.dataSetNum || 0}万,占总人口的比例为${analysisResultInfo.patientPopulationRate || | ||
| 292 | 0}%,m值取推荐值150,数据集包含熟人的概率为${analysisResultInfo.randomAcquaintancePr || 0}。` }}</div> | ||
| 293 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 294 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px;">数据泄露概率 | ||
| 187 | </div> | 295 | </div> |
| 296 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 297 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;white-space: pre-wrap;">{{ | ||
| 298 | ' 对于安全和隐私控制能力评估为低的情况,推荐将数据泄露概率设定为0.55。' }}</div> | ||
| 299 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 300 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;white-space: pre-wrap;">{{ ' 对于安全和隐私控制能力评估为中的情况,推荐将数据泄露概率设定为0.27。' }}</div> | ||
| 301 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 302 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;white-space: pre-wrap;">{{ ' 对于安全和隐私控制能力评估为高的情况,推荐将数据泄露概率设定为 0.14。' }}</div> | ||
| 303 | <div v-show="oldAnonTaskValueInfo.dataSharingTypeCode != '01'" | ||
| 304 | style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;white-space: pre-wrap;">{{ ` 数据接收方的安全和隐私控制能力为高,按照推荐值将数据泄露概率设定为${analysisResultInfo.dataBreachPr || 0}。` }}</div> | ||
| 305 | <div style="font-family: simsun;display: block;line-height: 21px;font-size: 14px;color: #212121;font-weight: 700;margin: 8px 0px;">备注:</div> | ||
| 306 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;margin-top: 8px;white-space: pre-wrap;">{{ ' 重标识风险最大值:所有等价类的重标识风险最大值;' }}</p> | ||
| 307 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 重标识风险平均值:所有等价类的重标识风险平均值;'}}</p> | ||
| 308 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 环境重标识攻击概率:完全公开共享数据发布,攻击者对数据集进行环境重标识攻击的概率为1。领地公开共享与受控公开共享数据发布,环境重标识攻击概率为内部故意攻击概率、数据集包含熟人概率和数据泄露概率三者的最大值;' }}</p> | ||
| 309 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 等价类门限风险:完全公开共享数据发布,门限阈值取值1/20;受控公开共享数据发布,门限阈值取值 1/5;领地公开共享数据发布,门限阈值取值 1/3;等价类门限风险为:等价类的重标识风险大于门限阈值为1,小于等于为0,求和后除以等价类个数;'}}</p> | ||
| 310 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 重标识风险总体风险:完全公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险最大值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。' }}</p> | ||
| 311 | <p style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 受控公开共享和领地公开共享,当等价类门限风险=0时,重标识风险总体风险公式为等价类重标识风险平均值*环境重标识攻击概率;当等价类门限风险!=0时,重标识风险总体风险为1。' }}</p> | ||
| 312 | <p v-show="analysisResultInfo.rating >= 3" style="font-family: simsun;margin: 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 依据《数据安全技术个人信息匿名化处理指南及评价方法(征求意见稿)》4.2,去标识化的数据集达到3级可展开对数据级的对抗性测试和不可复原性核验。对抗性测试关键变量如下表:' }}</p> | ||
| 313 | <table border="0" cellspacing="0" v-show="analysisResultInfo.rating >= 3" | ||
| 314 | style="width:100%;word-break: break-all;margin: 4px auto 0px auto;text-align: center;border-collapse: collapse;color: #212121;"> | ||
| 315 | <thead> | ||
| 316 | <tr> | ||
| 317 | <th v-for="(item, index) in adversarialTestFields" :key="index" style="border: 0.5px solid #d9d9d9"> | ||
| 318 | <span>{{ item.label }}</span> | ||
| 319 | </th> | ||
| 320 | </tr> | ||
| 321 | </thead> | ||
| 322 | <tbody> | ||
| 323 | <tr v-for="(recordItem, j) in analysisResultInfo?.adversarialTest || []" :key="j"> | ||
| 324 | <td v-for="(columnItem, i) in adversarialTestFields" :key="i" | ||
| 325 | :style="{ border: '0.5px solid #d9d9d9', 'text-align': <any>(columnItem.align ?? 'left') }"> | ||
| 326 | <span :style="{ 'word-break': 'break-all' }"> | ||
| 327 | {{ columnItem.field == 'index' ? (j + 1) : recordItem[columnItem.field] === 0 ? 0 : (recordItem[columnItem.field] || '--') }} | ||
| 328 | </span> | ||
| 329 | </td> | ||
| 330 | </tr> | ||
| 331 | </tbody> | ||
| 332 | </table> | ||
| 333 | <p v-show="analysisResultInfo.rating >= 3" style="font-family: simsun;margin: 4px 0px 0px;font-size: 14px;line-height: 21px;color: #212121;white-space: pre-wrap;">{{ ' 通过识别关键变量,选择标识度高于0.5的属性作为攻击测试的关键变量,在攻击测试中,涉及到的攻击场景,其重标识概率均远小于所接受的风险水平0.05。另外,未发现任何其它可能的安全性问题。' }}</p> | ||
| 188 | </div> | 334 | </div> |
| 189 | </div> | 335 | </div> |
| 190 | </template> | 336 | </template> |
| 191 | <script lang="ts" setup name="anonResultAnalysis"> | 337 | <script lang="ts" setup name="anonResultAnalysis"> |
| 192 | import { TableColumnWidth } from '@/utils/enum'; | 338 | import { TableColumnWidth } from '@/utils/enum'; |
| 339 | import { QuestionFilled } from "@element-plus/icons-vue"; | ||
| 193 | import Moment from 'moment'; | 340 | import Moment from 'moment'; |
| 194 | 341 | ||
| 195 | defineProps({ | 342 | const props = defineProps({ |
| 343 | isWordStyle: { //显示的是否是word报告的样式 | ||
| 344 | type: Boolean, | ||
| 345 | default: false, | ||
| 346 | }, | ||
| 196 | showTitle: { | 347 | showTitle: { |
| 197 | type: Boolean, | 348 | type: Boolean, |
| 198 | default: false, | 349 | default: false, |
| ... | @@ -213,7 +364,11 @@ defineProps({ | ... | @@ -213,7 +364,11 @@ defineProps({ |
| 213 | type: Object, | 364 | type: Object, |
| 214 | default: {}, | 365 | default: {}, |
| 215 | }, | 366 | }, |
| 216 | resultData: { | 367 | resultData: { //分页后的数据 |
| 368 | type: Array, | ||
| 369 | default: [] | ||
| 370 | }, | ||
| 371 | fullResultData: { //全部数据,下载word时使用。 | ||
| 217 | type: Array, | 372 | type: Array, |
| 218 | default: [] | 373 | default: [] |
| 219 | }, | 374 | }, |
| ... | @@ -231,11 +386,33 @@ defineProps({ | ... | @@ -231,11 +386,33 @@ defineProps({ |
| 231 | }, | 386 | }, |
| 232 | }) | 387 | }) |
| 233 | 388 | ||
| 389 | const report = ref(); | ||
| 390 | |||
| 391 | const resultTableFields = computed(() => { | ||
| 392 | let arr: any[] = [{ chName: '等价类', dataType: 'int', enName: 'index' }] | ||
| 393 | return arr.concat(props.analysisResultTableFields).concat([{ | ||
| 394 | chName: '等价类大小', | ||
| 395 | enName: 'equivalenceClassNum', | ||
| 396 | dataType: 'int' | ||
| 397 | }, { | ||
| 398 | chName: '重标识风险', | ||
| 399 | enName: 'reIdentifyRisk', | ||
| 400 | dataType: 'decimal' | ||
| 401 | }, { | ||
| 402 | chName: '判断重风险是否大于门限阈值', | ||
| 403 | enName: 'isGtThreshold', | ||
| 404 | dataType: 'text' | ||
| 405 | }]); | ||
| 406 | }) | ||
| 407 | |||
| 234 | const emits = defineEmits([ | 408 | const emits = defineEmits([ |
| 235 | "pageChange" | 409 | "pageChange" |
| 236 | ]); | 410 | ]); |
| 237 | 411 | ||
| 238 | const getTextAlign = (field) => { | 412 | const getTextAlign = (field) => { |
| 413 | if (field.enName === 'index') { | ||
| 414 | return 'center'; | ||
| 415 | } | ||
| 239 | if (field.dataType === 'decimal' || field.dataType === 'int') { | 416 | if (field.dataType === 'decimal' || field.dataType === 'int') { |
| 240 | return 'right'; | 417 | return 'right'; |
| 241 | } | 418 | } |
| ... | @@ -265,6 +442,17 @@ const formatterPreviewDate = (row, info) => { | ... | @@ -265,6 +442,17 @@ const formatterPreviewDate = (row, info) => { |
| 265 | }; | 442 | }; |
| 266 | 443 | ||
| 267 | /** 内部故意攻击概率的表格 */ | 444 | /** 内部故意攻击概率的表格 */ |
| 445 | const innerResultFields = ref([{ | ||
| 446 | field: 'level', | ||
| 447 | label: '风险减缓控制水平' | ||
| 448 | }, { | ||
| 449 | field: 'competencyLevel', | ||
| 450 | label: '动机和能力' | ||
| 451 | }, { | ||
| 452 | field: 'value', | ||
| 453 | label: '重标识攻击概率', | ||
| 454 | align: 'right' | ||
| 455 | }]); | ||
| 268 | const innerResultData = ref([{ | 456 | const innerResultData = ref([{ |
| 269 | guid: '1', | 457 | guid: '1', |
| 270 | level: '高', | 458 | level: '高', |
| ... | @@ -350,15 +538,42 @@ const handleInnerCellClass = ({ row, column, rowIndex, columnIndex }) => { | ... | @@ -350,15 +538,42 @@ const handleInnerCellClass = ({ row, column, rowIndex, columnIndex }) => { |
| 350 | return ''; | 538 | return ''; |
| 351 | } | 539 | } |
| 352 | 540 | ||
| 541 | /** 对抗性测试变量表格字段配置 */ | ||
| 542 | const adversarialTestFields = ref([{ | ||
| 543 | field: 'index', | ||
| 544 | label: '序号', | ||
| 545 | align: 'center', | ||
| 546 | }, { | ||
| 547 | field: 'chName', | ||
| 548 | label: '数据项' | ||
| 549 | }, { | ||
| 550 | field: 'uniqueScore', | ||
| 551 | label: '唯一性分值', | ||
| 552 | align: 'right' | ||
| 553 | }, { | ||
| 554 | field: 'influenceScore', | ||
| 555 | label: '影响力分值', | ||
| 556 | align: 'right' | ||
| 557 | }, { | ||
| 558 | field: 'dataAttrIdentScore', | ||
| 559 | label: '数据属性标识度分值', | ||
| 560 | align: 'right' | ||
| 561 | }]); | ||
| 562 | |||
| 353 | const pageChange = (info) => { | 563 | const pageChange = (info) => { |
| 354 | emits('pageChange', info); | 564 | emits('pageChange', info); |
| 355 | } | 565 | } |
| 356 | 566 | ||
| 567 | defineExpose({ | ||
| 568 | report, | ||
| 569 | }) | ||
| 570 | |||
| 357 | </script> | 571 | </script> |
| 358 | 572 | ||
| 359 | <style lang="scss" scoped> | 573 | <style lang="scss" scoped> |
| 360 | .analysis-result-main { | 574 | .analysis-result-main { |
| 361 | min-height: 250px; | 575 | min-height: 250px; |
| 576 | width: 100%; | ||
| 362 | 577 | ||
| 363 | .value-desc { | 578 | .value-desc { |
| 364 | font-size: 14px; | 579 | font-size: 14px; |
| ... | @@ -480,4 +695,17 @@ const pageChange = (info) => { | ... | @@ -480,4 +695,17 @@ const pageChange = (info) => { |
| 480 | color: #b2b2b2; | 695 | color: #b2b2b2; |
| 481 | } | 696 | } |
| 482 | } | 697 | } |
| 698 | |||
| 699 | .report-main.analysis-result-main { | ||
| 700 | width: 625px; | ||
| 701 | |||
| 702 | .kpi-content { | ||
| 703 | margin-bottom: 0px; | ||
| 704 | |||
| 705 | .border-content { | ||
| 706 | width: calc(33% - 8px); | ||
| 707 | min-width: 168px; | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 483 | </style> | 711 | </style> |
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
-
Please register or sign in to post a comment