1117 lines
30 KiB
Vue
1117 lines
30 KiB
Vue
<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>
|