Files
----/前端源码/uni-app/components/screens/Renaming.vue

1298 lines
35 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.
<template>
<view class="renaming-screen">
<view class="renaming-bg"></view>
<!-- 状态栏占位 -->
<view class="status-bar-placeholder"></view>
<!-- 主体 -->
<scroll-view scroll-y class="renaming-scroll">
<!-- 表单 -->
<view v-if="step === 'form'" class="renaming-form">
<!-- 顶部模式切换 -->
<view class="renaming-mode-switch">
<view class="renaming-mode-switch-bg">
<view class="renaming-mode-switch-slider"
:style="{ left: mode === 'personal' ? '4px' : '50%', width: '50%' }" />
<view class="renaming-mode-btn" :class="mode === 'personal' ? 'renaming-mode-btn-active' : ''"
@click="mode = 'personal'">个人改名</view>
<view class="renaming-mode-btn" :class="mode === 'company' ? 'renaming-mode-btn-active' : ''"
@click="mode = 'company'">公司改名</view>
</view>
</view>
<!-- <view class="renaming-form-header">
<view class="renaming-form-title">
{{ mode === 'personal' ? '易名改运' : '重塑品牌' }}
</view>
<view class="renaming-form-subtitle">
{{ mode === 'personal' ? '名不正则言不顺,改名以补运势之缺' : '顺应时代变迁,重塑品牌形象' }}
</view>
</view> -->
<!-- 个人表单 -->
<view v-if="mode === 'personal'" class="renaming-form-section">
<view class="renaming-row-inner">
<text class="renaming-row-label renaming-row-label-highlight">原名</text>
<input v-model="personal.originalName" placeholder="请输入当前姓名" class="renaming-input" />
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">姓氏</text>
<input v-model="personal.lastName" placeholder="保留姓氏" class="renaming-input" />
</view>
<view class="renaming-row-inner renaming-row-inner-radio">
<text class="renaming-row-label">性别</text>
<view class="renaming-radio-group">
<view class="renaming-radio" @click="personal.gender = 'male'">
<view class="renaming-radio-outer"
:class="personal.gender === 'male' ? 'renaming-radio-outer-active' : ''">
<view v-if="personal.gender === 'male'" class="renaming-radio-inner"></view>
</view>
<text class="renaming-radio-text"></text>
</view>
<view class="renaming-radio" @click="personal.gender = 'female'">
<view class="renaming-radio-outer"
:class="personal.gender === 'female' ? 'renaming-radio-outer-active' : ''">
<view v-if="personal.gender === 'female'" class="renaming-radio-inner"></view>
</view>
<text class="renaming-radio-text"></text>
</view>
</view>
</view>
<view class="renaming-row-inner" @click="activeDateField = 'personal'">
<text class="renaming-row-label">生辰</text>
<view class="renaming-picker-text" :class="{ 'renaming-picker-text-filled': personal.birthDateDisplay }">
{{ personal.birthDateDisplay || '请选择生辰' }}
</view>
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">出生地</text>
<input v-model="personal.birthPlace" placeholder="请填写出身地" class="renaming-input" />
</view>
<view class="renaming-row-inner" @click="showStylePicker = true">
<text class="renaming-row-label">偏好</text>
<view class="renaming-picker-text renaming-picker-text-filled">
{{ personal.styleLabel }}
</view>
</view>
<view class="renaming-subsection">
<text class="renaming-subsection-title">改名要求</text>
<textarea v-model="personal.reason" placeholder="事业瓶颈/婚姻不顺等" class="renaming-textarea" />
</view>
<view class="renaming-subsection">
<text class="renaming-subsection-title">家族信息 (选填)</text>
<view class="renaming-row-inner">
<text class="renaming-row-label renaming-form-label-alt">家谱字辈</text>
<input v-model="personal.familyBook" placeholder="中间字/末尾字" class="renaming-input renaming-input-sm" />
</view>
<view class="renaming-two-col">
<view class="renaming-two-col-left">
<text class="renaming-two-col-left-label">父亲</text>
<input v-model="personal.fatherName" placeholder="姓名" class="renaming-two-col-left-input" />
</view>
<view class="renaming-two-col-right" @click="activeDateField = 'father'">
<view class="renaming-date-trigger"
:class="{ 'renaming-date-trigger-filled': personal.fatherBirthDateDisplay }">
{{ personal.fatherBirthDateDisplay || '选择生辰' }}
</view>
</view>
</view>
<view class="renaming-two-col">
<view class="renaming-two-col-left">
<text class="renaming-two-col-left-label">母亲</text>
<input v-model="personal.motherName" placeholder="姓名" class="renaming-two-col-left-input" />
</view>
<view class="renaming-two-col-right" @click="activeDateField = 'mother'">
<view class="renaming-date-trigger"
:class="{ 'renaming-date-trigger-filled': personal.motherBirthDateDisplay }">
{{ personal.motherBirthDateDisplay || '选择生辰' }}
</view>
</view>
</view>
</view>
</view>
<!-- 公司表单 -->
<view v-else class="renaming-form-section">
<view class="renaming-row-inner">
<text class="renaming-row-label renaming-row-label-highlight">原商号</text>
<input v-model="company.originalName" placeholder="输入当前商号" class="renaming-input" />
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">所属行业</text>
<input v-model="company.industry" placeholder="如:科技、餐饮、贸易" class="renaming-input" />
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">地址</text>
<input v-model="company.address" placeholder="如:北京" class="renaming-input" />
</view>
<view class="renaming-subsection">
<text class="renaming-subsection-title">改名要求</text>
<textarea v-model="company.reason" placeholder="市场遇冷/品牌升级/合伙人纠纷等" class="renaming-textarea" />
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">愿景</text>
<input v-model="company.vision" placeholder="如:做行业第一、走向国际化" class="renaming-input" />
</view>
<view class="renaming-row-inner">
<text class="renaming-row-label">偏好</text>
<input v-model="company.preference" placeholder="如2字/3字偏传统/偏现代" class="renaming-input" />
</view>
<view class="renaming-subsection">
<view class="renaming-subsection-header">
<text class="renaming-subsection-title">核心成员</text>
<text class="renaming-subsection-tip">创始人必填合伙人选填</text>
</view>
<!-- 创始人 -->
<view class="renaming-member-item">
<view class="renaming-member-tag">创始人</view>
<input v-model="company.founderName" placeholder="姓名" class="renaming-member-name" />
<view class="renaming-member-date" @click="activeDateField = 'founder'">
<text :class="{ 'renaming-member-date-filled': company.founderBirthDisplay }">
{{ company.founderBirthDisplay || '选择生辰' }}
</text>
</view>
</view>
<!-- 合伙人列表 -->
<view v-for="(partner, idx) in company.partners" :key="idx" class="renaming-member-item">
<view class="renaming-member-tag renaming-member-tag-partner">合伙人{{ Number(idx) + 1 }}</view>
<input v-model="partner.name" placeholder="姓名" class="renaming-member-name" />
<view class="renaming-member-date" @click="activeDateField = `partner-${idx}`">
<text :class="{ 'renaming-member-date-filled': partner.birthDisplay }">
{{ partner.birthDisplay || '选择生辰' }}
</text>
</view>
<view class="renaming-member-remove" @click="removePartner(idx)">×</view>
</view>
<!-- 添加合伙人按钮 -->
<view v-if="company.partners.length < 5" class="renaming-add-partner" @click="addPartner">
<text class="renaming-add-partner-icon">+</text>
<text class="renaming-add-partner-text">添加合伙人 ({{ company.partners.length }}/5)</text>
</view>
</view>
</view>
<button class="renaming-submit-btn" @click="start">
<view class="renaming-submit-btn-overlay"></view>
<text class="renaming-submit-btn-text">{{ mode === 'personal' ? '立即改名' : '重塑商号' }}</text>
</button>
</view>
<!-- Loading -->
<view v-else-if="step === 'loading'" class="renaming-loading">
<MysticCompass :title="mode === 'personal' ? '改名推演中' : '商号重塑中'"
:subtitle="mode === 'personal' ? '正在分析原名运势与改名方案...' : '正在分析品牌五行与重塑策略...'" @back="handleLoadingBack" />
</view>
<!-- 结果 -->
<view v-else class="renaming-result">
<view class="renaming-result-header">
<text class="renaming-result-title">改名方案</text>
<button class="renaming-result-reset" @click="step = 'form'">
<text>重测</text>
</button>
</view>
<view class="renaming-result-list">
<view v-for="item in results" :key="item.id" class="renaming-result-item">
<view class="renaming-result-item-bg"></view>
<view class="renaming-result-item-actions">
<button class="renaming-result-action-btn renaming-result-action-btn-heart"
:class="item.isFavorite ? 'renaming-result-action-btn-heart-active' : ''"
@click.stop="toggleFav(item.id)"></button>
</view>
<view class="renaming-result-item-content" @click="handleItemClick(item)">
<text class="renaming-result-item-name">{{ item.name }}</text>
<text class="renaming-result-item-pinyin">{{ item.pinyin }}</text>
</view>
<view class="renaming-result-item-tags" @click="handleItemClick(item)">
<text v-for="(t, i) in item.tags" :key="i" class="renaming-result-tag">{{ t }}</text>
</view>
<view class="renaming-result-item-divider"></view>
<view class="renaming-result-item-details" @click="handleItemClick(item)">
<view class="renaming-result-detail-row">
<text class="renaming-result-detail-label renaming-result-detail-label-primary">寓意</text>
<text class="renaming-result-detail-text">{{ item.meaning }}</text>
</view>
<view class="renaming-result-detail-row">
<text class="renaming-result-detail-label">出处</text>
<text class="renaming-result-detail-text renaming-result-detail-text-small">{{ item.source }}</text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 日期选择器 -->
<MysticDatePicker :is-open="!!activeDateField" :title="activeDateField === 'personal' ? '请择良辰' : '选择生辰'"
:default-value="currentDateValue" @close="activeDateField = null" @confirm="handleDateConfirm" />
<!-- 偏好选择器 -->
<MysticSelect :is-open="showStylePicker" title="选择偏好" tip="根据您的喜好推荐合适的名字风格" :options="styleOptions"
:default-value="personal.style" @close="showStylePicker = false" @confirm="handleStyleConfirm" />
<!-- 支付弹窗 -->
<view v-if="false" class="renaming-modal" @click="showPay = false">
<view class="renaming-modal-content" @click.stop>
<button class="renaming-modal-close" @click="showPay = false">×</button>
<view class="renaming-modal-header">
<text class="renaming-modal-header-title">改名解析</text>
<text class="renaming-modal-header-subtitle">
{{ mode === 'personal' ? '个人改名运势报告' : '企业品牌重塑深度分析' }}
</text>
</view>
<view class="renaming-modal-body">
<text class="renaming-modal-item"> 新旧名字运势对比分析</text>
<text class="renaming-modal-item"> 五行补救与转运时机</text>
<text class="renaming-modal-item"> PDF 报告 & 品牌策略建议</text>
<view class="renaming-modal-price">
¥{{ mode === 'personal' ? '666' : '888' }} <text class="renaming-modal-price-unit">/</text>
</view>
<button class="renaming-modal-pay-btn" @click="pay">立即解锁深度解析</button>
<text class="renaming-modal-note">支付后自动生成并发送</text>
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { reactive, ref, computed, onUnmounted } from "vue";
import MysticDatePicker from '../MysticDatePicker.vue';
import MysticSelect, { type SelectOption } from '../MysticSelect.vue';
import MysticCompass from '../MysticCompass.vue';
import { userApi } from "@/api";
import { namingApi } from '../../api/naming';
import { pollSolutionDetailUntilReady, pollUntilSolutionIdFromScoring } from '../../utils/poll-test-solution-detail';
declare const uni: any;
type Mode = "personal" | "company";
type Step = "form" | "loading" | "result";
const mode = ref<Mode>("personal");
const step = ref<Step>("form");
const showPay = ref(false);
let pollAbort: AbortController | null = null;
const emit = defineEmits<{
showDetail: [data: any, mode: Mode];
}>();
// 日期选择器状态
const activeDateField = ref<string | null>(null);
// 下拉选择器状态
const showStylePicker = ref(false);
const personal = reactive({
originalName: "",
reason: "",
lastName: "",
gender: "male",
birthDateDisplay: "",
birthDateApi: "",
birthPlace: "",
style: "典雅",
styleLabel: "典雅古风",
fatherName: "",
fatherBirthDateDisplay: "",
fatherBirthDateApi: "",
motherName: "",
motherBirthDateDisplay: "",
motherBirthDateApi: "",
familyBook: "",
});
const styleOptions: SelectOption[] = [
{ value: "典雅", label: "典雅古风", desc: "诗词歌赋,古韵悠长" },
{ value: "大气", label: "大气磅礴", desc: "气势恢宏,志存高远" },
{ value: "新颖", label: "新颖独特", desc: "别具一格,独树一帜" },
{ value: "温婉", label: "温婉贤淑", desc: "温柔似水,贤良淑德" },
];
// 获取当前日期选择器的默认值
const currentDateValue = computed(() => {
if (!activeDateField.value) return '';
if (activeDateField.value === 'personal') return personal.birthDateDisplay;
if (activeDateField.value === 'father') return personal.fatherBirthDateDisplay;
if (activeDateField.value === 'mother') return personal.motherBirthDateDisplay;
if (activeDateField.value === 'founder') return company.founderBirthDisplay;
if (activeDateField.value.startsWith('partner-')) {
const idx = parseInt(activeDateField.value.split('-')[1]);
return company.partners[idx]?.birthDisplay || '';
}
return '';
});
const handleDateConfirm = (displayVal: string, apiVal: string) => {
if (!activeDateField.value) return;
if (activeDateField.value === 'personal') {
personal.birthDateDisplay = displayVal;
personal.birthDateApi = apiVal;
} else if (activeDateField.value === 'father') {
personal.fatherBirthDateDisplay = displayVal;
personal.fatherBirthDateApi = apiVal;
} else if (activeDateField.value === 'mother') {
personal.motherBirthDateDisplay = displayVal;
personal.motherBirthDateApi = apiVal;
} else if (activeDateField.value === 'founder') {
company.founderBirthDisplay = displayVal;
company.founderBirthApi = apiVal;
} else if (activeDateField.value.startsWith('partner-')) {
const idx = parseInt(activeDateField.value.split('-')[1]);
if (company.partners[idx]) {
company.partners[idx].birthDisplay = displayVal;
company.partners[idx].birthApi = apiVal;
}
}
activeDateField.value = null;
};
const handleStyleConfirm = (option: SelectOption) => {
personal.style = option.value;
personal.styleLabel = option.label;
showStylePicker.value = false;
};
interface Partner {
name: string;
birthDisplay: string;
birthApi: string;
}
const company = reactive({
originalName: "",
reason: "",
industry: "",
address: "",
vision: "",
preference: "",
founderName: "",
founderBirthDisplay: "",
founderBirthApi: "",
partners: [] as Partner[],
});
const addPartner = () => {
if (company.partners.length < 5) {
company.partners.push({ name: "", birthDisplay: "", birthApi: "" });
}
};
const removePartner = (index: number) => {
company.partners.splice(index, 1);
};
type GeneratedName = {
id: string;
name: string;
pinyin: string;
meaning: string;
source: string;
tags: string[];
total_score?: number;
star_rating?: number;
name_meaning?: string;
poetry_source?: string;
isFavorite?: boolean;
};
const results = ref<GeneratedName[]>([]);
const start = async () => {
results.value = [];
pollAbort?.abort();
pollAbort = new AbortController();
const signal = pollAbort.signal;
if (mode.value === "personal") {
if (!personal.originalName) {
uni.showToast({ title: "请填写原名", icon: "none" });
return;
}
if (!personal.lastName) {
uni.showToast({ title: "请填写姓氏", icon: "none" });
return;
}
if (!personal.birthDateDisplay) {
uni.showToast({ title: "请选择生辰", icon: "none" });
return;
}
if (!personal.birthPlace) {
uni.showToast({ title: "请填写出生地", icon: "none" });
return;
}
if (!personal.reason) {
uni.showToast({ title: "请填写改名要求", icon: "none" });
return;
}
step.value = "loading";
const loadingStartTime = Date.now();
const waitMinLoading = async () => {
const elapsed = Date.now() - loadingStartTime;
const minLoadingTime = 1700;
const remainingTime = Math.max(0, minLoadingTime - elapsed);
if (remainingTime > 0) {
await new Promise<void>((resolve) => setTimeout(resolve, remainingTime));
}
};
try {
const response: any = await namingApi.personalRenaming({
last_name: String(personal.lastName || '').trim(),
gender: String(personal.gender || '').trim(),
birth_date: personal.birthDateApi || personal.birthDateDisplay,
birth_place: String(personal.birthPlace || '').trim(),
style_label: String(personal.styleLabel || personal.style || '').trim(),
father_name: String(personal.fatherName || '').trim() || undefined,
father_birth_date: personal.fatherBirthDateApi || undefined,
mother_name: String(personal.motherName || '').trim() || undefined,
mother_birth_date: personal.motherBirthDateApi || undefined,
family_book: String(personal.familyBook || '').trim() || undefined,
original_name: String(personal.originalName || '').trim(),
reason: String(personal.reason || '').trim(),
});
const solutionId = await pollUntilSolutionIdFromScoring(response, {
intervalMs: 5000,
signal,
});
const detail = await pollSolutionDetailUntilReady(solutionId, {
intervalMs: 5000,
signal,
});
await waitMinLoading();
emit('showDetail', detail as any, mode.value);
} catch (error) {
if (String((error as any)?.message || "") === "aborted") return;
console.error('个人改名接口调用失败:', error);
await waitMinLoading();
const msg = (error as any)?.msg || (error as any)?.message || '接口请求失败';
uni.showToast({ title: msg, icon: 'none' });
step.value = 'form';
}
return;
}
if (!company.originalName) {
uni.showToast({ title: "请填写原商号", icon: "none" });
return;
}
if (!company.industry) {
uni.showToast({ title: "请填写所属行业", icon: "none" });
return;
}
if (!company.address) {
uni.showToast({ title: "请填写地址", icon: "none" });
return;
}
if (!company.reason) {
uni.showToast({ title: "请填写改名要求", icon: "none" });
return;
}
if (!company.vision) {
uni.showToast({ title: "请填写愿景", icon: "none" });
return;
}
if (!company.preference) {
uni.showToast({ title: "请填写偏好", icon: "none" });
return;
}
if (!company.founderName) {
uni.showToast({ title: "请填写创始人姓名", icon: "none" });
return;
}
if (!company.founderBirthDisplay) {
uni.showToast({ title: "请选择创始人生辰", icon: "none" });
return;
}
step.value = "loading";
const loadingStartTime = Date.now();
const waitMinLoading = async () => {
const elapsed = Date.now() - loadingStartTime;
const minLoadingTime = 1700;
const remainingTime = Math.max(0, minLoadingTime - elapsed);
if (remainingTime > 0) {
await new Promise<void>((resolve) => setTimeout(resolve, remainingTime));
}
};
try {
const coreMembers = [
{
name: String(company.founderName || '').trim(),
birthday: company.founderBirthApi || company.founderBirthDisplay,
},
...company.partners
.filter((p: Partner) => String(p?.name || '').trim() && (p.birthApi || p.birthDisplay))
.map((p: Partner) => ({
name: String(p.name || '').trim(),
birthday: p.birthApi || p.birthDisplay,
})),
];
const response: any = await namingApi.companyRenaming({
industry: String(company.industry || '').trim(),
address: String(company.address || '').trim(),
core_members: coreMembers,
vision: String(company.vision || '').trim(),
preference: String(company.preference || '').trim(),
original_name: String(company.originalName || '').trim(),
reason: String(company.reason || '').trim(),
});
const solutionId = await pollUntilSolutionIdFromScoring(response, {
intervalMs: 5000,
signal,
});
const detail = await pollSolutionDetailUntilReady(solutionId, {
intervalMs: 5000,
signal,
});
await waitMinLoading();
emit('showDetail', detail as any, mode.value);
} catch (error) {
if (String((error as any)?.message || "") === "aborted") return;
console.error('企业改名接口调用失败:', error);
await waitMinLoading();
const msg = (error as any)?.msg || (error as any)?.message || '接口请求失败';
uni.showToast({ title: msg, icon: 'none' });
step.value = 'form';
}
};
const toggleFav = async (id: string) => {
const idx = results.value.findIndex((r: GeneratedName) => r.id === id);
if (idx < 0) return;
const solutionId = Number(id);
if (!Number.isFinite(solutionId)) {
const prev = !!results.value[idx].isFavorite;
results.value[idx].isFavorite = !prev;
return;
}
const prev = !!results.value[idx].isFavorite;
try {
if (!prev) {
const res = await userApi.favoriteSolution({ solution_id: solutionId, category: mode.value });
results.value[idx].isFavorite = true;
uni.showToast({ title: res?.msg || '收藏成功', icon: 'success' });
} else {
const res = await userApi.unfavoriteSolution({ solution_id: solutionId });
results.value[idx].isFavorite = false;
uni.showToast({ title: res?.msg || '已取消收藏', icon: 'success' });
}
} catch (e: any) {
uni.showToast({ title: e?.msg || '操作失败', icon: 'none' });
}
};
const handleItemClick = (item: GeneratedName) => {
emit('showDetail', item, mode.value);
};
// 处理 loading 页面的返回按钮
const handleLoadingBack = () => {
pollAbort?.abort();
uni.showToast({
title: '测算结果可在"我的方案"中查看',
icon: 'none',
duration: 2000
});
step.value = 'form';
};
onUnmounted(() => {
pollAbort?.abort();
});
const pay = () => {
uni.showToast({ title: '缺少业务ID暂无法发起正式支付', icon: 'none' });
showPay.value = false;
};
</script>
<style scoped>
.renaming-screen {
height: 100%;
display: flex;
flex-direction: column;
position: relative;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
background-color: #f0efe9;
}
/* 状态栏占位 */
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
.renaming-bg {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.3;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
}
.renaming-mode-switch {
padding: 0 0 20px;
margin-bottom: 12px;
}
.renaming-mode-switch-bg {
background-color: #eaddcf;
padding: 4px;
border-radius: 999px;
position: relative;
display: flex;
overflow: hidden;
}
.renaming-mode-switch-slider {
position: absolute;
top: 4px;
bottom: 4px;
border-radius: 999px;
background-color: #8b2323;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
z-index: 0;
transition: left 0.25s ease;
}
.renaming-mode-btn {
flex: 1;
position: relative;
z-index: 1;
padding: 12px 0;
border: none;
background: transparent;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.2em;
color: #5a5a5a;
display: flex;
justify-content: center;
align-items: center;
}
.renaming-mode-btn-active {
color: #f2e6d8;
}
.renaming-scroll {
flex: 1;
height: 0;
position: relative;
z-index: 1;
}
.renaming-form {
width: 100%;
max-width: 100%;
padding: 20px 20px 54px;
display: flex;
flex-direction: column;
gap: 20px;
box-sizing: border-box;
}
.renaming-form-header {
text-align: center;
padding: 20px;
}
.renaming-form-title {
font-size: 22px;
font-weight: 700;
color: #2c2c2c;
letter-spacing: 0.2em;
margin-bottom: 6px;
}
.renaming-form-subtitle {
font-size: 12px;
color: #5a5a5a;
letter-spacing: 0.12em;
}
.renaming-form-section {
background-color: #f9f7f2;
border-top: 1px solid #dcd3c9;
border-bottom: 1px solid #dcd3c9;
border-radius: 8px;
padding: 24px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
gap: 12px;
}
.renaming-row-inner {
display: flex;
align-items: center;
border-bottom: 1px solid #e5e5e5;
padding: 8px 0;
}
.renaming-row-inner-radio {
padding-bottom: 17px;
}
.renaming-row-label {
width: 80px;
font-weight: 700;
color: #8b2323;
font-size: 14px;
}
.renaming-row-label-highlight {
color: #d4af37;
}
.renaming-form-label-alt {
color: #5a5a5a;
}
.renaming-input {
flex: 1;
border: none;
background: transparent;
outline: none;
padding: 12px 6px;
font-size: 14px;
color: #2c2c2c;
}
.renaming-input-sm {
font-size: 13px;
}
.renaming-picker-text {
flex: 1;
color: #dcd3c9;
padding: 12px 6px;
font-size: 14px;
}
.renaming-picker-text-filled {
color: #2c2c2c;
}
.renaming-date-trigger {
font-size: 12px;
color: #dcd3c9;
padding: 6px 0;
}
.renaming-date-trigger-filled {
color: #2c2c2c;
}
.renaming-radio-group {
display: flex;
gap: 16px;
}
.renaming-radio {
display: flex;
align-items: center;
gap: 6px;
padding: 3px 0;
}
.renaming-radio-outer {
width: 19px;
height: 19px;
border-radius: 50%;
border: 1px solid #5a5a5a;
display: flex;
align-items: center;
justify-content: center;
}
.renaming-radio-outer-active {
border-color: #8b2323;
}
.renaming-radio-inner {
width: 11px;
height: 11px;
border-radius: 50%;
background-color: #8b2323;
}
.renaming-radio-text {
font-size: 14px;
color: #2c2c2c;
}
.renaming-subsection {
padding-top: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.renaming-subsection-title {
font-size: 12px;
font-weight: 700;
color: #8b2323;
border-left: 2px solid #8b2323;
padding-left: 6px;
}
.renaming-subsection-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.renaming-subsection-tip {
font-size: 10px;
color: #8a8a8a;
}
.renaming-textarea {
width: 95%;
height: 107px;
border-radius: 6px;
border: 1px solid #e5e5e5;
padding: 14px 12px;
background: transparent;
font-size: 14px;
color: #2c2c2c;
outline: none;
}
.renaming-two-col {
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid #e5e5e5;
padding: 8px 0;
}
.renaming-two-col-left {
display: flex;
align-items: center;
width: 40%;
}
.renaming-two-col-left-label {
width: 54px;
font-size: 12px;
font-weight: 700;
color: #5a5a5a;
}
.renaming-two-col-left-input {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
font-size: 14px;
color: #2c2c2c;
}
.renaming-two-col-right {
flex: 1;
display: flex;
align-items: center;
}
.renaming-member-item {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 0;
border-bottom: 1px solid #e5e5e5;
}
.renaming-member-tag {
font-size: 10px;
padding: 4px 8px;
background: #8b2323;
color: #fff;
border-radius: 3px;
white-space: nowrap;
}
.renaming-member-tag-partner {
background: #5a5a5a;
}
.renaming-member-name {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
font-size: 14px;
color: #2c2c2c;
}
.renaming-member-name::placeholder {
color: #dcd3c9;
}
.renaming-member-date {
font-size: 12px;
color: #dcd3c9;
padding: 6px 12px;
background: #fcfaf5;
border: 1px solid #eaddcf;
border-radius: 3px;
white-space: nowrap;
}
.renaming-member-date-filled {
color: #2c2c2c;
}
.renaming-member-remove {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 16px;
border-radius: 50%;
background: #f0f0f0;
}
.renaming-add-partner {
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
padding: 14px;
margin-top: 12px;
border: 1px dashed #dcd3c9;
border-radius: 6px;
color: #8a8a8a;
}
.renaming-add-partner-icon {
font-size: 16px;
color: #8b2323;
}
.renaming-add-partner-text {
font-size: 12px;
}
.renaming-submit-btn {
width: 100%;
border: none;
border-radius: 8px;
padding: 18px 0;
background-color: #8b2323;
color: #f2e6d8;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8px 12px -5px rgba(139, 35, 35, 0.35);
position: relative;
overflow: hidden;
}
.renaming-submit-btn-overlay {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.12);
}
.renaming-submit-btn-text {
position: relative;
z-index: 1;
font-weight: 700;
letter-spacing: 0.2em;
}
.renaming-loading {
height: 100%;
min-height: 80vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f0f23 100%);
}
.renaming-result {
padding-bottom: 54px;
}
.renaming-result-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.renaming-result-title {
font-size: 18px;
font-weight: 700;
color: #8b2323;
}
.renaming-result-reset {
border: none;
background: transparent;
font-size: 12px;
color: #5a5a5a;
}
.renaming-result-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.renaming-result-item {
background-color: #f9f7f2;
border-radius: 12px;
border: 1px solid #dcd3c9;
padding: 20px 16px 16px;
position: relative;
overflow: hidden;
}
.renaming-result-item-bg {
position: absolute;
top: 0;
right: 0;
padding: 6px;
opacity: 0.08;
pointer-events: none;
}
.renaming-result-item-actions {
position: absolute;
top: 8px;
right: 8px;
display: flex;
align-items: center;
gap: 6px;
z-index: 2;
}
.renaming-result-action-btn {
border-radius: 999px;
border: 1px solid #dcd3c9;
background: rgba(255, 255, 255, 0.6);
font-size: 10px;
color: #5a5a5a;
padding: 4px 8px;
}
.renaming-result-action-btn-heart {
padding: 4px;
}
.renaming-result-action-btn-heart-active {
color: #8b2323;
}
.renaming-result-item-content {
padding-right: 54px;
}
.renaming-result-item-name {
font-size: 22px;
font-weight: 700;
color: #2c2c2c;
margin-bottom: 3px;
display: block;
}
.renaming-result-item-pinyin {
font-size: 12px;
color: #5a5a5a;
margin-bottom: 6px;
display: block;
}
.renaming-result-item-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 6px;
}
.renaming-result-tag {
font-size: 10px;
padding: 3px 6px;
border-radius: 4px;
border: 1px solid rgba(139, 35, 35, 0.2);
color: #8b2323;
background: rgba(139, 35, 35, 0.04);
}
.renaming-result-tag-gold {
border-color: #d4af37;
color: #d4af37;
background: rgba(212, 175, 55, 0.08);
}
.renaming-result-item-divider {
height: 1px;
margin: 8px 0;
background-color: #e5e5e5;
}
.renaming-result-item-details {
display: flex;
flex-direction: column;
gap: 6px;
}
.renaming-result-detail-row {
display: flex;
align-items: flex-start;
gap: 8px;
}
.renaming-result-detail-label {
font-size: 10px;
padding: 3px 6px;
border-radius: 3px;
border: 1px solid #5a5a5a;
color: #5a5a5a;
margin-top: 3px;
}
.renaming-result-detail-label-primary {
border-color: #8b2323;
color: #8b2323;
}
.renaming-result-detail-text {
font-size: 14px;
color: #2c2c2c;
}
.renaming-result-detail-text-small {
font-size: 12px;
color: #5a5a5a;
font-style: italic;
}
.renaming-modal {
position: fixed;
inset: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
background: rgba(0, 0, 0, 0.6);
}
.renaming-modal-content {
background-color: #f9f7f2;
width: 100%;
max-width: 400px;
border-radius: 12px;
overflow: hidden;
position: relative;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
}
.renaming-modal-close {
position: absolute;
top: 16px;
right: 16px;
border: none;
background: transparent;
color: #5a5a5a;
font-size: 16px;
}
.renaming-modal-header {
background-color: #8b2323;
padding: 32px;
text-align: center;
}
.renaming-modal-header-title {
color: #d4af37;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.2em;
margin-bottom: 6px;
display: block;
}
.renaming-modal-header-subtitle {
color: rgba(242, 230, 216, 0.8);
font-size: 12px;
letter-spacing: 0.12em;
}
.renaming-modal-body {
padding: 32px;
display: flex;
flex-direction: column;
gap: 16px;
font-size: 14px;
color: #5a5a5a;
}
.renaming-modal-item {
display: block;
}
.renaming-modal-price {
text-align: center;
font-size: 20px;
font-weight: 700;
color: #8b2323;
}
.renaming-modal-price-unit {
font-size: 14px;
color: #5a5a5a;
}
.renaming-modal-pay-btn {
width: 100%;
border: none;
border-radius: 6px;
padding: 16px 0;
background-color: #d4af37;
color: #2c2c2c;
font-weight: 700;
}
.renaming-modal-note {
text-align: center;
font-size: 10px;
color: #999;
}
</style>