feat: 概念测试引用文件

This commit is contained in:
钱冠学
2024-11-15 11:08:46 +08:00
parent 9905fc3081
commit 67ba5abe19
4 changed files with 83 additions and 520 deletions

View File

@@ -1,18 +0,0 @@
import request from '@/utils/request';
/* 新增方案配置 */
export function postTemplates(sn, data) {
return request({
method: 'POST',
url: `/console/templates/${sn}`,
data
});
}
/* 获取品类品牌 */
export function getSurveyBrands() {
return request({
method: 'GET',
url: `/console/survey_brands`
});
}

View File

@@ -3,54 +3,52 @@
<div class="survey-box"> <div class="survey-box">
<div class="title">项目配置</div> <div class="title">项目配置</div>
<a-form layout="Inline" ref="formRefSet" :model="ruleFormSet" :rules="rules" :label-col="{ span: 4 }"> <a-form ref="formRefSet" :model="ruleFormSet" :rules="rules">
<a-row :gutter="6"> <div class="flex">
<a-col :span="16"> <a-form-item label="品类品牌" name="surveyCategoryStr" class="flex-auto" style="width: calc(50% + 40px);">
<a-form-item label="品类品牌" name="surveyCategoryStr" :label-col="{ span: 6 }"> <a-select
<a-select v-model:value="ruleFormSet.surveyCategoryStr"
v-model:value="ruleFormSet.surveyCategoryStr" class="custom-select"
class="custom-select" placeholder="请选择测试品类"
placeholder="请选择测试品类" style="margin-right: 6px"
style="margin-right: 6px" :getPopupContainer="
:getPopupContainer=" (triggerNode) => {
return triggerNode.parentNode.parentNode || document.body;
}
"
>
<a-select-option
v-for="(item, index) in oneOption"
:key="index"
:value="item.title"
@click="selectHandleOne(item)"
>{{ item.title }}
</a-select-option
>
</a-select>
</a-form-item>
<a-form-item name="surveyBrandId" class="flex-auto ml-12" style="width: calc(50% - 40px);">
<a-select
v-model:value="ruleFormSet.surveyBrandId"
class="custom-select"
placeholder="请选择测试品牌"
:getPopupContainer="
(triggerNode) => { (triggerNode) => {
return triggerNode.parentNode.parentNode || document.body; return triggerNode.parentNode.parentNode || document.body;
} }
" "
>
<a-select-option
v-for="(item, index) in twoOption"
:key="index"
:value="String(item.id)"
@click="selectHandleTwo(item)"
>{{ item.title }}
</a-select-option
> >
<a-select-option </a-select>
v-for="(item, index) in oneOption" </a-form-item>
:key="index" </div>
:value="item.title"
@click="selectHandleOne(item)"
>{{ item.title }}</a-select-option
>
</a-select>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item name="surveyBrandId">
<a-select
v-model:value="ruleFormSet.surveyBrandId"
class="custom-select"
placeholder="请选择测试品牌"
:getPopupContainer="
(triggerNode) => {
return triggerNode.parentNode.parentNode || document.body;
}
"
>
<a-select-option
v-for="(item, index) in twoOption"
:key="index"
:value="String(item.id)"
@click="selectHandleTwo(item)"
>{{ item.title }}</a-select-option
>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="测试版本" name="testType"> <a-form-item label="测试版本" name="testType">
<a-radio-group v-model:value="ruleFormSet.testType"> <a-radio-group v-model:value="ruleFormSet.testType">
@@ -68,7 +66,12 @@
<div class="survey-box mrt-18"> <div class="survey-box mrt-18">
<div class="title">问卷配置</div> <div class="title">问卷配置</div>
<StandardConceptConfig :projectConfig="ruleFormSet" ref="standardConceptConfigRef"></StandardConceptConfig> <SurveyConfig
:projectConfig="ruleFormSet"
ref="standardConceptConfigRef"
suffix-title="概念测试"
scene-code="22"
/>
</div> </div>
<div class="line"></div> <div class="line"></div>
@@ -166,7 +169,10 @@
</div> </div>
<div v-if="ruleFormSet.testType !== 3 && isNoAdd"> <div v-if="ruleFormSet.testType !== 3 && isNoAdd">
<a-button class="custom-button" type="primary" @click="onAddConcept"> <PlusOutlined /> 新增概念 </a-button> <a-button class="custom-button current-button" type="primary" @click="onAddConcept">
<PlusOutlined />
新增概念
</a-button>
</div> </div>
</div> </div>
@@ -286,15 +292,15 @@
<script setup> <script setup>
import { defineEmits, ref, reactive, onBeforeMount, watch, computed } from 'vue'; import { defineEmits, ref, reactive, onBeforeMount, watch, computed } from 'vue';
import StandardConceptConfig from './StandardConceptConfig.vue'; import SurveyConfig from '@/views/ProjectManage/create/presets/components/SurveyConfig.vue';
import conceptData from '../json/concept.json'; import conceptJson from '@/views/ProjectManage/create/presets/json/concept.json';
import { PlusOutlined } from '@ant-design/icons-vue'; import { PlusOutlined } from '@ant-design/icons-vue';
import ConceptSetItem from './ConceptSetItem.vue'; import ConceptSetItem from './ConceptSetItem.vue';
import { getSurveyBrands } from '../api'; import { getSurveyBrands } from '@/views/ProjectManage/create/presets/api';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@@ -328,7 +334,7 @@ const testVersionList = computed(() => [
]); ]);
/** 标准版 */ /** 标准版 */
const testOneData = ref(conceptData.testOneCheckInfo); const testOneData = ref(conceptJson.check_list_standard);
const testOneSelectedValues = ref(testOneData.value.filter((item) => item.disabled).map((item) => item.value)); const testOneSelectedValues = ref(testOneData.value.filter((item) => item.disabled).map((item) => item.value));
@@ -408,7 +414,7 @@ function deleteConcept(id) {
} }
/** 快测版 */ /** 快测版 */
const testTwoData = ref(conceptData.testTwoCheckInfo); const testTwoData = ref(conceptJson.check_list_quick_test);
const testTwoSelectedValues = ref(testTwoData.value.filter((item) => item.disabled).map((item) => item.value)); const testTwoSelectedValues = ref(testTwoData.value.filter((item) => item.disabled).map((item) => item.value));
@@ -419,7 +425,7 @@ const testTwoConcepts = ref([
]); ]);
/** 配对版 */ /** 配对版 */
const testThreeData = ref(conceptData.testThreeCheckInfo); const testThreeData = ref(conceptJson.check_list_pair);
const testThreeSelectedValues = ref(testThreeData.value.filter((item) => item.disabled).map((item) => item.value)); const testThreeSelectedValues = ref(testThreeData.value.filter((item) => item.disabled).map((item) => item.value));
@@ -695,6 +701,8 @@ defineExpose({
} }
.my-checkbox { .my-checkbox {
margin-bottom: 16px;
:deep(.ant-checkbox-disabled .ant-checkbox-inner) { :deep(.ant-checkbox-disabled .ant-checkbox-inner) {
background-color: #d5ebc3 !important; background-color: #d5ebc3 !important;
border-color: #d5ebc3 !important; border-color: #d5ebc3 !important;
@@ -751,4 +759,27 @@ defineExpose({
.mrt-18 { .mrt-18 {
margin-top: 18px; margin-top: 18px;
} }
.current-button {
padding-left: 10px !important;
padding-right: 10px !important;
}
:deep(.ant-form-item-label) {
label {
width: 80px !important;
text-align: right;
justify-content: flex-end;
}
}
.flex {
display: flex;
justify-content: space-between;
align-items: center;
}
.ml-12 {
margin-left: 12px;
}
</style> </style>

View File

@@ -43,7 +43,7 @@ import { useRouter } from 'vue-router';
import { Modal, message } from 'ant-design-vue'; import { Modal, message } from 'ant-design-vue';
import { postTemplates } from '../api'; import { postTemplates } from '@/views/ProjectManage/create/presets/api';
const store = useStore(); const store = useStore();
const props = defineProps(['testType', 'sn', 'snList']); const props = defineProps(['testType', 'sn', 'snList']);

View File

@@ -1,450 +0,0 @@
<template>
<a-form ref="formRef" :model="ruleForm" :rules="rules" :label-col="{ span: 4 }">
<a-form-item label="问卷名称" name="project_name">
<a-input
class="project-input"
v-model:value="ruleForm.project_name"
placeholder="请输入问卷名称"
:maxlength="30"
showCount
>
<template #suffix>
<span class="suffix">
{{ `${ruleForm.project_name.length} / 30` }}
</span>
</template>
</a-input>
<a-button class="auto-button" @click="onAutoCreate">自动生成</a-button>
</a-form-item>
<a-form-item label="问卷场景" name="scene_code_info" v-if="isShow">
<a-select
disabled
v-model:value="ruleForm.scene_code_info"
style="width: 100%; border-radius: 4px"
placeholder="请选择场景"
@change="handleSceneChange"
class="custom-select show-select"
:dropdownStyle="{ zIndex: 10000 }"
>
<a-select-option :value="`${item.code}`" :label="item.title" v-for="item in scenesList" :key="`${item.code}`">
{{ item.parentTitle }}-{{ item.title }}
</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="问卷标签" name="tags" v-if="isShow">
<a-select
v-model:value="ruleForm.tags"
style="width: 100%; border-radius: 4px"
mode="multiple"
placeholder="搜索或新建标签"
@change="handleChange"
:filterOption="filterOption"
class="custom-select show-select"
:dropdownStyle="{ zIndex: 10000 }"
>
<a-select-option :value="item.id" :label="item.title" v-for="item in tagsList" :key="item.id">
<div style="display: flex; justify-content: space-between">
<span :style="countColor(item.color)" :title="item.title">{{ item.title }}</span>
<span class="icon" v-show="isAdmin">
<EditOutlined class="edit" @click.stop="edit(item)" />
<DeleteOutlined class="del" @click.stop="del(item.id)" />
</span>
</div>
</a-select-option>
<template #dropdownRender="{ menuNode: menu }" v-if="isAdmin">
<v-nodes :vnodes="menu" />
<a-divider style="margin: 4px 0" />
<div
style="padding: 2px 8px; cursor: pointer; color: #70b936"
@mousedown="(e) => e.preventDefault()"
@click="onAddTag"
>
<plus-outlined />
新建标签
</div>
</template>
</a-select>
</a-form-item>
<a-form-item label="问卷简介" name="remarks" v-if="isShow">
<a-input
style="border-radius: 4px"
autoSize
:maxlength="150"
placeholder="请输入"
allowClear
v-model:value="ruleForm.remarks"
>
<template #suffix>
<span class="suffix">
{{ `${ruleForm.remarks.length} / 150` }}
</span>
</template>
</a-input>
</a-form-item>
<!-- <a-form-item class="button" style="text-align: right">
<a-button style="margin-right: 12px;border-radius: 4px;" type="default" @click="$emit('cancel')">取消</a-button>
<a-button type="primary" style="border-radius: 4px;" @click="onSubmit">确定</a-button>
</a-form-item> -->
</a-form>
<a-modal v-model:visible="visibleTags" title="新建标签" :destroyOnClose="true" :footer="null">
<addTag @cancel="visibleTags = false" @update="addTagUpdata"></addTag>
</a-modal>
</template>
<script>
import { defineComponent, reactive, ref, watch, onBeforeMount, createVNode } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { message, Modal } from 'ant-design-vue';
import { PlusOutlined, EditOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { getTagsList, deleteTags, getSceneListForSelect } from '@/views/ProjectManage/api';
import addTag from '@/views/ProjectManage/components/addTag.vue';
import useEmitter from '@/composables/useEmitter';
export default defineComponent({
name: 'StandardConceptConfig',
components: {
addTag,
PlusOutlined,
DeleteOutlined,
EditOutlined,
VNodes: (_, { attrs }) => {
return attrs.vnodes;
}
},
props: {
projectConfig: {
type: Object,
default: () => {}
}
},
setup(props, context) {
const store = useStore();
const emitter = useEmitter();
const items = ref([]);
const router = useRouter();
const formRef = ref();
const tagsList = ref([]);
const loading = ref(false);
const scenesList = ref([]);
const isAdmin = ref(false);
const isShow = ref(true);
const ruleForm = reactive({
project_name: '',
tags: [],
scene_code_info: '22',
remarks: ''
});
const rules = {
project_name: [
{ required: true, message: '请输入问卷名称', trigger: 'blur' },
{ min: 1, max: 30, message: '字数超过限制', trigger: 'blur' }
],
scene_code_info: [{ required: true, message: '请选择场景', trigger: 'blur' }]
};
const visibleTags = ref(false);
// 标签颜色
const countColor = (value) => {
let style = {};
switch (value) {
case 1:
style = {
color: '#4DB8FA',
border: '1px solid #4DB8FA',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 2:
style = {
color: '#0CC126',
// 'border-color' : '#0CC126',
border: '1px solid #0CC126',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 3:
style = {
color: '#FF8800',
// 'border-color' : '#FF8800',
border: '1px solid #FF8800',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 4:
style = {
color: '#FF374F',
// 'border-color' : '#FF374F',
border: '1px solid #FF374F',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 5:
style = {
color: '#1C6FFF',
// 'border-color' : '#1C6FFF',
border: '1px solid #1C6FFF',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 6:
style = {
color: '#11AEA7',
// 'border-color' : '#11AEA7',
border: '1px solid #11AEA7',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 7:
style = {
color: '#25D8C8',
// 'border-color' : '#25D8C8',
border: '1px solid #25D8C8',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
break;
case 8:
style = {
color: '#FECB0D',
// 'border-color' : '#FECB0D',
border: '1px solid #FECB0D',
padding: '0 5px',
'border-radius': '4px',
'line-height': '20px'
};
// break;
// case 9:
// style = {
// 'color' : '4DB8FA',
// 'border-color' : '4DB8FA',
// }
}
return style;
};
/** 获取项目标签列表 */
const getTagsListRequest = async (val) => {
try {
const { data } = await getTagsList();
tagsList.value = data;
} catch (error) {
context.message.error(error.data?.message || error.message || '服务器错误');
}
};
// 删除
const delRequest = async (id) => {
try {
const { data } = await deleteTags(id);
getTagsListRequest();
} catch (error) {
console.log(error);
}
};
const del = (id) => {
Modal.confirm({
content: '删除后,此标签会从当前团队的所有问卷中移除,且无法恢复!',
icon: () => createVNode(ExclamationCircleOutlined),
cancelText: '取消',
okText: '确认',
zIndex: 100001,
onOk: () => {
delRequest(id);
}
});
};
const edit = (item) => {
context.emit('labelEdit', item);
};
const addItem = () => {
emitter.emit('addGroup');
};
const onAddTag = () => {
visibleTags.value = true;
};
const addTagUpdata = () => {
visibleTags.value = false;
getTagsListRequest();
};
// const onSubmitStatus = () => {
// formRef.value
// .validate()
// .then(() => {
// })
// .catch((error) => {
// console.log("error", error);
// });
// };
const handleChange = (value) => {};
const handleSceneChange = (value) => {
const item = scenesList.value.find((item) => item.code === +value);
ruleForm.scene_code = item.parentCode;
};
const getScenes = async () => {
loading.value = true;
const data = await getSceneListForSelect();
if (data?.code) {
message.error(data?.message || '获取场景失败,请刷新!');
return;
}
loading.value = false;
scenesList.value = normalizeScenes(data?.data || []);
};
function normalizeScenes(list) {
const result = [];
const parent = [];
let index = 0;
list.forEach((item) => {
if (item.parentCode > 0) {
// if(!item.sn){
result.push(item);
// }
} else {
parent.push(item);
}
});
result.forEach((item) => {
item.parentTitle = parent.find((pItem) => pItem.code === item.parentCode).title.slice(0, 2);
});
result.sort((a, b) => a.sort - b.sort);
return result;
}
function filterOption(inputValue, option) {
const reg = new RegExp(inputValue);
const result = reg.test(option.label);
return result;
}
// 自动生成
const onAutoCreate = () => {
if (!props?.projectConfig?.surveyBrandId || !props?.projectConfig?.surveyCategoryStr) {
return message.error('请先填写完全项目信息');
}
const newTitle = `${getNowTime()}${props?.projectConfig?.surveyBrandStr}${
props?.projectConfig?.surveyCategoryStr
}概念测试`;
ruleForm.project_name = newTitle;
};
// 获取当前时间 年月
const getNowTime = () => {
const yy = new Date().getFullYear();
const MM = new Date().getMonth() + 1;
return yy + '年' + MM + '月';
};
onBeforeMount(() => {
getTagsListRequest();
getScenes();
var user = localStorage.getItem('plantUserInfo');
user = JSON.parse(user);
isAdmin.value = user?.super_admin_flag;
});
return {
formRef,
rules,
ruleForm,
// onSubmitStatus,
loading,
value: ref([]),
handleChange,
handleSceneChange,
options: [],
del,
edit,
items,
addItem,
onAddTag,
tagsList,
filterOption,
getTagsListRequest,
visibleTags,
addTagUpdata,
countColor,
isShow,
scenesList,
getScenes,
isAdmin,
onAutoCreate
};
}
});
</script>
<style lang="scss" scoped>
.icon {
font-size: 16px;
.del {
margin: 0 10px;
}
}
.tags {
margin: 0 5px 5px;
min-width: 75px;
width: 48px;
height: 19px;
border-radius: 4px;
// position: relative;
box-sizing: border-box;
.title {
// position: absolute;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
line-height: 17px;
display: flex;
justify-content: center;
}
.tagStyle {
border-radius: 4px;
max-width: 120px;
padding: 0 5px;
margin: 0 5px;
display: inline-block;
border-width: 1px;
border-style: solid;
color: #4db8fa;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.show-select {
.icon {
display: none !important;
}
}
::v-deep .ant-input-textarea-clear-icon {
margin: 4px 3px 0 0;
}
.show-select:deep(.ant-select-selector) {
border-radius: 4px;
}
.project-input {
width: 80%;
border-radius: 4px 0 0 4px;
// border-right: 0px;
}
.auto-button {
width: 20%;
border-radius: 0 4px 4px 0;
}
</style>