Merge branch 'feature/feature-20250331-h5' into uat

This commit is contained in:
liu.huiying@ebiz-digits.com
2025-03-17 10:12:16 +08:00
55 changed files with 1007 additions and 1471 deletions

4
auto-imports.d.ts vendored
View File

@@ -5,4 +5,6 @@
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global {} declare global {
}

11
components.d.ts vendored
View File

@@ -8,15 +8,9 @@ export {}
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
Contenteditable: typeof import('./src/components/contenteditable.vue')['default'] Contenteditable: typeof import('./src/components/contenteditable.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElText: typeof import('element-plus/es')['ElText']
RichText: typeof import('./src/components/RichText.vue')['default'] RichText: typeof import('./src/components/RichText.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
@@ -29,8 +23,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']
VanNavBar: typeof import('vant/es')['NavBar'] VanNavBar: typeof import('vant/es')['NavBar']
VanPicker: typeof import('vant/es')['Picker'] VanPicker: typeof import('vant/es')['Picker']
@@ -38,12 +30,9 @@ declare module 'vue' {
VanRadio: typeof import('vant/es')['Radio'] VanRadio: typeof import('vant/es')['Radio']
VanRadioGroup: typeof import('vant/es')['RadioGroup'] VanRadioGroup: typeof import('vant/es')['RadioGroup']
VanRow: typeof import('vant/es')['Row'] VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStepper: typeof import('vant/es')['Stepper'] VanStepper: typeof import('vant/es')['Stepper']
VanSwitch: typeof import('vant/es')['Switch'] VanSwitch: typeof import('vant/es')['Switch']
VanTab: typeof import('vant/es')['Tab'] VanTab: typeof import('vant/es')['Tab']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']
VanTabs: typeof import('vant/es')['Tabs'] VanTabs: typeof import('vant/es')['Tabs']
YLCascader: typeof import('./src/components/YLCascader.vue')['default'] YLCascader: typeof import('./src/components/YLCascader.vue')['default']
YLInput: typeof import('./src/components/YLInput.vue')['default'] YLInput: typeof import('./src/components/YLInput.vue')['default']

View File

@@ -19,11 +19,13 @@
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.1",
"axios": "^1.8.2", "axios": "^1.8.2",
"cos-js-sdk-v5": "^1.8.7",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"element-plus": "^2.7.8", "element-plus": "^2.7.8",
"js-base64": "^3.7.7", "js-base64": "^3.7.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"shrinkpng": "^1.2.0-beta.1",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vant": "^4.9.17", "vant": "^4.9.17",

View File

@@ -4,7 +4,7 @@ import { onMounted } from 'vue';
import appBridge from '@/assets/js/appBridge'; import appBridge from '@/assets/js/appBridge';
import utils from '@/assets/js/common'; import utils from '@/assets/js/common';
onMounted(async () => { onMounted(async() => {
if (utils.getParameter('digitalYiliToken')) { if (utils.getParameter('digitalYiliToken')) {
// 隐藏/显示 header // 隐藏/显示 header
appBridge.setHeaderShown(false); appBridge.setHeaderShown(false);

View File

@@ -85,9 +85,9 @@ export default class CommonApi {
}; };
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
const reg = /\\|\/|\?|\|\*|"|“|”|'|||<|>|{|}|\[|\]|\【|\】||:|、|\^|\$|!|~|`|\s|\+/g; const reg = /\\|\/|\?|\|\*|"|“|”|'|||<|>|{|}|\[|\]|\【|\】||:|、|\^|\$|!|~|`|\s|\+/g;
name = name
name || = name
getRandomFileName(file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`); || getRandomFileName(file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`);
const res = await CommonApi.getOssInfo(); const res = await CommonApi.getOssInfo();
/* eslint-enable no-useless-escape */ /* eslint-enable no-useless-escape */

View File

@@ -1,7 +1,8 @@
.van-nav-bar { .van-nav-bar {
padding-top: calc(var(--status-bar-height) + 10px) !important; padding-top: calc(var(--status-bar-height) + 15px) !important;
// height: calc(46px + var(--status-bar-height)) !important; z-index:999;
} }
.van-cell { .van-cell {
padding: 8px !important; padding: 8px !important;
} }

View File

@@ -311,11 +311,14 @@ export default {
* @returns {Boolean} 操作是否成功 * @returns {Boolean} 操作是否成功
*/ */
async h5RouterBack(router) { async h5RouterBack(router) {
const canGoBack = this.routerCanGoBack(router); const routerCanGoBack = () => {
if (canGoBack) { const position = router.options.history.state?.position;
return typeof position === 'number' && position > 0;
};
if (routerCanGoBack()) {
router.go(-1); router.go(-1);
} else { } else {
this.navigateBack(); appBridge.navigateBack();
} }
} }
}; };

View File

@@ -170,8 +170,8 @@ const getMaxDateLimit = computed(() => {
props.format props.format
); );
const tempStr = '0000-12-31 23:59:59'; const tempStr = '0000-12-31 23:59:59';
const result = const result
props.maxDate.length !== 0 && thisMax.length > props.maxDate.length = props.maxDate.length !== 0 && thisMax.length > props.maxDate.length
? thisMax.slice(0, props.maxDate.length) + tempStr.slice(props.maxDate.length) ? thisMax.slice(0, props.maxDate.length) + tempStr.slice(props.maxDate.length)
: thisMax; : thisMax;
return result.slice(0, props.format.length); return result.slice(0, props.format.length);
@@ -194,8 +194,8 @@ function onChange({ selectedValues, columnIndex }) {
renderMinuteColumns, renderMinuteColumns,
renderSecondColumns renderSecondColumns
]; ];
updateColumns[columnIndex] && updateColumns[columnIndex]
updateColumns[columnIndex](changeValue, getMinDateLimit.value, getMaxDateLimit.value, false); && updateColumns[columnIndex](changeValue, getMinDateLimit.value, getMaxDateLimit.value, false);
} }
// 渲染全部列 // 渲染全部列

View File

@@ -4,17 +4,17 @@
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: src:
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix')
format('embedded-opentype'), format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont')
format('svg'); format('svg');
} }
.logo { .logo {
font-family: 'iconfont logo';
font-size: 160px;
font-style: normal; font-style: normal;
font-size: 160px;
font-family: 'iconfont logo';
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
@@ -29,8 +29,8 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
height: 42px; height: 42px;
line-height: 42px;
color: #666; color: #666;
line-height: 42px;
} }
#tabs { #tabs {
@@ -38,17 +38,17 @@
} }
#tabs li { #tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative; position: relative;
z-index: 1; z-index: 1;
width: 100px;
height: 40px;
margin-bottom: -1px; margin-bottom: -1px;
border-bottom: 2px solid transparent;
color: #666; color: #666;
font-size: 16px;
line-height: 40px;
text-align: center;
cursor: pointer;
} }
#tabs .active { #tabs .active {
@@ -62,25 +62,25 @@
/* 页面布局 */ /* 页面布局 */
.main { .main {
padding: 30px 100px;
width: 960px; width: 960px;
margin: 0 auto; margin: 0 auto;
padding: 30px 100px;
} }
.main .logo { .main .logo {
color: #333; overflow: hidden;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px; height: 110px;
margin-top: -50px; margin-top: -50px;
overflow: hidden; margin-bottom: 30px;
color: #333;
line-height: 1;
text-align: left;
*zoom: 1; *zoom: 1;
} }
.main .logo a { .main .logo a {
font-size: 160px;
color: #333; color: #333;
font-size: 160px;
} }
.helps { .helps {
@@ -88,25 +88,25 @@
} }
.helps pre { .helps pre {
padding: 20px; overflow: auto;
margin: 10px 0; margin: 10px 0;
padding: 20px;
border: solid 1px #e7e1cd; border: solid 1px #e7e1cd;
background-color: #fffdef; background-color: #fffdef;
overflow: auto;
} }
.icon_lists { .icon_lists {
width: 100% !important;
overflow: hidden; overflow: hidden;
width: 100% !important;
*zoom: 1; *zoom: 1;
} }
.icon_lists li { .icon_lists li {
width: 100px; width: 100px;
margin-bottom: 10px;
margin-right: 20px; margin-right: 20px;
text-align: center; margin-bottom: 10px;
list-style: none !important; list-style: none !important;
text-align: center;
cursor: default; cursor: default;
} }
@@ -117,10 +117,10 @@
.icon_lists .icon { .icon_lists .icon {
display: block; display: block;
height: 100px; height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto; margin: 10px auto;
color: #333; color: #333;
font-size: 42px;
line-height: 100px;
-webkit-transition: -webkit-transition:
font-size 0.25s linear, font-size 0.25s linear,
width 0.25s linear; width 0.25s linear;
@@ -137,15 +137,18 @@
} }
.icon_lists .svg-icon { .icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */ /* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em; vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */ normalize.css 中也包含这行 */
overflow: hidden; overflow: hidden;
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentcolor;
} }
.icon_lists li .name, .icon_lists li .name,
@@ -170,10 +173,10 @@
} }
.markdown h1 { .markdown h1 {
margin-bottom: 24px;
color: #404040; color: #404040;
font-weight: 500; font-weight: 500;
line-height: 40px; line-height: 40px;
margin-bottom: 24px;
} }
.markdown h2, .markdown h2,
@@ -181,10 +184,10 @@
.markdown h4, .markdown h4,
.markdown h5, .markdown h5,
.markdown h6 { .markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both; clear: both;
margin: 1.6em 0 0.6em;
color: #404040;
font-weight: 500;
} }
.markdown h1 { .markdown h1 {
@@ -212,11 +215,11 @@
} }
.markdown hr { .markdown hr {
clear: both;
height: 1px; height: 1px;
margin: 16px 0;
border: 0; border: 0;
background: #e9e9e9; background: #e9e9e9;
margin: 16px 0;
clear: both;
} }
.markdown p { .markdown p {
@@ -259,8 +262,8 @@
.markdown code { .markdown code {
margin: 0 3px; margin: 0 3px;
padding: 0 5px; padding: 0 5px;
background: #eee;
border-radius: 3px; border-radius: 3px;
background: #eee;
} }
.markdown strong, .markdown strong,
@@ -269,24 +272,24 @@
} }
.markdown > table { .markdown > table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%; width: 95%;
margin-bottom: 24px; margin-bottom: 24px;
border: 1px solid #e9e9e9;
border-spacing: 0;
border-collapse: collapse;
empty-cells: show;
} }
.markdown > table th { .markdown > table th {
white-space: nowrap;
color: #333; color: #333;
font-weight: 600; font-weight: 600;
white-space: nowrap;
} }
.markdown > table th, .markdown > table th,
.markdown > table td { .markdown > table td {
border: 1px solid #e9e9e9;
padding: 8px 16px; padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left; text-align: left;
} }
@@ -295,11 +298,11 @@
} }
.markdown blockquote { .markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0; margin: 1em 0;
padding-left: 0.8em;
border-left: 4px solid #e9e9e9;
color: #999;
font-size: 90%;
} }
.markdown blockquote p { .markdown blockquote p {
@@ -307,9 +310,9 @@
} }
.markdown .anchor { .markdown .anchor {
margin-left: 8px;
opacity: 0; opacity: 0;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
margin-left: 8px;
} }
.markdown .waiting { .markdown .waiting {
@@ -322,8 +325,8 @@
.markdown h4:hover .anchor, .markdown h4:hover .anchor,
.markdown h5:hover .anchor, .markdown h5:hover .anchor,
.markdown h6:hover .anchor { .markdown h6:hover .anchor {
opacity: 1;
display: inline-block; display: inline-block;
opacity: 1;
} }
.markdown > br, .markdown > br,
@@ -333,10 +336,10 @@
.hljs { .hljs {
display: block; display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto; overflow-x: auto;
padding: 0.5em;
background: white;
color: #333;
} }
.hljs-comment, .hljs-comment,
@@ -372,7 +375,7 @@
} }
.hljs-tag { .hljs-tag {
color: #333333; color: #333;
} }
.hljs-title, .hljs-title,
@@ -385,13 +388,13 @@
} }
.hljs-addition { .hljs-addition {
color: #55a532;
background-color: #eaffea; background-color: #eaffea;
color: #55a532;
} }
.hljs-deletion { .hljs-deletion {
color: #bd2c00;
background-color: #ffecec; background-color: #ffecec;
color: #bd2c00;
} }
.hljs-link { .hljs-link {
@@ -399,8 +402,10 @@
} }
/* 代码高亮 */ /* 代码高亮 */
/* PrismJS 1.15.0 /* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/** /**
* prism.js default theme for JavaScript, CSS and HTML * prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com) * Based on dabblet (http://dabblet.com)
@@ -408,21 +413,19 @@ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javasc
*/ */
code[class*='language-'], code[class*='language-'],
pre[class*='language-'] { pre[class*='language-'] {
color: black;
background: none; background: none;
text-shadow: 0 1px white; color: black;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 1.5;
text-align: left; text-align: left;
text-shadow: 0 1px white;
white-space: pre; white-space: pre;
word-spacing: normal; word-spacing: normal;
word-break: normal;
word-wrap: normal; word-wrap: normal;
line-height: 1.5; word-break: normal;
-moz-tab-size: 4; -moz-tab-size: 4;
-o-tab-size: 4; -o-tab-size: 4;
tab-size: 4; tab-size: 4;
-webkit-hyphens: none; -webkit-hyphens: none;
-moz-hyphens: none; -moz-hyphens: none;
-ms-hyphens: none; -ms-hyphens: none;
@@ -433,16 +436,16 @@ pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection, pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection, code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection { code[class*='language-'] ::-moz-selection {
text-shadow: none;
background: #b3d4fc; background: #b3d4fc;
text-shadow: none;
} }
pre[class*='language-']::selection, pre[class*='language-']::selection,
pre[class*='language-'] ::selection, pre[class*='language-'] ::selection,
code[class*='language-']::selection, code[class*='language-']::selection,
code[class*='language-'] ::selection { code[class*='language-'] ::selection {
text-shadow: none;
background: #b3d4fc; background: #b3d4fc;
text-shadow: none;
} }
@media print { @media print {
@@ -454,9 +457,9 @@ code[class*='language-'] ::selection {
/* Code blocks */ /* Code blocks */
pre[class*='language-'] { pre[class*='language-'] {
padding: 1em;
margin: 0.5em 0;
overflow: auto; overflow: auto;
margin: 0.5em 0;
padding: 1em;
} }
:not(pre) > code[class*='language-'], :not(pre) > code[class*='language-'],
@@ -510,8 +513,8 @@ pre[class*='language-'] {
.token.url, .token.url,
.language-css .token.string, .language-css .token.string,
.style .token.string { .style .token.string {
background: hsla(0deg, 0%, 100%, 0.5);
color: #9a6e3a; color: #9a6e3a;
background: hsla(0, 0%, 100%, 0.5);
} }
.token.atrule, .token.atrule,

View File

@@ -23,8 +23,8 @@
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script> <script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
<style> <style>
.main .logo { .main .logo {
margin-top: 0;
height: auto; height: auto;
margin-top: 0;
} }
.main .logo a { .main .logo a {
@@ -34,10 +34,10 @@
.main .logo .sub-title { .main .logo .sub-title {
margin-left: 0.5em; margin-left: 0.5em;
font-size: 22px;
color: #fff;
background: linear-gradient(-45deg, #3967ff, #b500fe); background: linear-gradient(-45deg, #3967ff, #b500fe);
-webkit-background-clip: text; -webkit-background-clip: text;
color: #fff;
font-size: 22px;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
</style> </style>

View File

@@ -1,5 +1,5 @@
@font-face { @font-face {
font-family: 'mobilefont'; /* Project id 4841764 */ font-family: mobilefont; /* Project id 4841764 */
src: src:
url('iconfont.woff2?t=1742102742566') format('woff2'), url('iconfont.woff2?t=1742102742566') format('woff2'),
url('iconfont.woff?t=1742102742566') format('woff'), url('iconfont.woff?t=1742102742566') format('woff'),
@@ -7,133 +7,133 @@
} }
.mobilefont { .mobilefont {
font-family: 'mobilefont' !important;
font-size: 16px;
font-style: normal; font-style: normal;
font-size: 16px;
font-family: mobilefont !important;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.mobilefont-rubber:before { .mobilefont-rubber::before {
content: '\ea15'; content: '\ea15';
} }
.mobilefont-huabi:before { .mobilefont-huabi::before {
content: '\e61f'; content: '\e61f';
} }
.mobilefont-chexiao:before { .mobilefont-chexiao::before {
content: '\e6e2'; content: '\e6e2';
} }
.mobilefont-shangchuan:before { .mobilefont-shangchuan::before {
content: '\e613'; content: '\e613';
} }
.mobilefont-qingkong:before { .mobilefont-qingkong::before {
content: '\e6dc'; content: '\e6dc';
} }
.mobilefont-jiacu:before { .mobilefont-jiacu::before {
content: '\e71d'; content: '\e71d';
} }
.mobilefont-qingxie:before { .mobilefont-qingxie::before {
content: '\e71e'; content: '\e71e';
} }
.mobilefont-xiahuaxian:before { .mobilefont-xiahuaxian::before {
content: '\e720'; content: '\e720';
} }
.mobilefont-tupian:before { .mobilefont-tupian::before {
content: '\e730'; content: '\e730';
} }
.mobilefont-del1:before { .mobilefont-del1::before {
content: '\e637'; content: '\e637';
} }
.mobilefont-gengduo:before { .mobilefont-gengduo::before {
content: '\e600'; content: '\e600';
} }
.mobilefont-delete1:before { .mobilefont-delete1::before {
content: '\e66d'; content: '\e66d';
} }
.mobilefont-del:before { .mobilefont-del::before {
content: '\e6f5'; content: '\e6f5';
} }
.mobilefont-option:before { .mobilefont-option::before {
content: '\e6ff'; content: '\e6ff';
} }
.mobilefont-setting:before { .mobilefont-setting::before {
content: '\e633'; content: '\e633';
} }
.mobilefont-add:before { .mobilefont-add::before {
content: '\e686'; content: '\e686';
} }
.mobilefont-radiobox:before { .mobilefont-radiobox::before {
content: '\e75b'; content: '\e75b';
} }
.mobilefont-juzhendafen:before { .mobilefont-juzhendafen::before {
content: '\e641'; content: '\e641';
} }
.mobilefont-checkbox-checked:before { .mobilefont-checkbox-checked::before {
content: '\e6c3'; content: '\e6c3';
} }
.mobilefont-nps:before { .mobilefont-nps::before {
content: '\e6b0'; content: '\e6b0';
} }
.mobilefont-input:before { .mobilefont-input::before {
content: '\e6fd'; content: '\e6fd';
} }
.mobilefont-juzhentiankong:before { .mobilefont-juzhentiankong::before {
content: '\e62e'; content: '\e62e';
} }
.mobilefont-wenjianshangchuan:before { .mobilefont-wenjianshangchuan::before {
content: '\e631'; content: '\e631';
} }
.mobilefont-qianming:before { .mobilefont-qianming::before {
content: '\e661'; content: '\e661';
} }
.mobilefont-tuwen:before { .mobilefont-tuwen::before {
content: '\e62c'; content: '\e62c';
} }
.mobilefont-juzhenduoxuan:before { .mobilefont-juzhenduoxuan::before {
content: '\e818'; content: '\e818';
} }
.mobilefont-juzhendanxuan:before { .mobilefont-juzhendanxuan::before {
content: '\13c7f'; content: '\13c7f';
} }
.mobilefont-edit2:before { .mobilefont-edit2::before {
content: '\e630'; content: '\e630';
} }
.mobilefont-copy:before { .mobilefont-copy::before {
content: '\e632'; content: '\e632';
} }
.mobilefont-delete:before { .mobilefont-delete::before {
content: '\e63f'; content: '\e63f';
} }
.mobilefont-sort:before { .mobilefont-sort::before {
content: '\e6a0'; content: '\e6a0';
} }

View File

@@ -19,13 +19,15 @@ import appBridge from '@/assets/js/appBridge';
const router = useRouter(); const router = useRouter();
function goBack() { function goBack() {
appBridge.h5RouterBack(router); if (window.history.length > 1) {
router.go(-1);
} else {
appBridge.navigateBack();
}
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.common-layout { .common-layout {
//min-height: calc(100vh);
background-color: white; background-color: white;
color: #333; color: #333;
} }

View File

@@ -1,2 +1,2 @@
export const surveyQuestion = export const surveyQuestion
'https://yls-api-uat.dctest.digitalyili.com/api/console/surveys/RWNK9BYp/questions'; = 'https://yls-api-uat.dctest.digitalyili.com/api/console/surveys/RWNK9BYp/questions';

View File

@@ -38,10 +38,10 @@ service.interceptors.request.use(
service.interceptors.response.use( service.interceptors.response.use(
(response) => { (response) => {
if ( if (
response.status === 200 || response.status === 200
response.status === 201 || || response.status === 201
response.status === 202 || || response.status === 202
response.status === 204 || response.status === 204
) { ) {
if (response.config.method === 'put') { if (response.config.method === 'put') {
// message.success('保存中...'); // message.success('保存中...');

View File

@@ -43,10 +43,10 @@ service.interceptors.request.use(
service.interceptors.response.use( service.interceptors.response.use(
(response) => { (response) => {
if ( if (
response.status === 200 || response.status === 200
response.status === 201 || || response.status === 201
response.status === 202 || || response.status === 202
response.status === 204 || response.status === 204
) { ) {
if (response.config.method === 'put') { if (response.config.method === 'put') {
// message.success('保存中...'); // message.success('保存中...');

View File

@@ -52,8 +52,8 @@
<martrix-question <martrix-question
v-if=" v-if="
element.question_type === 8 || element.question_type === 8 ||
element.question_type === 9 || element.question_type === 9 ||
element.question_type === 10 element.question_type === 10
" "
:element="computedElement(element)" :element="computedElement(element)"
:index="index" :index="index"
@@ -77,7 +77,7 @@
:index="index" :index="index"
:active="chooseQuestionId === element.id" :active="chooseQuestionId === element.id"
@update:element="updateElement" @update:element="updateElement"
></file-upload> />
<!--图文--> <!--图文-->
<TextWithImages <TextWithImages

View File

@@ -134,23 +134,23 @@ const openOptionActionModel = (item, index) => {
// 上下移动 // 上下移动
const optionMove = (action) => { const optionMove = (action) => {
switch (action.action) { switch (action.action) {
case 'up': case 'up':
if (activeIndex.value === 0) { if (activeIndex.value === 0) {
moveShow.value = false; moveShow.value = false;
return false; return false;
} }
// 向上移动 // 向上移动
element.value.splice(activeIndex.value - 1, 0, element.value.splice(activeIndex.value, 1)[0]); element.value.splice(activeIndex.value - 1, 0, element.value.splice(activeIndex.value, 1)[0]);
activeIndex.value -= 1; activeIndex.value -= 1;
break; break;
case 'down': case 'down':
if (activeIndex.value === element.value.length - 1) { if (activeIndex.value === element.value.length - 1) {
moveShow.value = false; moveShow.value = false;
return false; return false;
} }
element.value.splice(activeIndex.value + 1, 0, element.value.splice(activeIndex.value, 1)[0]); element.value.splice(activeIndex.value + 1, 0, element.value.splice(activeIndex.value, 1)[0]);
activeIndex.value += 1; activeIndex.value += 1;
break; break;
} }
}; };

View File

@@ -260,8 +260,8 @@ const getSkipTypeText = (skipType) => {
const ls = []; const ls = [];
logics.map((item) => { logics.map((item) => {
if ( if (
item.skip_type === skipType && item.skip_type === skipType
item.question_index === activeQuestion.value.question_index && item.question_index === activeQuestion.value.question_index
) { ) {
ls.push(item); ls.push(item);
} }
@@ -277,13 +277,13 @@ const getSkipTypeText = (skipType) => {
const questionSetting = (type) => { const questionSetting = (type) => {
switch (type) { switch (type) {
case 'before': case 'before':
questionBeforeShow.value = true; questionBeforeShow.value = true;
break; break;
case 'after': case 'after':
questionBeforeShow.value = true; questionBeforeShow.value = true;
break; break;
} }
skipType.value = type === 'before' ? 1 : 0; skipType.value = type === 'before' ? 1 : 0;
}; };

View File

@@ -144,8 +144,8 @@
<BeforeRate <BeforeRate
v-if=" v-if="
log.logic !== 'always' && log.logic !== 'always' &&
log.is_answer !== 0 && log.is_answer !== 0 &&
[5, 106].includes(log.question_type) [5, 106].includes(log.question_type)
" "
:activeQuestion="activeQuestion" :activeQuestion="activeQuestion"
:logic="log" :logic="log"

View File

@@ -108,9 +108,9 @@ function isSurplus() {
return false; return false;
} else { } else {
return ( return (
parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10)) % parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10))
parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10)) === % parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10))
0 === 0
); );
} }
} }

View File

@@ -1,47 +1,31 @@
<template> <template>
<van-field <van-field
v-model="element.stem" v-model="element.stem" :label="element.stem" :required="element.config.is_required === 1" label-align="top"
:label="element.stem"
:required="element.config.is_required === 1"
label-align="top"
class="contenteditable-question-title base-select" class="contenteditable-question-title base-select"
> >
<template #left-icon> <template #left-icon>
{{ index + 1 }} {{ index + 1 }}
</template> </template>
<template #label> <template #label>
<contenteditable <contenteditable v-model="element.stem" className="contenteditable-label" :active="active" @blur="emitValue">
v-model="element.stem" </contenteditable>
className="contenteditable-label"
:active="active"
@blur="emitValue"
></contenteditable>
</template> </template>
<template #input> <template #input>
<template v-for="(item, optionIndex) in element.options" :key="item.id"> <template v-for="(item, optionIndex) in element.options" :key="item.id">
<van-radio-group v-if="element.question_type === 1" v-model="choiceValue"> <van-radio-group v-if="element.question_type === 1" v-model="choiceValue">
<option-action <option-action
v-model:data="element.options[optionIndex]" :data="isPreview ? item.options : item" :active="active" :question="element"
:active="active"
:question="element"
handle=".moverQues" handle=".moverQues"
> >
<template #item="{ element: it, index: itIndex }"> <template #item="{ element: it, index: itIndex }">
<van-radio <van-radio
:key="itIndex" :key="itIndex" :name="it.option_index" :label="it.label" :disabled="it.disabled"
:name="it.option_index"
:label="it.label"
:disabled="it.disabled"
icon-size="0.45rem" icon-size="0.45rem"
> >
<!-- 自定义文本 --> <!-- 自定义文本 -->
<template #default> <template #default>
<div class="flex align-center van-cell"> <div class="flex align-center van-cell">
<contenteditable <contenteditable v-model="it.option" className="contenteditable-input" :active="active">
v-model="it.option"
className="contenteditable-input"
:active="active"
>
<template #right-icon> <template #right-icon>
<div v-if="active" class="moverQues"> <div v-if="active" class="moverQues">
<van-icon class-prefix="mobilefont" name="option "></van-icon> <van-icon class-prefix="mobilefont" name="option "></van-icon>
@@ -60,26 +44,17 @@
<van-checkbox-group v-if="element.question_type === 2" v-model="value" shape="square"> <van-checkbox-group v-if="element.question_type === 2" v-model="value" shape="square">
<option-action <option-action
v-model:data="element.options[optionIndex]" v-model:data="element.options[optionIndex]" handle=".moverQues" :active="active"
handle=".moverQues"
:active="active"
:question="element" :question="element"
> >
<template #item="{ element: it, index: itIndex }"> <template #item="{ element: it, index: itIndex }">
<van-checkbox <van-checkbox
:key="itIndex" :key="itIndex" :name="it.option_index" :label="it.label" :disabled="it.disabled"
:name="it.option_index"
:label="it.label"
:disabled="it.disabled"
icon-size="0.45rem" icon-size="0.45rem"
> >
<template #default> <template #default>
<div class="flex align-center van-cell"> <div class="flex align-center van-cell">
<contenteditable <contenteditable v-model="it.option" className="contenteditable-input" :active="active">
v-model="it.option"
className="contenteditable-input"
:active="active"
>
<template #right-icon> <template #right-icon>
<div v-if="active" class="moverQues"> <div v-if="active" class="moverQues">
<van-icon class-prefix="mobilefont" name="option "></van-icon> <van-icon class-prefix="mobilefont" name="option "></van-icon>
@@ -103,7 +78,12 @@
import OptionAction from '@/views/Design/components/ActionCompoents/OptionAction.vue'; import OptionAction from '@/views/Design/components/ActionCompoents/OptionAction.vue';
import { defineAsyncComponent, toRefs, ref } from 'vue'; import { defineAsyncComponent, toRefs, ref } from 'vue';
const choiceValue = ref('checked'); // 是否是预览
const isPreview = defineModel('isPreview', { default: false });
const choiceValue = defineModel('answer', { default: '1', type: String });
console.log(`choiceValue.value`, choiceValue.value);
const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue')); const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue'));
const props = defineProps({ const props = defineProps({
element: { element: {

View File

@@ -18,6 +18,7 @@
</template> </template>
<template #input> <template #input>
<textarea <textarea
v-model="completionValue"
class="other_input" class="other_input"
:placeholder="element.config.placeholder" :placeholder="element.config.placeholder"
:rows="element.config.line_height" :rows="element.config.line_height"
@@ -29,6 +30,8 @@
<script setup> <script setup>
import { toRefs } from 'vue'; import { toRefs } from 'vue';
const completionValue = defineModel('completionValue', { default: '' });
const props = defineProps({ const props = defineProps({
element: { element: {
type: Object, type: Object,

View File

@@ -21,14 +21,14 @@ const { element } = toRefs(props);
*/ */
const tableInputTypeMapping = (/** regx?: any */) => { const tableInputTypeMapping = (/** regx?: any */) => {
switch (element.value.question_type) { switch (element.value.question_type) {
case 8: case 8:
return 'text'; return 'text';
case 9: case 9:
return 'radio'; return 'radio';
case 10: case 10:
return 'checkbox'; return 'checkbox';
default: default:
return 'radio'; return 'radio';
} }
}; };

View File

@@ -3,29 +3,23 @@
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<td v-for="col in columns" :key="col.option"> <td v-for="col in cols" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 --> <!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input <input
v-if="col.editor" v-if="col.editor" v-model="col.option" v-focus type="text" @focusout="col.editor = false"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)" @click="handleRowNameChange(col.option!)"
/> />
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option" /> <span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
</td> </td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex"> <tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option" /> <th v-html="row.option"></th>
<td v-for="(col, colIndex) in columns" :key="colIndex"> <td v-for="(col, colIndex) in cols" :key="colIndex">
<input <input
type="checkbox" type="checkbox" :name="`R${rowIndex + 1}`" :value="`${rowIndex + 1}_${colIndex + 1}`"
:value="`${rowIndex + 1}_${colIndex + 1}`" :checked="isOptionChecked(rowIndex, colIndex)" @change="handleMatrixRadioChange(rowIndex, colIndex)"
:checked="isOptionChecked(rowIndex, colIndex)"
@change="handleColNameChange(row.option, col.option, $event)"
/> />
</td> </td>
</tr> </tr>
@@ -37,50 +31,73 @@
import { defineProps } from 'vue'; import { defineProps } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus'; import { vFocus } from '@/utils/directives/useVFocus';
const props = defineProps<{ // 记录行和列的索引
rows: { option: string }[]; const rowRecord = defineModel<number[][]>('rowRecord', { required: false, default: () => [] });
columns: { option: string; editor?: boolean }[]; // const matrixAnswer = defineModel<{ [key: string]: 1 }>('matrixAnswer', { required: false, default: () => ({}) });
questionType: number; // 检查 rowRecord 是否存在
matrixAnswer: { [key: string]: any }; console.log(`rowRecord:`, rowRecord.value);
rowRecord: (number | string)[];
/* const isPreview = */defineModel<boolean>('isPreview', { required: false, default: false });
defineProps<{
rows: OptionType[];
cols: OptionType[];
}>(); }>();
/* const emits = */ defineEmits(['update:matrixAnswer', 'update:rowRecord']); // const emits = defineEmits(['update:matrixAnswer', 'update:rowRecord']);
// 判断是否选中
const isOptionChecked = (rowIndex: number, colIndex: number): boolean => { const isOptionChecked = (rowIndex: number, colIndex: number): boolean => {
const key = `R${rowIndex + 1}_C${colIndex + 1}`; // [
return !!props.matrixAnswer[key]; // [0, 1],
// [0, 1]
// ]
if (!rowRecord.value[rowIndex]) {
return false;
}
return rowRecord.value[rowIndex].includes(colIndex);
}; };
const handleRowNameChange = () => { const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑 // 你可以在这里添加其他逻辑
}; };
// const handleColNameChange = (rowOption: string, colOption: string, e: Event) => { // 当 matrix radio 选中时,更新 rowRecord 和 matrixAnswer
// const target = e.target as HTMLInputElement; function handleMatrixRadioChange(row: number, col: number) {
// const col = props.columns.findIndex(option => option.option === colOption); // 获取 colIndexArray
// const row = props.rows.findIndex(option => option.option === rowOption); if (!rowRecord.value[row]) {
// // 如果没有对应的row创建一个
// if (props.questionType === 10) { rowRecord.value[row] = [];
// if (target.checked) { }
// props.rowRecord[col] = (props.rowRecord[col] || []).concat(row + 1); // cols 的逻辑 和 handleMatrixRadioChange 一致
// } else { const cols = rowRecord.value[row];
// props.rowRecord[col] = (props.rowRecord[col] || []).filter(item => item !== row + 1);
// } // 检查 cols 对应的 col 是否有 数值
// const newMatrixAnswer: { [key: string]: boolean } = {}; if (cols.includes(col)) {
// const newRowRecord: (number | string)[] = [...props.rowRecord]; // 如果有,删除
// props.rows.forEach((rowOption, rowIndex) => { cols.splice(cols.indexOf(col), 1);
// const colOptions = newRowRecord[rowIndex]; } else {
// if (colOptions) { // 如果没有,添加
// colOptions.forEach((col: any) => { cols.push(col);
// newMatrixAnswer[`R${rowIndex + 1}_C${col}`] = true; }
// });
// } console.log(`rowRecord:`, rowRecord.value);
// }); }
// } // const handleColNameChange = (rowOption: string, colOption: string) => {
// // // const target = e.target as HTMLInputElement;
// emits('update:matrixAnswer', newMatrixAnswer); // const col = props.columns.findIndex((option) => option.option === colOption);
// emits('update:rowRecord', newRowRecord); // const row = props.rows.findIndex((option) => option.option === rowOption);
// if (props.questionType === 9) {
// props.rowRecord[col] = row + 1;
// props.matrixAnswer = {};
// props.rowRecord.forEach((row, index) => {
// props.matrixAnswer[`${index + 1}_${row}`] = 1;
// });
// }
// emits('update:matrixAnswer', props.matrixAnswer);
// emits('update:rowRecord', props.rowRecord);
// }; // };
</script> </script>

View File

@@ -23,14 +23,14 @@ const matrixAnswer = ref({
*/ */
const tableInputTypeMapping = (/** regx?: any */) => { const tableInputTypeMapping = (/** regx?: any */) => {
switch (element.question_type) { switch (element.question_type) {
case 8: case 8:
return 'text'; return 'text';
case 9: case 9:
return 'radio'; return 'radio';
case 10: case 10:
return 'checkbox'; return 'checkbox';
default: default:
return 'radio'; return 'radio';
} }
}; };
@@ -67,55 +67,55 @@ function handleColNameChange(rowOption: string, colOption: string, e: any) {
// 不同的 question_type 的 matrix 问卷处理不同的结果 // 不同的 question_type 的 matrix 问卷处理不同的结果
switch (element.question_type) { switch (element.question_type) {
case 8: { case 8: {
// 获取输入框元素 // 获取输入框元素
const inputElement = e.target as HTMLInputElement; const inputElement = e.target as HTMLInputElement;
// 如果没有获取到输入框元素,则直接返回 // 如果没有获取到输入框元素,则直接返回
if (!inputElement) return; if (!inputElement) return;
// 将输入框的值保存到 rowRecord 对应位置 // 将输入框的值保存到 rowRecord 对应位置
rowRecord[col] = e!.target!.value; rowRecord[col] = e!.target!.value;
// 清空 matrixAnswer 的 answer 属性 // 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {}; matrixAnswer.value.answer = {};
// 遍历所有行选项 // 遍历所有行选项
element.options[0].forEach((_, rowIndex) => { element.options[0].forEach((_, rowIndex) => {
// 获取当前行记录 // 获取当前行记录
const colOptions = rowRecord[rowIndex]; const colOptions = rowRecord[rowIndex];
// 如果当前行有记录,则更新 matrixAnswer 的 answer 属性 // 如果当前行有记录,则更新 matrixAnswer 的 answer 属性
if (colOptions) { if (colOptions) {
matrixAnswer.value.answer[`R${rowIndex + 1}_C${col + 1}`] = colOptions; matrixAnswer.value.answer[`R${rowIndex + 1}_C${col + 1}`] = colOptions;
} }
}); });
break; break;
} }
case 9: case 9:
// 将选择的行索引加1后保存到 rowRecord 对应位置 // 将选择的行索引加1后保存到 rowRecord 对应位置
rowRecord[col] = row + 1; rowRecord[col] = row + 1;
// 清空 matrixAnswer 的 answer 属性 // 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {}; matrixAnswer.value.answer = {};
// 遍历 rowRecord更新 matrixAnswer 的 answer 属性 // 遍历 rowRecord更新 matrixAnswer 的 answer 属性
rowRecord.forEach((row, index) => { rowRecord.forEach((row, index) => {
matrixAnswer.value.answer[`${index + 1}_${row}`] = 1; matrixAnswer.value.answer[`${index + 1}_${row}`] = 1;
}); });
break; break;
case 10: case 10:
// 将选择的行索引加1后添加到 rowRecord 对应位置的数组中 // 将选择的行索引加1后添加到 rowRecord 对应位置的数组中
rowRecord[col] = (rowRecord[col] || []).concat(row + 1); rowRecord[col] = (rowRecord[col] || []).concat(row + 1);
// 清空 matrixAnswer 的 answer 属性 // 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {}; matrixAnswer.value.answer = {};
// 遍历所有行选项 // 遍历所有行选项
element.options[0].forEach((rowOption, rowIndex) => { element.options[0].forEach((rowOption, rowIndex) => {
// 获取当前行记录 // 获取当前行记录
const colOptions = rowRecord[rowIndex]; const colOptions = rowRecord[rowIndex];
// 如果当前行有记录,则更新 matrixAnswer 的 answer 属性 // 如果当前行有记录,则更新 matrixAnswer 的 answer 属性
if (colOptions) { if (colOptions) {
colOptions.forEach((col: any) => { colOptions.forEach((col: any) => {
matrixAnswer.value.answer[`R${rowIndex + 1}_C${col}`] = true; matrixAnswer.value.answer[`R${rowIndex + 1}_C${col}`] = true;
}); });
} }
}); });
break; break;
default: default:
break; break;
} }
} }
</script> </script>

View File

@@ -3,14 +3,10 @@
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<td v-for="col in columns" :key="col.option"> <td v-for="col in cols" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 --> <!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input <input
v-if="col.editor" v-if="col.editor" v-model="col.option" v-focus type="text" @focusout="col.editor = false"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)" @click="handleRowNameChange(col.option!)"
/> />
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span> <span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
@@ -20,13 +16,10 @@
<tbody> <tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex"> <tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option"></th> <th v-html="row.option"></th>
<td v-for="(col, colIndex) in columns" :key="colIndex"> <td v-for="(col, colIndex) in cols" :key="colIndex">
<input <input
type="radio" type="radio" :name="`R${rowIndex + 1}`" :value="`${rowIndex + 1}_${colIndex + 1}`"
:name="`R${rowIndex + 1}`" :checked="isOptionChecked(rowIndex, colIndex)" @change="handleMatrixRadioChange(rowIndex, colIndex)"
:value="`${rowIndex + 1}_${colIndex + 1}`"
:checked="isOptionChecked(rowIndex, colIndex)"
@change="handleColNameChange(row.option, col.option, $event)"
/> />
</td> </td>
</tr> </tr>
@@ -38,19 +31,28 @@
import { defineProps } from 'vue'; import { defineProps } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus'; import { vFocus } from '@/utils/directives/useVFocus';
const props = defineProps<{ // 记录行和列的索引
rows: { option: string }[]; const rowRecord = defineModel<number[]>('rowRecord', { required: false, default: () => [] });
columns: { option: string; editor?: boolean }[]; // const matrixAnswer = defineModel<{ [key: string]: 1 }>('matrixAnswer', { required: false, default: () => ({}) });
questionType: number; // 检查 rowRecord 是否存在
matrixAnswer: { [key: string]: any }; console.log(`rowRecord:`, rowRecord.value);
rowRecord: (number | string)[];
/* const isPreview = */defineModel<boolean>('isPreview', { required: false, default: false });
defineProps<{
rows: OptionType[];
cols: OptionType[];
}>(); }>();
/* const emits = */ defineEmits(['update:matrixAnswer', 'update:rowRecord']); // const emits = defineEmits(['update:matrixAnswer', 'update:rowRecord']);
// 判断是否选中
const isOptionChecked = (rowIndex: number, colIndex: number): boolean => { const isOptionChecked = (rowIndex: number, colIndex: number): boolean => {
const key = `R${rowIndex + 1}_C${colIndex + 1}`; // console.log(`rowIndex: ${rowIndex}, colIndex: ${colIndex}`);
return !!props.matrixAnswer[key]; // console.log(`rowRecord.value[rowIndex] === colIndex`, rowRecord.value[rowIndex] === colIndex);
return rowRecord.value[rowIndex] === colIndex;
// const key = `R${rowIndex + 1}_C${colIndex + 1}`;
// return !!matrixAnswer.value?.[key];
}; };
const handleRowNameChange = (/* value: string */) => { const handleRowNameChange = (/* value: string */) => {
@@ -58,21 +60,29 @@ const handleRowNameChange = (/* value: string */) => {
// 你可以在这里添加其他逻辑 // 你可以在这里添加其他逻辑
}; };
// 当 matrix radio 选中时,更新 rowRecord 和 matrixAnswer
function handleMatrixRadioChange(row: number, col: number) {
rowRecord.value[row] = col;
// matrixAnswer.value = {};
// rowRecord.value.forEach((row, col) => {
// matrixAnswer.value[`${col + 1}_${row}`] = 1;
// });
}
// const handleColNameChange = (rowOption: string, colOption: string) => { // const handleColNameChange = (rowOption: string, colOption: string) => {
// // const target = e.target as HTMLInputElement; // // const target = e.target as HTMLInputElement;
// const col = props.columns.findIndex((option) => option.option === colOption); // const col = props.columns.findIndex((option) => option.option === colOption);
// const row = props.rows.findIndex((option) => option.option === rowOption); // const row = props.rows.findIndex((option) => option.option === rowOption);
//
// if (props.questionType === 9) { // if (props.questionType === 9) {
// props.rowRecord[col] = row + 1; // props.rowRecord[col] = row + 1;
// props.matrixAnswer = {}; // props.matrixAnswer = {};
// props.rowRecord.forEach((row, index) => { // props.rowRecord.forEach((row, index) => {
// props.matrixAnswer[`${index + 1}_${row}`] = 1; // props.matrixAnswer[`${index + 1}_${row}`] = 1;
// }); // });
// } // }
//
// emits('update:matrixAnswer', props.matrixAnswer); // emits('update:matrixAnswer', props.matrixAnswer);
// emits('update:rowRecord', props.rowRecord); // emits('update:rowRecord', props.rowRecord);
// }; // };
</script> </script>

View File

@@ -3,14 +3,10 @@
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<td v-for="col in columns" :key="col.option"> <td v-for="col in cols" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 --> <!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input <input
v-if="col.editor" v-if="col.editor" v-model="col.option" v-focus type="text" @focusout="col.editor = false"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)" @click="handleRowNameChange(col.option!)"
/> />
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span> <span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
@@ -20,11 +16,10 @@
<tbody> <tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex"> <tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option"></th> <th v-html="row.option"></th>
<td v-for="(col, colIndex) in columns" :key="colIndex"> <td v-for="(col, colIndex) in cols" :key="colIndex">
<input <input
type="text" type="text" :name="`R${rowIndex + 1}`" :value="getInputValue(rowIndex, colIndex)"
:value="matrixAnswer[`${rowIndex + 1}_${colIndex + 1}`]" @change="handleMatrixTextChange(rowIndex, colIndex, $event)"
@change="handleColNameChange(row.option, col.option, $event, rowIndex, colIndex)"
/> />
</td> </td>
</tr> </tr>
@@ -33,36 +28,66 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, defineEmits } from 'vue'; import { defineProps } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus'; import { vFocus } from '@/utils/directives/useVFocus';
const props = defineProps<{ // 记录行和列的索引
rows: { option: string }[]; const rowRecord = defineModel<string[][]>('rowRecord', { required: false, default: () => [] });
columns: { option: string; editor?: boolean }[]; // const matrixAnswer = defineModel<{ [key: string]: 1 }>('matrixAnswer', { required: false, default: () => ({}) });
questionType: number; // 检查 rowRecord 是否存在
matrixAnswer: { [key: string]: any }; console.log(`rowRecord:`, rowRecord.value);
/* const isPreview = */defineModel<boolean>('isPreview', { required: false, default: false });
defineProps<{
rows: OptionType[];
cols: OptionType[];
}>(); }>();
const emits = defineEmits(['update:matrixAnswer']); // const emits = defineEmits(['update:matrixAnswer', 'update:rowRecord']);
const handleRowNameChange = (/* value: string */) => { const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`); // console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑 // 你可以在这里添加其他逻辑
}; };
const handleColNameChange = ( function getInputValue(row: number, col: number) {
rowOption: string, console.log(`row: ${row}, col: ${col}`);
colOption: string, console.log(`rowRecord:`, rowRecord.value);
e: Event,
rowIndex: number,
colIndex: number
) => {
const target = e.target as HTMLInputElement;
const key = `R${rowIndex + 1}_C${colIndex + 1}`;
const newMatrixAnswer = { ...props.matrixAnswer, [key]: target.value };
emits('update:matrixAnswer', newMatrixAnswer); return rowRecord.value?.[row]?.[col] ?? '';
}; }
// 当 matrix text 选中时,更新 rowRecord 和 matrixAnswer
function handleMatrixTextChange(row: number, col: number, e: Event) {
const target = e.target as HTMLInputElement;
const inputValue = target.value;
// 获取 colIndexArray
if (!rowRecord.value[row]) {
// 如果没有对应的row创建一个
rowRecord.value[row] = [];
}
// cols 的逻辑 和 handleMatrixRadioChange 一致
const cols = rowRecord.value[row];
cols[col] = inputValue;
console.log(`rowRecord:`, rowRecord.value);
}
// const handleColNameChange = (rowOption: string, colOption: string) => {
// // const target = e.target as HTMLInputElement;
// const col = props.columns.findIndex((option) => option.option === colOption);
// const row = props.rows.findIndex((option) => option.option === rowOption);
// if (props.questionType === 9) {
// props.rowRecord[col] = row + 1;
// props.matrixAnswer = {};
// props.rowRecord.forEach((row, index) => {
// props.matrixAnswer[`${index + 1}_${row}`] = 1;
// });
// }
// emits('update:matrixAnswer', props.matrixAnswer);
// emits('update:rowRecord', props.rowRecord);
// };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,9 +1,7 @@
<template> <template>
<div class="content"> <div class="content">
<van-field <van-field
v-model="element.stem" v-model="element.stem" :label="element.stem" :required="element.config.is_required === 1"
:label="element.stem"
:required="element.config.is_required === 1"
label-align="top" label-align="top"
> >
<template #left-icon> <template #left-icon>
@@ -13,12 +11,13 @@
<contenteditable v-model="element.stem" :active="active" @blur="saveStem"></contenteditable> <contenteditable v-model="element.stem" :active="active" @blur="saveStem"></contenteditable>
</template> </template>
<template #input> <template #input>
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex"> <div v-for="(optionItem, optionItemIndex) in isPreview ? element.list : element.options" :key="optionItemIndex">
<div <div
v-for="(item, optionIndex) in optionItem" v-for="(item, optionIndex) in isPreview ? optionItem.options : optionItem" :key="optionIndex"
:key="optionIndex"
@click="chooseOption(item)" @click="chooseOption(item)"
> >
<RateCharacter v-model="rate" :index="optionIndex" :config="element.config" @change="handleRateChange">
</RateCharacter>
<div class="tips"> <div class="tips">
<p>{{ element.config.prompt_left }}</p> <p>{{ element.config.prompt_left }}</p>
<p>{{ element.config.prompt_center }}</p> <p>{{ element.config.prompt_center }}</p>
@@ -36,6 +35,7 @@
import { ref, toRefs } from 'vue'; import { ref, toRefs } from 'vue';
import RateCharacter from './RateCharacter.vue'; import RateCharacter from './RateCharacter.vue';
const isPreview = defineModel('isPreview', { default: false });
const props = defineProps({ const props = defineProps({
element: { element: {
type: Object type: Object
@@ -51,6 +51,36 @@ const props = defineProps({
sn: { type: String, default: '' }, sn: { type: String, default: '' },
questionType: { type: [String, Number], default: 4 } questionType: { type: [String, Number], default: 4 }
}); });
// answer 的答案以 矩阵形式存储, 例如 [4,7],上层更新答案的时候也容易
const rates = defineModel('rates', { default: [] });
const rate = ref(0);
// 不知道的 BUG ,开始的时候不能重置颜色。 故如此
setTimeout(() => {
rate.value = localStorage.getItem(props.sn);
console.log(`rate value:`, rate.value);
// if (rates.value[0] !== undefined) {
// console.log(`rates value:`, rates.value);
// rate.value = rates.value[0]
// }
// else return
}, 1000);
/**
*
* @param index {number} 索引
* @param rate {number} 具体数值
*/
function handleRateChange(index, rate) {
// 如果没有查询到对应索引的数值, 那么就直接push一个直到有数值为止
while (rates.value.length < index) {
rates.value.push(NaN);
}
rates.value[index] = rate;
localStorage.setItem(props.sn, rate.value);
}
const { element } = toRefs(props); const { element } = toRefs(props);
const chooseId = ref(''); const chooseId = ref('');
const emit = defineEmits(['update:element']); const emit = defineEmits(['update:element']);

View File

@@ -1,11 +1,8 @@
<template> <template>
<div> <div>
<ul> <ul :key="test">
<li <li
v-for="(rate, rateIndex) in rateItem" v-for="(rate, rateIndex) in rateItem" :key="rateIndex" class="rate_item" :class="{ active_item: rate.active }"
:key="rateIndex"
class="rate_item"
:class="{ active_item: rate.active }"
@click="getItem(rate)" @click="getItem(rate)"
> >
{{ rate.label }} {{ rate.label }}
@@ -40,6 +37,11 @@ const rateItem = ref([
} }
]); ]);
const test = ref(1);
setTimeout(() => {
test.value = 2;
}, 300);
const props = defineProps({ const props = defineProps({
config: { config: {
type: Object, type: Object,
@@ -49,6 +51,8 @@ const props = defineProps({
const model = defineModel(); const model = defineModel();
const index = defineModel('index');
const emit = defineEmits(['change']);
const renderScore = (min, max, interval) => { const renderScore = (min, max, interval) => {
const result = []; const result = [];
for (let i = min; i <= max; i += interval) { for (let i = min; i <= max; i += interval) {
@@ -64,15 +68,18 @@ const renderScore = (min, max, interval) => {
rateItem.value = result; rateItem.value = result;
}; };
// 重置颜色 // 重置颜色
const getItem = (value) => { function getItem(value) {
model.value = value.label; model.value = value.label;
rateItem.value.forEach((item, index) => { rateItem.value.forEach((item, index) => {
rateItem.value[index].active = item.label <= value.label; rateItem.value[index].active = item.label <= value.label;
}); });
}; }
watch(model, () => { watch(model, () => {
getItem({ label: model.value, active: false }); getItem({ label: model.value, active: false });
emit('change', index.value, model.value);
}, {
// immediate: true
}); });
// 监听 min、max 和 score_interval 的变化 // 监听 min、max 和 score_interval 的变化

View File

@@ -11,8 +11,7 @@
class="iconfont active-icon" class="iconfont active-icon"
:style="{ marginRight: isLastPage ? '0' : '16px' }" :style="{ marginRight: isLastPage ? '0' : '16px' }"
@click="activePage" @click="activePage"
>&#xe86c;</i >&#xe86c;</i>
>
<template v-if="!isLastPage"> <template v-if="!isLastPage">
<i class="iconfont moverQues" style="margin-right: 16px">&#xe71b;</i> <i class="iconfont moverQues" style="margin-right: 16px">&#xe71b;</i>
<i class="iconfont" @click="deleteHandle">&#xe6c5;</i> <i class="iconfont" @click="deleteHandle">&#xe6c5;</i>

View File

@@ -38,11 +38,12 @@ type questionsList = {
}; };
type question = { type question = {
answer?: unknown;
id?: string; id?: string;
title?: string; title?: string;
stem?: string; stem?: string;
other?: string; other?: string;
list: questionsList[][]; list: questionsList[];
question_index?: number; question_index?: number;
question_type?: number; question_type?: number;
config?: Config; config?: Config;

View File

@@ -13,7 +13,7 @@ const router = useRouter();
const contentShow = ref(false); const contentShow = ref(false);
const show = ref(false); const show = ref(false);
onMounted(async () => { onMounted(async() => {
contentShow.value = true; contentShow.value = true;
// if (utils.getSessionStorage('xToken')) { // if (utils.getSessionStorage('xToken')) {
// const appToken = utils.getParameter('digitalYiliToken'); // const appToken = utils.getParameter('digitalYiliToken');

View File

@@ -46,7 +46,7 @@ import { consoleSurveys } from '@/api/home/index.js';
const survey = ref({ const survey = ref({
project_name: '' project_name: ''
}); });
const fetchSurveys = async () => { const fetchSurveys = async() => {
const params = { const params = {
page: 1, page: 1,
per_page: 10, per_page: 10,

View File

@@ -20,7 +20,7 @@ const marketList = ref([]);
const active = ref(null); const active = ref(null);
const marketInfo = ref([]); const marketInfo = ref([]);
const getTableList = async () => { const getTableList = async() => {
const res = await getListScene(); const res = await getListScene();
if (res.data.code === 0) { if (res.data.code === 0) {
res.data.data.forEach((item) => { res.data.data.forEach((item) => {
@@ -32,7 +32,7 @@ const getTableList = async () => {
getMarketInfo(marketList.value[0]); getMarketInfo(marketList.value[0]);
} }
}; };
const getMarketInfo = async (item) => { const getMarketInfo = async(item) => {
const code = marketList.value.filter((market, index) => item === index)[0].code; const code = marketList.value.filter((market, index) => item === index)[0].code;
const params = { const params = {
page: 1, page: 1,

View File

@@ -3,9 +3,7 @@
<div v-for="item in 10" :key="item" class="template"> <div v-for="item in 10" :key="item" class="template">
<img src="https://picsum.photos/131/128" width="110" height="100" alt="" /> <img src="https://picsum.photos/131/128" width="110" height="100" alt="" />
<span>报名/签到模板</span> <span>报名/签到模板</span>
<span style="color: rgb(127, 127, 127)" <span style="color: rgb(127, 127, 127)">报名签到 | 引用 {{ item }} | 创建人: {{ '张三' }}</span>
>报名签到 | 引用 {{ item }} | 创建人: {{ '张三' }}</span
>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -105,8 +105,7 @@ import { Io5EllipsisHorizontalSharp } from 'vue-icons-plus/io5';
top: 0; top: 0;
right: 10px; right: 10px;
padding: 28px 13px 19px 7px; padding: 28px 13px 19px 7px;
background: url('https://lanhu-oss-2537-2.lanhuapp.com/SketchPngbb370d01215f9cedc28d567c637c011047f83a99fbb5e7ac348ebd0ef0015f32') background: url('https://lanhu-oss-2537-2.lanhuapp.com/SketchPngbb370d01215f9cedc28d567c637c011047f83a99fbb5e7ac348ebd0ef0015f32') 100% no-repeat;
100% no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
opacity: 1; opacity: 1;
@@ -130,7 +129,7 @@ import { Io5EllipsisHorizontalSharp } from 'vue-icons-plus/io5';
font-size: 15px; font-size: 15px;
} }
& > :nth-child(2) { &> :nth-child(2) {
position: relative; position: relative;
left: 10px; left: 10px;
padding: 1px; padding: 1px;

View File

@@ -299,6 +299,7 @@
:config="question.config" :config="question.config"
:question="question" :question="question"
isMobile isMobile
:questionIndex="question.question_index"
@change-answer="onRelation($event, question)" @change-answer="onRelation($event, question)"
/> />
<!-- &lt;!&ndash; 地理位置题 &ndash;&gt;--> <!-- &lt;!&ndash; 地理位置题 &ndash;&gt;-->

View File

@@ -227,8 +227,8 @@ export default defineComponent({
.map((option) => { .map((option) => {
return this.wares.find( return this.wares.find(
(ware) => (ware) =>
ware.question_index === option.question_index && ware.question_index === option.question_index
ware.option_index === option.option_index && ware.option_index === option.option_index
); );
}); });
wares = wares.filter((x) => x); wares = wares.filter((x) => x);

View File

@@ -144,7 +144,7 @@ export default {
methods: { methods: {
debounce(func, wait) { debounce(func, wait) {
let timeout; let timeout;
return function (...args) { return function(...args) {
clearTimeout(timeout); clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait); timeout = setTimeout(() => func.apply(this, args), wait);
}; };

View File

@@ -1,341 +1,42 @@
<template> <template>
<van-field <choice v-model:answer="choiceValue" :element="question" :index="answerIndex" :is-preview="true" />
v-model="stem"
:label="element.stem"
:required="element.config.is_required === 1"
label-align="top"
class="base-select"
>
<template #label>
<contenteditable v-model="element.stem" :active="active" @blur="emitValue"></contenteditable>
</template>
<template #input>
<template v-for="listItem in list" :key="listItem.options">
<template v-for="(option, optionIndex) in listItem.options" :key="option.option">
<!-- 单选题 -->
<van-radio-group v-if="question?.question_type === 1" v-model="choiceValue">
<van-radio :name="optionIndex">{{ getDomText(option.option!) }}</van-radio>
</van-radio-group>
<!-- 多选题 -->
<van-checkbox-group
v-else-if="question!.question_type! === 2"
v-model="choiceValue"
shape="square"
>
<van-checkbox :name="optionIndex">{{ getDomText(option.option!) }}</van-checkbox>
</van-checkbox-group>
</template>
</template>
</template>
</van-field>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineAsyncComponent, ref, watch } from 'vue'; import { watch, ref } from 'vue';
import { getDomText } from '@/utils/utils'; import Choice from '@/views/Design/components/Questions/Choice.vue';
type answerType = {
options: any[];
value: number;
};
// // 预览新增 v-model // // 预览新增 v-model
const answer = defineModel('answer'); const answer = defineModel<answerType>('answer', { default: undefined });
// const answerIndex = defineModel('answerIndex'); const answerIndex = defineModel<number>('answerIndex');
const stem = defineModel('stem'); // const stem = defineModel<string>('stem', { default: '' });4
const question = defineModel<question>('question', {}); const question = defineModel<question>('question', { default: { config: { is_required: false } } });
const list = defineModel<questionsList[]>('list'); const list = defineModel<questionsList[]>('list', { default: [[]] });
const choiceValue = ref<string>((answer.value?.value - 1).toString() ?? '0');
// 初始化数据,因为 preview 的数据源和 element 的数据源不相同, 所以需要配置一遍数据
initData();
function initData() {
question.value.options = list.value;
}
// // 预览新增 emit ['changeAnswer', 'previous', 'next'] // // 预览新增 emit ['changeAnswer', 'previous', 'next']
const emit = defineEmits(['update:element', 'changeAnswer', 'previous', 'next', 'update:element']); const emit = defineEmits(['update:element', 'changeAnswer', 'previous', 'next', 'update:element']);
// 用戶選擇的答案 watch(
const choiceValue = ref(answer.value?.value - 1 ?? undefined); () => choiceValue.value,
const Contenteditable = defineAsyncComponent(() => import('@/components/contenteditable.vue')); () => {
const res = {
// console.log(`choice question.value:`, question.value); options: list.value.flatMap((group) => group.options || []),
watch(choiceValue, () => { value: Number(choiceValue.value) + 1
answer.value = { };
options: list.value.flatMap((group) => group.options || []), answer.value = res;
value: choiceValue.value + 1 emit('changeAnswer', res);
}; }
// console.log(answer.value); );
// 需要在 question 里面附加 answer 信息
// question.value[`answer`] =
emit('changeAnswer', {
options: list.value.flatMap((group) => group.options || []),
value: choiceValue.value + 1
});
});
// console.log(`question:`, question.value);
// console.log(`list: `, list.value);
// const props = defineProps({
// // 预览新增 props
// config: {
// type: Object,
// default: () => {
// return {};
// }
// },
// hideOptions: {
// type: Object,
// default: () => {
// return {};
// }
// },
// answerSn: {
// type: String,
// default: ''
// },
// answerSurveySn: {
// type: String,
// default: ''
// },
// // 原先 props
// element: {
// type: Object,
// default: () => {
// return {
// config: {
// is_required: undefined
// }
// };
// }
// },
// active: {
// type: Boolean,
// default: false
// },
// index: {
// type: Number,
// default: 0
// }
// });
// initData();
//
// function initData() {
// // 当预览时, 某些数据可能不存在
// // 若 element有些数据不存在 默认去 props 里面 取值
// !props.element.stem && (props.element.stem = stem.value);
//
// !props.element.options && (props.element.stem = stem.value);
// }
// console.log(`props.element`, props.element);
// console.log(`props.question: `, question.value);
// const { element } = toRefs(props);
const emitValue = () => {
// emit('update:element', element.value);
};
// const value = ref(''); // 值
// const options = ref([]); // 选项
// const optionGroups = ref([]); // 分组
//
// console.log(`radio input question: `, props.question);
//
// // 初始化
// function init() {
// props.list.forEach((list) => {
// options.value = [...options.value, ...list.options];
// });
// if (props.answer) {
// value.value = Object.keys(props.answer)[0];
// options.value.forEach((option) => {
// if (option.is_other && option.option_key === value.value) {
// option.value = props.answer[value.value];
// }
// });
// }
// }
//
// init();
//
// // 选项分组
// function setOptionGroups() {
// optionGroups.value = [];
// const copyOptions = JSON.parse(JSON.stringify(options.value));
// props.config.option_groups?.option_group.forEach((optionGroup) => {
// const group = {
// title: optionGroup.title,
// options: []
// };
// optionGroup.groups.forEach((groups) => {
// const index = copyOptions.findIndex((option) => option.option_key === groups.option_key);
// if (index === -1) return;
// group.options.push(copyOptions.splice(index, 1)[0]);
// });
// group.options = sortOptions(
// group.options,
// props.question.config.select_random && props.question.config.option_group_random_inside
// );
// optionGroups.value.push(group);
// });
// optionGroups.value = sortOptions(
// optionGroups.value,
// props.question.config.select_random && props.question.config.option_group_random_outside
// );
// // 若是 group 是undefined ,默认给一个空对象
// const group = optionGroups.value.find((group) => !group.title) ?? {};
// // console.log(group);
// group.options = sortOptions(copyOptions, props.question.config.select_random);
// }
//
// // 排序(固定项和其他项不参与随机)
// function sortOptions(oldOptions, isRandom) {
// const sorts = [];
// const fixed = [];
// const others = [];
// const removeOther = [];
// oldOptions.forEach((option) => {
// if (option.is_remove_other) {
// removeOther.push(option);
// } else if (option.is_other) {
// others.push(option);
// } else if (option.is_fixed) {
// fixed.push(option);
// } else {
// sorts.push(option);
// }
// });
// return [...randomOptions(sorts, isRandom), ...fixed, ...others, ...removeOther];
// }
//
// // 选择回调
// function changeValue(e) {
// // 更新答案
// const option = options.value.find((option) => option.option_key === e.target.value);
// context.emit('update:answer', {
// [e.target.value]: option.value || (option.is_other ? '' : '1')
// });
// // 清空未选中项输入框值
// options.value.forEach((option) => {
// if (option.is_other && option.option_key !== e.target.value) {
// option.value = '';
// }
// });
// }
//
// function onChangeValue(key) {
// value.value = key;
// changeValue({ target: { value: key } });
// }
//
// // 输入回调
// function changeInput(e, key) {
// const option = options.value.find((option) => option.option_key === key);
// option.value = e.target.value;
// value.value = key;
// // 更新答案
// context.emit('update:answer', { [key]: e.target.value });
// }
//
// // 监听答案
// watch(
// () => props.answer,
// () => {
// context.emit('changeAnswer', {
// options: optionGroups.value.flatMap((group) => group.options || []),
// value: value.value
// });
// // 质量控制
// const timer = setTimeout(() => {
// if (value.value) {
// const index = optionGroups.value
// .flatMap((group) => group.options.map((option) => option))
// .findIndex((option) => option.option_key === value.value);
// context.emit('update:answerIndex', `${index}`);
// } else if (props.answerIndex) {
// context.emit('update:answerIndex', '');
// }
// clearTimeout(timer);
// });
// },
// {
// deep: true,
// immediate: true
// }
// );
//
// // 监听list更新关联选项
// watch(
// () => props.list,
// () => {
// // 更新关联题选项
// let newOptions = [];
// props.list.forEach((list) => {
// newOptions = [...newOptions, ...list.options];
// });
// // 其他项
// newOptions.forEach((option) => {
// if (option.is_other && option.option_key === value.value) {
// const timer = setTimeout(() => {
// option.value = props.answer[value.value];
// clearTimeout(timer);
// });
// }
// });
//
// if (
// !compareArrayByField(options.value, newOptions, 'option_key') ||
// !compareArrayByField(options.value, newOptions, 'option')
// ) {
// options.value = newOptions;
// }
//
// // 清空值和答案
// if (
// value.value &&
// options.value.findIndex((option) => option.option_key === value.value) === -1
// ) {
// // 清空值
// value.value = '';
// // 清空答案
// context.emit('update:answer', null);
// }
// },
// {
// deep: true
// }
// );
//
// // 监听list更新关联选项
// watch(
// () => options.value,
// (val, oldVal) => {
// if (
// compareArrayByField(val, oldVal || [], 'option_key') &&
// compareArrayByField(val, oldVal || [], 'option')
// ) {
// return;
// }
// setOptionGroups();
// },
// {
// deep: true,
// immediate: true
// }
// );
//
// const onHoldToCart = (target) => {
// value.value = target.option_key;
//
// changeValue({
// target: {
// value: target.option_key
// }
// });
// };
//
// const cartWaresLength = computed(() => {
// if (!props.answer) return 0;
// return Object.keys(props.answer).length;
// });
//
// console.log(`car wares length:`, cartWaresLength.value);
//
// // 显示分组标题
// function showGroupTitle(groupOptions) {
// const option = groupOptions.find((option) => !props.hideOptions.includes(option.option_key));
// return !!option;
// }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.choice-html { .choice-html {

View File

@@ -1,13 +1,19 @@
<template> <template>
<van-field v-model="inputValue" v-focus placeholder="请输入内容"> </van-field> <!-- <van-field v-model="inputValue" v-focus placeholder="请输入内容"> </van-field>-->
<!-- <completion />-->
<completion
v-model:completionValue="completionValue"
:index="answerIndex"
:element="question"
></completion>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineEmits, ref, watch } from 'vue'; import { defineEmits, ref, watch } from 'vue';
import { vFocus } from '@/utils/directives/useVFocus'; import Completion from '@/views/Design/components/Questions/Completion.vue';
// 预览新增 v-model // 预览新增 v-model
// const config = defineModel('config'); // const config = defineModel('config');
const answer = defineModel<{ value: string | number }>('answer'); const answer = defineModel<{ value: string | number }>('answer', { default: { value: '' } });
// const answerIndex = defineModel('answerIndex'); const answerIndex = defineModel<number>('answerIndex');
// const stem = defineModel('stem'); // const stem = defineModel('stem');
const question = defineModel<question>('question', {}); const question = defineModel<question>('question', {});
// const list = defineModel<questionsList[]>('list'); // const list = defineModel<questionsList[]>('list');
@@ -17,82 +23,24 @@ const question = defineModel<question>('question', {});
// // 预览新增 emit ['changeAnswer', 'previous', 'next'] // // 预览新增 emit ['changeAnswer', 'previous', 'next']
const emit = defineEmits(['previous', 'next', 'update:modelValue', 'saveOption', 'changeAnswer']); const emit = defineEmits(['previous', 'next', 'update:modelValue', 'saveOption', 'changeAnswer']);
// console.log(answer);
const inputValue = ref(answer.value?.value ?? ''); console.log(`answer`, answer.value);
console.log(question.value);
const completionValue = ref(answer.value?.value ?? '');
// console.log(`question:`, question.value); // console.log(`question:`, question.value);
// console.log(`list: `, list.value); // console.log(`list: `, list.value);
// console.log(question.value);
// 进行提交答案 // 进行提交答案
watch(inputValue, () => { watch(
const res = { () => completionValue.value,
value: inputValue.value () => {
}; const res = {
value: completionValue.value
question.value.answer = res; };
emit('changeAnswer', res); answer.value = res;
}); question.value!.answer = res;
emit('changeAnswer', res);
// function handleKeyDown() { }
// emit('changeAnswer', { );
// options: list.value,
// value: inputValue.value + 1
// });
// }
// const props = defineProps({
// modelValue: {
// type: Object,
// required: false,
// default: () => {
// return {};
// }
// }
// });
// const selectTextTypeModel = ref(false);
// const textTypeList = [
// {
// text: '不限',
// value: 0
// },
// {
// text: '整数',
// value: 1
// },
// {
// text: '小数',
// value: 2
// },
// {
// text: '字母',
// value: 3
// },
// {
// text: '中文',
// value: 4
// },
// {
// text: 'email',
// value: 5
// },
// {
// text: '手机号',
// value: 6
// },
// {
// text: '身份证号',
// value: 7
// }
// ];
// const selectText = (textType) => {
// return textTypeList.filter((item) => item.value === textType)[0]?.text;
// };
// const confirm = ({ selectedValues }) => {
// actionQuestion.value.config.text_type = Number(selectedValues[0]);
// selectTextTypeModel.value = false;
// emit('saveOption');
// };
</script> </script>
<style scoped lang="scss"></style> <style scoped lang="scss"></style>

View File

@@ -1,82 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import { showSuccessToast, showFailToast } from 'vant'; import FileUpload from '@/views/Design/components/Questions/FileUpload.vue';
const questionIndex = defineModel<number>('questionIndex', { default: NaN });
// const { element } = defineProps({ const question = defineModel<question>('question', {});
// element: { // 接口还未稳定
// require: false // 需要的数据暂时先放在这里
// } // 格式如下
// }); // {
// "name": "3-12.png",
// const config = defineModel('question', { required: false }); // "size": 238875,
// console.log(config.value); // "type": "image/png",
/** // "url": "https://test-cxp-public-web-1302259445.cos.ap-beijing.myqcloud.com/uat-yls/answer/7JjQp7am/1742022258458_388_3-12.png"
* 文件大小限制 // }
* @property {number} max - 最大文件大小
* @property {number} min - 最小文件大小
*/
const fileLimit = {
// 默认4MB
max: 1024 * 1024 * 40,
min: 0
};
/**
* 上传文件
* @description 上传文件
*/
function handleFileUpload() {
const fileInput = document.createElement('input');
fileInput.type = 'file';
// fileInput.accept = '.jpg,.jpeg,.png,.gif';
// fileInput.multiple = true;
fileInput.click();
fileInput.addEventListener('change', handleFileChange);
function handleFileChange(event: Event) {
const files = (event.target as HTMLInputElement).files;
if (files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
// console.log(file.size);
if (file.size > fileLimit.max) {
showFailToast(`文件太大,超过${fileLimit.max / 1024 / 1024}MB`);
return;
} else if (file.size < fileLimit.min) {
showFailToast(`文件太小,小于${fileLimit.min / 1024 / 1024}MB`);
return;
}
}
// 这里保留上传的区域
// 成功
showSuccessToast('成功文案');
}
}
}
</script> </script>
<template> <template>
<van-field> <!-- 文件上传题 -->
<template #input> <file-upload :element="question" :index="questionIndex" :active="false" />
<div>
<div class="file-upload-label" @click="handleFileUpload">
<van-icon name="photo"></van-icon>
<span>上传文件</span>
</div>
</div>
</template>
</van-field>
</template> </template>
<style lang="scss" scoped> <style scoped></style>
.file-upload-label {
display: flex;
gap: 10px;
align-items: center;
width: 100%;
height: 50px;
}
</style>

View File

@@ -1,132 +1,97 @@
<template> <template>
<table class="matrix-table"> <MatrixCheckbox
<thead> v-model:rowRecord="rowRecord" v-model:matrix-radio-answer="answer!" :rows="rows" :cols="cols"
<tr> :is-preview="true"
<th></th> />
<td v-for="col in columns" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input
v-if="col.editor"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)"
/>
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option" />
</td>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option" />
<td v-for="(col, colIndex) in columns" :key="colIndex">
<input
type="checkbox"
:value="`${rowIndex + 1}_${colIndex + 1}`"
:checked="isOptionChecked(rowIndex, colIndex)"
@change="handleColValueChange(row.option, col.option)"
/>
</td>
</tr>
</tbody>
</table>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { vFocus } from '@/utils/directives/useVFocus'; import MatrixCheckbox from '@/views/Design/components/Questions/MatrixCheckbox.vue';
import { computed, ref, watch } from 'vue';
const rows = defineModel<OptionType[]>('rows', { required: false, default: [] });
const columns = defineModel<OptionType[]>('columns', { required: false, default: [] });
// const questionType = defineModel<number>('questionType', { required: false }); // const questionType = defineModel<number>('questionType', { required: false });
// const matrixAnswer = defineModel<{ [key: string]: any }>('matrixAnswer', { required: false });
const rowRecord = defineModel<number[]>('rowRecord', { required: false }); // 矩阵多选的答案类型
type answerType = {
[key: string]: 1;
};
// preview props // preview props
// const stem = defineModel('stem'); // const stem = defineModel('stem');
const list = defineModel<questionsList[]>('list', { required: false }); // const list = defineModel<questionsList[]>('list', { required: false });
// const config = defineModel<OptionConfigType>('config', { required: false }); // const config = defineModel<OptionConfigType>('config', { required: false });
// const question = defineModel('question'); const question = defineModel<question>('question');
// const answer = defineModel('answer'); const emit = defineEmits(['changeAnswer', 'previous', 'next']);
// 示例
// {
// "1_1": 1,
// "1_2": 1,
// "2_1": 1,
// "2_2": 1
// }
const answer = defineModel<answerType>('answer', {
// 临时赋值, 用于测试
// default: () => ({
// "1_1": 1,
// "1_2": 1,
// "2_1": 1,
// "2_2": 1
// })
});
// const answerIndex = defineModel('answerIndex'); // const answerIndex = defineModel('answerIndex');
// const answerSn = defineModel('answerSn'); // const answerSn = defineModel('answerSn');
// const answerSurveySn = defineModel('answerSurveySn'); // const answerSurveySn = defineModel('answerSurveySn');
initData(); // 记录行和列的索引
// 记录的格式如下,
// [
// [0, 1],
// [0, 1]
// ]
const rowRecord = ref<number[][]>([]);
// 数据初始化 // 假如 answer 有数值,需要解析 answer ,然后传递 record 给子组件
function initData() { answer.value && parseAnswer(answer.value);
if (!list.value) return;
console.log(list.value);
if (list.value[0].options.length >= 1) rows.value = list.value[0].options;
if (list.value[1].options.length >= 1) columns.value = list.value[1].options;
// 矩阵多选数组形式 [[1,2],[3]], 长度和 行数有关, 选中的位置跟里面的位置有关 console.log(`answer value`, answer.value);
rowRecord.value = new Array(rows.value.length).fill(new Array(columns.value.length).fill(0)); /**
* 解析 answer
*/
function parseAnswer(answer: answer) {
console.log(`come in parseAnswer`);
const rowRecordList: number[][] = [];
Object.entries(answer).forEach(([key]) => {
const [row, col] = key.split('_').map(Number);
if (!rowRecordList[row - 1]) rowRecordList[row - 1] = [];
rowRecordList[row - 1].push(col - 1);
});
rowRecord.value = rowRecordList;
return rowRecordList;
} }
// 查看parseAnswer的返回值
// console.log(`parseAnswer value:`, parseAnswer(answer.value!))
// console.log(`stem:`, stem.value); /**
// console.log(`list:`, list.value); * 获取行和列的内容
// console.log(`config:`, config.value); * 行的内容在 question.list[0].options
// console.log(`question:`, question.value); * 列的内容在 question.list[1].options
// console.log(`answer:`, answer.value); */
// console.log(`answerIndex:`, answerIndex.value); const rows = computed(() => question.value?.list[0]?.options ?? []);
// console.log(`answerSn:`, answerSn.value); const cols = computed(() => question.value?.list[1]?.options ?? []);
// console.log(`answerSurveySn:`, answerSurveySn.value);
// rows.value && console.log(`matrix rows:`, rows.value); watch(rowRecord, () => {
// columns.value && console.log(`matrix columns:`, columns.value); console.log(`record has changed`, rowRecord.value);
// columns.value && console.log(`matrix questionType:`, questionType.value); // 重新生成 answer
// columns.value && console.log(`matrix matrixAnswer:`, matrixAnswer.value); const newAnswer: answer = {};
// columns.value && console.log(`matrix rowRecord:`, rowRecord.value); rowRecord.value.forEach((rowOptions, rowIndex) => {
rowOptions.forEach((colIndex) => {
newAnswer[`${rowIndex + 1}_${colIndex + 1}`] = 1;
});
});
answer.value = newAnswer;
emit('changeAnswer', newAnswer);
}, { deep: true });
/* const emits = */
defineEmits(['update:matrixAnswer', 'update:rowRecord']);
const isOptionChecked = (/* rowIndex: number, colIndex: number */): boolean => {
// const key = `R${rowIndex + 1}_C${colIndex + 1}`;
// console.log(key);
// return !!matrixAnswer.value[key];
};
const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑
};
const handleColValueChange = (rowOption: string, colOption: string) => {
// const target = e.target as HTMLInputElement;
// 寻找行列的索引
const col = columns.value.findIndex((option) => option.option === colOption);
const row = rows.value.findIndex((option) => option.option === rowOption);
// 此处的矩阵是由二维数组组成,两者是通过 索引和 索引对应的数值来表示
// rowRecord 是负责记录的数组
// 记录之后清空结果对象,遍历数组获取相应的结果内容给 matrixAnswer
// rowRecord.value[row] = col;
// matrixAnswer.value = {};
// rowRecord.value.forEach((row, col) => {
// matrixAnswer.value[`${col + 1}_${row + 1}`] = 1;
// });
// 获取row行对应的数组
const rowArray = rowRecord.value[row];
// console.log(...rowRecord.value);
// console.log(`rowArray`, rowArray);
// 检查第 col 个元素
const value = rowArray[col];
if (value) {
// 若元素存在,则变动元素数值
rowArray[col] = 0;
} else {
// 不存在记录为1
rowArray[col] = 1;
}
// console.log(...rowRecord.value);
// emits('update:matrixAnswer', matrixAnswer.value);
// emits('update:rowRecord', rowRecord.value);
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,117 +1,80 @@
<template> <template>
<table class="matrix-table"> <matrix-radio
<thead> v-model:rowRecord="rowRecord" v-model:matrix-radio-answer="answer!" :rows="rows" :cols="cols"
<tr> :is-preview="true"
<th></th> ></matrix-radio>
<!-- 1 是行标签 -->
<td v-for="col in columns" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input
v-if="col.editor"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)"
/>
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
</td>
</tr>
</thead>
<tbody>
<!-- 0 是列标签 -->
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option"></th>
<td v-for="(col, colIndex) in columns" :key="colIndex">
<input
type="radio"
:name="`R${rowIndex + 1}`"
:value="`${rowIndex + 1}_${colIndex + 1}`"
:checked="isOptionChecked(rowIndex, colIndex)"
@change="handleColValueChange(row.option, col.option, $event)"
/>
</td>
</tr>
</tbody>
</table>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { vFocus } from '@/utils/directives/useVFocus'; import matrixRadio from '@/views/Design/components/Questions/MatrixRadio.vue';
import { computed, ref, watch } from 'vue';
const rows = defineModel<OptionType[]>('rows', { required: false, default: [] });
const columns = defineModel<OptionType[]>('columns', { required: false, default: [] });
// const questionType = defineModel<number>('questionType', { required: false }); // const questionType = defineModel<number>('questionType', { required: false });
const matrixAnswer = defineModel<{ [key: string]: any }>('matrixAnswer', { required: false });
const rowRecord = defineModel<number[]>('rowRecord', { required: false }); // 矩阵单选的答案类型
type answerType = {
[key: string]: 1;
};
// preview props // preview props
// const stem = defineModel('stem'); // const stem = defineModel('stem');
const list = defineModel<questionsList[]>('list', { required: false }); // const list = defineModel<questionsList[]>('list', { required: false });
// const config = defineModel<OptionConfigType>('config', { required: false }); // const config = defineModel<OptionConfigType>('config', { required: false });
// const question = defineModel('question'); const question = defineModel<question>('question');
// const answer = defineModel('answer'); const emit = defineEmits(['changeAnswer', 'previous', 'next']);
// 示例
// {
// "1_2": 1,
// "2_2": 1
// }
const answer = defineModel<answerType>('answer', {
// 临时赋值, 用于测试
default: () => ({
// "1_2": 1,
// "2_2": 1
})
});
// const answerIndex = defineModel('answerIndex'); // const answerIndex = defineModel('answerIndex');
// const answerSn = defineModel('answerSn'); // const answerSn = defineModel('answerSn');
// const answerSurveySn = defineModel('answerSurveySn'); // const answerSurveySn = defineModel('answerSurveySn');
// 记录行和列的索引
const rowRecord = ref<number[]>([]);
initData(); // 假如 answer 有数值,需要解析 answer ,然后传递 record 给子组件
// 数据初始化 answer.value && parseAnswer(answer.value);
function initData() {
if (!list.value) return;
// console.log(list.value);
rows.value = list.value[0].options;
columns.value = list.value[1].options;
rowRecord.value = new Array(rows.value.length); /**
* 解析 answer
*/
function parseAnswer(answer: answerType) {
console.log(`come in parseAnswer`);
const rowRecordList: number[] = [];
Object.entries(answer).forEach(([key]) => {
const [row, col] = key.split('_');
rowRecordList[Number(row) - 1] = Number(col) - 1;
});
rowRecord.value = rowRecordList;
return rowRecordList;
} }
// console.log(`stem:`, stem.value); /**
// console.log(`list:`, list.value); * 获取行和列的内容
// console.log(`config:`, config.value); * 行的内容在 question.list[0].options
// console.log(`question:`, question.value); * 列的内容在 question.list[1].options
// console.log(`answer:`, answer.value); */
// console.log(`answerIndex:`, answerIndex.value); const rows = computed(() => question.value?.list[0]?.options ?? []);
// console.log(`answerSn:`, answerSn.value); const cols = computed(() => question.value?.list[1]?.options ?? []);
// console.log(`answerSurveySn:`, answerSurveySn.value);
//
// rows.value && console.log(`matrix rows:`, rows.value);
// columns.value && console.log(`matrix columns:`, columns.value);
// columns.value && console.log(`matrix questionType:`, questionType.value);
// columns.value && console.log(`matrix matrixAnswer:`, matrixAnswer.value);
// columns.value && console.log(`matrix rowRecord:`, rowRecord.value);
/* const emits = */ watch(rowRecord, () => {
defineEmits(['update:matrixAnswer', 'update:rowRecord']); console.log(`record has changed`, rowRecord.value);
// 生成 answer
const isOptionChecked = (/* rowIndex: number, colIndex: number */): boolean => { const newAnswer: answerType = {};
// const key = `R${rowIndex + 1}_C${colIndex + 1}`;
// console.log(key);
// return !!matrixAnswer.value[key];
};
const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑
};
const handleColValueChange = (rowOption: string, colOption: string) => {
// const target = e.target as HTMLInputElement;
// 寻找行列的索引
const col = columns.value.findIndex((option) => option.option === colOption);
const row = rows.value.findIndex((option) => option.option === rowOption);
// 此处的矩阵是由一维数组组成,两者是通过 索引和 索引对应的数值来表示
// rowRecord 是负责记录的数组
// 记录之后清空结果对象,遍历数组获取相应的结果内容给 matrixAnswer
rowRecord.value[row] = col;
matrixAnswer.value = {};
rowRecord.value.forEach((row, col) => { rowRecord.value.forEach((row, col) => {
matrixAnswer.value[`${col + 1}_${row + 1}`] = 1; newAnswer[`${col + 1}_${row + 1}`] = 1;
}); });
// emits('update:matrixAnswer', matrixAnswer.value); answer.value = newAnswer;
// emits('update:rowRecord', rowRecord.value); emit('changeAnswer', newAnswer);
}; }, { deep: true });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,109 +1,97 @@
<template> <template>
<table class="matrix-table"> <MatrixText
<thead> v-model:rowRecord="rowRecord" v-model:matrix-radio-answer="answer!" :rows="rows" :cols="cols"
<tr> :is-preview="true"
<th></th> />
<td v-for="col in columns" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input
v-if="col.editor"
v-model="col.option"
v-focus
type="text"
@focusout="col.editor = false"
@click="handleRowNameChange(col.option!)"
/>
<span v-else @click="handleRowNameChange(col.option!)" v-html="col.option"></span>
</td>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in rows" :key="rowIndex">
<th v-html="row.option"></th>
<td v-for="(col, colIndex) in columns" :key="colIndex">
<input type="text" @change="handleColValueChange(rowIndex, colIndex, $event)" />
</td>
</tr>
</tbody>
</table>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { vFocus } from '@/utils/directives/useVFocus'; import MatrixText from '@/views/Design/components/Questions/MatrixText.vue';
import { computed, ref, watch } from 'vue';
const rows = defineModel<OptionType[]>('rows', { required: false, default: [] });
const columns = defineModel<OptionType[]>('columns', { required: false, default: [] });
// const questionType = defineModel<number>('questionType', { required: false }); // const questionType = defineModel<number>('questionType', { required: false });
const matrixAnswer = defineModel<{ [key: string]: any }>('matrixAnswer', { required: false });
const rowRecord = defineModel<number[]>('rowRecord', { required: false }); // 矩阵多选的答案类型
type answerType = {
[key: string]: string;
};
// preview props // preview props
// const stem = defineModel('stem'); // const stem = defineModel('stem');
const list = defineModel<questionsList[]>('list', { required: false }); // const list = defineModel<questionsList[]>('list', { required: false });
// const config = defineModel<OptionConfigType>('config', { required: false }); // const config = defineModel<OptionConfigType>('config', { required: false });
// const question = defineModel('question'); const question = defineModel<question>('question');
// const answer = defineModel('answer'); const emit = defineEmits(['changeAnswer', 'previous', 'next']);
// 示例
// {
// "1_1": 1,
// "1_2": 1,
// "2_1": 1,
// "2_2": 1
// }
const answer = defineModel<answerType>('answer', {
// 临时赋值, 用于测试
// default: () => ({
// "1_1": 1,
// "1_2": 1,
// "2_1": 1,
// "2_2": 1
// })
});
// const answerIndex = defineModel('answerIndex'); // const answerIndex = defineModel('answerIndex');
// const answerSn = defineModel('answerSn'); // const answerSn = defineModel('answerSn');
// const answerSurveySn = defineModel('answerSurveySn'); // const answerSurveySn = defineModel('answerSurveySn');
initData(); // 记录行和列的索引
// 数据初始化 // 记录的格式如下,
function initData() { // [
if (!list.value) return; // ['', ''],
// console.log(list.value); // ['', '']
rows.value = list.value[0].options; // ]
columns.value = list.value[1].options; const rowRecord = ref<string[][]>([]);
rowRecord.value = new Array(rows.value.length); // 假如 answer 有数值,需要解析 answer ,然后传递 record 给子组件
} answer.value && parseAnswer(answer.value);
// console.log(`stem:`, stem.value); console.log(`answer value`, answer.value);
// console.log(`list:`, list.value); /**
// console.log(`config:`, config.value); * 解析 answer
// console.log(`question:`, question.value); */
// console.log(`answer:`, answer.value); function parseAnswer(answer: answerType) {
// console.log(`answerIndex:`, answerIndex.value); const rowRecordList: string[][] = [];
// console.log(`answerSn:`, answerSn.value); Object.entries(answer).forEach(([key, value]) => {
// console.log(`answerSurveySn:`, answerSurveySn.value); const [row, col] = key.split('_');
// // 如果对应位置不存在数组, 重新建立
// rows.value && console.log(`matrix rows:`, rows.value); if (!rowRecordList[Number(row) - 1]) rowRecordList[Number(row) - 1] = [];
// columns.value && console.log(`matrix columns:`, columns.value); console.log(`value ${value}`);
// columns.value && console.log(`matrix questionType:`, questionType.value); rowRecordList[Number(row) - 1][Number(col) - 1] = value;
// columns.value && console.log(`matrix matrixAnswer:`, matrixAnswer.value);
// columns.value && console.log(`matrix rowRecord:`, rowRecord.value);
/* const emits = */
defineEmits(['update:matrixAnswer', 'update:rowRecord']);
// const isOptionChecked = (/*rowIndex: number, colIndex: number*/): boolean => {
// const key = `R${rowIndex + 1}_C${colIndex + 1}`;
// console.log(key);
// return !!matrixAnswer.value[key];
// };
const handleRowNameChange = (/* value: string */) => {
// console.log(`row change: ${value}`);
// 你可以在这里添加其他逻辑
};
const handleColValueChange = (row: number, col: number) => {
// const target = e.target as HTMLInputElement;
// 寻找行列的索引
// const col = columns.value.findIndex((option) => option.option === colOption);
// const row = rows.value.findIndex((option) => option.option === rowOption);
// console.log(row, col);
// 此处的矩阵是由一维数组组成,两者是通过 索引和 索引对应的数值来表示
// rowRecord 是负责记录的数组
// 记录之后清空结果对象,遍历数组获取相应的结果内容给 matrixAnswer
rowRecord.value[row] = col;
matrixAnswer.value = {};
rowRecord.value.forEach((row, col) => {
matrixAnswer.value[`${col + 1}_${row + 1}`] = 1;
}); });
// emits('update:matrixAnswer', matrixAnswer.value); rowRecord.value = rowRecordList;
// emits('update:rowRecord', rowRecord.value);
}; return rowRecordList;
}
// 查看parseAnswer的返回值
// console.log(`parseAnswer value:`, parseAnswer(answer.value!))
/**
* 获取行和列的内容
* 行的内容在 question.list[0].options
* 列的内容在 question.list[1].options
*/
const rows = computed(() => question.value?.list[0]?.options ?? []);
const cols = computed(() => question.value?.list[1]?.options ?? []);
watch(rowRecord, () => {
// 重新生成 answer
const newAnswer: answerType = {};
rowRecord.value.forEach((rows, rowIndex) => {
rows.forEach((col, colIndex) => {
newAnswer[`${rowIndex + 1}_${colIndex + 1}`] = col;
});
});
answer.value = newAnswer;
emit('changeAnswer', newAnswer);
}, { deep: true });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -1,72 +1,43 @@
<template> <template>
<div class="content"> <n-p-s v-model:element="question" v-model:rates="rates" :active="false" :isPreview="true" />
<van-field
v-model="stem"
:label="stem"
:required="question.config.is_required === 1"
label-align="top"
>
<template #left-icon>
{{ index + 1 }}
</template>
<template #label>
<contenteditable v-model="stem" :active="active" @blur="saveStem"></contenteditable>
</template>
<template #input>
<div v-for="(optionItem, optionItemIndex) in list" :key="optionItemIndex">
<div
v-for="(item, optionIndex) in optionItem.options"
:key="optionIndex"
@click="chooseOption(item)"
>
<RateCharacter v-model="answerValue" :config="config"></RateCharacter>
<div class="tips">
<p>{{ config.prompt_left }}</p>
<p>{{ config.prompt_center }}</p>
<p>{{ config.prompt_right }}</p>
</div>
</div>
</div>
</template>
</van-field>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import NPS from '@/views/Design/components/Questions/NPS.vue';
import RateCharacter from '@/views/Design/components/Questions/RateCharacter.vue'; import { ref, watch } from 'vue';
const answerValue = ref(false); const question = defineModel<question>('question', { default: { config: { is_required: false } } });
// const isPreview = defineModel('isPreview', { required: false }); const answer = defineModel<{ [key: string]: number }>('answer', { default: undefined });
const stem = defineModel('stem', { required: false });
const element = defineModel('element', { required: false });
const question = defineModel('question', { required: false });
const list = defineModel('list', { required: false });
const config = defineModel('config', { required: false });
const index = defineModel('index', { required: false });
const active = defineModel('active', { required: false });
// const sn = defineModel('sn', { required: false });
// const questionType = defineModel('questionType', { required: false });
const chooseId = ref(''); // rates 数值取决与 answer 没有数据重新建立一个对应长度的数组
const emit = defineEmits(['update:element']); const rates = ref(answer.value ? getRates() : new Array(question.value.list[0].options!.length));
const saveStem = () => { // // 预览新增 emit ['changeAnswer', 'previous', 'next']
emit('update:element', element.value); const emit = defineEmits(['changeAnswer', 'previous', 'next', 'update:element']);
};
const chooseOption = (item) => { // 获取 rates
chooseId.value = item.id; function getRates() {
}; const keys = Object.keys(answer.value);
return keys.map((item) => {
return answer.value[item];
});
}
console.log(answer.value && getRates());
watch(
rates,
() => {
const res = {};
rates.value.map((item, index) => {
// index 是 key, item 是 value
res[index + 1] = item;
});
answer.value = res;
emit('changeAnswer', res);
},
{
deep: true
}
);
</script> </script>
<style scoped lang="scss"> <style scoped></style>
.content {
background-color: #fff;
}
.tips {
display: flex;
justify-content: space-between;
color: #bfbfbf;
}
</style>

View File

@@ -1,28 +1,18 @@
<template> <template>
<div class="content"> <div class="content">
<van-field <van-field
v-model="element.stem" v-model="element.stem" :label="element.stem" :required="element.config.is_required === 1"
:label="element.stem"
:required="element.config.is_required === 1"
label-align="top" label-align="top"
> >
<template #left-icon> <template #left-icon>
{{ index + 1 }} {{ index + 1 }}
</template> </template>
<template #label> <template #label>
<contenteditable <contenteditable v-model="element.stem" :active="active" @blur="emitValue"></contenteditable>
v-model="element.stem"
:active="active"
@blur="emitValue"
></contenteditable>
</template> </template>
<template #input> <template #input>
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex"> <div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex">
<div <div v-for="(item, optionIndex) in optionItem" :key="optionIndex" @click="chooseOption(item)">
v-for="(item, optionIndex) in optionItem"
:key="optionIndex"
@click="chooseOption(item)"
>
<contenteditable v-model="item.option" :active="active"></contenteditable> <contenteditable v-model="item.option" :active="active"></contenteditable>
<RateCharacter :config="element.config"></RateCharacter> <RateCharacter :config="element.config"></RateCharacter>
<div class="tips"> <div class="tips">
@@ -39,7 +29,7 @@
<script setup> <script setup>
import { ref, toRefs, watch } from 'vue'; import { ref, toRefs, watch } from 'vue';
import RateCharacter from './RateCharacter.vue'; // import RateCharacter from './RateCharacter.vue';
const props = defineProps({ const props = defineProps({
element: { element: {

View File

@@ -102,37 +102,37 @@ export default defineComponent({
const isEndUrl = computed(() => { const isEndUrl = computed(() => {
const code = props.action ? props.action.code : props.code; const code = props.action ? props.action.code : props.code;
return ( return (
(code === 20004 && (code === 20004
props.survey.screening_end_url_select && && props.survey.screening_end_url_select
props.survey.screening_end_url) || && props.survey.screening_end_url)
(code === 20011 && props.survey.success_end_url_select && props.survey.success_end_url) || || (code === 20011 && props.survey.success_end_url_select && props.survey.success_end_url)
(code === 20016 && props.survey.quota_end_url_select && props.survey.quota_end_url) || (code === 20016 && props.survey.quota_end_url_select && props.survey.quota_end_url)
); );
}); });
// 跳转 // 跳转
function toEndUrl() { function toEndUrl() {
switch (props.action.code) { switch (props.action.code) {
case 20004: // 被甄别 case 20004: // 被甄别
if (props.survey.screening_end_url_select && props.survey.screening_end_url) { if (props.survey.screening_end_url_select && props.survey.screening_end_url) {
const url = props.survey.screening_end_url; const url = props.survey.screening_end_url;
toUrl(url); toUrl(url);
} }
break; break;
case 20011: // 成功 case 20011: // 成功
if (props.survey.success_end_url_select && props.survey.success_end_url) { if (props.survey.success_end_url_select && props.survey.success_end_url) {
const url = props.survey.success_end_url; const url = props.survey.success_end_url;
toUrl(url); toUrl(url);
} }
break; break;
case 20016: // 配额超限 case 20016: // 配额超限
if (props.survey.quota_end_url_select && props.survey.quota_end_url) { if (props.survey.quota_end_url_select && props.survey.quota_end_url) {
const url = props.survey.quota_end_url; const url = props.survey.quota_end_url;
toUrl(url); toUrl(url);
} }
break; break;
default: default:
break; break;
} }
} }

View File

@@ -264,16 +264,16 @@ export default defineComponent({
}); });
if ( if (
!compareArrayByField(options.value, newOptions, 'option_key') || !compareArrayByField(options.value, newOptions, 'option_key')
!compareArrayByField(options.value, newOptions, 'option') || !compareArrayByField(options.value, newOptions, 'option')
) { ) {
options.value = newOptions; options.value = newOptions;
} }
// 清空值和答案 // 清空值和答案
if ( if (
value.value && value.value
options.value.findIndex((option) => option.option_key === value.value) === -1 && options.value.findIndex((option) => option.option_key === value.value) === -1
) { ) {
// 清空值 // 清空值
value.value = ''; value.value = '';
@@ -291,8 +291,8 @@ export default defineComponent({
() => options.value, () => options.value,
(val, oldVal) => { (val, oldVal) => {
if ( if (
compareArrayByField(val, oldVal || [], 'option_key') && compareArrayByField(val, oldVal || [], 'option_key')
compareArrayByField(val, oldVal || [], 'option') && compareArrayByField(val, oldVal || [], 'option')
) { ) {
return; return;
} }

View File

@@ -132,8 +132,8 @@ export default defineComponent({
const value = matchValue.replace('[%cite(', '').replace(')%]', ''); const value = matchValue.replace('[%cite(', '').replace(')%]', '');
let replacement = ''; // 替换文本 let replacement = ''; // 替换文本
// 查找引用问题 // 查找引用问题
const question = const question
props.questions.find((question) => { = props.questions.find((question) => {
// 矩阵题 // 矩阵题
if (question.question_type >= 8 && question.question_type <= 11) { if (question.question_type >= 8 && question.question_type <= 11) {
return question.title === value.split('_R')[0].split('_C')[0]; return question.title === value.split('_R')[0].split('_C')[0];
@@ -143,8 +143,8 @@ export default defineComponent({
return question.title === value.split('_A')[0]; return question.title === value.split('_A')[0];
} }
return question.title === value; return question.title === value;
}) || })
props.questions.find((question) => { || props.questions.find((question) => {
// 矩阵题 // 矩阵题
if (question.question_type >= 8 && question.question_type <= 11) { if (question.question_type >= 8 && question.question_type <= 11) {
return question.title === (value + cycleIndexStr).split('_R')[0].split('_C')[0]; return question.title === (value + cycleIndexStr).split('_R')[0].split('_C')[0];
@@ -171,8 +171,8 @@ export default defineComponent({
replacement = answer[option.option_key]; replacement = answer[option.option_key];
} }
} else if ( } else if (
question.question_type === 2 && question.question_type === 2
Object.keys(answer).length >= question.config.min_select && Object.keys(answer).length >= question.config.min_select
) { ) {
// 查找引用选项(多选) // 查找引用选项(多选)
options.forEach((option) => { options.forEach((option) => {

View File

@@ -350,7 +350,7 @@ export const language = {
zh: '请点击查看图片' zh: '请点击查看图片'
}, },
NoteCantViewAfterTimeLimit: { NoteCantViewAfterTimeLimit: {
en: "Note: Can't view after time limit", en: 'Note: Can\'t view after time limit',
zh: '注意:超过显示时间限制后将无法再次查看' zh: '注意:超过显示时间限制后将无法再次查看'
}, },
DisplayTimeLimitExceeded: { DisplayTimeLimitExceeded: {

View File

@@ -250,10 +250,10 @@ function quesHandle(answer, logChild) {
const matrixRateHandle = () => { const matrixRateHandle = () => {
// 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验 // 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验
if ( if (
logChild.cell_index === 0 || logChild.cell_index === 0
logChild.row_index === 0 || || logChild.row_index === 0
!logChild.operator || || !logChild.operator
!logChild.value || !logChild.value
) { ) {
return true; return true;
} }
@@ -265,10 +265,10 @@ function quesHandle(answer, logChild) {
const matrixInputHandle = () => { const matrixInputHandle = () => {
// 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验 // 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验
if ( if (
logChild.cell_index === 0 || logChild.cell_index === 0
logChild.row_index === 0 || || logChild.row_index === 0
!logChild.operator || || !logChild.operator
!logChild.value || !logChild.value
) { ) {
return true; return true;
} }
@@ -428,59 +428,59 @@ function getConditionStatus(logChild) {
if (answer) { if (answer) {
try { try {
switch (logChild.question_type) { switch (logChild.question_type) {
case 1: case 1:
quesStatus = quesHandles.choiceHandle(config); quesStatus = quesHandles.choiceHandle(config);
break; break;
case 2: case 2:
quesStatus = quesHandles.choiceCheckBoxHandle(config); quesStatus = quesHandles.choiceCheckBoxHandle(config);
break; break;
case 3: case 3:
quesStatus = quesHandles.cascaderHandle(); quesStatus = quesHandles.cascaderHandle();
break; break;
case 4: case 4:
quesStatus = quesHandles.inputHandle(); quesStatus = quesHandles.inputHandle();
break; break;
case 5: case 5:
quesStatus = quesHandles.rateHandle(); quesStatus = quesHandles.rateHandle();
break; break;
case 7: case 7:
quesStatus = quesHandles.dateTimeHandle(); quesStatus = quesHandles.dateTimeHandle();
break; break;
case 8: case 8:
quesStatus = quesHandles.matrixInputHandle(); quesStatus = quesHandles.matrixInputHandle();
break; break;
case 9: case 9:
quesStatus = quesHandles.matrixRadioHandle(config); quesStatus = quesHandles.matrixRadioHandle(config);
break; break;
case 10: case 10:
quesStatus = quesHandles.matrixCheckboxHandle(config); quesStatus = quesHandles.matrixCheckboxHandle(config);
break; break;
case 11: case 11:
quesStatus = quesHandles.matrixRateHandle(); quesStatus = quesHandles.matrixRateHandle();
break; break;
case 13: case 13:
quesStatus = quesHandles.imgRadioHandle(); quesStatus = quesHandles.imgRadioHandle();
break; break;
case 14: case 14:
quesStatus = quesHandles.imgCheckBoxHandle(); quesStatus = quesHandles.imgCheckBoxHandle();
break; break;
case 15: case 15:
quesStatus = quesHandles.classifyHandle(); quesStatus = quesHandles.classifyHandle();
break; break;
case 16: case 16:
quesStatus = quesHandles.sortHandle(); quesStatus = quesHandles.sortHandle();
break; break;
case 17: case 17:
quesStatus = quesHandles.constantSumHandle(); quesStatus = quesHandles.constantSumHandle();
break; break;
case 23: case 23:
quesStatus = quesHandles.agreementHandle(); quesStatus = quesHandles.agreementHandle();
break; break;
case 101: case 101:
quesStatus = quesHandles.psmHandle(config); quesStatus = quesHandles.psmHandle(config);
break; break;
default: default:
break; break;
} }
} catch (error) { } catch (error) {
// console.log(error); // console.log(error);
@@ -519,10 +519,10 @@ function getlogicStatus(questionData) {
// } else { // } else {
// statusStr = statusStr + conditionStatus; // statusStr = statusStr + conditionStatus;
// } // }
statusStr = statusStr
statusStr + = statusStr
(logChild.logic === 'and' ? '&&' : logChild.logic === 'or' ? '||' : '') + + (logChild.logic === 'and' ? '&&' : logChild.logic === 'or' ? '||' : '')
conditionStatus; + conditionStatus;
}); });
// eslint-disable-next-line no-eval // eslint-disable-next-line no-eval
logs.logicStatus = eval(statusStr); logs.logicStatus = eval(statusStr);

View File

@@ -105,8 +105,8 @@ export default function answerMock(questionsData, page) {
} else if (logic.skip_type === 4) { } else if (logic.skip_type === 4) {
// 只计算跳转后所在页面的隐藏逻辑,否则会出现只返回最后一道隐藏选项题目的情况,导致失效 // 只计算跳转后所在页面的隐藏逻辑,否则会出现只返回最后一道隐藏选项题目的情况,导致失效
const toPage = page + 1; const toPage = page + 1;
const hasHiddenLogicQuizPage = const hasHiddenLogicQuizPage
data.pages.findIndex((page) => page.includes(logic.question_index)) + 1; = data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
if (hasHiddenLogicQuizPage === toPage) { if (hasHiddenLogicQuizPage === toPage) {
// 选项隐藏逻辑 // 选项隐藏逻辑
updateOptionHidden(data.hide_options, logic); updateOptionHidden(data.hide_options, logic);

View File

@@ -154,9 +154,9 @@ export default defineComponent({
question.error = translatedText.value.ThisIsARequiredQuestion; question.error = translatedText.value.ThisIsARequiredQuestion;
} }
} else if ( } else if (
answer && answer
questionType === 1 && && questionType === 1
Object.keys(answer).findIndex((value) => !answer[value]) !== -1 && Object.keys(answer).findIndex((value) => !answer[value]) !== -1
) { ) {
// 单选题 // 单选题
isError = true; isError = true;
@@ -302,9 +302,9 @@ export default defineComponent({
} else if (answer && questionType === 12) { } else if (answer && questionType === 12) {
question.error = ''; question.error = '';
} else if ( } else if (
answer && answer
questionType === 14 && && questionType === 14
Object.keys(answer).length < config.min_select && Object.keys(answer).length < config.min_select
) { ) {
// 图片多选题 // 图片多选题
isError = true; isError = true;
@@ -340,46 +340,46 @@ export default defineComponent({
const { value } = answer; const { value } = answer;
const newValue = value.replace(/\n|\r|\r\n/g, ''); const newValue = value.replace(/\n|\r|\r\n/g, '');
switch (config.text_type) { switch (config.text_type) {
case 3: // 字母 case 3: // 字母
isError = isError
config.include_mark === 1 = config.include_mark === 1
? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test( ? !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
newValue newValue
) || !newValue.length ) || !newValue.length
: !/^[a-zA-Z]+$/.test(newValue) || !newValue.length; : !/^[a-zA-Z]+$/.test(newValue) || !newValue.length;
question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : ''; question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : '';
break; break;
case 4: // 中文 case 4: // 中文
isError = isError
config.include_mark === 1 = 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]|[·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test( ? !/^(?:[\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
) || !newValue.length ) || !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( : !/^(?:[\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
) || !newValue.length; ) || !newValue.length;
question.error = isError ? translatedText.value.PleaseEnterChineseWords : ''; question.error = isError ? translatedText.value.PleaseEnterChineseWords : '';
break; break;
case 5: // 邮箱 case 5: // 邮箱
isError = 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( = !/^(([^<>()[\]\\.,;:\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 value
); );
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : ''; question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
break; break;
case 6: // 手机号 case 6: // 手机号
isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value); isError = !/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value);
question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : ''; question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : '';
break; break;
case 7: // 身份证号 case 7: // 身份证号
isError = 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( = !/^[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 value
); );
question.error = isError ? translatedText.value.PleaseEnterACorrectID : ''; question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
break; break;
default: default:
break; break;
} }
if (!isError && value.length < config.min && ![1, 2].includes(config.text_type)) { if (!isError && value.length < config.min && ![1, 2].includes(config.text_type)) {
isError = true; isError = true;
@@ -391,54 +391,54 @@ export default defineComponent({
Object.keys(answer).forEach((key) => { Object.keys(answer).forEach((key) => {
const value = answer[key]; const value = answer[key];
switch (config.text_type) { switch (config.text_type) {
case 3: // 字母 case 3: // 字母
if ( if (
!/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test( !/^[a-zA-Z·~@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
value value
) )
) { ) {
question.error = translatedText.value.PleaseEnterEnglishLetters; question.error = translatedText.value.PleaseEnterEnglishLetters;
} }
break; break;
case 4: // 中文 case 4: // 中文
if ( if (
!/^(?:[\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( !/^(?:[\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(
value value
) )
) { ) {
question.error = translatedText.value.PleaseEnterChineseWords; question.error = translatedText.value.PleaseEnterChineseWords;
} }
break; break;
case 5: // 邮箱 case 5: // 邮箱
if ( if (
!/^(([^<>()[\]\\.,;:\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( !/^(([^<>()[\]\\.,;:\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 value
) )
) { ) {
question.error = translatedText.value.PleaseEnterACorrectEmail; question.error = translatedText.value.PleaseEnterACorrectEmail;
} }
break; break;
case 6: // 手机号 case 6: // 手机号
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) { if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) {
question.error = translatedText.value.PleaseEnterACorrectPhone; question.error = translatedText.value.PleaseEnterACorrectPhone;
} }
break; break;
case 7: // 身份证号 case 7: // 身份证号
if ( if (
!/^[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( !/^[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 value
) )
) { ) {
question.error = translatedText.value.PleaseEnterACorrectID; question.error = translatedText.value.PleaseEnterACorrectID;
} }
break; break;
default: default:
break; break;
} }
if ( if (
!question.error && !question.error
value.length < config.min && && value.length < config.min
![1, 2].includes(config.text_type) && ![1, 2].includes(config.text_type)
) { ) {
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min); question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
} }
@@ -814,8 +814,8 @@ export default defineComponent({
const evt1 = {}; const evt1 = {};
if ([1].includes(question.question_type)) { if ([1].includes(question.question_type)) {
evt1.value = evt1.value
Object.keys(question.answer) = Object.keys(question.answer)
.map((key) => (question.answer[key] ? key : undefined)) .map((key) => (question.answer[key] ? key : undefined))
.filter((i) => !!i)?.[0] || undefined; .filter((i) => !!i)?.[0] || undefined;
evt1.options = question.list.flatMap((i) => i.options); evt1.options = question.list.flatMap((i) => i.options);

View File

@@ -6,8 +6,8 @@
</template> </template>
<script setup> <script setup>
const successImg = const successImg
'https://files.axshare.com/gsc/DR6075/c7/5a/53/c75a534148d349f1bb8e185629f784ac/images/%E9%A2%84%E8%A7%88/u123.png?pageId=18fb9d8a-b9b7-465f-9bd7-625b1b78f72e'; = 'https://files.axshare.com/gsc/DR6075/c7/5a/53/c75a534148d349f1bb8e185629f784ac/images/%E9%A2%84%E8%A7%88/u123.png?pageId=18fb9d8a-b9b7-465f-9bd7-625b1b78f72e';
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>