修改部分样式
This commit is contained in:
+239
-26
@@ -11,11 +11,20 @@
|
||||
}"
|
||||
>
|
||||
<ModulePageBackground module="profile" />
|
||||
<view class="page-layer"><PageHeader title="意见反馈" custom-back @back="requestBack" /></view>
|
||||
<view class="page-layer"
|
||||
><PageHeader title="意见反馈" custom-back @back="requestBack"
|
||||
/></view>
|
||||
<view class="page-content page-layer">
|
||||
<text class="lead">你的建议会帮助我们把家谱做得更好</text>
|
||||
<view class="type-field" role="group" aria-label="反馈类型(选填)" aria-describedby="feedback-type-note">
|
||||
<text id="feedback-type-note" class="field-note">反馈类型为选填,可再次点击取消选择。</text>
|
||||
<view
|
||||
class="type-field"
|
||||
role="group"
|
||||
aria-label="反馈类型(选填)"
|
||||
aria-describedby="feedback-type-note"
|
||||
>
|
||||
<text id="feedback-type-note" class="field-note"
|
||||
>反馈类型为选填,可再次点击取消选择。</text
|
||||
>
|
||||
<view class="type-grid">
|
||||
<button
|
||||
v-for="type in feedbackTypes"
|
||||
@@ -25,7 +34,9 @@
|
||||
:aria-pressed="feedbackForm.feedbackType === type"
|
||||
:disabled="feedbackState === 'submitting'"
|
||||
@click="selectFeedbackType(type)"
|
||||
>{{ type }}</button>
|
||||
>
|
||||
{{ type }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-panel">
|
||||
@@ -35,7 +46,11 @@
|
||||
maxlength="500"
|
||||
aria-label="问题描述"
|
||||
aria-required="true"
|
||||
:aria-describedby="errors.feedbackContent ? 'feedback-content-help feedback-content-error' : 'feedback-content-help'"
|
||||
:aria-describedby="
|
||||
errors.feedbackContent
|
||||
? 'feedback-content-help feedback-content-error'
|
||||
: 'feedback-content-help'
|
||||
"
|
||||
:aria-invalid="Boolean(errors.feedbackContent)"
|
||||
:disabled="feedbackState === 'submitting'"
|
||||
:focus="feedbackContentFocused"
|
||||
@@ -43,9 +58,18 @@
|
||||
@input="errors.feedbackContent = ''"
|
||||
@blur="feedbackContentFocused = false"
|
||||
/>
|
||||
<text class="counter">{{ feedbackForm.feedbackContent.length }}/500</text>
|
||||
<text id="feedback-content-help" class="field-note">请勿填写密码、验证码等敏感信息。</text>
|
||||
<text v-if="errors.feedbackContent" id="feedback-content-error" class="field-error">{{ errors.feedbackContent }}</text>
|
||||
<text class="counter"
|
||||
>{{ feedbackForm.feedbackContent.length }}/500</text
|
||||
>
|
||||
<text id="feedback-content-help" class="field-note"
|
||||
>请勿填写密码、验证码等敏感信息。</text
|
||||
>
|
||||
<text
|
||||
v-if="errors.feedbackContent"
|
||||
id="feedback-content-error"
|
||||
class="field-error"
|
||||
>{{ errors.feedbackContent }}</text
|
||||
>
|
||||
<view class="contact-row">
|
||||
<text>联系方式</text>
|
||||
<input
|
||||
@@ -68,8 +92,14 @@
|
||||
}"
|
||||
:role="feedbackResultTone === 'error' ? 'alert' : 'status'"
|
||||
:aria-live="feedbackResultTone === 'error' ? 'assertive' : 'polite'"
|
||||
>{{ feedbackResult }}</view>
|
||||
<AppButton block :disabled="submitDisabled" :label="submitLabel" @click="submitFeedback" />
|
||||
>{{ feedbackResult }}</view
|
||||
>
|
||||
<AppButton
|
||||
block
|
||||
:disabled="submitDisabled"
|
||||
:label="submitLabel"
|
||||
@click="submitFeedback"
|
||||
/>
|
||||
</view>
|
||||
<AppDialog
|
||||
:visible="discardVisible"
|
||||
@@ -93,12 +123,20 @@ import AppButton from "@/components/AppButton.vue";
|
||||
import AppDialog from "@/components/AppDialog.vue";
|
||||
import ModulePageBackground from "@/components/ModulePageBackground.vue";
|
||||
import PageHeader from "@/components/PageHeader.vue";
|
||||
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
|
||||
import {
|
||||
appApi,
|
||||
createRequestController,
|
||||
isRequestCancelled,
|
||||
} from "@/utils/api.js";
|
||||
import { createDiscardConfirmation } from "@/utils/discard-confirmation.js";
|
||||
import { handleBackPress, runBackGuard } from "@/utils/navigation.js";
|
||||
|
||||
const feedbackTypes = ["功能问题", "使用建议", "内容纠错", "其他"];
|
||||
const feedbackForm = reactive({ feedbackType: "", feedbackContent: "", contactInfo: "" });
|
||||
const feedbackForm = reactive({
|
||||
feedbackType: "",
|
||||
feedbackContent: "",
|
||||
contactInfo: "",
|
||||
});
|
||||
const errors = reactive({ feedbackContent: "" });
|
||||
const feedbackState = ref("ready");
|
||||
const feedbackResult = ref("");
|
||||
@@ -117,10 +155,13 @@ const normalizedForm = computed(() => ({
|
||||
const formSnapshot = computed(() => JSON.stringify(normalizedForm.value));
|
||||
const baseline = ref(formSnapshot.value);
|
||||
const isDirty = computed(() => formSnapshot.value !== baseline.value);
|
||||
const submitDisabled = computed(() =>
|
||||
feedbackState.value === "submitting" ||
|
||||
(Boolean(lastSubmittedSnapshot.value) && formSnapshot.value === lastSubmittedSnapshot.value) ||
|
||||
(Boolean(lastUncertainSnapshot.value) && formSnapshot.value === lastUncertainSnapshot.value),
|
||||
const submitDisabled = computed(
|
||||
() =>
|
||||
feedbackState.value === "submitting" ||
|
||||
(Boolean(lastSubmittedSnapshot.value) &&
|
||||
formSnapshot.value === lastSubmittedSnapshot.value) ||
|
||||
(Boolean(lastUncertainSnapshot.value) &&
|
||||
formSnapshot.value === lastUncertainSnapshot.value),
|
||||
);
|
||||
const submitLabel = computed(() => {
|
||||
if (feedbackState.value === "submitting") return "正在提交";
|
||||
@@ -136,7 +177,9 @@ const requestDiscardConfirmation = discardConfirmation.request;
|
||||
const confirmDiscard = discardConfirmation.confirm;
|
||||
const cancelDiscard = discardConfirmation.cancel;
|
||||
const validateFeedback = () => {
|
||||
errors.feedbackContent = feedbackForm.feedbackContent.trim() ? "" : "请填写问题描述";
|
||||
errors.feedbackContent = feedbackForm.feedbackContent.trim()
|
||||
? ""
|
||||
: "请填写问题描述";
|
||||
if (errors.feedbackContent) {
|
||||
feedbackContentFocused.value = false;
|
||||
nextTick(() => {
|
||||
@@ -156,14 +199,23 @@ const feedbackErrorMessage = (error) => {
|
||||
if (error?.httpStatus === 401) {
|
||||
return "登录状态已失效,服务器未确认提交成功。请重新登录后再处理。";
|
||||
}
|
||||
return error?.message ? `服务器未确认提交成功:${error.message}` : "服务器未确认提交成功,请稍后再试。";
|
||||
return error?.message
|
||||
? `服务器未确认提交成功:${error.message}`
|
||||
: "服务器未确认提交成功,请稍后再试。";
|
||||
};
|
||||
const submittedFeedbackMessage = "反馈已提交。以下内容为本次提交记录;修改任一项后可提交新反馈。";
|
||||
const submittedFeedbackMessage =
|
||||
"反馈已提交。以下内容为本次提交记录;修改任一项后可提交新反馈。";
|
||||
const submittedWithPendingEditsMessage = "上一份反馈已提交,当前修改尚未提交。";
|
||||
const uncertainFeedbackMessage = "提交结果可能未知。为避免重复记录,当前内容不能直接重提;请稍后确认或修改后提交新反馈。";
|
||||
const uncertainWithPendingEditsMessage = "上一份反馈的提交结果仍待确认,当前修改尚未提交。";
|
||||
const uncertainFeedbackMessage =
|
||||
"提交结果可能未知。为避免重复记录,当前内容不能直接重提;请稍后确认或修改后提交新反馈。";
|
||||
const uncertainWithPendingEditsMessage =
|
||||
"上一份反馈的提交结果仍待确认,当前修改尚未提交。";
|
||||
const isFeedbackResultUncertain = (error) => {
|
||||
if (["REQUEST_TIMEOUT", "NETWORK_ERROR", "RESPONSE_INVALID"].includes(error?.code)) {
|
||||
if (
|
||||
["REQUEST_TIMEOUT", "NETWORK_ERROR", "RESPONSE_INVALID"].includes(
|
||||
error?.code,
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (error?.code !== "HTTP_ERROR") return false;
|
||||
@@ -173,9 +225,12 @@ const isFeedbackResultUncertain = (error) => {
|
||||
const submitFeedback = async () => {
|
||||
if (!validateFeedback() || feedbackState.value === "submitting") return;
|
||||
if (
|
||||
(lastSubmittedSnapshot.value && formSnapshot.value === lastSubmittedSnapshot.value) ||
|
||||
(lastUncertainSnapshot.value && formSnapshot.value === lastUncertainSnapshot.value)
|
||||
) return;
|
||||
(lastSubmittedSnapshot.value &&
|
||||
formSnapshot.value === lastSubmittedSnapshot.value) ||
|
||||
(lastUncertainSnapshot.value &&
|
||||
formSnapshot.value === lastUncertainSnapshot.value)
|
||||
)
|
||||
return;
|
||||
const submittedSnapshot = formSnapshot.value;
|
||||
const submittedPayload = JSON.parse(submittedSnapshot);
|
||||
feedbackState.value = "submitting";
|
||||
@@ -259,5 +314,163 @@ onUnload(() => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
|
||||
.feedback-page{display:flex;min-height:100vh;flex-direction:column;background:$paper}.page-layer{z-index:1}.page-content{flex:1;padding:26rpx 30rpx 72rpx}.lead{display:block;color:$ink-muted;font-size:23rpx;line-height:1.5;text-align:center}.type-field{margin-top:20rpx}.field-note{display:block;color:$ink-muted;font-size:20rpx;line-height:1.5}.type-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14rpx;margin-top:10rpx}.type-chip{margin:0;padding:0;border:0;background:transparent;line-height:normal;@include adaptive.adaptive-profile-field;display:flex;min-height:88rpx;align-items:center;justify-content:center;color:$ink-muted;font-size:22rpx;text-align:center}.type-chip::after{border:0}.type-chip.active{color:$brand-red;font-weight:700;filter:saturate(1.2)}.type-chip[disabled]{opacity:.55}.form-panel{@include adaptive.adaptive-profile-content;margin-top:20rpx;padding:30rpx 34rpx}.form-panel textarea{display:block;width:auto;min-width:0;min-height:210rpx;color:$ink;font-size:24rpx;line-height:1.65}.form-panel textarea[disabled],.contact-row input[disabled]{opacity:.65}.counter{display:block;color:$ink-muted;font-size:20rpx;text-align:right}.contact-row{display:grid;grid-template-columns:130rpx minmax(0,1fr);min-height:88rpx;align-items:center;gap:16rpx;margin-top:16rpx;border-top:1px solid rgba(181,137,63,.38)}.contact-row text{color:$ink;font-size:23rpx;font-weight:700}.contact-row input{width:auto;min-width:0;min-height:68rpx;color:$ink;font-size:22rpx;text-align:right}.field-error{display:block;margin-top:7rpx;color:#b42318;font-size:20rpx;line-height:1.4;text-align:right}.privacy-note{display:block;margin-top:15rpx;color:$ink-muted;font-size:20rpx;line-height:1.5;text-align:center}.feedback-result{margin-top:18rpx;padding:18rpx 20rpx;border:1px solid rgba(122,91,46,.24);border-radius:12rpx;font-size:21rpx;line-height:1.55}.feedback-result--success{border-color:rgba(41,112,66,.34);background:rgba(234,246,237,.88);color:#245f39}.feedback-result--error{border-color:rgba(180,35,24,.3);background:rgba(255,241,239,.9);color:#8f241c}.feedback-result--uncertain{border-color:rgba(155,101,22,.34);background:rgba(255,248,229,.92);color:#76500f}.page-content>.app-button{margin-top:24rpx}@media(max-width:340px){.page-content{padding-right:22rpx;padding-left:22rpx}.form-panel{padding-right:26rpx;padding-left:26rpx}}
|
||||
.feedback-page {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
background: $paper;
|
||||
}
|
||||
.page-layer {
|
||||
z-index: 1;
|
||||
}
|
||||
.page-content {
|
||||
flex: 1;
|
||||
padding: 26rpx 30rpx 72rpx;
|
||||
}
|
||||
.lead {
|
||||
display: block;
|
||||
color: $ink-muted;
|
||||
font-size: 23rpx;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
.type-field {
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.field-note {
|
||||
display: block;
|
||||
color: $ink-muted;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.type-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
.type-chip {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
line-height: normal;
|
||||
@include adaptive.adaptive-profile-field;
|
||||
display: flex;
|
||||
min-height: 88rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $ink-muted;
|
||||
font-size: 22rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.type-chip::after {
|
||||
border: 0;
|
||||
}
|
||||
.type-chip.active {
|
||||
color: $brand-red;
|
||||
font-weight: 700;
|
||||
filter: saturate(1.2);
|
||||
}
|
||||
.type-chip[disabled] {
|
||||
opacity: 0.55;
|
||||
}
|
||||
.form-panel {
|
||||
@include adaptive.adaptive-profile-content;
|
||||
margin-top: 20rpx;
|
||||
padding: 30rpx 34rpx;
|
||||
}
|
||||
.form-panel textarea {
|
||||
display: block;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
min-height: 210rpx;
|
||||
color: $ink;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.form-panel textarea[disabled],
|
||||
.contact-row input[disabled] {
|
||||
opacity: 0.65;
|
||||
}
|
||||
.counter {
|
||||
display: block;
|
||||
color: $ink-muted;
|
||||
font-size: 20rpx;
|
||||
text-align: right;
|
||||
}
|
||||
.contact-row {
|
||||
display: grid;
|
||||
grid-template-columns: 130rpx minmax(0, 1fr);
|
||||
min-height: 88rpx;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-top: 16rpx;
|
||||
border-top: 1px solid rgba(181, 137, 63, 0.38);
|
||||
}
|
||||
.contact-row text {
|
||||
color: $ink;
|
||||
font-size: 23rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.contact-row input {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
min-height: 68rpx;
|
||||
color: $ink;
|
||||
font-size: 22rpx;
|
||||
text-align: right;
|
||||
}
|
||||
.field-error {
|
||||
display: block;
|
||||
margin-top: 7rpx;
|
||||
color: #b42318;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.4;
|
||||
text-align: right;
|
||||
}
|
||||
.privacy-note {
|
||||
display: block;
|
||||
margin-top: 15rpx;
|
||||
color: $ink-muted;
|
||||
font-size: 20rpx;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
.feedback-result {
|
||||
margin-top: 18rpx;
|
||||
padding: 18rpx 20rpx;
|
||||
border: 1px solid rgba(122, 91, 46, 0.24);
|
||||
border-radius: 12rpx;
|
||||
font-size: 21rpx;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.feedback-result--success {
|
||||
border-color: rgba(41, 112, 66, 0.34);
|
||||
background: rgba(234, 246, 237, 0.88);
|
||||
color: #245f39;
|
||||
}
|
||||
.feedback-result--error {
|
||||
border-color: rgba(180, 35, 24, 0.3);
|
||||
background: rgba(255, 241, 239, 0.9);
|
||||
color: #8f241c;
|
||||
}
|
||||
.feedback-result--uncertain {
|
||||
border-color: rgba(155, 101, 22, 0.34);
|
||||
background: rgba(255, 248, 229, 0.92);
|
||||
color: #76500f;
|
||||
}
|
||||
.page-content > .app-button {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
@media (max-width: 340px) {
|
||||
.page-content {
|
||||
padding-right: 22rpx;
|
||||
padding-left: 22rpx;
|
||||
}
|
||||
.form-panel {
|
||||
padding-right: 26rpx;
|
||||
padding-left: 26rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user