Merge remote-tracking branch 'origin/feature/feature-20250331-h5' into feature/feature-20250331-h5
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -31,8 +31,6 @@ declare module 'vue' {
|
|||||||
VanCol: typeof import('vant/es')['Col']
|
VanCol: typeof import('vant/es')['Col']
|
||||||
VanDivider: typeof import('vant/es')['Divider']
|
VanDivider: typeof import('vant/es')['Divider']
|
||||||
VanField: typeof import('vant/es')['Field']
|
VanField: typeof import('vant/es')['Field']
|
||||||
VanGrid: typeof import('vant/es')['Grid']
|
|
||||||
VanGridItem: typeof import('vant/es')['GridItem']
|
|
||||||
VanIcon: typeof import('vant/es')['Icon']
|
VanIcon: typeof import('vant/es')['Icon']
|
||||||
VanList: typeof import('vant/es')['List']
|
VanList: typeof import('vant/es')['List']
|
||||||
VanNavBar: typeof import('vant/es')['NavBar']
|
VanNavBar: typeof import('vant/es')['NavBar']
|
||||||
|
|||||||
@@ -65,6 +65,7 @@
|
|||||||
"unplugin-auto-import": "^0.18.6",
|
"unplugin-auto-import": "^0.18.6",
|
||||||
"unplugin-vue-components": "^0.27.5",
|
"unplugin-vue-components": "^0.27.5",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
|
"vitest": "^3.0.9",
|
||||||
"vue-tsc": "^2.0.21"
|
"vue-tsc": "^2.0.21"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export const useQuestionStore = defineStore('questionStore', () => {
|
|||||||
return ![102, 104, 105, 201].includes(questions.value[0]?.question_type);
|
return ![102, 104, 105, 201].includes(questions.value[0]?.question_type);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 作用未知
|
// 作用未知, 应该是 language
|
||||||
const l = ref({});
|
const l = ref({});
|
||||||
// 主题颜色
|
// 主题颜色
|
||||||
const themeColor = ref({});
|
const themeColor = ref({});
|
||||||
|
|||||||
110
src/types/question.d.ts
vendored
Normal file
110
src/types/question.d.ts
vendored
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
export declare interface OptionConfigType {
|
||||||
|
type?: number;
|
||||||
|
price?: number;
|
||||||
|
title?: string;
|
||||||
|
gradient?: string;
|
||||||
|
image_url?: string[];
|
||||||
|
child_area?: null;
|
||||||
|
option_type?: number;
|
||||||
|
instructions?: string[];
|
||||||
|
binding_goods_id?: string;
|
||||||
|
limit_right_content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare interface questionOptionType {
|
||||||
|
// 包含 HTML 标签的字符串,例如 "<p>选项1</p>"
|
||||||
|
option?: string;
|
||||||
|
is_other?: number;
|
||||||
|
is_fixed?: number;
|
||||||
|
is_remove_other?: number;
|
||||||
|
level?: number;
|
||||||
|
option_key?: string;
|
||||||
|
option_index?: string;
|
||||||
|
option_code?: string;
|
||||||
|
option_config?: OptionConfigType;
|
||||||
|
parent_option_index?: number;
|
||||||
|
children?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface IQuestionOption {
|
||||||
|
type: number;
|
||||||
|
cite_type: number;
|
||||||
|
relation_type: number;
|
||||||
|
relation_out_scope: number[];
|
||||||
|
relation_last_scope: number;
|
||||||
|
relation_first_scope: number;
|
||||||
|
relation_question_index: number;
|
||||||
|
options?: questionOptionType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 答案 config 类型
|
||||||
|
export declare interface IQuestionConfig {
|
||||||
|
text_type?: number;
|
||||||
|
include_mark;
|
||||||
|
disabled?: string[];
|
||||||
|
version?: string;
|
||||||
|
scene?: string | null;
|
||||||
|
shelf?: string | null;
|
||||||
|
ware?: string | null;
|
||||||
|
row_option_groups?: string | null;
|
||||||
|
cell_option_groups?: string | null;
|
||||||
|
is_repeat?: number;
|
||||||
|
allow_repeat_num?: number;
|
||||||
|
repeat_type?: number;
|
||||||
|
alert_text?: string;
|
||||||
|
is_required?: number;
|
||||||
|
is_change_row_cell?: number;
|
||||||
|
select_random?: number;
|
||||||
|
row_random?: number;
|
||||||
|
cell_random?: number;
|
||||||
|
is_three_dimensions?: number;
|
||||||
|
material_sn?: string;
|
||||||
|
scene_information?: string | null;
|
||||||
|
simple_scene_information?: string | null;
|
||||||
|
is_behavior?: number;
|
||||||
|
is_price_tag?: number;
|
||||||
|
is_brand?: number;
|
||||||
|
is_initialize?: number;
|
||||||
|
is_default_perspective?: number;
|
||||||
|
is_disable_lines_same?: number;
|
||||||
|
disable_lines_same?: number;
|
||||||
|
float_window?: number;
|
||||||
|
is_disable?: number;
|
||||||
|
float_window_content?: string;
|
||||||
|
popup_window?: number;
|
||||||
|
popup_window_content?: string;
|
||||||
|
is_show?: string[];
|
||||||
|
quick_type?: number;
|
||||||
|
is_limit_right_content?: number;
|
||||||
|
option_group_random_inside?: string | null;
|
||||||
|
option_group_random_outside?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 答案 question
|
||||||
|
export declare interface IQuestion<QuestionConfig> {
|
||||||
|
error: string;
|
||||||
|
answer?: unknown;
|
||||||
|
id?: string;
|
||||||
|
title?: string;
|
||||||
|
stem?: string;
|
||||||
|
other?: string;
|
||||||
|
// options 列表项,第一个是默认
|
||||||
|
list: questionOptionType[];
|
||||||
|
question_index?: number;
|
||||||
|
question_type?: number;
|
||||||
|
config?: QuestionConfig;
|
||||||
|
created_at?: string;
|
||||||
|
created_user_id?: number;
|
||||||
|
updated_user_id?: number | null;
|
||||||
|
survey_id?: number;
|
||||||
|
logic_config?: LogicConfig;
|
||||||
|
options: questionOptionType[];
|
||||||
|
associate?: any[];
|
||||||
|
logics_has?: string | null;
|
||||||
|
last_option_index?: number;
|
||||||
|
question_code?: string;
|
||||||
|
question_value?: string;
|
||||||
|
question_tag?: string;
|
||||||
|
planet_id?: string;
|
||||||
|
permissions?: any | null;
|
||||||
|
}
|
||||||
33
src/types/questions/completion.d.ts
vendored
Normal file
33
src/types/questions/completion.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
declare interface ICompletionConfig {
|
||||||
|
decimal_few: number;
|
||||||
|
float_window: number;
|
||||||
|
float_window_content: string;
|
||||||
|
include_mark: number;
|
||||||
|
is_behavior: number;
|
||||||
|
is_brand: number;
|
||||||
|
is_default_perspective: number;
|
||||||
|
is_initialize: number;
|
||||||
|
is_price_tag: number;
|
||||||
|
is_required: number;
|
||||||
|
is_show: string[];
|
||||||
|
is_three_dimensions: number;
|
||||||
|
left_prompt: string;
|
||||||
|
line_height: number;
|
||||||
|
line_type: number;
|
||||||
|
material_sn: string;
|
||||||
|
max: number | '';
|
||||||
|
min: number | '';
|
||||||
|
placeholder: string;
|
||||||
|
popup_window: number;
|
||||||
|
popup_window_content: string;
|
||||||
|
quick_type: number;
|
||||||
|
right_prompt: string;
|
||||||
|
scene: string | null;
|
||||||
|
scene_information: string | null;
|
||||||
|
shelf: string | null;
|
||||||
|
simple_scene_information: string | null;
|
||||||
|
text_type: number;
|
||||||
|
type_name: string;
|
||||||
|
version: string;
|
||||||
|
ware: string | null;
|
||||||
|
}
|
||||||
@@ -62,86 +62,6 @@ type Option = {
|
|||||||
cascade?: any[];
|
cascade?: any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置类型
|
|
||||||
* @property {string[]} disabled - 禁用的选项列表(通常为空数组)
|
|
||||||
* @property {string} version - 版本号(通常为空字符串)
|
|
||||||
* @property {string | null} scene - 场景(通常为 null)
|
|
||||||
* @property {string | null} shelf - 货架(通常为 null)
|
|
||||||
* @property {string | null} ware - 商品(通常为 null)
|
|
||||||
* @property {string | null} row_option_groups - 行选项组(通常为 null)
|
|
||||||
* @property {string | null} cell_option_groups - 列选项组(通常为 null)
|
|
||||||
* @property {number} is_repeat - 是否重复(0 表示否,1 表示是)
|
|
||||||
* @property {number} allow_repeat_num - 允许重复的数量
|
|
||||||
* @property {number} repeat_type - 重复类型(通常为 0)
|
|
||||||
* @property {string} alert_text - 提示文本
|
|
||||||
* @property {number} is_required - 是否必填(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_change_row_cell - 是否允许更改行列选项(0 表示否,1 表示是)
|
|
||||||
* @property {number} select_random - 是否随机选择行(0 表示否,1 表示是)
|
|
||||||
* @property {number} row_random - 是否随机选择行选项(0 表示否,1 表示是)
|
|
||||||
* @property {number} cell_random - 是否随机选择列选项(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_three_dimensions - 是否为三维矩阵(0 表示否,1 表示是)
|
|
||||||
* @property {string} material_sn - 材料编号(通常为空字符串)
|
|
||||||
* @property {string | null} scene_information - 场景信息(通常为 null)
|
|
||||||
* @property {string | null} simple_scene_information - 简单场景信息(通常为 null)
|
|
||||||
* @property {number} is_behavior - 是否为行为相关(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_price_tag - 是否为价格标签(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_brand - 是否为品牌相关(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_initialize - 是否初始化(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_default_perspective - 是否为默认视角(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_disable_lines_same - 是否禁用行列相同(0 表示否,1 表示是)
|
|
||||||
* @property {number} disable_lines_same - 禁用行列相同的值(通常为 1)
|
|
||||||
* @property {number} float_window - 是否显示悬浮窗(0 表示否,1 表示是)
|
|
||||||
* @property {number} is_disable - 是否禁用(0 表示否,1 表示是)
|
|
||||||
* @property {string} float_window_content - 悬浮窗内容(通常为空字符串)
|
|
||||||
* @property {number} popup_window - 是否显示弹窗(0 表示否,1 表示是)
|
|
||||||
* @property {string} popup_window_content - 弹窗内容(通常为空字符串)
|
|
||||||
* @property {string[]} is_show - 是否显示的选项列表(通常为空数组)
|
|
||||||
* @property {number} quick_type - 快速类型(通常为 0)
|
|
||||||
* @property {number} is_limit_right_content - 是否限制右侧内容(0 表示否,1 表示是)
|
|
||||||
* @property {string | null} option_group_random_inside - 行列选项组内随机(通常为 null)
|
|
||||||
* @property {string | null} option_group_random_outside - 行列选项组外随机(通常为 null)
|
|
||||||
*/
|
|
||||||
type Config = {
|
|
||||||
disabled?: string[];
|
|
||||||
version?: string;
|
|
||||||
scene?: string | null;
|
|
||||||
shelf?: string | null;
|
|
||||||
ware?: string | null;
|
|
||||||
row_option_groups?: string | null;
|
|
||||||
cell_option_groups?: string | null;
|
|
||||||
is_repeat?: number;
|
|
||||||
allow_repeat_num?: number;
|
|
||||||
repeat_type?: number;
|
|
||||||
alert_text?: string;
|
|
||||||
is_required?: number;
|
|
||||||
is_change_row_cell?: number;
|
|
||||||
select_random?: number;
|
|
||||||
row_random?: number;
|
|
||||||
cell_random?: number;
|
|
||||||
is_three_dimensions?: number;
|
|
||||||
material_sn?: string;
|
|
||||||
scene_information?: string | null;
|
|
||||||
simple_scene_information?: string | null;
|
|
||||||
is_behavior?: number;
|
|
||||||
is_price_tag?: number;
|
|
||||||
is_brand?: number;
|
|
||||||
is_initialize?: number;
|
|
||||||
is_default_perspective?: number;
|
|
||||||
is_disable_lines_same?: number;
|
|
||||||
disable_lines_same?: number;
|
|
||||||
float_window?: number;
|
|
||||||
is_disable?: number;
|
|
||||||
float_window_content?: string;
|
|
||||||
popup_window?: number;
|
|
||||||
popup_window_content?: string;
|
|
||||||
is_show?: string[];
|
|
||||||
quick_type?: number;
|
|
||||||
is_limit_right_content?: number;
|
|
||||||
option_group_random_inside?: string | null;
|
|
||||||
option_group_random_outside?: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 逻辑配置类型
|
* 逻辑配置类型
|
||||||
* @property {string} expect - 期望值(通常为空字符串)
|
* @property {string} expect - 期望值(通常为空字符串)
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
interface OptionConfigType {
|
|
||||||
type?: number;
|
|
||||||
price?: number;
|
|
||||||
title?: string;
|
|
||||||
gradient?: string;
|
|
||||||
image_url?: string[];
|
|
||||||
child_area?: null;
|
|
||||||
option_type?: number;
|
|
||||||
instructions?: string[];
|
|
||||||
binding_goods_id?: string;
|
|
||||||
limit_right_content?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface questionOptionType {
|
|
||||||
// 包含 HTML 标签的字符串,例如 "<p>选项1</p>"
|
|
||||||
option?: string;
|
|
||||||
is_other?: number;
|
|
||||||
is_fixed?: number;
|
|
||||||
is_remove_other?: number;
|
|
||||||
level?: number;
|
|
||||||
option_key?: string;
|
|
||||||
option_index?: string;
|
|
||||||
option_code?: string;
|
|
||||||
option_config?: OptionConfigType;
|
|
||||||
parent_option_index?: number;
|
|
||||||
children?: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
type questionsList = {
|
|
||||||
type: number;
|
|
||||||
cite_type: number;
|
|
||||||
relation_type: number;
|
|
||||||
relation_out_scope: number[];
|
|
||||||
relation_last_scope: number;
|
|
||||||
relation_first_scope: number;
|
|
||||||
relation_question_index: number;
|
|
||||||
options?: questionOptionType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
type question = {
|
|
||||||
error: string;
|
|
||||||
answer?: unknown;
|
|
||||||
id?: string;
|
|
||||||
title?: string;
|
|
||||||
stem?: string;
|
|
||||||
other?: string;
|
|
||||||
list: questionsList[];
|
|
||||||
question_index?: number;
|
|
||||||
question_type?: number;
|
|
||||||
config?: Config;
|
|
||||||
created_at?: string;
|
|
||||||
created_user_id?: number;
|
|
||||||
updated_user_id?: number | null;
|
|
||||||
survey_id?: number;
|
|
||||||
logic_config?: LogicConfig;
|
|
||||||
options: questionsList[];
|
|
||||||
associate?: any[];
|
|
||||||
logics_has?: string | null;
|
|
||||||
last_option_index?: number;
|
|
||||||
question_code?: string;
|
|
||||||
question_value?: string;
|
|
||||||
question_tag?: string;
|
|
||||||
planet_id?: string;
|
|
||||||
permissions?: any | null;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -24,10 +24,7 @@
|
|||||||
<!-- <img :src="icon" alt="icon" />-->
|
<!-- <img :src="icon" alt="icon" />-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div v-if="styleInfo.logo_status && styleInfo.logo_url" class="example-logo" :style="[
|
||||||
v-if="styleInfo.logo_status && styleInfo.logo_url"
|
|
||||||
class="example-logo"
|
|
||||||
:style="[
|
|
||||||
{
|
{
|
||||||
'justify-content':
|
'justify-content':
|
||||||
styleInfo.logo_site === 1
|
styleInfo.logo_site === 1
|
||||||
@@ -42,8 +39,7 @@
|
|||||||
!styleInfo.head_img_status && styleInfo.background_status
|
!styleInfo.head_img_status && styleInfo.background_status
|
||||||
? `background-color: ${styleInfo.background_color};background-image: url(${styleInfo.background_url})`
|
? `background-color: ${styleInfo.background_color};background-image: url(${styleInfo.background_url})`
|
||||||
: ''
|
: ''
|
||||||
]"
|
]">
|
||||||
>
|
|
||||||
<img class="logo" :src="styleInfo.logo_url" alt="logo" />
|
<img class="logo" :src="styleInfo.logo_url" alt="logo" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -51,14 +47,8 @@
|
|||||||
<!-- eslint-disable-next-line -->
|
<!-- eslint-disable-next-line -->
|
||||||
<div class="questions">
|
<div class="questions">
|
||||||
<!-- 提前终止和正常完成 -->
|
<!-- 提前终止和正常完成 -->
|
||||||
<q-last
|
<q-last v-if="page === pages.length + 1" :code="questionsData?.action?.code" :action="questionsData?.action"
|
||||||
v-if="page === pages.length + 1"
|
:survey="questionsData?.survey" :isAnswer="isAnswer" :isTemplate="isTemplate" />
|
||||||
:code="questionsData?.action?.code"
|
|
||||||
:action="questionsData?.action"
|
|
||||||
:survey="questionsData?.survey"
|
|
||||||
:isAnswer="isAnswer"
|
|
||||||
:isTemplate="isTemplate"
|
|
||||||
/>
|
|
||||||
<!-- 问卷名和描述 -->
|
<!-- 问卷名和描述 -->
|
||||||
<!-- <q-first v-else-if="page === 0" isMobile :title="questionsData?.survey?.title"
|
<!-- <q-first v-else-if="page === 0" isMobile :title="questionsData?.survey?.title"
|
||||||
:desc="questionsData?.survey?.introduction" :questions="questionsData?.questions" :isAnswer="isAnswer"
|
:desc="questionsData?.survey?.introduction" :questions="questionsData?.questions" :isAnswer="isAnswer"
|
||||||
@@ -70,13 +60,8 @@
|
|||||||
:questionType="question.question_type" :questionIndex="question.question_index"
|
:questionType="question.question_type" :questionIndex="question.question_index"
|
||||||
:showTitle="styleInfo.is_question_number && true" isMobile :isAnswer="isAnswer"> -->
|
:showTitle="styleInfo.is_question_number && true" isMobile :isAnswer="isAnswer"> -->
|
||||||
|
|
||||||
<div
|
<div v-for="question in questions" v-else :id="'questionIndex' + question.question_index"
|
||||||
v-for="question in questions"
|
:key="question.question_index" class="question">
|
||||||
v-else
|
|
||||||
:id="'questionIndex' + question.question_index"
|
|
||||||
:key="question.question_index"
|
|
||||||
class="question"
|
|
||||||
>
|
|
||||||
<!-- <q-radio-->
|
<!-- <q-radio-->
|
||||||
<!-- v-if="question.question_type === 1"-->
|
<!-- v-if="question.question_type === 1"-->
|
||||||
<!-- :list="question.list"-->
|
<!-- :list="question.list"-->
|
||||||
@@ -92,37 +77,17 @@
|
|||||||
<!-- :question="question"-->
|
<!-- :question="question"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- 单选题 -->
|
<!-- 单选题 -->
|
||||||
<preview-choice
|
<preview-choice v-if="question.question_type === 1" v-model:answer="question.answer"
|
||||||
v-if="question.question_type === 1"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:config="question.config" :hideOptions="question.hideOptions" :stem="question.stem"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
:answerSn="questionsData.answer.sn" :answerSurveySn="questionsData.answer.survey_sn" :question="question"
|
||||||
:list="question.list"
|
@previous="previous" @next="next" @change-answer="onRelation($event, question)" />
|
||||||
:config="question.config"
|
|
||||||
:hideOptions="question.hideOptions"
|
|
||||||
:stem="question.stem"
|
|
||||||
:answerSn="questionsData.answer.sn"
|
|
||||||
:answerSurveySn="questionsData.answer.survey_sn"
|
|
||||||
:question="question"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- 多选题 -->
|
<!-- 多选题 -->
|
||||||
<preview-checkbox
|
<preview-checkbox v-else-if="question.question_type === 2" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 2"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:config="question.config" :hideOptions="question.hideOptions" :stem="question.stem"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
:answerSn="questionsData.answer.sn" :answerSurveySn="questionsData.answer.survey_sn" :question="question"
|
||||||
:list="question.list"
|
@change-answer="onRelation($event, question)" @previous="previous" @next="next" />
|
||||||
:config="question.config"
|
|
||||||
:hideOptions="question.hideOptions"
|
|
||||||
:stem="question.stem"
|
|
||||||
:answerSn="questionsData.answer.sn"
|
|
||||||
:answerSurveySn="questionsData.answer.survey_sn"
|
|
||||||
:question="question"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
/>
|
|
||||||
<!-- <!– 级联题 –>-->
|
<!-- <!– 级联题 –>-->
|
||||||
<!-- <q-cascader-->
|
<!-- <q-cascader-->
|
||||||
<!-- v-else-if="question.question_type === 3"-->
|
<!-- v-else-if="question.question_type === 3"-->
|
||||||
@@ -133,43 +98,19 @@
|
|||||||
<!-- isMobile-->
|
<!-- isMobile-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- 填空题 -->
|
<!-- 填空题 -->
|
||||||
<preview-completion
|
<preview-completion v-else-if="question.question_type === 4" :config="question.config" :answer="question.answer"
|
||||||
v-else-if="question.question_type === 4"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :stem="question.stem"
|
||||||
:config="question.config"
|
:answerSn="questionsData.answer.sn" :answerSurveySn="questionsData.answer.survey_sn" :question="question"
|
||||||
:answer="question.answer"
|
@previous="previous" @next="next" @change-answer="onRelation($event, question)" />
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
|
||||||
:stem="question.stem"
|
|
||||||
:answerSn="questionsData.answer.sn"
|
|
||||||
:answerSurveySn="questionsData.answer.survey_sn"
|
|
||||||
:question="question"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- 打分题 -->
|
<!-- 打分题 -->
|
||||||
<preview-rate
|
<preview-rate v-else-if="question.question_type === 5" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 5"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:config="question.config" :question="question" isMobile @change-answer="onRelation($event, question)" />
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
|
||||||
:list="question.list"
|
|
||||||
:config="question.config"
|
|
||||||
:question="question"
|
|
||||||
isMobile
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- 图文说明题 -->
|
<!-- 图文说明题 -->
|
||||||
<preview-text-with-images
|
<preview-text-with-images :answerIndex="getQuestionIndex(questionsData.questions, question)"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
:stem="question.stem" v-else-if="question.question_type === 6" :config="question.config" @previous="previous"
|
||||||
:stem="question.stem"
|
@next="next" v-model:answer="question.answer" :answerSn="questionsData.answer.sn"
|
||||||
v-else-if="question.question_type === 6"
|
:answerSurveySn="questionsData.answer.survey_sn" :question="question" />
|
||||||
:config="question.config"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
v-model:answer="question.answer"
|
|
||||||
:answerSn="questionsData.answer.sn"
|
|
||||||
:answerSurveySn="questionsData.answer.survey_sn"
|
|
||||||
:question="question"
|
|
||||||
/>
|
|
||||||
<!-- <!– 日期时间题 –>-->
|
<!-- <!– 日期时间题 –>-->
|
||||||
<!-- <q-date-->
|
<!-- <q-date-->
|
||||||
<!-- v-else-if="question.question_type === 7"-->
|
<!-- v-else-if="question.question_type === 7"-->
|
||||||
@@ -179,45 +120,20 @@
|
|||||||
<!-- isMobile-->
|
<!-- isMobile-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- 矩阵填空题 -->
|
<!-- 矩阵填空题 -->
|
||||||
<preview-matrix-text
|
<preview-matrix-text v-else-if="question.question_type === 8" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 8"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:questionIndex="question.question_index" :config="question.config" :stem="question.stem" :question="question"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
@previous="previous" @next="next" @change-answer="onRelation($event, question)" />
|
||||||
:list="question.list"
|
|
||||||
:questionIndex="question.question_index"
|
|
||||||
:config="question.config"
|
|
||||||
:stem="question.stem"
|
|
||||||
:question="question"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- 矩阵单选题 -->
|
<!-- 矩阵单选题 -->
|
||||||
<preview-matrix-radio
|
<preview-matrix-radio v-else-if="question.question_type === 9" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 9"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:questionIndex="question.question_index" :config="question.config" :stem="question.stem"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
:answerSn="questionsData.answer.sn" :answerSurveySn="questionsData.answer.survey_sn" :question="question"
|
||||||
:list="question.list"
|
@change-answer="onRelation($event, question)" @previous="previous" @next="next" />
|
||||||
:questionIndex="question.question_index"
|
|
||||||
:config="question.config"
|
|
||||||
:stem="question.stem"
|
|
||||||
:answerSn="questionsData.answer.sn"
|
|
||||||
:answerSurveySn="questionsData.answer.survey_sn"
|
|
||||||
:question="question"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
/>
|
|
||||||
<!-- 矩阵多选题 -->
|
<!-- 矩阵多选题 -->
|
||||||
<preview-matrix-checkbox
|
<preview-matrix-checkbox v-else-if="question.question_type === 10" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 10"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :list="question.list"
|
||||||
v-model:answer="question.answer"
|
:config="question.config" :question="question" @change-answer="onRelation($event, question)" />
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
|
||||||
:list="question.list"
|
|
||||||
:config="question.config"
|
|
||||||
:question="question"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- <!– 矩阵打分题 –>-->
|
<!-- <!– 矩阵打分题 –>-->
|
||||||
<!-- <matrix-rate-->
|
<!-- <matrix-rate-->
|
||||||
<!-- v-else-if="question.question_type === 11"-->
|
<!-- v-else-if="question.question_type === 11"-->
|
||||||
@@ -285,16 +201,10 @@
|
|||||||
<!-- isMobile-->
|
<!-- isMobile-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- 文件上传题 -->
|
<!-- 文件上传题 -->
|
||||||
<preview-file-upload
|
<preview-file-upload v-else-if="question.question_type === 18" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 18"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :config="question.config"
|
||||||
v-model:answer="question.answer"
|
:question="question" isMobile :questionIndex="question.question_index"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
@change-answer="onRelation($event, question)" />
|
||||||
:config="question.config"
|
|
||||||
:question="question"
|
|
||||||
isMobile
|
|
||||||
:questionIndex="question.question_index"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- <!– 地理位置题 –>-->
|
<!-- <!– 地理位置题 –>-->
|
||||||
<!-- <q-map-->
|
<!-- <q-map-->
|
||||||
<!-- v-else-if="question.question_type === 19"-->
|
<!-- v-else-if="question.question_type === 19"-->
|
||||||
@@ -323,14 +233,9 @@
|
|||||||
<!-- isMobile-->
|
<!-- isMobile-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- 签名题 -->
|
<!-- 签名题 -->
|
||||||
<preview-sign
|
<preview-sign v-else-if="question.question_type === 22" :config="question.config" :question="question"
|
||||||
v-else-if="question.question_type === 22"
|
v-model:answer="question.answer" :answerIndex="getQuestionIndex(questionsData.questions, question)"
|
||||||
:config="question.config"
|
@change-answer="onRelation($event, question)" />
|
||||||
:question="question"
|
|
||||||
v-model:answer="question.answer"
|
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
<!-- <!– 知情同意书 –>-->
|
<!-- <!– 知情同意书 –>-->
|
||||||
<!-- <q-consent-->
|
<!-- <q-consent-->
|
||||||
<!-- v-else-if="question.question_type === 23"-->
|
<!-- v-else-if="question.question_type === 23"-->
|
||||||
@@ -454,26 +359,12 @@
|
|||||||
<!-- :question="question"-->
|
<!-- :question="question"-->
|
||||||
<!-- />-->
|
<!-- />-->
|
||||||
<!-- <!– 高级题型-NPS –>-->
|
<!-- <!– 高级题型-NPS –>-->
|
||||||
<preview-n-p-s
|
<preview-n-p-s v-else-if="question.question_type === 106" v-model:answer="question.answer"
|
||||||
v-else-if="question.question_type === 106"
|
:answerIndex="getQuestionIndex(questionsData.questions, question)" :isPreview="isPreview"
|
||||||
v-model:answer="question.answer"
|
:title="question.title" :stem="question.stem" :list="question.list" :config="question.config"
|
||||||
:answerIndex="getQuestionIndex(questionsData.questions, question)"
|
:isAnswer="isAnswer" :questionIndex="question.question_index" :label="question.title" :loading="loading"
|
||||||
:isPreview="isPreview"
|
:isTemplate="isTemplate" :showTitle="styleInfo.is_question_number" :question="question" @previous="previous"
|
||||||
:title="question.title"
|
@next="next" @change-answer="onRelation($event, question)" />
|
||||||
:stem="question.stem"
|
|
||||||
:list="question.list"
|
|
||||||
:config="question.config"
|
|
||||||
:isAnswer="isAnswer"
|
|
||||||
:questionIndex="question.question_index"
|
|
||||||
:label="question.title"
|
|
||||||
:loading="loading"
|
|
||||||
:isTemplate="isTemplate"
|
|
||||||
:showTitle="styleInfo.is_question_number"
|
|
||||||
:question="question"
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
@change-answer="onRelation($event, question)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- <LangTranslate v-if="isAnswer && styleInfo.is_yip" translate-key="PoweredByDigitalTechnologyCenterYIP"
|
<!-- <LangTranslate v-if="isAnswer && styleInfo.is_yip" translate-key="PoweredByDigitalTechnologyCenterYIP"
|
||||||
@@ -484,33 +375,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<!-- eslint-disable max-len -->
|
<!-- eslint-disable max-len -->
|
||||||
<pfe-pagination
|
<pfe-pagination v-if="isPreview" class="pagination" :isPreview="isPreview" :page="page" :pages="pages.length + 1"
|
||||||
v-if="isPreview"
|
:min="styleInfo.is_home ? 0 : 1" :loading="loading" :showPrevious="styleInfo.is_up_button"
|
||||||
class="pagination"
|
:showStart="styleInfo.is_start_button" :startText="styleInfo.start_button_text"
|
||||||
:isPreview="isPreview"
|
:showSubmit="styleInfo.is_submit_button" :submitText="localPageTimer.is_show && localPageTimer.short_time
|
||||||
:page="page"
|
|
||||||
:pages="pages.length + 1"
|
|
||||||
:min="styleInfo.is_home ? 0 : 1"
|
|
||||||
:loading="loading"
|
|
||||||
:showPrevious="styleInfo.is_up_button"
|
|
||||||
:showStart="styleInfo.is_start_button"
|
|
||||||
:startText="styleInfo.start_button_text"
|
|
||||||
:showSubmit="styleInfo.is_submit_button"
|
|
||||||
:submitText="
|
|
||||||
localPageTimer.is_show && localPageTimer.short_time
|
|
||||||
? `${localPageTimer.short_time}S`
|
? `${localPageTimer.short_time}S`
|
||||||
: styleInfo.submit_button_text
|
: styleInfo.submit_button_text
|
||||||
"
|
" :buttonTextColor="styleInfo.button_text_color" :buttonColor="styleInfo.button_color" :nextText="localPageTimer.is_show && localPageTimer.short_time ? `${localPageTimer.short_time}S` : ''
|
||||||
:buttonTextColor="styleInfo.button_text_color"
|
" :nextDisabled="localPageTimer.short_time" isMobile @previous="previous" @next="next">
|
||||||
:buttonColor="styleInfo.button_color"
|
|
||||||
:nextText="
|
|
||||||
localPageTimer.is_show && localPageTimer.short_time ? `${localPageTimer.short_time}S` : ''
|
|
||||||
"
|
|
||||||
:nextDisabled="localPageTimer.short_time"
|
|
||||||
isMobile
|
|
||||||
@previous="previous"
|
|
||||||
@next="next"
|
|
||||||
>
|
|
||||||
</pfe-pagination>
|
</pfe-pagination>
|
||||||
</div>
|
</div>
|
||||||
<!-- eslint-enable max-len -->
|
<!-- eslint-enable max-len -->
|
||||||
@@ -865,62 +737,6 @@ async function answer(callback, callbackBeforePage) {
|
|||||||
isError = true;
|
isError = true;
|
||||||
question.error = translatedText.value.PleaseUploadAtLeastOneFiles(config.min_number);
|
question.error = translatedText.value.PleaseUploadAtLeastOneFiles(config.min_number);
|
||||||
} else if (answer && questionType === 4) {
|
} else if (answer && questionType === 4) {
|
||||||
question.error = '';
|
|
||||||
// 填空题
|
|
||||||
const { value } = answer;
|
|
||||||
const newValue = value.replace(/\n|\r|\r\n/g, '');
|
|
||||||
switch (config.text_type) {
|
|
||||||
// 字母
|
|
||||||
case 3:
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const reg =
|
|
||||||
/^[a-zA-Z·~!@#¥%…&*()—\-+={}|《》?:“”【】、;‘’,。`!$^()_<>?:",./;'\\[\]]+$/;
|
|
||||||
isError =
|
|
||||||
config.include_mark === 1
|
|
||||||
? !reg.test(newValue) || !newValue.length
|
|
||||||
: !/^[a-zA-Z]+$/.test(newValue) || !newValue.length;
|
|
||||||
question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : '';
|
|
||||||
break;
|
|
||||||
// 中文
|
|
||||||
case 4:
|
|
||||||
isError =
|
|
||||||
config.include_mark === 1
|
|
||||||
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[a-zA-Z·~!@#¥%…&*()—\-+={}|《》?:“”【】、;‘’,。`!$^()_<>?:",./;'\\[\]])+$/.test(
|
|
||||||
newValue
|
|
||||||
) || !newValue.length
|
|
||||||
: !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test(
|
|
||||||
newValue
|
|
||||||
) || !newValue.length;
|
|
||||||
question.error = isError ? translatedText.value.PleaseEnterChineseWords : '';
|
|
||||||
break;
|
|
||||||
// 邮箱
|
|
||||||
case 5:
|
|
||||||
isError =
|
|
||||||
!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
||||||
value
|
|
||||||
);
|
|
||||||
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
|
|
||||||
break;
|
|
||||||
// 手机号
|
|
||||||
case 6:
|
|
||||||
isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value);
|
|
||||||
question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : '';
|
|
||||||
break;
|
|
||||||
// 身份证号
|
|
||||||
case 7:
|
|
||||||
isError =
|
|
||||||
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
|
|
||||||
value
|
|
||||||
);
|
|
||||||
question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!isError && value.length < config.min && ![1, 2].includes(config.text_type)) {
|
|
||||||
isError = true;
|
|
||||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
|
||||||
}
|
|
||||||
} else if (answer && questionType === 8) {
|
} else if (answer && questionType === 8) {
|
||||||
// 矩阵填空题
|
// 矩阵填空题
|
||||||
question.error = '';
|
question.error = '';
|
||||||
@@ -1483,6 +1299,7 @@ function clearAnswer(questions) {
|
|||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/assets/css/main';
|
@import '@/assets/css/main';
|
||||||
|
|
||||||
:deep(.van-cell::after) {
|
:deep(.van-cell::after) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -1490,7 +1307,8 @@ function clearAnswer(questions) {
|
|||||||
.preview-icon {
|
.preview-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 65px; /* 根据实际图片大小调整 */
|
width: 65px;
|
||||||
|
/* 根据实际图片大小调整 */
|
||||||
height: 50px;
|
height: 50px;
|
||||||
margin-right: 40px;
|
margin-right: 40px;
|
||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
@@ -1502,8 +1320,10 @@ function clearAnswer(questions) {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -70px;
|
bottom: -70px;
|
||||||
left: -10px;
|
left: -10px;
|
||||||
width: 65px; /* 根据实际图片大小调整 */
|
width: 65px;
|
||||||
height: 140px; /* 根据实际图片大小调整 */
|
/* 根据实际图片大小调整 */
|
||||||
|
height: 140px;
|
||||||
|
/* 根据实际图片大小调整 */
|
||||||
background: url('@/assets/img/create-right-back.png') no-repeat center center;
|
background: url('@/assets/img/create-right-back.png') no-repeat center center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
|
||||||
@@ -1535,6 +1355,7 @@ function clearAnswer(questions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.end-text {
|
.end-text {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 65px;
|
bottom: 65px;
|
||||||
@@ -1544,11 +1365,13 @@ function clearAnswer(questions) {
|
|||||||
color: #a5a5ac;
|
color: #a5a5ac;
|
||||||
background-color: #f2f2f2;
|
background-color: #f2f2f2;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
|
||||||
& .el-text {
|
& .el-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #4b4b59 !important;
|
color: #4b4b59 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-info {
|
.preview-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
grid-template-columns: 1fr 80px;
|
grid-template-columns: 1fr 80px;
|
||||||
|
|||||||
@@ -12,40 +12,47 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, defineEmits, ref, watch } from 'vue';
|
import { computed, defineEmits, ref, watch } from 'vue';
|
||||||
import Completion from '@/views/Design/components/Questions/Completion.vue';
|
import Completion from '@/views/Design/components/Questions/Completion.vue';
|
||||||
import Rate from '@/views/Design/components/Questions/Rate.vue';
|
import type { IQuestion } from '@/types/question';
|
||||||
// 预览新增 v-model
|
import { validateCompletion } from '@/views/Survey/views/Preview/components/questions/validate/previewCompletion';
|
||||||
// const config = defineModel('config');
|
|
||||||
const answer = defineModel<{ value: string | number }>('answer', { default: { value: '' } });
|
interface ICompletionAnswer {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = defineModel<ICompletionAnswer>('answer', { default: {} });
|
||||||
|
|
||||||
const answerIndex = computed(() => {
|
const answerIndex = computed(() => {
|
||||||
return question.value.title;
|
return question.value.title;
|
||||||
});
|
});
|
||||||
// const stem = defineModel('stem');
|
|
||||||
const question = defineModel<question>('question', { default: () => {} });
|
|
||||||
// const list = defineModel<questionsList[]>('list');
|
|
||||||
// const answerSn = defineModel<string>('answerSn');
|
|
||||||
// const answerSurveySn = defineModel<answerSurveySn>('answerSurveySn');
|
|
||||||
// const modelValue = defineModel('modelValue');
|
|
||||||
|
|
||||||
// // 预览新增 emit ['changeAnswer', 'previous', 'next']
|
const question = defineModel<IQuestion<ICompletionConfig>>('question', { default: () => {} });
|
||||||
const emit = defineEmits(['previous', 'next', 'update:modelValue', 'saveOption', 'changeAnswer']);
|
const emit = defineEmits(['previous', 'next', 'update:modelValue', 'saveOption', 'changeAnswer']);
|
||||||
|
|
||||||
// console.log(`answer`, answer.value);
|
const completionValue = ref<string>(answer.value?.value ?? '');
|
||||||
// console.log(question.value);
|
|
||||||
const completionValue = ref(answer.value?.value ?? '');
|
|
||||||
// console.log(`question:`, question.value);
|
|
||||||
// console.log(`list: `, list.value);
|
|
||||||
|
|
||||||
// 进行提交答案
|
// 进行提交答案
|
||||||
watch(
|
watch(
|
||||||
() => completionValue.value,
|
() => completionValue.value,
|
||||||
() => {
|
(value) => {
|
||||||
const res = {
|
// 答案校验,生成最终答案
|
||||||
value: completionValue.value
|
const { isError } = validateCompletion(question.value, question.value.config!, String(value));
|
||||||
};
|
console.log(`isError, question`, isError, question.value);
|
||||||
|
if (isError) return;
|
||||||
|
|
||||||
|
const res = generateAnswer(value);
|
||||||
answer.value = res;
|
answer.value = res;
|
||||||
question.value!.answer = res;
|
question.value!.answer = res;
|
||||||
emit('changeAnswer', res);
|
// answer emit 提交失效
|
||||||
|
// emit('changeAnswer', res);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成最终答案
|
||||||
|
* @param answer {string}
|
||||||
|
*/
|
||||||
|
function generateAnswer(answer: string) {
|
||||||
|
return { value: answer };
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
interface ICompletionAnswer {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ICompletionAnswer };
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// 语言转换类型
|
||||||
|
// export interface ITranslatedText {
|
||||||
|
// [key: string]: string | ((value: number) => string);
|
||||||
|
// }
|
||||||
|
|
||||||
|
import { language } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
|
||||||
|
type originLanguage = typeof language;
|
||||||
|
export type languageArea = 'zh' | 'en';
|
||||||
|
/**
|
||||||
|
* 转换之后的 translatedText
|
||||||
|
* originLanguage[K] 属性 value
|
||||||
|
* 对应的属性格式为
|
||||||
|
* ```typescript
|
||||||
|
* ITranslatedText{
|
||||||
|
* some: string,
|
||||||
|
* other: (value: number) => string
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export type ITranslatedText = {
|
||||||
|
[K in keyof originLanguage]: originLanguage[K][languageArea];
|
||||||
|
};
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import type { IQuestion } from '@/types/question';
|
||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
import { validateEnglishLetters } from '@/views/Survey/views/Preview/components/questions/validate/validateEnglishLetters';
|
||||||
|
import { validateChineseLetter } from '@/views/Survey/views/Preview/components/questions/validate/validateChineseLetter';
|
||||||
|
import { validateEmail } from '@/views/Survey/views/Preview/components/questions/validate/validateEmail';
|
||||||
|
import { validatePhone } from '@/views/Survey/views/Preview/components/questions/validate/validatePhone';
|
||||||
|
import { validateIDCard } from '@/views/Survey/views/Preview/components/questions/validate/validateIDCard';
|
||||||
|
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/validate/validateChineseLetter';
|
||||||
|
import { validateMinLength,validateMaxLength } from '@/views/Survey/views/Preview/components/questions/validate/validateStringLength';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成对应的语言文字
|
||||||
|
* 目标语言暂时写死,有需求后续更改
|
||||||
|
*/
|
||||||
|
const translatedText = getLanguage(['zh']) as ITranslatedText;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 填空题验证
|
||||||
|
* @date 2022-11-22
|
||||||
|
* @param {IQuestion<ICompletionConfig>} question - 问卷
|
||||||
|
* @param {IQuestionConfig} config - 相关配置
|
||||||
|
* @param {ICompletionAnswer} answer - 答案
|
||||||
|
*/
|
||||||
|
function validateCompletion(
|
||||||
|
question: IQuestion<ICompletionConfig>,
|
||||||
|
config: ICompletionConfig,
|
||||||
|
answer: string
|
||||||
|
) {
|
||||||
|
// 是否有错误
|
||||||
|
let isError = false;
|
||||||
|
|
||||||
|
question.error = '';
|
||||||
|
// 处理空白字符
|
||||||
|
const newValue = answer.replace(/\n|\r|\r\n/g, '');
|
||||||
|
|
||||||
|
// 根据对应的类型进行验证
|
||||||
|
switch (config.text_type) {
|
||||||
|
// 字母校验
|
||||||
|
case 3:
|
||||||
|
question.error = validateEnglishLetters(config, newValue, translatedText);
|
||||||
|
break;
|
||||||
|
// 中文校验
|
||||||
|
case 4:
|
||||||
|
question.error = validateChineseLetter(config, newValue, translatedText);
|
||||||
|
break;
|
||||||
|
// 邮箱校验
|
||||||
|
case 5:
|
||||||
|
question.error = validateEmail(newValue, translatedText);
|
||||||
|
break;
|
||||||
|
// 手机号
|
||||||
|
case 6:
|
||||||
|
question.error = validatePhone(newValue, translatedText);
|
||||||
|
break;
|
||||||
|
// 身份证号
|
||||||
|
case 7:
|
||||||
|
question.error = validateIDCard(newValue, translatedText);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最小输入字数校验
|
||||||
|
const minStringValidateResult = validateMinLength(answer, isError, config, translatedText);
|
||||||
|
// 如果内容存在, 则说明有错误,初始化赋值数据
|
||||||
|
if (minStringValidateResult) {
|
||||||
|
const { isError: minStringError, errorMessage: minStringErrMessage } = minStringValidateResult;
|
||||||
|
|
||||||
|
minStringError && (isError = minStringError);
|
||||||
|
question.error = minStringErrMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最大输入字数校验
|
||||||
|
const maxStringValidateResult = validateMaxLength(answer, isError, config, translatedText);
|
||||||
|
// 如果内容存在, 则说明有错误,初始化赋值数据
|
||||||
|
if (maxStringValidateResult) {
|
||||||
|
const { isError: maxStringError, errorMessage: maxStringErrMessage } = maxStringValidateResult;
|
||||||
|
|
||||||
|
maxStringError && (isError = maxStringError);
|
||||||
|
question.error = maxStringErrMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回错误和对应的 question
|
||||||
|
return {
|
||||||
|
question,
|
||||||
|
isError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export { validateCompletion };
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { ICompletionAnswer } from '@/views/Survey/views/Preview/components/questions/types/previewCompletion';
|
||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
export type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验中文字符
|
||||||
|
* @param {ICompletionConfig} config - 相关配置
|
||||||
|
* @param {ICompletionAnswer} answer - 答案
|
||||||
|
* @param {ITranslatedText} translatedText - 语言转换文本
|
||||||
|
*/
|
||||||
|
export function validateChineseLetter(
|
||||||
|
config: ICompletionConfig,
|
||||||
|
answer: string,
|
||||||
|
translatedText = getLanguage(['zh']) as ITranslatedText
|
||||||
|
) {
|
||||||
|
let isError: boolean;
|
||||||
|
|
||||||
|
// 1. 包含标点 2. 不包含标点
|
||||||
|
// 如果包含标点,则使用正则校验,如果不包含标点,则使用
|
||||||
|
// /^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test(newValue) || !newValue.length
|
||||||
|
// 如果包含标点,校验规则
|
||||||
|
// 如果不包含标点,校验规则
|
||||||
|
isError =
|
||||||
|
config.include_mark === 1
|
||||||
|
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[a-zA-Z·~!@#¥%…&*()—\-+={}|《》?:“”【】、;‘’,。`!$^()_<>?:",./;'\\[\]])+$/.test(
|
||||||
|
answer
|
||||||
|
) || !answer.length
|
||||||
|
: !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/.test(
|
||||||
|
answer
|
||||||
|
) || !answer.length;
|
||||||
|
return isError ? translatedText.PleaseEnterChineseWords : '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import type { ICompletionAnswer } from '@/views/Survey/views/Preview/components/questions/types/previewCompletion';
|
||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
|
||||||
|
export type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证邮箱格式
|
||||||
|
* @param answer {ICompletionAnswer} 答案
|
||||||
|
* @param translatedText {ITranslatedText} 语言转换文本
|
||||||
|
*/
|
||||||
|
export function validateEmail(
|
||||||
|
answer: string,
|
||||||
|
translatedText = getLanguage(['zh']) as ITranslatedText
|
||||||
|
) {
|
||||||
|
let isError: boolean;
|
||||||
|
isError =
|
||||||
|
!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
||||||
|
answer
|
||||||
|
);
|
||||||
|
return isError ? translatedText.PleaseEnterACorrectEmail : '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证字母
|
||||||
|
* @param {ICompletionConfig} config - 相关配置
|
||||||
|
* @param {ICompletionAnswer} answer - 答案
|
||||||
|
* @param {ITranslatedText} translatedText - 语言转换文本
|
||||||
|
*/
|
||||||
|
export function validateEnglishLetters(
|
||||||
|
config: ICompletionConfig,
|
||||||
|
answer: string,
|
||||||
|
translatedText = getLanguage(['zh']) as ITranslatedText
|
||||||
|
) {
|
||||||
|
let isError: boolean;
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const reg = /^[a-zA-Z·~!@#¥%…&*()—\-+={}|《》?:“”【】、;‘’,。`!$^()_<>?:",./;'\\[\]]+$/;
|
||||||
|
// 是否包含标点
|
||||||
|
// 1. 包含标点 2. 不包含标点
|
||||||
|
// 如果包含标点,则使用正则校验,如果不包含标点,则使用 /^[a-zA-Z]+$/ 校验
|
||||||
|
isError =
|
||||||
|
config.include_mark === 1
|
||||||
|
? !reg.test(answer) || !answer.length
|
||||||
|
: !/^[a-zA-Z]+$/.test(answer) || !answer.length;
|
||||||
|
|
||||||
|
return isError ? translatedText.PleaseEnterEnglishLetters : '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import type { ICompletionAnswer } from '@/views/Survey/views/Preview/components/questions/types/previewCompletion';
|
||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证手机号
|
||||||
|
* @param answer {ICompletionAnswer} 答案
|
||||||
|
* @param translatedText {ITranslatedText} 语言转换文本
|
||||||
|
*/
|
||||||
|
export function validateIDCard(
|
||||||
|
answer: string,
|
||||||
|
translatedText = getLanguage(['zh']) as ITranslatedText
|
||||||
|
) {
|
||||||
|
let isError: boolean;
|
||||||
|
|
||||||
|
isError =
|
||||||
|
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
|
||||||
|
answer
|
||||||
|
);
|
||||||
|
|
||||||
|
return isError ? translatedText.PleaseEnterACorrectID : '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type { ICompletionAnswer } from '@/views/Survey/views/Preview/components/questions/types/previewCompletion';
|
||||||
|
import { getLanguage } from '@/views/Survey/views/Preview/js/language';
|
||||||
|
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证手机号
|
||||||
|
* @param answer {ICompletionAnswer} 答案
|
||||||
|
* @param translatedText {ITranslatedText} 语言转换文本
|
||||||
|
*/
|
||||||
|
export function validatePhone(
|
||||||
|
answer: string,
|
||||||
|
translatedText = getLanguage(['zh']) as ITranslatedText
|
||||||
|
) {
|
||||||
|
let isError: boolean;
|
||||||
|
|
||||||
|
isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(answer);
|
||||||
|
return isError ? translatedText.PleaseEnterACorrectPhone : '';
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/types/translatedText';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证最小字符串长度
|
||||||
|
* @param answer {ICompletionAnswer} 答案
|
||||||
|
* @param isError {boolean} 是否有错误
|
||||||
|
* @param config {ICompletionConfig} 配置信息
|
||||||
|
* @param translatedText {ITranslatedText} 语言转换文本
|
||||||
|
* @return {string} 错误信息
|
||||||
|
*/
|
||||||
|
export function validateMinLength(
|
||||||
|
answer: string,
|
||||||
|
isError: boolean,
|
||||||
|
config: ICompletionConfig,
|
||||||
|
translatedText: ITranslatedText
|
||||||
|
) {
|
||||||
|
// 处理异常数据,因为不给数据的时候,默认是空字符串
|
||||||
|
if (typeof config.min === 'string' && config.min.length === 0) return;
|
||||||
|
|
||||||
|
// 如果已经包含报错,不处理
|
||||||
|
if (isError) return;
|
||||||
|
// 如果包含相应的 text_type, 也不处理,1是整数,2是小数
|
||||||
|
if ([1, 2].includes(config.text_type)) return;
|
||||||
|
|
||||||
|
if (answer.length > Number(config.min)) return;
|
||||||
|
return {
|
||||||
|
isError: true,
|
||||||
|
errorMessage: translatedText.PleaseEnterMoreCharacters(config.min)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证最大字符串长度
|
||||||
|
* @param answer {ICompletionAnswer} 答案
|
||||||
|
* @param isError {boolean} 是否有错误
|
||||||
|
* @param config {ICompletionConfig} 配置信息
|
||||||
|
* @param translatedText {ITranslatedText} 语言转换文本
|
||||||
|
* @return {string} 错误信息
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function validateMaxLength(
|
||||||
|
answer: string,
|
||||||
|
isError: boolean,
|
||||||
|
config: ICompletionConfig,
|
||||||
|
translatedText: ITranslatedText
|
||||||
|
) {
|
||||||
|
// 处理异常数据,因为不给数据的时候,默认是空字符串
|
||||||
|
if (typeof config.max === 'string' && config.max.length === 0) return;
|
||||||
|
|
||||||
|
// 如果已经包含报错,不处理
|
||||||
|
if (isError) return;
|
||||||
|
// 如果包含相应的 text_type, 也不处理, 1是整数,2是小数
|
||||||
|
if ([1, 2].includes(config.text_type)) return;
|
||||||
|
|
||||||
|
if (answer.length < Number(config.max)) return;
|
||||||
|
return {
|
||||||
|
isError: true,
|
||||||
|
errorMessage: translatedText.PleaseEnterLessCharacters(config.max)
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -71,7 +71,11 @@ export const language = {
|
|||||||
en: 'Please enter a valid ID card number.',
|
en: 'Please enter a valid ID card number.',
|
||||||
zh: '请输入正确的身份证号。'
|
zh: '请输入正确的身份证号。'
|
||||||
},
|
},
|
||||||
PleaseEnterMoreThanOneCharacters: {
|
PleaseEnterLessCharacters: {
|
||||||
|
en: (count) => `Please enter less than ${count} character${count > 1 ? 's' : ''}.`,
|
||||||
|
zh: (count) => `请输入小于${count}个字符。`
|
||||||
|
},
|
||||||
|
PleaseEnterMoreCharacters: {
|
||||||
en: (count) => `Please enter more than ${count} character${count > 1 ? 's' : ''}.`,
|
en: (count) => `Please enter more than ${count} character${count > 1 ? 's' : ''}.`,
|
||||||
zh: (count) => `请输入大于${count}个字符。`
|
zh: (count) => `请输入大于${count}个字符。`
|
||||||
},
|
},
|
||||||
@@ -543,7 +547,24 @@ export function setLanguageTypes(types) {
|
|||||||
languageTypes.push(...types);
|
languageTypes.push(...types);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@/views/Survey/views/Preview/components/questions/types/translatedText').languageArea} LanguageArea
|
||||||
|
* @typedef {import('@/views/Survey/views/Preview/components/questions/types/translatedText').ITranslatedText} ITranslatedText I
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @alias
|
||||||
|
* @param langArr { languageArea[] } 语言区域类型
|
||||||
|
* @returns {ITranslatedText} 获取到的文本
|
||||||
|
*/
|
||||||
export function getLanguage(langArr = languageTypes) {
|
export function getLanguage(langArr = languageTypes) {
|
||||||
|
/**
|
||||||
|
* langArr: 语言数组 default: ['zh']
|
||||||
|
* 1. langArr 为空时,使用 ['zh']
|
||||||
|
* 2. langArr 是数组时,使用传入的语言
|
||||||
|
* 3. langArr 是字符串时,使用该字符串语言
|
||||||
|
*/
|
||||||
const l = [];
|
const l = [];
|
||||||
if (!langArr) {
|
if (!langArr) {
|
||||||
l.push('zh');
|
l.push('zh');
|
||||||
|
|||||||
7
src/views/Survey/views/Preview/js/test/language.spec.ts
Normal file
7
src/views/Survey/views/Preview/js/test/language.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { test, spec } from "vitest";
|
||||||
|
import { getLanguage } from "../language";
|
||||||
|
|
||||||
|
test("检测 language 列表", () => {
|
||||||
|
const res = getLanguage(['zh'])
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// vite.config.ts
|
/// <reference types="vitest" />
|
||||||
import { defineConfig, loadEnv } from 'vite'; // 从 vite 导入 loadEnv
|
import { defineConfig, loadEnv } from 'vite'; // 从 vite 导入 loadEnv
|
||||||
import vue from '@vitejs/plugin-vue';
|
import vue from '@vitejs/plugin-vue';
|
||||||
import { fileURLToPath, URL } from 'node:url';
|
import { fileURLToPath, URL } from 'node:url';
|
||||||
|
|||||||
Reference in New Issue
Block a user