Enhance UI with improved state management and button functionality

This commit is contained in:
HoshinoKoji 2025-03-02 17:15:10 +08:00
parent 294cb6853d
commit 8fd1415ef8
2 changed files with 91 additions and 86 deletions

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script>
const data = {
type: 'iframe',
type: 'spec',
title: 'Contact Form',
items: [{
type: 'text',
@ -23,6 +23,9 @@
optValues: ['Option 1', 'Option 2', 'Option 3']
}]
};
window.addEventListener('message', (event) => {
console.log(event.data);
});
window.onload = () => {
const iframe = document.getElementById('iframe');
iframe.contentWindow.postMessage(data, window.origin);

View File

@ -30,12 +30,18 @@
return {
title: undefined,
items: [],
currentTitle: undefined,
currentIdx: undefined,
currentItem: undefined,
currentAnswer: undefined,
currentAnswerValue: undefined,
currentRefilled: false,
timestamp: undefined,
uiStatus: {
backButtonDisabled: true,
nextButtonText: 'Next',
nextButtonStatus: 'primary',
},
settings: {
allowBack: true,
},
@ -44,7 +50,7 @@
created() {
window.addEventListener('message', (event) => {
const data = event.data;
if (event.source === window.parent && data && data.type === 'iframe') {
if (event.source === window.parent && data && data.type === 'spec') {
this.settings = Object.assign(this.settings, data.settings);
this.items = structuredClone(data.items);
this.title = data.title;
@ -53,8 +59,7 @@
}
this.currentIdx = 0;
this.currentItem = this.items[this.currentIdx];
this.timestamp = new Date().getTime();
this.updateItem();
}
});
},
@ -62,20 +67,39 @@
isReady() {
return this.items && this.title;
},
displayTitle() {
return `${this.currentIdx + 1}. ${this.items[this.currentIdx].title}`;
},
nextButtonText() {
return this.currentIdx === this.items.length - 1 ? 'Submit' : 'Next';
},
nextButtonStatus() {
return this.currentIdx === this.items.length - 1 ? 'submit' : '';
},
backButtonStatus() {
return this.currentIdx === 0 ? 'disabled' : '';
}
},
methods: {
updateBackButton() {
this.uiStatus.backButtonDisabled = !this.settings.allowBack || (this.currentIdx === 0);
},
updateNextButton() {
if (this.currentIdx === this.items.length - 1) {
this.uiStatus.nextButtonText = 'Submit';
this.uiStatus.nextButtonStatus = 'success';
} else {
this.uiStatus.nextButtonText = 'Next';
this.uiStatus.nextButtonStatus = 'primary';
}
},
updateTitle() {
this.currentTitle = this.currentItem ?
`${this.currentIdx + 1}. ${this.currentItem.title}` : '';
},
updateItem() {
this.currentItem = this.items[this.currentIdx];
this.currentAnswer = this.items[this.currentIdx].answer;
this.currentRefilled = (this.currentItem.answer !== undefined);
this.timestamp = new Date().getTime();
this.updateTitle();
this.updateBackButton();
this.updateNextButton();
},
iterOptions() {
return this.currentItem.optTexts.map((optText, index) => [
optText,
this.currentItem.optValues ? this.currentItem.optValues[index] : optText,
]);
},
clickNext() {
const item = this.items[this.currentIdx];
item.answer = this.currentAnswer;
@ -86,29 +110,29 @@
this.submit();
} else {
this.currentIdx++;
this.currentAnswer = null;
this.currentItem = this.items[this.currentIdx];
this.currentRefilled = (this.currentItem.answer !== undefined);
this.timestamp = new Date().getTime();
this.updateItem();
}
},
autoNext() {
if (this.currentIdx < this.items.length - 1) {
this.clickNext();
}
},
clickBack() {
if (this.currentIdx > 0) {
this.currentIdx--;
this.currentAnswer = this.items[this.currentIdx].answer;
this.currentItem = this.items[this.currentIdx];
this.currentRefilled = (this.currentItem.answer !== undefined);
this.timestamp = new Date().getTime();
this.updateItem();
}
},
submit() {
const results = this.items.map((item, index) => ({
index: index,
title: item.title,
answer: item.answer,
answer: item.answer || '',
refilled: item.refilled,
responseTime: item.responseTime,
}));
window.parent.postMessage({ type: 'response', results }, window.origin);
const elem = document.createElement('a');
elem.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(results));
elem.download = 'results.json';
@ -119,75 +143,53 @@
</script>
<template>
<div class="container" v-if="isReady">
<span class="display-title">{{ displayTitle }}</span>
<div v-if="!isReady">Loading...</div>
<div id="container" v-if="isReady">
<span id="display-title">{{ currentTitle }}</span>
<div id="display-content">
<template v-if="currentItem.type === 'text'">
<div class="item text">
<input type="text" v-model="currentAnswer">
</div>
<el-input v-model="currentAnswer" style="width: 240px" placeholder="Please input" />
</template>
<template v-else-if="currentItem.type === 'radio'">
<div class="item radio" v-for="(optText, index) in currentItem.optTexts" :key="index">
<input type="radio" :id="index" :value="currentItem.optValues[index]" v-model="currentAnswer">
<label :for="index">{{ optText }}</label>
<div>
<el-radio-group
v-model="currentAnswer"
v-for="[optText, optValue] in iterOptions()"
:key="optValue"
@change="autoNext"
>
<el-radio-button :label="optText" :value="optValue" />
</el-radio-group>
</div>
</template>
<button
v-if="settings.allowBack"
@click="clickBack"
:class="backButtonStatus">Back</button>
<button @click="clickNext" :class="nextButtonStatus">{{ nextButtonText }}</button>
</div>
<div id="display-buttons">
<el-button-group>
<el-button type="primary" round :disabled="uiStatus.backButtonDisabled" @click="clickBack">
<el-icon class="el-icon--left"><ArrowLeft /></el-icon>Back
</el-button>
<el-button :type="uiStatus.nextButtonStatus" round @click="clickNext">
{{ uiStatus.nextButtonText }}<el-icon class="el-icon--right"><ArrowRight /></el-icon>
</el-button>
</el-button-group>
</div>
</div>
</template>
<style scoped>
.container {
#container {
text-align: left;
width: 100%;
}
button {
background-color: #008CBA;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
border-radius: 5px;
display: inline-block;
font-size: 18px;
margin-right: 20px;
}
button.submit {
background-color: #4CAF50;
}
button.disabled {
background-color: #cccccc;
}
input[type=text] {
width: 100%;
padding: 12px 18px;
margin: 8px 20px 20px 0px;
display: inline-block;
box-sizing: border-box;
border: 2px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
input[type=radio] {
margin-bottom: 4px;
margin-right: 10px;
text-align: left;
}
label {
font-size: 16px;
}
.item {
margin-bottom: 20px;
width: 100%;
}
.display-title {
#display-title {
font-size: 20px;
font-weight: bold;
}
#display-content {
margin-top: 1ex;
margin-bottom: 2ex;
}
#display-buttons {
text-align: center;
}
</style>