first
This commit is contained in:
237
user-app/src/pages/settings/index.vue
Normal file
237
user-app/src/pages/settings/index.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { onShow } from "@dcloudio/uni-app";
|
||||
import { appApi, type SettingsData } from "../../api/app";
|
||||
import { authApi } from "../../api/auth";
|
||||
import { settingsFallback } from "../../mocks/app";
|
||||
import { useAppraisalStore } from "../../stores/appraisal";
|
||||
import { resolveErrorMessage, showErrorToast, showInfoToast, withLoading } from "../../utils/feedback";
|
||||
import { logoutAndRedirect } from "../../utils/auth";
|
||||
import { setPrivacyMode } from "../../utils/privacy";
|
||||
|
||||
const detail = ref<SettingsData>(settingsFallback);
|
||||
const saving = ref(false);
|
||||
const nickname = ref(settingsFallback.profile_info.nickname);
|
||||
const appraisalStore = useAppraisalStore();
|
||||
const loading = ref(false);
|
||||
const pageReady = ref(false);
|
||||
const loadError = ref("");
|
||||
|
||||
function goBack() {
|
||||
uni.navigateBack();
|
||||
}
|
||||
|
||||
function openLegal(url: string) {
|
||||
if (!url) return;
|
||||
uni.navigateTo({ url });
|
||||
}
|
||||
|
||||
function openPasswordPage() {
|
||||
const hasPassword = detail.value.profile_info.password_set ? "1" : "0";
|
||||
uni.navigateTo({ url: `/pages/settings/password?has_password=${hasPassword}` });
|
||||
}
|
||||
|
||||
function handlePreferenceChange(key: keyof SettingsData["preferences"], value: boolean) {
|
||||
detail.value.preferences[key] = value;
|
||||
}
|
||||
|
||||
function handlePreferenceEvent(key: keyof SettingsData["preferences"], event: any) {
|
||||
handlePreferenceChange(key, !!event?.detail?.value);
|
||||
}
|
||||
|
||||
async function fetchSettings() {
|
||||
loading.value = true;
|
||||
if (!pageReady.value) {
|
||||
loadError.value = "";
|
||||
}
|
||||
try {
|
||||
const data = await appApi.getSettings();
|
||||
detail.value = data;
|
||||
nickname.value = data.profile_info.nickname;
|
||||
setPrivacyMode(data.preferences.privacy_mode);
|
||||
pageReady.value = true;
|
||||
} catch (error) {
|
||||
console.warn("settings fallback", error);
|
||||
if (!pageReady.value) {
|
||||
loadError.value = resolveErrorMessage(error, "设置加载失败,请稍后重试。");
|
||||
} else {
|
||||
showErrorToast(error, "设置加载失败");
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
if (!nickname.value.trim()) {
|
||||
showInfoToast("昵称不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
try {
|
||||
const data = await withLoading("正在保存设置", async () =>
|
||||
appApi.saveSettings({
|
||||
nickname: nickname.value.trim(),
|
||||
preferences: detail.value.preferences,
|
||||
}),
|
||||
);
|
||||
detail.value = data;
|
||||
nickname.value = data.profile_info.nickname;
|
||||
setPrivacyMode(data.preferences.privacy_mode);
|
||||
showInfoToast("设置已保存");
|
||||
} catch (error) {
|
||||
showErrorToast(error, "设置保存失败");
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
await authApi.logout();
|
||||
} catch (error) {
|
||||
console.warn("logout failed", error);
|
||||
} finally {
|
||||
appraisalStore.resetForNewFlow();
|
||||
logoutAndRedirect();
|
||||
}
|
||||
}
|
||||
|
||||
onShow(fetchSettings);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="app-page app-page--tight">
|
||||
<view v-if="!pageReady && loading" class="section notice-card">
|
||||
<view class="notice-card__title">正在加载设置</view>
|
||||
<view class="notice-card__desc">请稍候,我们正在同步账号资料与通知偏好。</view>
|
||||
</view>
|
||||
|
||||
<view v-else-if="!pageReady && loadError" class="section notice-card">
|
||||
<view class="notice-card__title">设置加载失败</view>
|
||||
<view class="notice-card__desc">{{ loadError }}</view>
|
||||
<view style="margin-top: 20rpx">
|
||||
<text class="btn btn--ghost" @click="fetchSettings">重新加载</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template v-else>
|
||||
<view class="section-card">
|
||||
<view class="tag tag--accent">设置中心</view>
|
||||
<view class="page-title" style="margin-top: 20rpx; font-size: var(--font-size-2xl); color: var(--color-heading);">
|
||||
账号与通知偏好
|
||||
</view>
|
||||
<view class="section__desc">您可以在这里维护昵称、查看账号状态,并选择希望接收的服务通知类型。</view>
|
||||
</view>
|
||||
|
||||
<view class="section section-card section-card--soft">
|
||||
<view class="section__title">账号资料</view>
|
||||
<view class="form-group">
|
||||
<view class="form-group__label">昵称</view>
|
||||
<view class="field-box">
|
||||
<input v-model="nickname" class="field-input" maxlength="20" placeholder="请输入昵称" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="report-meta__row" style="margin-top: 20rpx">
|
||||
<text class="report-meta__label">手机号</text>
|
||||
<text class="report-meta__value">{{ detail.profile_info.mobile }}</text>
|
||||
</view>
|
||||
<view class="report-meta__row">
|
||||
<text class="report-meta__label">账号状态</text>
|
||||
<text class="report-meta__value">{{ detail.profile_info.status_text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section section-card">
|
||||
<view class="section__title">登录与安全</view>
|
||||
<view class="section__desc">可为当前账号设置或修改登录密码,用于手机号 + 密码登录。</view>
|
||||
<view class="info-list__row" style="margin-top: 20rpx" @click="openPasswordPage">
|
||||
<text class="info-list__label">登录密码</text>
|
||||
<text class="info-list__value">{{ detail.profile_info.password_set ? "已设置,去修改" : "未设置,去创建" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section form-panel">
|
||||
<view class="form-panel__title">通知偏好</view>
|
||||
<view class="form-panel__desc">关闭某类提醒后,不会影响订单和工单本身处理,只会减少消息提醒。</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">订单通知</view>
|
||||
<view class="form-group__hint">下单成功、运单提交等订单相关提醒</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.notify_order" color="#edbd00" @change="handlePreferenceEvent('notify_order', $event)" />
|
||||
</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">报告通知</view>
|
||||
<view class="form-group__hint">报告出具、验真与下载相关提醒</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.notify_report" color="#edbd00" @change="handlePreferenceEvent('notify_report', $event)" />
|
||||
</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">补资料通知</view>
|
||||
<view class="form-group__hint">鉴定师发起补资料后的提醒</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.notify_supplement" color="#edbd00" @change="handlePreferenceEvent('notify_supplement', $event)" />
|
||||
</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">工单通知</view>
|
||||
<view class="form-group__hint">客服回复、工单状态变化相关提醒</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.notify_ticket" color="#edbd00" @change="handlePreferenceEvent('notify_ticket', $event)" />
|
||||
</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">营销与活动提醒</view>
|
||||
<view class="form-group__hint">接收活动、权益和服务升级通知</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.marketing_notify" color="#edbd00" @change="handlePreferenceEvent('marketing_notify', $event)" />
|
||||
</view>
|
||||
|
||||
<view class="setting-row">
|
||||
<view>
|
||||
<view class="form-group__label">隐私模式</view>
|
||||
<view class="form-group__hint">减少在列表页展示部分敏感账号信息</view>
|
||||
</view>
|
||||
<switch :checked="detail.preferences.privacy_mode" color="#edbd00" @change="handlePreferenceEvent('privacy_mode', $event)" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section section-card">
|
||||
<view class="section__title">说明与协议</view>
|
||||
<view
|
||||
v-for="item in detail.legal_entries"
|
||||
:key="item.code"
|
||||
class="info-list__row"
|
||||
@click="openLegal(item.target_url)"
|
||||
>
|
||||
<text class="info-list__label">{{ item.title }}</text>
|
||||
<text class="info-list__value">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section section-card">
|
||||
<view class="section__title">登录管理</view>
|
||||
<view class="section__desc">当前设备退出后,需要重新使用手机号验证码或密码登录。</view>
|
||||
<view style="margin-top: 20rpx;">
|
||||
<text class="btn btn--secondary" @click="handleLogout">退出登录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="fixed-action-bar">
|
||||
<view class="btn btn--secondary" @click="goBack">返回</view>
|
||||
<view :class="['btn', 'btn--primary', saving ? 'btn--disabled' : '']" @click="saveSettings">
|
||||
{{ saving ? "保存中..." : "保存设置" }}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
Reference in New Issue
Block a user