1298 lines
35 KiB
Vue
1298 lines
35 KiB
Vue
<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>
|