Files
anxinyan/user-app/src/pages/settings/index.vue
wushumin 9aac78b8da first
2026-05-11 15:28:27 +08:00

238 lines
8.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>