修改部分样式

This commit is contained in:
rain
2026-07-27 17:29:58 +08:00
parent 04287e6777
commit 1e9e25fe67
53 changed files with 11562 additions and 2617 deletions
+239 -26
View File
@@ -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>