feat ui设计稿bug修复

This commit is contained in:
Ironsp
2026-10-01 10:53:13 +08:00
parent 9ab6253c81
commit 1601bdd82c
89 changed files with 4741 additions and 3652 deletions

View File

@@ -1,69 +1,55 @@
<script setup lang="ts">
interface MenuItem {
to: string
icon: string
label: string
}
// 设计稿(个人中心)菜单项 y268/304/340/376/412/448/484/520,行距 36,文字 x179(卡内缩进 24)
const menus: MenuItem[] = [
{ to: '/profile/security', icon: '🔒', label: '账号安全' },
{ to: '/profile/address', icon: '📍', label: '收货地址' },
{ to: '/profile/favorites', icon: '⭐', label: '我的收藏' },
{ to: '/profile/qualifications', icon: '📜', label: '资质管理' },
{ to: '/profile/feedback', icon: '💡', label: '意见反馈' },
{ to: '/profile/help', icon: '❓', label: '帮助中心' },
{ to: '/profile/about', icon: 'ℹ️', label: '关于我们' },
{ to: '/profile/hotline', icon: '📞', label: '客服热线' },
{ to: '/profile/security', label: '账号安全' },
{ to: '/profile/address', label: '地址管理' },
{ to: '/profile/favorites', label: '收藏列表' },
{ to: '/profile/qualifications', label: '资质列表' },
{ to: '/profile/feedback', label: '意见反馈' },
{ to: '/profile/help', label: '帮助中心' },
{ to: '/profile/about', label: '关于我们' },
{ to: '/profile/hotline', label: '专属客服热线' },
]
</script>
<template>
<nav class="profile-sider" aria-label="个人中心导航">
<ul class="profile-sider__menu">
<li v-for="item in menus" :key="item.to">
<RouterLink :to="item.to" class="profile-sider__item">
<span class="profile-sider__label">{{ item.label }}</span>
</RouterLink>
</li>
</ul>
<div class="profile-sider__footer">
<RouterLink
to="/profile/hotline"
class="profile-sider__hotline"
title="专属客服热线"
>
📞 专属客服热线
</RouterLink>
</div>
<RouterLink
v-for="item in menus"
:key="item.to"
:to="item.to"
class="profile-sider__item"
>
{{ item.label }}
</RouterLink>
</nav>
</template>
<style lang="less" scoped>
// 设计稿:菜单为独立白卡(245 宽),选中项浅蓝底 @profile-nav-active-bg 圆角 6
.profile-sider {
.flex(column, space-between);
min-height: 480px;
padding: @spacing-sm 0;
.flex(column, flex-start);
gap: 0;
padding: 4px 0;
border-radius: @radius-lg;
background: @white;
&__menu {
.flex(column, flex-start);
gap: 0;
list-style: none;
}
// 设计 14/28:纯文字菜单,选中项浅蓝底 + 蓝色加粗,行距 36 × 0.77 ≈ 28
&__item {
.flex(row, flex-start, center);
height: 28px;
padding: 0 @spacing-sm;
height: 36px; // 设计行距 36
padding: 0 24px; // 卡 x155 → 文字 x179
border-radius: @profile-radius;
color: @text-color;
font-size: 11px;
line-height: 1;
font-size: @font-size-base; // 设计 12/400
font-weight: 400;
transition: all 0.2s;
&:hover {
background: @primary-light;
color: @primary-strong;
}
@@ -73,25 +59,5 @@ const menus: MenuItem[] = [
font-weight: 600;
}
}
&__label {
white-space: nowrap;
}
&__footer {
padding: @spacing-sm @spacing-sm 0;
border-top: 1px solid @project-divider;
}
&__hotline {
display: inline-block;
font-size: 11px;
color: @text-color-secondary;
transition: color 0.2s;
&:hover {
color: @primary-strong;
}
}
}
</style>

View File

@@ -32,61 +32,52 @@ function save(): void {
<div class="profile-user-card__avatar" aria-hidden="true">
王
</div>
<div class="profile-user-card__info">
<input
v-model="draft"
class="profile-user-card__name"
type="text"
placeholder="请输入名称"
maxlength="20"
@keyup.enter="save"
@blur="save"
>
<p class="profile-user-card__phone text-secondary">
{{ profile?.phone ?? '' }}
</p>
</div>
<input
v-model="draft"
class="profile-user-card__name"
type="text"
placeholder="请输入名称"
maxlength="20"
@keyup.enter="save"
@blur="save"
>
<p class="profile-user-card__phone">
{{ profile?.phone ?? '' }}
</p>
</div>
</template>
<style lang="less" scoped>
// 设计 14:左列紫渐变用户卡(渐变实取 #5459E8 → #D2BDF3),内容居中
// 设计稿(个人中心):头像 46 圆形居中,名称 18/600,手机 14/400;
// 整块 245×110 紫渐变卡,内容垂直居中
.profile-user-card {
.flex(column, center);
gap: @spacing-xs; // 设计 14:头像与文字块间距约 4px
padding: @spacing-base @spacing-sm; // 设计:上 20 / 下 25 设计px ≈ 16
.flex(column, center, center);
height: 110px;
border-radius: @radius-lg;
background: linear-gradient(170deg, @profile-user-from 0%, @profile-user-to 100%);
background: linear-gradient(180deg, @profile-user-from 0%, @profile-user-to 100%);
box-sizing: border-box;
&__avatar {
.flex(row, center);
flex-shrink: 0;
width: 42px; // 设计 54 × 0.77
width: 42px;
height: 42px;
border-radius: @radius-round;
background: @profile-user-avatar;
background: fade(@white, 22%);
color: @white;
font-size: @font-size-lg;
font-weight: 600;
}
&__info {
.flex(column, center);
gap: @spacing-xs;
min-width: 0;
width: 100%;
text-align: center;
}
// 卡内为深色底:名称/号码一律白字(设计 14 实取 #FFFFFF)
&__name {
width: 100%;
height: 16px; // 设计 19 × 0.77
height: 26px;
margin-top: 6px;
padding: 0 @spacing-xs;
border: 1px solid transparent;
border-radius: @profile-radius;
background: transparent;
font-size: 11px;
font-size: @font-size-lg; // 设计 18/600
font-weight: 600;
color: @white;
text-align: center;
@@ -103,15 +94,17 @@ function save(): void {
}
&::placeholder {
font-size: 11px;
font-size: @font-size-lg;
font-weight: 400;
color: fade(@white, 75%);
}
}
&__phone {
font-size: 11px;
margin-top: 6px;
font-size: @font-size-sm; // 设计 14/400
color: @white;
text-align: center;
}
}
</style>

View File

@@ -1,6 +1,5 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { AppButton } from '@/components'
import { computed } from 'vue'
import type { Profile } from '@/types/business/profile'
interface Props {
@@ -17,354 +16,148 @@ const emit = defineEmits<{
deleteAccount: []
}>()
const agree = ref(false)
const hasEmail = computed(() => Boolean(props.profile?.email))
/** 注销条件(mock 全部满足) */
const conditions = [
'账号无未完成的订单',
'无未结清的款项',
'无进行中的合同',
'无未处理的工单',
]
/** 注销风险提示 */
const risks = [
'账号注销后将无法登录本应用,所有个人信息将被永久删除',
'您名下的订单、合同、工单等业务数据将一并清除,无法找回',
'注销后同一手机号/邮箱将无法再次注册本应用',
'如您有未完成的订单或服务请先处理完毕后再进行注销',
]
</script>
<template>
<div class="security-panel">
<!-- 修改密码 -->
<section class="security-panel__row">
<span class="security-panel__icon security-panel__icon--pwd" aria-hidden="true" />
<!-- 设计稿:三行白卡 1672×86,行距 16,左缩进 24 -->
<section
class="security-panel__row"
@click="emit('changePassword')"
>
<div class="security-panel__main">
<h3 class="security-panel__title">
修改密码
</h3>
<p class="security-panel__status">
<span class="security-panel__badge security-panel__badge--ok">已设置</span>
<span class="security-panel__note">上次修改:{{ profile?.passwordModifiedAt }}</span>
<p class="security-panel__sub">
上次修改:{{ profile?.passwordModifiedAt }}
</p>
</div>
<AppButton type="default" @click="emit('changePassword')">
修改密码
</AppButton>
<span class="security-panel__state security-panel__state--ok">已设置</span>
<span class="security-panel__arrow" aria-hidden="true">›</span>
</section>
<!-- 绑定手机 -->
<section class="security-panel__row">
<span class="security-panel__icon security-panel__icon--phone" aria-hidden="true" />
<section
class="security-panel__row"
@click="emit('bindPhone')"
>
<div class="security-panel__main">
<h3 class="security-panel__title">
绑定手机
</h3>
<p class="security-panel__status">
<span class="security-panel__badge security-panel__badge--ok">已绑定 {{ profile?.phone }}</span>
</p>
<p class="security-panel__note">
注:换绑新手机号之后,可以用新的手机号及当前密码登录
<p class="security-panel__sub">
{{ profile?.phone ?? '' }}
</p>
</div>
<AppButton type="default" @click="emit('bindPhone')">
绑定手机
</AppButton>
<span class="security-panel__state security-panel__state--ok">已绑定</span>
<span class="security-panel__arrow" aria-hidden="true">›</span>
</section>
<!-- 绑定邮箱 -->
<section class="security-panel__row">
<span class="security-panel__icon security-panel__icon--email" aria-hidden="true" />
<section
class="security-panel__row"
@click="emit('bindEmail', hasEmail ? 'email-rebind' : 'email-bind')"
>
<div class="security-panel__main">
<h3 class="security-panel__title">
绑定邮箱
</h3>
<p class="security-panel__status">
<template v-if="hasEmail">
<span class="security-panel__badge security-panel__badge--ok">已绑定 {{ profile?.email }}</span>
</template>
<template v-else>
<span class="security-panel__badge">未绑定</span>
</template>
<p class="security-panel__sub">
{{ hasEmail ? profile?.email : '未绑定邮箱' }}
</p>
</div>
<div class="security-panel__actions">
<AppButton
v-if="!hasEmail"
type="default"
@click="emit('bindEmail', 'email-bind')"
>
绑定邮箱
</AppButton>
<template v-else>
<AppButton type="default" @click="emit('bindEmail', 'email-rebind')">
更换绑定邮箱
</AppButton>
<AppButton type="text" @click="emit('unbindEmail')">
取消绑定
</AppButton>
</template>
</div>
<span
class="security-panel__state"
:class="{ 'security-panel__state--ok': hasEmail }"
>{{ hasEmail ? '已绑定' : '未绑定' }}</span>
<span class="security-panel__arrow" aria-hidden="true">›</span>
</section>
<!-- 注销账号(danger 区) -->
<section class="security-panel__danger">
<h3 class="security-panel__danger-title">
注销账号
</h3>
<div class="security-panel__block">
<h4 class="security-panel__block-title security-panel__block-title--ok">
注销条件
</h4>
<ul class="security-panel__checks">
<li v-for="item in conditions" :key="item" class="security-panel__check">
<span class="security-panel__check-ok">✓</span>{{ item }}
</li>
</ul>
</div>
<div class="security-panel__block">
<h4 class="security-panel__block-title security-panel__block-title--risk">
注销风险提示
</h4>
<ol class="security-panel__risks">
<li v-for="(item, index) in risks" :key="item">
{{ index + 1 }}. {{ item }}
</li>
</ol>
</div>
<label class="security-panel__agree">
<input v-model="agree" type="checkbox">
<span>已阅读并同意<em>《账号注销协议》</em></span>
</label>
<AppButton
type="default"
class="security-panel__delete-btn"
:disabled="!agree"
@click="emit('deleteAccount')"
>
注销账号
</AppButton>
</section>
<!-- 设计稿:注销账号 1672×48,灰字,浅灰描边 -->
<button
type="button"
class="security-panel__delete"
@click="emit('deleteAccount')"
>
注销账号
</button>
</div>
</template>
<style lang="less" scoped>
// 设计 28:白卡行 + 浅灰描边、图标块 44×44 设计px、标题 14 设计px w600、副文 11 设计px
// 设计稿(个人中心):
// 行卡 1672×86 bg #FFFFFF bd #F0F1F3/1,行距 16;标题 14/600 #1F2937 @x428
// 副文 11/400 #9CA3AF;右端状态 11/500(已设置 #16A34A / 未绑定 #6B7280)+ 箭头 ›
// 注销按钮 1672×48 bd #E5E7EB/1,文字 15/500 #9CA3AF
.security-panel {
.flex(column, flex-start);
.flex(column, flex-start, stretch);
gap: @spacing-base;
&__row {
.flex(row, flex-start, center);
gap: @spacing-base;
padding: @spacing-base;
.flex(row, space-between, center);
height: 86px;
padding: 0 24px;
border: 1px solid @profile-card-border;
border-radius: @radius-base;
background: @white;
}
box-sizing: border-box;
cursor: pointer;
transition: border-color 0.2s;
// 设计 28 实取图标块底色:#C7D2FE / #FDEBEB / #FEE2E2
&__icon {
flex-shrink: 0;
width: 34px; // 设计 44 × 0.77
height: 34px;
border-radius: @radius-base;
&--pwd {
background: @profile-icon-pwd;
}
&--phone {
background: @profile-icon-phone;
}
&--email {
background: @profile-icon-email;
&:hover {
border-color: @profile-line;
}
}
&__main {
flex: 1;
min-width: 0;
}
&__title {
font-size: 11px; // 设计 14 → 10.8,取可读下限 11
font-size: @font-size-base; // 设计 14/600
font-weight: 600;
color: @heading-color;
color: @profile-ink;
}
&__status {
.flex(row, flex-start, center);
flex-wrap: wrap;
gap: @spacing-sm;
margin-top: @spacing-xs;
font-size: 11px;
&__sub {
margin-top: 6px;
font-size: @font-size-sm; // 设计 11/400
color: @finance-axis-text; // #9CA3AF
}
// 设计 28:已设置/已绑定 = 浅绿底绿字胶囊,未绑定 = 纯灰字
&__badge {
padding: 2px 8px;
border-radius: @radius-sm;
background: transparent;
color: @text-color-secondary;
font-size: 11px;
&__state {
margin-left: auto;
font-size: @font-size-sm;
font-weight: 500;
color: @finance-muted-text; // 未绑定 #6B7280
&--ok {
background: @tag-success-bg;
color: @success-color;
color: @success-color; // 已设置 / 已绑定 #16A34A
}
}
&__note {
margin-top: @spacing-xs;
color: @text-color-disabled;
font-size: 11px;
line-height: 1.6;
&__arrow {
margin-left: 20px;
font-size: 21px;
line-height: 1;
color: @finance-axis-text;
}
&__actions {
.flex(row, center);
gap: @spacing-sm;
flex-shrink: 0;
}
// 行内操作按钮:白底灰描边小按钮(设计 20/17 操作按钮同规格)
.app-button {
height: 28px;
padding: 0 12px;
&__delete {
width: 100%;
height: 48px;
border: 1px solid @profile-line;
border-radius: @profile-radius;
background: @white;
color: @text-color;
font-size: 11px;
font-weight: 500;
&:hover:not(:disabled) {
border-color: @primary-strong;
color: @primary-strong;
background: @white;
}
}
.app-button--text {
border-color: transparent;
background: transparent;
color: @text-color;
&:hover:not(:disabled) {
border-color: transparent;
background: transparent;
color: @primary-strong;
}
}
&__danger {
.flex(column, flex-start);
gap: @spacing-base;
padding: @spacing-lg;
border: 1px solid @profile-card-border;
border-radius: @radius-base;
background: @white;
}
&__danger-title {
font-size: 12px; // 设计 15 → 12
font-weight: 700;
color: @danger-color;
}
&__block {
.flex(column, flex-start);
gap: @spacing-sm;
}
// 设计 29:注销条件标题绿、注销风险提示标题红
&__block-title {
font-size: 11px;
font-weight: 600;
&--ok {
color: @success-color;
}
&--risk {
color: @danger-color;
}
}
&__checks {
.flex(column, flex-start);
gap: @spacing-xs;
list-style: none;
}
&__check {
font-size: 11px;
color: @text-color;
}
&__check-ok {
margin-right: @spacing-xs;
font-weight: 600;
color: @success-color;
}
&__risks {
margin: 0;
padding-left: @spacing-base;
.flex(column, flex-start);
gap: @spacing-xs;
font-size: 11px;
color: @text-color;
line-height: 1.6;
}
&__agree {
.flex(row, flex-start, center);
gap: @spacing-sm;
font-size: 11px;
color: @text-color-secondary;
cursor: pointer;
input {
accent-color: @primary-strong;
}
em {
font-style: normal;
color: @primary-strong;
}
}
// 注销账号:白底灰描边整行按钮(设计 28 实取文字 #9CA3AF、15 设计px)
&__delete-btn {
width: 100%;
height: 36px;
border: 1px solid @profile-line !important;
background: @white !important;
color: @text-color-disabled !important;
font-size: 12px;
color: @finance-axis-text;
font-size: @font-size-base; // 设计 15/500
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
&:hover:not(:disabled) {
border-color: @danger-color !important;
background: @white !important;
color: @danger-color !important;
}
&:disabled {
opacity: 1;
cursor: not-allowed;
&:hover {
border-color: @danger-color;
color: @danger-color;
}
}
}