Files

1117 lines
30 KiB
Vue
Raw Permalink 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="naming-screen">
<view class="naming-bg"></view>
<!-- 头部占位 -->
<view class="naming-header-placeholder"></view>
<!-- 主体 -->
<scroll-view scroll-y class="naming-scroll">
<!-- 表单 -->
<view v-if="step === 'form'" class="naming-form">
<!-- 顶部模式切换 -->
<view class="naming-mode-switch">
<view class="naming-mode-switch-bg">
<view class="naming-mode-switch-slider"
:style="{ left: mode === 'personal' ? '4px' : '50%', width: '50%' }" />
<view class="naming-mode-btn" :class="mode === 'personal' ? 'naming-mode-btn-active' : ''"
@click="mode = 'personal'">个人起名</view>
<view class="naming-mode-btn" :class="mode === 'company' ? 'naming-mode-btn-active' : ''"
@click="mode = 'company'">
公司起名</view>
</view>
</view>
<view class="naming-form-header">
<view class="naming-form-title">
{{ mode === 'personal' ? '赐子千金' : '名扬四海' }}
</view>
<view class="naming-form-subtitle">
{{ mode === 'personal' ? '不如赐子好名' : '好商号是企业成功第一步' }}
</view>
</view>
<!-- 个人表单 -->
<view v-if="mode === 'personal'" class="naming-form-section">
<view class="naming-row-inner">
<text class="naming-row-label">姓氏</text>
<input v-model="personal.lastName" placeholder="请输入姓氏" class="naming-input" />
</view>
<view class="naming-row-inner naming-row-inner-radio">
<text class="naming-row-label">性别</text>
<view class="naming-radio-group">
<view class="naming-radio" @click="personal.gender = 'male'">
<view class="naming-radio-outer" :class="personal.gender === 'male' ? 'naming-radio-outer-active' : ''">
<view v-if="personal.gender === 'male'" class="naming-radio-inner"></view>
</view>
<text class="naming-radio-text"></text>
</view>
<view class="naming-radio" @click="personal.gender = 'female'">
<view class="naming-radio-outer"
:class="personal.gender === 'female' ? 'naming-radio-outer-active' : ''">
<view v-if="personal.gender === 'female'" class="naming-radio-inner"></view>
</view>
<text class="naming-radio-text"></text>
</view>
</view>
</view>
<view class="naming-row-inner" @click="activeDateField = 'personal'">
<text class="naming-row-label">生辰</text>
<view class="naming-picker-text" :class="{ 'naming-picker-text-filled': personal.birthDateDisplay }">
{{ personal.birthDateDisplay || '请选择生辰' }}
</view>
</view>
<view class="naming-row-inner">
<text class="naming-row-label">出生地</text>
<input v-model="personal.birthPlace" placeholder="请填写出身地" class="naming-input" />
</view>
<view class="naming-row-inner" @click="showStylePicker = true">
<text class="naming-row-label">偏好</text>
<view class="naming-picker-text naming-picker-text-filled">
{{ personal.styleLabel }}
</view>
</view>
<view class="naming-subsection">
<text class="naming-subsection-title">家族信息 (选填)</text>
<view class="naming-row-inner">
<text class="naming-row-label naming-form-label-alt">家谱字辈</text>
<input v-model="personal.familyBook" placeholder="中间字/末尾字" class="naming-input naming-input-sm" />
</view>
<view class="naming-two-col">
<view class="naming-two-col-left">
<text class="naming-two-col-left-label">父亲</text>
<input v-model="personal.fatherName" placeholder="姓名" class="naming-two-col-left-input" />
</view>
<view class="naming-two-col-right" @click="activeDateField = 'father'">
<view class="naming-date-trigger"
:class="{ 'naming-date-trigger-filled': personal.fatherBirthDateDisplay }">
{{ personal.fatherBirthDateDisplay || '选择生辰' }}
</view>
</view>
</view>
<view class="naming-two-col">
<view class="naming-two-col-left">
<text class="naming-two-col-left-label">母亲</text>
<input v-model="personal.motherName" placeholder="姓名" class="naming-two-col-left-input" />
</view>
<view class="naming-two-col-right" @click="activeDateField = 'mother'">
<view class="naming-date-trigger"
:class="{ 'naming-date-trigger-filled': personal.motherBirthDateDisplay }">
{{ personal.motherBirthDateDisplay || '选择生辰' }}
</view>
</view>
</view>
</view>
</view>
<!-- 公司表单 -->
<view v-else class="naming-form-section">
<view class="naming-row-inner">
<text class="naming-row-label">所属行业</text>
<input v-model="company.industry" placeholder="如:科技、餐饮、贸易" class="naming-input" />
</view>
<view class="naming-row-inner">
<text class="naming-row-label">所在城市</text>
<input v-model="company.city" placeholder="如:北京" class="naming-input" />
</view>
<view class="naming-subsection">
<view class="naming-subsection-header">
<text class="naming-subsection-title">核心成员</text>
<text class="naming-subsection-tip">创始人必填合伙人选填</text>
</view>
<!-- 创始人 -->
<view class="naming-member-item">
<view class="naming-member-tag">创始人</view>
<input v-model="company.founderName" placeholder="姓名" class="naming-member-name" />
<view class="naming-member-date" @click="activeDateField = 'founder'">
<text :class="{ 'naming-member-date-filled': company.founderBirthDisplay }">
{{ company.founderBirthDisplay || '选择生辰' }}
</text>
</view>
</view>
<!-- 合伙人列表 -->
<view v-for="(partner, idx) in company.partners" :key="idx" class="naming-member-item">
<view class="naming-member-tag naming-member-tag-partner">合伙人{{ Number(idx) + 1 }}</view>
<input v-model="partner.name" placeholder="姓名" class="naming-member-name" />
<view class="naming-member-date" @click="activeDateField = `partner-${idx}`">
<text :class="{ 'naming-member-date-filled': partner.birthDisplay }">
{{ partner.birthDisplay || '选择生辰' }}
</text>
</view>
<view class="naming-member-remove" @click="removePartner(idx)">×</view>
</view>
<!-- 添加合伙人按钮 -->
<view v-if="company.partners.length < 5" class="naming-add-partner" @click="addPartner">
<text class="naming-add-partner-icon">+</text>
<text class="naming-add-partner-text">添加合伙人 ({{ company.partners.length }}/5)</text>
</view>
</view>
</view>
<view class="naming-submit-btn" @click="start">
<PenToolIcon :size="16" class="naming-submit-icon" />
<text class="naming-submit-btn-text">{{ mode === 'personal' ? '立即起名' : '商号推演' }}</text>
</view>
</view>
<!-- Loading -->
<view v-else-if="step === 'loading'" class="naming-loading">
<MysticCompass :title="mode === 'personal' ? '命盘推演中' : '商号推演中'"
:subtitle="mode === 'personal' ? '正在解析三才五格与流年运势...' : '正在分析五行生克与行业契合度...'" @back="handleLoadingBack" />
</view>
<!-- 结果 -->
<NamingResult v-else :data="results" :category="mode" :pay-business-id="reportId || undefined"
@reset="step = 'form'" @showDetail="handleShowDetail" />
</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="naming-modal" @click="showPay = false">
<view class="naming-modal-content" @click.stop>
<button class="naming-modal-close" @click="showPay = false">×</button>
<view class="naming-modal-header">
<text class="naming-modal-header-title">天机解锁</text>
<text class="naming-modal-header-subtitle">
{{ mode === 'personal' ? '个人年度财运深度解析' : '企业年度商业运程报告' }}
</text>
</view>
<view class="naming-modal-body">
<text class="naming-modal-item"> 流年运势 + 五行喜忌 + 开运色彩</text>
<text class="naming-modal-item"> PDF 报告 & 名字全维测评</text>
<text class="naming-modal-item"> 一对一线上解读</text>
<view class="naming-modal-price">
¥{{ mode === 'personal' ? '666' : '888' }} <text class="naming-modal-price-unit">/</text>
</view>
<button class="naming-modal-pay-btn" @click="pay">立即解锁深度解析</button>
<text class="naming-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 PenToolIcon from '../icons/PenToolIcon.vue';
import NamingResult, { type GeneratedName } from '../NamingResult.vue';
import { namingApi } from '../../api/naming';
import { wxPay } from "@/utils/payment";
import { pollSolutionDetailUntilReady, pollUntilSolutionIdFromScoring } from '../../utils/poll-test-solution-detail';
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 activeDateField = ref<string | null>(null);
// 下拉选择器状态
const showStylePicker = ref(false);
const personal = reactive({
lastName: "",
gender: "male",
birthDateDisplay: "",
birthDateApi: "", // 接口格式: "1990-04-05 09:00:00"
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; // 接口格式: "1990-04-05 09:00:00"
}
const company = reactive({
industry: "",
city: "",
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);
};
const results = ref<GeneratedName[]>([]);
const reportId = ref<number | null>(null);
const subscribeTemplateId = '5LLf7oDiXp0s8U99B8HkEhrFa0RzwETBRNu5iW-T-hc';
// Mini-program specific subscription removed for H5
const requestSubscribeMessage = async () => {
// Not applicable in H5 environment
return Promise.resolve();
};
const promptSubscribeMessage = async () => {
// Subscription not applicable in H5 environment
return Promise.resolve();
};
const start = async () => {
results.value = [];
reportId.value = null;
pollAbort?.abort();
pollAbort = new AbortController();
const signal = pollAbort.signal;
if (mode.value === "personal" && !personal.lastName) {
uni.showToast({ title: "请填写姓氏", icon: "none" });
return;
}
if (mode.value === "personal") {
if (!personal.birthDateDisplay) {
uni.showToast({ title: "请选择生辰", icon: "none" });
return;
}
if (!personal.birthPlace) {
uni.showToast({ title: "请填写出生地", icon: "none" });
return;
}
}
if (mode.value === "company") {
if (!company.industry) {
uni.showToast({ title: "请填写行业", icon: "none" });
return;
}
if (!company.city) {
uni.showToast({ title: "请填写所在城市", icon: "none" });
return;
}
if (!company.founderName) {
uni.showToast({ title: "请填写创始人姓名", icon: "none" });
return;
}
if (!company.founderBirthDisplay) {
uni.showToast({ title: "请选择创始人生辰", icon: "none" });
return;
}
}
await promptSubscribeMessage();
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));
}
};
if (mode.value === "personal") {
try {
const response: any = await namingApi.personalNaming({
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,
});
reportId.value = Number(response?.report_id) || null;
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';
}
} else {
// 公司起名调用真实接口
try {
// 构建核心成员列表直接使用API格式的日期
const coreMembers = [
{
name: company.founderName,
birthday: company.founderBirthApi,
},
...company.partners
.filter((p: Partner) => p.name && p.birthApi)
.map((p: Partner) => ({
name: p.name,
birthday: p.birthApi,
})),
];
const response: any = await namingApi.companyNaming({
industry: company.industry,
address: company.city,
core_members: coreMembers,
});
reportId.value = Number(response?.report_id) || null;
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 pay = () => {
(async () => {
if (!reportId.value) {
uni.showToast({ title: '缺少报告ID无法发起支付', icon: 'none' });
return;
}
try {
const amount = mode.value === 'personal' ? 666 : 888;
const result = await wxPay({
description: mode.value === 'personal' ? '个人年度财运深度解析' : '企业年度商业运程报告',
total_amount: amount,
business_type: 'naming_report',
business_id: reportId.value,
});
if (result.success) {
showPay.value = false;
uni.showToast({ title: '支付成功', icon: 'success' });
return;
}
uni.showToast({ title: result.msg || '支付失败', icon: 'none' });
} catch (e: any) {
uni.showToast({ title: e?.msg || '支付失败', icon: 'none' });
}
})();
};
// 定义emit
const emit = defineEmits<{
showDetail: [data: any, mode: Mode];
}>();
// 处理显示详情
const handleShowDetail = async (data: GeneratedName) => {
if (mode.value === 'company') {
emit("showDetail", data, mode.value);
} else {
// 个人模式:直接跳转
emit("showDetail", data, mode.value);
}
};
// 处理 loading 页面的返回按钮
const handleLoadingBack = () => {
pollAbort?.abort();
uni.showToast({
title: '测算结果可在"我的方案"中查看',
icon: 'none',
duration: 2000
});
step.value = 'form';
};
onUnmounted(() => {
pollAbort?.abort();
});
</script>
<style scoped>
.naming-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;
}
/* 固定头部容器 */
.naming-fixed-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
background: #f0efe9;
}
/* 状态栏占位 */
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
/* 头部占位 */
.naming-header-placeholder {
height: calc(var(--status-bar-height, 0) + 67px);
flex-shrink: 0;
}
.naming-header {
padding: 16px 20px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #eaddcf;
}
.naming-header-title {
font-size: 18px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.3em;
}
.naming-header-spacer {
width: 32px;
}
.naming-bg {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.3;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
}
.naming-mode-switch {
padding: 0 0 20px;
margin-bottom: 12px;
}
.naming-mode-switch-bg {
background-color: #eaddcf;
padding: 4px;
border-radius: 999px;
position: relative;
display: flex;
overflow: hidden;
}
.naming-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;
}
.naming-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;
}
.naming-mode-btn-active {
color: #f2e6d8;
}
.naming-scroll {
flex: 1;
position: relative;
z-index: 1;
}
.naming-form {
width: 100%;
max-width: 100%;
padding: 20px 20px 54px;
display: flex;
flex-direction: column;
gap: 20px;
box-sizing: border-box;
}
.naming-form-header {
text-align: center;
}
.naming-form-title {
font-size: 22px;
font-weight: 700;
color: #2c2c2c;
letter-spacing: 0.2em;
margin-bottom: 6px;
}
.naming-form-subtitle {
font-size: 12px;
color: #5a5a5a;
letter-spacing: 0.12em;
}
.naming-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;
}
.naming-row-inner {
display: flex;
align-items: center;
border-bottom: 1px solid #e5e5e5;
padding: 8px 0;
}
.naming-row-inner-radio {
padding-bottom: 17px;
}
.naming-row-label {
width: 80px;
font-weight: 700;
color: #8b2323;
font-size: 14px;
}
.naming-form-label-alt {
color: #5a5a5a;
}
.naming-input {
flex: 1;
border: none;
background: transparent;
outline: none;
padding: 12px 6px;
font-size: 14px;
color: #2c2c2c;
}
.naming-input-sm {
font-size: 13px;
}
.naming-picker-text {
flex: 1;
color: #dcd3c9;
padding: 12px 6px;
font-size: 14px;
}
.naming-picker-text-filled {
color: #2c2c2c;
}
.naming-date-trigger {
font-size: 12px;
color: #dcd3c9;
padding: 6px 0;
}
.naming-date-trigger-filled {
color: #2c2c2c;
}
.naming-core-date {
flex: 1;
border-bottom: 1px solid #e5e5e5;
padding: 8px 3px;
font-size: 14px;
}
.naming-core-date-filled {
color: #2c2c2c;
}
.naming-radio-group {
display: flex;
gap: 16px;
}
.naming-radio {
display: flex;
align-items: center;
gap: 6px;
padding: 3px 0;
}
.naming-radio-outer {
width: 19px;
height: 19px;
border-radius: 50%;
border: 1px solid #5a5a5a;
display: flex;
align-items: center;
justify-content: center;
}
.naming-radio-outer-active {
border-color: #8b2323;
}
.naming-radio-inner {
width: 11px;
height: 11px;
border-radius: 50%;
background-color: #8b2323;
}
.naming-radio-text {
font-size: 14px;
color: #2c2c2c;
}
.naming-subsection {
padding-top: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.naming-subsection-title {
font-size: 12px;
font-weight: 700;
color: #8b2323;
border-left: 2px solid #8b2323;
padding-left: 6px;
}
.naming-two-col {
display: flex;
align-items: center;
gap: 8px;
border-bottom: 1px solid #e5e5e5;
padding: 8px 0;
}
.naming-two-col-left {
display: flex;
align-items: center;
width: 40%;
}
.naming-two-col-left-label {
width: 54px;
font-size: 12px;
font-weight: 700;
color: #5a5a5a;
}
.naming-two-col-left-input {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
font-size: 14px;
color: #2c2c2c;
}
.naming-two-col-right {
flex: 1;
display: flex;
align-items: center;
}
.naming-two-col-right-input {
flex: 1;
background: transparent;
border: none;
outline: none;
font-size: 12px;
color: #2c2c2c;
}
.naming-core-group {
display: flex;
align-items: center;
gap: 8px;
}
.naming-core-input-name {
width: 30%;
border-bottom: 1px solid #e5e5e5;
padding: 8px 3px;
font-size: 14px;
color: #2c2c2c;
background: transparent;
outline: none;
}
.naming-core-input-date {
flex: 1;
border-bottom: 1px solid #e5e5e5;
padding: 8px 3px;
font-size: 14px;
color: #2c2c2c;
background: transparent;
outline: none;
}
.naming-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;
gap: 12px;
}
.naming-submit-icon {
width: 21px;
height: 21px;
flex-shrink: 0;
}
.naming-submit-btn-overlay {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.12);
}
.naming-submit-btn-text {
position: relative;
z-index: 1;
font-weight: 700;
letter-spacing: 0.2em;
}
/* Loading */
.naming-loading {
height: 100%;
min-height: 80vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 50%, #0f0f23 100%);
}
.naming-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);
}
.naming-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);
}
.naming-modal-close {
position: absolute;
top: 16px;
right: 16px;
border: none;
background: transparent;
color: #5a5a5a;
font-size: 16px;
}
.naming-modal-header {
background-color: #8b2323;
padding: 32px;
text-align: center;
}
.naming-modal-header-title {
color: #d4af37;
font-size: 14px;
font-weight: 700;
letter-spacing: 0.2em;
margin-bottom: 6px;
display: block;
}
.naming-modal-header-subtitle {
color: rgba(242, 230, 216, 0.8);
font-size: 12px;
letter-spacing: 0.12em;
}
.naming-modal-body {
padding: 32px;
display: flex;
flex-direction: column;
gap: 16px;
font-size: 14px;
color: #5a5a5a;
}
.naming-modal-item {
display: block;
}
.naming-modal-price {
text-align: center;
font-size: 20px;
font-weight: 700;
color: #8b2323;
}
.naming-modal-price-unit {
font-size: 14px;
color: #5a5a5a;
}
.naming-modal-pay-btn {
width: 100%;
border: none;
border-radius: 6px;
padding: 16px 0;
background-color: #d4af37;
color: #2c2c2c;
font-weight: 700;
}
.naming-modal-note {
text-align: center;
font-size: 10px;
color: #999;
}
/* 核心成员样式 */
.naming-subsection-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.naming-subsection-tip {
font-size: 10px;
color: #8a8a8a;
}
.naming-member-item {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 0;
border-bottom: 1px solid #e5e5e5;
}
.naming-member-tag {
font-size: 10px;
padding: 4px 8px;
background: #8b2323;
color: #fff;
border-radius: 3px;
white-space: nowrap;
}
.naming-member-tag-partner {
background: #5a5a5a;
}
.naming-member-name {
flex: 1;
min-width: 0;
background: transparent;
border: none;
outline: none;
font-size: 14px;
color: #2c2c2c;
}
.naming-member-name::placeholder {
color: #dcd3c9;
}
.naming-member-date {
font-size: 12px;
color: #dcd3c9;
padding: 6px 12px;
background: #fcfaf5;
border: 1px solid #eaddcf;
border-radius: 3px;
white-space: nowrap;
}
.naming-member-date-filled {
color: #2c2c2c;
}
.naming-member-remove {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
font-size: 16px;
border-radius: 50%;
background: #f0f0f0;
}
.naming-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;
}
.naming-add-partner-icon {
font-size: 16px;
color: #8b2323;
}
.naming-add-partner-text {
font-size: 12px;
}
</style>