feat-实现数据分析-原始数据AI标记筛选和数据导出功能

This commit is contained in:
zhang.weiwei
2025-03-07 17:18:53 +08:00
parent 3ee27a207a
commit 4f5203d4e0
13 changed files with 390 additions and 145 deletions

View File

@@ -3,7 +3,7 @@ VUE_APP_CURRENTMODE = 'dev'
VUE_APP_BASEOSS = 'https://diaoyan-files.automark.cc'
#VUE_APP_BASEURL = 'http://planetg-java.test.automark.cc/'
VUE_APP_BASEURL = 'http://yls-api-uat.dctest.digitalyili.com/'
VUE_APP_BASEURL = 'https://yls-api-uat.dctest.digitalyili.com'
VUE_APP_DELiVERY_BASEURL='https://javaxq.test.automark.cc/'
VUE_APP_MESSAGE_CENTER ='http://gtech-gateway.dcin-test.digitalyili.com/apigtech/message-send-center/';

View File

@@ -2,7 +2,8 @@ NODE_ENV = 'production'
VUE_APP_CURRENTMODE = 'sit'
VUE_APP_BASEOSS = 'https://test-cxp-pubcos.yili.com/uat-yls'
VUE_APP_BASEURL = 'https://yls-api-uat.dctest.digitalyili.com'
VUE_APP_BASEURL = 'https://ylst-api-sit.dctest.digitalyili.com'
VUE_APP_JAVA_DELiVERY_BASEURL='https://ylsdist-net-uat.dctest.digitalyili.com'

View File

@@ -311,12 +311,12 @@ const toAiInspection = () => {
console.log('questions', questionInfo.questions);
if (!questionInfo.questions || questionInfo.questions.length === 0) {
// 未设计问卷,不允许质检
message.error('请先设计问卷');
message.error('您还没添加任何问题请添加后再进行AI质检。');
return;
}
const questions = questionInfo.questions;
emitter.emit('app-loading', { visible: true, description: '正在 AI 质检...请您稍候~' });
aiQualityInspection(sn)
aiQualityInspection(route.query.sn, { sn})
.then((res) => {
if (!res || !res.code === 0 || !res.data) {
// 质检接口调用失败
@@ -338,11 +338,11 @@ const toAiInspection = () => {
questions.forEach((question) => {
questionMap[question.title] = question;
});
resultList.forEach((resultQuestion) => {
const question = questionMap[resultQuestion.title];
resultList.forEach((result) => {
const question = questionMap[result.title];
if (question) {
// 有匹配上的问题,将质检结果合并到问题中
question.ai_inspection_result = resultQuestion;
question.ai_inspection_result = result.advice;
}
});
store.commit('common/M_COMMON_SET_QUESTION_INFO', questionInfo);

View File

@@ -5,22 +5,46 @@
v-if="[12, 13, 14].includes(question_type)"
v-html="content"
></div>
<div v-else-if="titleInfo.titleType === 'withIcon'" class="icon-title">
<a-tooltip placement="topLeft">
<template #title>
<div class="custom-head-preview">{{ titleInfo.title }}</div>
</template>
<!--带有图标的标题AI/人工样本标记-->
<div class="common-text">{{ titleInfo.title }}</div>
</a-tooltip>
<a-tooltip placement="topLeft">
<template #title>
<div class="custom-head-preview">{{ titleInfo.titleIconTip }}</div>
</template>
<!--带有图标的标题AI/人工样本标记-->
<img class="icon-title__icon" :src="titleInfo.titleIconSrc" alt="" @click="onIconClicked" />
</a-tooltip>
</div>
<a-tooltip placement="topLeft" v-else>
<template #title>
<div class="custom-head-preview" v-html="content"></div>
</template>
<div class="custom-head-main">
<!-- 下载题型-->
<div class="file-question" v-if="question_type === 18" :style="{ width: data.width + 'px' }">
<div
class="file-question"
v-if="question_type === 18"
:style="{ width: data.width + 'px' }"
>
<div class="label">{{ content }}</div>
<i class="iconfont icon-xiazai" style="color: #70b936; cursor: pointer" @click="download"></i>
<i
class="iconfont icon-xiazai"
style="color: #70b936; cursor: pointer"
@click="download"
></i>
<!-- <a-button size="mini" @click="download">下载全部附件333</a-button>-->
</div>
<!-- 图文题-->
<div class="common-text" v-else>{{ content }}</div>
</div>
</a-tooltip>
<!-- 下载中心 -->
<DownloadCenter v-model:visible="downloadVisible" v-if="downloadVisible"></DownloadCenter>
</div>
@@ -47,6 +71,8 @@ const props = defineProps({
type: String
}
});
const emit = defineEmits(['iconClick']);
const titleInfo = ref({});
const content = ref('');
const question_index = ref(null);
const question_type = ref(null);
@@ -90,7 +116,15 @@ function downloadCenter() {
});
}
/**
* 点击标题图标,发送事件
*/
function onIconClicked() {
emit('iconClick', titleInfo.value);
}
watchEffect(() => {
titleInfo.value = props.data;
content.value = props.data.title;
question_index.value = props.data.question_index;
question_type.value = props.data.question_type;
@@ -137,5 +171,15 @@ watchEffect(() => {
align-items: center;
word-wrap: break-word;
}
.icon-title {
display: flex;
align-items: center;
&__icon {
width: 20px;
height: 20px;
margin-left: 10px;
}
}
}
</style>

View File

@@ -12,7 +12,12 @@
<template v-for="col in tableHeaders" :key="col.dataIndex" #[col.dataIndex]="{ record, text }">
<template v-if="col.dataIndex === 'is_mark'">
<slot name="is_mark" v-bind:record="record">
<i v-if="record.is_mark == 1" class="icon iconfont show-sign curror" @click="hideSign(record)">&#xe7d6;</i>
<i
v-if="record.is_mark == 1"
class="icon iconfont show-sign curror"
@click="hideSign(record)"
>&#xe7d6;</i
>
<i v-else class="icon iconfont hide-sign curror" @click="showSign(record)">&#xe7d6;</i>
</slot>
</template>
@@ -126,7 +131,7 @@ const props = defineProps({
default: ''
}
});
const emit = defineEmits(['change', 'select']);
const emit = defineEmits(['change', 'select', 'headerClick']);
const store = useStore();
const data = ref([]);
const columns = ref(null);
@@ -134,7 +139,9 @@ const searchParams = ref(props.params);
const { perPage: per_page, total, page, rowKey } = toRefs(props);
const { pagination } = usePagination(per_page, total, page);
const { tableHeaders } = useGeneratorTableColumns(columns);
const { customHeaders } = useCustomHeaderTitle(tableHeaders, props.sn, searchParams);
const { customHeaders } = useCustomHeaderTitle(tableHeaders, props.sn, searchParams, (column) => {
emit('headerClick', column);
});
// 页码&分页条数,改变时候触发
const handlePageChange = (data) => {
@@ -187,6 +194,7 @@ const showSign = (item) => {
const hideSign = (item) => {
emit('sign', [item], false);
};
watch(
() => props.params,
(val) => {

View File

@@ -11,7 +11,10 @@
<img class="suffix-icon" :src="require('@/assets/img/select-arrow-down.png')" alt="">
</template>
</a-select> -->
<a-dropdown class="searchSelect operChd" :getPopupContainer="(triggerNode) => triggerNode.parentNode">
<a-dropdown
class="searchSelect operChd"
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
>
<template #overlay>
<a-menu @click="opChange">
<a-menu-item v-for="item in opList" :key="item.value">
@@ -24,7 +27,11 @@
<img class="suffix-icon" :src="require('@/assets/img/select-arrow-down.png')" alt="" />
</a-button>
</a-dropdown>
<a-button class="operChd custom-button" @click="emit('cleanAllBtn', true)">清空数据</a-button>
<!-- delete by zhangweiwei 20250331_ai 无效样本标记-不显示清空数据按钮 start -->
<!-- <a-button class="operChd custom-button" @click="emit('cleanAllBtn', true)">清空数据</a-button> -->
<!-- delete by zhangweiwei 20250331_ai 无效样本标记-不显示清空数据按钮 end -->
<!-- <a-button class="operChd" @click="handleDownload">下载全部答卷</a-button> -->
<!-- <a-dropdown class="searchSelect operChd" :getPopupContainer="(triggerNode) => triggerNode.parentNode">
<template #overlay>
@@ -40,7 +47,9 @@
<CaretDownOutlined class="suffix-icon" />
</a-button>
</a-dropdown> -->
<a-button type="primary" class="operChd custom-button" @click="dataChange({ key: 2 })">全量数据导出</a-button>
<a-button type="primary" class="operChd custom-button" @click="dataChange({ key: 2 })"
>全量数据导出</a-button
>
<!-- <a-select
:getPopupContainer="triggerNode=>triggerNode.parentNode"
class="searchSelect operChd"
@@ -51,8 +60,14 @@
<img class="suffix-icon" :src="require('@/assets/img/select-arrow-down.png')" alt="">
</template>
</a-select> -->
<a-button type="primary" class="operChd custom-button" @click="noData">无效样本处理</a-button>
<a-button type="primary" class="operChd custom-button" @click="configVisible = true">列表配置</a-button>
<!-- delete by zhangweiwei 20250331_ai 无效样本标记-不显示无效样本处理按钮 start -->
<!-- <a-button type="primary" class="operChd custom-button" @click="noData">无效样本处理</a-button> -->
<!-- delete by zhangweiwei 20250331_ai 无效样本标记-不显示无效样本处理按钮 end -->
<a-button type="primary" class="operChd custom-button" @click="configVisible = true"
>列表配置</a-button
>
<!-- 同步数据 -->
<a-tooltip :overlayStyle="{ 'max-width': 'none' }">
<template v-slot:title> 产品测试模块会同步该列表数据并作统计展示 </template>
@@ -68,7 +83,12 @@
</a-tooltip>
</div>
<!-- 配置 -->
<ColumnConfig v-model:visible="configVisible" :data="answer_columns" :sn="sn" @ok="handleConfig" />
<ColumnConfig
v-model:visible="configVisible"
:data="answer_columns"
:sn="sn"
@ok="handleConfig"
/>
<!-- 导出 -->
<DownloadData
v-model:visible="downloadVisible"
@@ -78,7 +98,10 @@
@ok="handleDownload"
/>
<!-- 下载中心 -->
<DownloadCenter v-model:visible="downloadCenterVisible" v-if="downloadCenterVisible"></DownloadCenter>
<DownloadCenter
v-model:visible="downloadCenterVisible"
v-if="downloadCenterVisible"
></DownloadCenter>
</template>
<script setup>
import { ref, toRefs, reactive, defineProps, watch, onMounted, computed, nextTick } from 'vue';
@@ -115,13 +138,15 @@ const searchData = ref({
test1: null,
test2: null
});
/** modify by zhangweiwei 20250331_ai 无效样本标记-批量操作中标记和取消标记按钮删除 start */
const opList = ref([
{ value: '3', label: '标记' },
{ value: '4', label: '取消标记' },
{ value: '0', label: '下载答卷' },
{ value: '1', label: '导出数据' },
{ value: '2', label: '删除' }
]);
/** modify by zhangweiwei 20250331_ai 无效样本标记-批量操作中标记和取消标记按钮删除 end */
const syncDataLoading = ref(false);
// 导出选中答卷

View File

@@ -76,10 +76,19 @@
</div>
</a-modal>
<!-- 导出框 -->
<a-modal class="custom-modal" v-model:visible="dataExportVis" title="数据导出" @ok="dataExportOk">
<a-modal
class="custom-modal"
v-model:visible="dataExportVis"
title="数据导出"
@ok="dataExportOk"
>
<a-form ref="formRef" :model="formState" :rules="rules" :labelCol="{ span: 5 }">
<a-form-item ref="type" label="文件格式" name="type">
<a-radio-group v-model:value="formState.type" @change="changeType" class="custom-radio-group">
<a-radio-group
v-model:value="formState.type"
@change="changeType"
class="custom-radio-group"
>
<a-radio class="custom-radio" value="1">Excel(.xlsx)</a-radio>
<a-radio class="custom-radio" value="2">CSV(.csv)</a-radio>
<a-radio class="custom-radio" value="3">SPSS(.sav)</a-radio>
@@ -96,7 +105,9 @@
<a-form-item ref="header_column_type" label="题目行格式" @change="changeHeaderColumnType">
<a-radio-group v-model:value="formState.header_column_type" class="custom-radio-group">
<a-radio class="custom-radio" value="0" :disabled="formState.type === '3'">文本</a-radio>
<a-radio class="custom-radio" value="0" :disabled="formState.type === '3'"
>文本</a-radio
>
<a-radio class="custom-radio" value="1">序号</a-radio>
</a-radio-group>
<!--<a-select class="custom-select" v-model:value="formState.header_column_type" placeholder="请选择">-->
@@ -126,6 +137,14 @@
<a-switch v-model:checked="formState.contains_base" />
</div>
</a-form-item>
<!-- modify by zhangweiwei 20250331_ai 无效样本标记-数据导出弹窗增加导出有效样本录入项 start -->
<a-form-item label="导出有效样本" name="is_mark">
<div class="switchBox">
<span></span>
<a-switch v-model:checked="formState.is_mark" />
</div>
</a-form-item>
<!-- modify by zhangweiwei 20250331_ai 无效样本标记-数据导出弹窗增加导出有效样本录入项 end -->
<a-form-item v-if="formState.contains_base">
<a-select
class="custom-select"
@@ -139,7 +158,9 @@
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll()">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll()">清空</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll()"
>清空</a-button
>
</div>
<a-divider style="margin: 4px 0" />
<v-nodes :vnodes="VNodes" />
@@ -154,7 +175,12 @@
</a-form-item> -->
<a-form-item ref="name" label="数据导出范围" v-if="operType !== 1">
<a-radio-group v-model:value="formState.isFilter" class="custom-radio-group">
<a-radio class="custom-radio" v-for="radio in isFilterData" :key="radio.value" :value="radio.value">
<a-radio
class="custom-radio"
v-for="radio in isFilterData"
:key="radio.value"
:value="radio.value"
>
{{ radio.label }}
</a-radio>
</a-radio-group>
@@ -202,7 +228,10 @@
<a-menu-item @click="putSurveysAnswersTop(item.id)" v-if="item.id">
<div>置顶方案</div>
</a-menu-item>
<a-menu-item @click="postSurveysAnswersClone(item.id)" v-if="planList.length < 10 && item.id">
<a-menu-item
@click="postSurveysAnswersClone(item.id)"
v-if="planList.length < 10 && item.id"
>
<div>复制方案</div>
</a-menu-item>
<a-menu-item>
@@ -472,6 +501,10 @@ const getSurveysAnswersDown = async () => {
let subData = formState;
subData.contains_base = subData.contains_base === true ? 1 : 0;
subData.include_open = subData.include_open === true ? 1 : 0;
/** modify by zhangweiwei 20250331_ai 无效样本标记-数据导出弹窗,增加【导出有效样本】录入项 start */
subData.is_mark = subData.is_mark === true ? '0' : '0,1,2'; //导出有效样本就是只传”0”;导出全部就是”0,1,2”
/** modify by zhangweiwei 20250331_ai 无效样本标记-数据导出弹窗,增加【导出有效样本】录入项 end */
//subData.is_import = subData.is_import===true?1:0
subData.contains_columns.push('id');
subData.sn = sn;
@@ -580,6 +613,7 @@ const formState = reactive({
header_column_type: '0', //答案行格式
column_type: '0', //答案行格式
contains_base: true, //作答信息开关
is_mark: true, // 有效样本开关
contains_columns: [], //作答信息多选
//is_import:true,//导入数据开关
isFilter: '1',
@@ -775,7 +809,9 @@ const onSearch = (e) => {
const planList = ref([]);
// 当前页数据
const nowPlanList = computed(() => {
return planList.value.filter((el, i) => i >= (nowPage.value - 1) * 5 && i + 1 <= nowPage.value * 5);
return planList.value.filter(
(el, i) => i >= (nowPage.value - 1) * 5 && i + 1 <= nowPage.value * 5
);
});
// 方案列表
const getSurveysAnswerFilter = async () => {

View File

@@ -87,8 +87,12 @@
</div>
</div>
<div class="addBtn">
<div class="addBtnLi" @click="addList"><PlusSquareOutlined class="addIcon" />添加筛选条件</div>
<div class="addBtnLi" @click="addGroup"><PlusSquareOutlined class="addIcon" />添加条件集合</div>
<div class="addBtnLi" @click="addList">
<PlusSquareOutlined class="addIcon" />添加筛选条件
</div>
<div class="addBtnLi" @click="addGroup">
<PlusSquareOutlined class="addIcon" />添加条件集合
</div>
</div>
</div>
</div>
@@ -109,9 +113,15 @@
@input="timeLang"
placeholder="请输入"
/>
<template v-if="searchData.useTimeVal == 'between' || searchData.useTimeVal == 'notBetween'">
<template
v-if="searchData.useTimeVal == 'between' || searchData.useTimeVal == 'notBetween'"
>
<p class="betweenSymbol">-</p>
<a-input style="width: 80px" v-model:value="searchData.useTimeInputValL" placeholder="请输入" />
<a-input
style="width: 80px"
v-model:value="searchData.useTimeInputValL"
placeholder="请输入"
/>
</template>
<a-select
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
@@ -182,8 +192,15 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('publish')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('publish')">清空</a-button>
<a-button size="small" type="primary" @click="selectAll('publish')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('publish')"
>清空</a-button
>
</div>
<a-divider style="margin: 4px 0" />
<v-nodes :vnodes="VNodes" />
@@ -206,8 +223,13 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('answerStatus')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('answerStatus')"
<a-button size="small" type="primary" @click="selectAll('answerStatus')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('answerStatus')"
>清空</a-button
>
</div>
@@ -233,8 +255,15 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('version')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('version')">清空</a-button>
<a-button size="small" type="primary" @click="selectAll('version')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('version')"
>清空</a-button
>
</div>
<a-divider style="margin: 4px 0" />
<v-nodes :vnodes="VNodes" />
@@ -271,8 +300,15 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('logics')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('logics')">清空</a-button>
<a-button size="small" type="primary" @click="selectAll('logics')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('logics')"
>清空</a-button
>
</div>
<a-divider style="margin: 4px 0" />
<v-nodes :vnodes="VNodes" />
@@ -297,8 +333,15 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('isMark')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('isMark')">清空</a-button>
<a-button size="small" type="primary" @click="selectAll('isMark')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('isMark')"
>清空</a-button
>
</div>
<a-divider style="margin: 4px 0" />
<v-nodes :vnodes="VNodes" />
@@ -323,7 +366,12 @@
<div class="codeInput" @click="isFocus = true" v-else>
<div class="codeContent">
<span v-if="inputValue.length == 0">{{ placeholder }}</span>
<div v-else v-for="(item, index) in inputValue" :key="index" style="display: inline-block">
<div
v-else
v-for="(item, index) in inputValue"
:key="index"
style="display: inline-block"
>
<span class="codeSpan">{{ item }}</span>
</div>
</div>
@@ -351,8 +399,13 @@
>
<template #dropdownRender="{ menuNode: VNodes }">
<div style="padding: 4px 12px; cursor: pointer">
<a-button size="small" type="primary" @click="selectAll('answer_type')">全选</a-button>
<a-button style="margin-left: 6px" size="small" @click="cancelSelectAll('answer_type')"
<a-button size="small" type="primary" @click="selectAll('answer_type')"
>全选</a-button
>
<a-button
style="margin-left: 6px"
size="small"
@click="cancelSelectAll('answer_type')"
>清空</a-button
>
</div>
@@ -412,7 +465,18 @@
<newModal ref="newModalRef" @newSave="newSave" />
</template>
<script setup>
import { ref, toRefs, toRaw, reactive, defineProps, onBeforeMount, onMounted, watch, nextTick, computed } from 'vue';
import {
ref,
toRefs,
toRaw,
reactive,
defineProps,
onBeforeMount,
onMounted,
watch,
nextTick,
computed
} from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex';
import moment from 'moment';
@@ -498,6 +562,9 @@ const logics = computed(() => {
return nVre;
});
const mark = [
/** add by zhangweiwei 20250331_ai 无效样本标记-筛选标记状态新增【AI标记】选项 start */
{ label: 'AI标记', value: '2' },
/** add by zhangweiwei 20250331_ai 无效样本标记-筛选标记状态新增【AI标记】选项 start */
{ label: '已标记', value: '1' },
{ label: '未标记', value: '0' }
];
@@ -711,7 +778,10 @@ const showData = {
// 配额逻辑
logics: [],
// 标记状态
isMark: ['1', '0'],
/** modify by zhangweiwei 20250331_ai 无效样本标记-筛选标记状态新增【AI标记】选项 start */
isMark: ['2', '1', '0'],
/** modify by zhangweiwei 20250331_ai 无效样本标记-筛选标记状态新增【AI标记】选项 start */
// 数据类型
answer_type: ['0', '1']
};
@@ -1119,7 +1189,8 @@ const answerAss = (el) => {
//大于等于啥的
if (el.operVal) data.operator = el.operVal;
// 最后那个答案
if (el.asVal || el.asVal == 0) data.answer = typeof el.asVal == 'object' ? el.asVal.join() : el.asVal;
if (el.asVal || el.asVal == 0)
data.answer = typeof el.asVal == 'object' ? el.asVal.join() : el.asVal;
// 最后那个答案如果是时间
if (el?.answerType == 'datetime') {
data.answer = {};

View File

@@ -4,7 +4,7 @@ const defaultOptions = {
left_fixed_key: 'id', // 左侧固定列
right_fixed_key: 'operation', //右侧固定列
show_operator: true, // 是否显示操作列
show_check: true,
show_check: false, // 去除标记和标记原因列
fixed: 'left'
};
@@ -21,39 +21,9 @@ export default function useGeneratorTableColumns(columns, options = {}) {
options = { ...defaultOptions, ...options };
const tableHeaders = ref([]);
function flatData(columns) {
let columnsSing = {};
let columnsType = {};
let columnsMemo = {};
if (options.show_check) {
columnsSing = {
title: '',
key: 'is_mark',
dataIndex: 'is_mark',
align: 'left',
width: 50,
slots: { customRender: 'is_mark' },
fixed: 'left'
};
// columnsType = {
// title: '数据类型',
// key: 'type',
// dataIndex: 'type',
// align: 'center',
// width: 100,
// slots: { customRender: 'type' },
// fixed: 'left'
// }
columnsMemo = {
title: '标记原因',
key: 'mark_des',
dataIndex: 'mark_des',
align: 'left',
width: 150,
slots: { customRender: 'mark_des' },
fixed: 'left'
};
}
let result = columns.reduce((previousValue, currentValue) => {
console.log('useGeneratorTableColumns currentValue---->', currentValue);
const arr = Object.entries(currentValue).map(([key, value]) => ({
...value,
key,
@@ -63,17 +33,48 @@ export default function useGeneratorTableColumns(columns, options = {}) {
// align: 'center',
slots: { customRender: key }
}));
console.log('useGeneratorTableColumns arr---->', arr);
return previousValue.concat(arr);
}, []);
console.log('useGeneratorTableColumns result---->', result);
// 手动修改作答ID数组位置
const snData = result.find((el) => el.key == 'id');
result = result.filter((el) => el.key != 'id');
// 手动修改数据类型数组位置
const typeData = result.find((el) => el.key == 'answer_type');
result = result.filter((el) => el.key != 'answer_type');
result.unshift(columnsMemo);
// result.unshift(columnsType)
/** add by zhangweiwei 20250331_ai 无效样本标记-列表新增AI/人工样本标记 start */
// 添加人工样本标记
const manualSampleMark = {
title: '人工样本标记',
titleType:'withIcon', // 标题类型,带图标
titleIconSrc: require('@/assets/img/mxd/rate2.png'), // 图标资源
titleIconTip: 'AI智筛精准标记', // 图标hover提示
key: 'manual_mark',
dataIndex: 'manual_mark',
align: 'left',
width: 150,
slots: { customRender: 'manual_mark' },
fixed: 'left'
};
result.unshift(manualSampleMark);
// 添加AI样本标记
const aiSampleMark = {
title: 'AI样本标记',
titleType:'withIcon', // 标题类型,带图标
titleIconSrc: require('@/assets/img/mxd/rate1.png'), // 图标资源
titleIconTip: '批量处理,方便快捷', // 图标hover提示
key: 'ai_mark',
dataIndex: 'ai_mark',
align: 'left',
width: 150,
slots: { customRender: 'ai_mark' },
fixed: 'left'
};
result.unshift(aiSampleMark);
/** add by zhangweiwei 20250331_ai 无效样本标记-列表新增AI/人工样本标记 end */
if (typeData) {
typeData.fixed = 'left';
typeData.width = 100;
@@ -84,7 +85,6 @@ export default function useGeneratorTableColumns(columns, options = {}) {
snData.fixed = 'left';
result.unshift(snData);
}
result.unshift(columnsSing);
if (options.show_operator) {
result.push({
title: '操作',

View File

@@ -1,11 +1,19 @@
import { ref, watch } from 'vue';
import CustomTableHeaderCell from '@/views/DataAnalyse/components/CustomTableHeaderCell';
export default function renderCustomHeader(columns, sn, search_params) {
export default function renderCustomHeader(columns, sn, search_params, iconClickFunc) {
const customHeaders = ref([]);
function renderCell(data, sn, search_params) {
return <CustomTableHeaderCell data={data} sn={sn} search_params={search_params} />;
function renderCell(data, sn, search_params, iconClickFunc) {
return (
<CustomTableHeaderCell
data={data}
sn={sn}
search_params={search_params}
onIconClick={iconClickFunc}
/>
);
}
watch(
columns,
(data) => {
@@ -13,7 +21,7 @@ export default function renderCustomHeader(columns, sn, search_params) {
customHeaders.value = data.map((item) => {
return {
...item,
title: renderCell(item, sn, search_params.value)
title: renderCell(item, sn, search_params.value, iconClickFunc)
};
});
}
@@ -29,7 +37,7 @@ export default function renderCustomHeader(columns, sn, search_params) {
customHeaders.value = columns.value.map((item) => {
return {
...item,
title: renderCell(item, sn, data)
title: renderCell(item, sn, data, iconClickFunc)
};
});
}

View File

@@ -80,6 +80,7 @@
@change="onPageChange"
@select="onSelectChange"
@sign="Sign"
@headerClick="onHeaderClick"
>
<template #operation="{ record }">
<a @click="openDetailModal(record)" style="margin-right: 5px">查看</a>
@@ -112,7 +113,10 @@
</DataTable>
</div>
</div>
<sign-invalid-modal v-model:visible="invalidVisible" @ok="handleSignInvalid"></sign-invalid-modal>
<sign-invalid-modal
v-model:visible="invalidVisible"
@ok="handleSignInvalid"
></sign-invalid-modal>
<download-data
v-model:visible="downloadVisible"
:params="queryState"
@@ -126,7 +130,13 @@
:sn="sn"
@ok="handleConfig"
></column-config> -->
<qs-detail v-model:visible="detailVisible" :data="tableData" :detailId="detailId" :sn="sn" :type="0"></qs-detail>
<qs-detail
v-model:visible="detailVisible"
:data="tableData"
:detailId="detailId"
:sn="sn"
:type="0"
></qs-detail>
</div>
</a-spin>
<newModal
@@ -142,8 +152,17 @@
@resShowFlag="resShowFlag"
/>
<!-- 下载中心 -->
<DownloadCenter v-model:visible="downloadCenterVisible" v-if="downloadCenterVisible"></DownloadCenter>
<a-modal v-model:visible="visible" title="标记原因" ok-text="确认" cancel-text="取消" @ok="hideModal">
<DownloadCenter
v-model:visible="downloadCenterVisible"
v-if="downloadCenterVisible"
></DownloadCenter>
<a-modal
v-model:visible="visible"
title="标记原因"
ok-text="确认"
cancel-text="取消"
@ok="hideModal"
>
<a-input placeholder="请输入标记原因" :maxlength="30" v-model:value="singDesc">
<template #prefix>
<user-outlined type="user" />
@@ -173,7 +192,13 @@ import ColumnConfig from './components/ColumnConfig';
import countIcon from '@/assets/img/data_count_icon.png';
import increaseIcon from '@/assets/img/newly_increase_icon.png';
import { answers_export, getParticularList, delParticularItem } from '@/api/data-analyse';
import { getNullDealConfig, answer_mark_batch, nullDeal, answer_mark, answer_mark_del } from '@/api/qc';
import {
getNullDealConfig,
answer_mark_batch,
nullDeal,
answer_mark,
answer_mark_del
} from '@/api/qc';
import { convertQueryToString } from '@utils/httpFormat';
import DataTable from '@views/DataAnalyse/components/DataTable';
import newDataTable from '@views/DataAnalyse/components/newDataTable';
@@ -262,7 +287,9 @@ const selectedRowKeys = ref([]);
const selectedSns = computed(() => {
return selectedRowKeys.value?.length
? tableData.value.filter((item, index) => selectedRowKeys.value.includes(index)).map((i) => i.sn) || []
? tableData.value
.filter((item, index) => selectedRowKeys.value.includes(index))
.map((i) => i.sn) || []
: [];
});
@@ -578,6 +605,21 @@ function onPageChange(data) {
per_page.value = data.pageSize;
getData({ page: page.value, per_page: per_page.value });
}
/**
* 监听表头点击事件
* @param column
*/
function onHeaderClick(column) {
console.log('onHeaderClick', column);
if (column.key === 'ai_mark') {
console.log('AI 标记');
} else if (column.key === 'manual_mark') {
console.log('人工标记');
noData();
}
}
const hideModal = (call) => {
let memo = (singDesc.value ??= '');
saveSing(memo);

View File

@@ -179,7 +179,7 @@
@update="editLabelUpdata"
/>
</a-modal>
<!-- add by zhangweiwei 20250331_ai 智能创建助手 start -->
<!-- add by zhangweiwei 20250331_ai 智能创建助手 start -->
<!-- <el-drawer
v-model="aiDrawerVisible"
:with-header="false"
@@ -194,21 +194,22 @@
>
</iframe>
</el-drawer> -->
<a-modal
v-model:visible="aiDialogVisible"
:closable="false"
:footer="null"
wrapClassName="ai-assistant-dialog-wrap"
dialogClass="ai-assistant-dialog"
>
<iframe
:src="aiAssistantUrl"
style="width: 100%; height: 100%; min-height: 500px"
frameborder="0"
<div ref="aiAssistantWrapper" class="ai-assistant-wrapper">
<a-modal
v-model:visible="aiAssistantVisible"
:getContainer="() => $refs.aiAssistantWrapper"
:closable="false"
:footer="null"
>
</iframe>
</a-modal>
<!-- add by zhangweiwei 20250331_ai 智能创建助手 end -->
<iframe
:src="aiAssistantUrl"
style="width: 100%; height: 100%; min-height: 500px"
frameborder="0"
>
</iframe>
</a-modal>
</div>
<!-- add by zhangweiwei 20250331_ai 智能创建助手 end -->
<!-- 产品测试(口味和包装) -->
<CreateSurveyProduct
ref="createSurveyProductRef"
@@ -279,7 +280,7 @@ const normalRef = ref([]);
const count = ref(5);
const tempVisible = ref(false);
/** add by zhangweiwei 20250331_ai 智能创建助手 start */
const aiDialogVisible = ref(false); // AI iFrame 是否显示
const aiAssistantVisible = ref(false); // AI iFrame 是否显示
// const aiDrawerOpened = ref(false); // AI iFrame 是否打开
/** add by zhangweiwei 20250331_ai 智能创建助手 end */
const visible = ref(false);
@@ -469,7 +470,7 @@ function createCustom(itemId) {
if (itemId === 1) {
console.log('智能创建');
// 显示AI iFrame
aiDialogVisible.value = true;
aiAssistantVisible.value = true;
} else if (itemId === 2) {
console.log('空白创建');
onCreate();
@@ -941,13 +942,20 @@ function professionalPrev() {
// }
// }
.ant-modal-root .ant-modal-mask .ant-modal-wrap .ant-modal {
top: 24px !important;
width: 780px !important;
.ant-modal-body {
height: calc(100vh - 24px) !important;
padding: 0 !important;
}
$ai-assistant-modal-top: 24px;
.ai-assistant-wrapper {
:deep(.ant-modal) {
top: $ai-assistant-modal-top !important;
width: auto !important;
max-width: 780px !important;
.ant-modal-body {
height: calc(100vh - $ai-assistant-modal-top * 2);
padding: 0;
}
}
}
/** add by zhangweiwei 20250331_ai 智能创建助手 end */
</style>

View File

@@ -1,11 +1,11 @@
<!-- add by zhangweiwei 20250331_ai AI 质检 新增 质检结果组件 start -->
<template>
<div class="ai-inspection-result" >
<div class="ai-inspection-result">
<i class="iconfont icon-yulan"></i>
<div class="result-content">
<div class="result-item" v-for="(item, index) in result" :key="index">
<div class="result-wrap">
<div class="result-item" v-for="(advice, index) in result" :key="index">
<span class="result-item__index">{{ index + 1 }}</span>
<span class="result-item__text">{{ item.text }}</span>
<span class="result-item__text">{{ advice }}</span>
</div>
</div>
</div>
@@ -22,34 +22,36 @@ const props = defineProps({
.ai-inspection-result {
display: flex;
flex-direction: row;
align-items: first baseline;
padding-left: calc(16px + 20px);
color: red;
font-size: 14px;
}
.esult-content {
.result-wrap {
margin-left: 6px;
}
.result-item {
.result-item {
display: flex;
flex-direction: row;
margin-top: 5px;
align-items: first center;
&__index {
display: flex;
flex-direction: row;
margin-top: 5px;
&__index {
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
width: 18px;
min-width: 18px;
height: 18px;
border: 1px solid red;
border-radius: 50%;
font-size: 12px;
}
&__text {
margin-left: 6px;
}
justify-content: center;
align-items: center;
white-space: nowrap;
width: 15px;
min-width: 15px;
height: 15px;
border: 1px solid red;
border-radius: 50%;
font-size: 12px;
}
&__text {
margin-left: 6px;
}
}
</style>