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

591 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="auspicious-form">
<view class="auspicious-texture"></view>
<!-- 固定Header -->
<view class="auspicious-fixed-header">
<view class="status-bar-placeholder"></view>
<view class="auspicious-header">
<view class="auspicious-back" @click="$emit('back')"></view>
<text class="auspicious-title">精准八字择吉</text>
<view class="auspicious-header-spacer"></view>
</view>
</view>
<!-- 头部占位 -->
<view class="auspicious-header-placeholder"></view>
<scroll-view scroll-y class="auspicious-scroll">
<view class="auspicious-container">
<view class="auspicious-intro">
<text class="auspicious-intro-title">顺天时 · 得地利 · 人和顺</text>
<text class="auspicious-intro-sub">根据您的生辰八字精准测算最佳黄道吉日</text>
</view>
<!-- 事项 -->
<view class="auspicious-section">
<label class="auspicious-label">
您要求测的事项
</label>
<view class="auspicious-grid">
<button v-for="type in eventTypes" :key="type.id" @click="form.eventType = type.id"
:class="['auspicious-event-card', form.eventType === type.id ? 'is-active' : '']">
<text class="auspicious-event-text">{{ type.label }}</text>
</button>
</view>
<view v-if="form.eventType === 'other'" class="auspicious-custom">
<input v-model="form.customEvent" type="text" placeholder="请输入您要求测的事项 (如: 签约, 出行, 动土...)"
class="auspicious-input" />
</view>
</view>
<!-- 福主信息 -->
<view class="auspicious-section">
<label class="auspicious-label">
福主信息
</label>
<view class="auspicious-card">
<view class="auspicious-field">
<label class="auspicious-field-label">您的姓名</label>
<input v-model="form.name" type="text" placeholder="请输入真实姓名" class="auspicious-field-input" />
</view>
<view class="auspicious-field">
<label class="auspicious-field-label">性别</label>
<view class="auspicious-gender">
<button :class="['auspicious-gender-btn', form.gender === 'male' ? 'is-active' : '']"
@click="form.gender = 'male'">
</button>
<button :class="['auspicious-gender-btn', form.gender === 'female' ? 'is-active' : '']"
@click="form.gender = 'female'">
</button>
</view>
</view>
<view class="auspicious-field">
<label class="auspicious-field-label">出生日期时辰</label>
<view class="auspicious-date-trigger" @click="showDatePicker = true">
<text class="auspicious-date-text" :class="{ 'is-placeholder': !form.birthDateDisplay }">
{{ form.birthDateDisplay || '请选择出生日期时辰' }}
</text>
<text class="auspicious-date-arrow"></text>
</view>
</view>
<view class="auspicious-field">
<label class="auspicious-field-label">出生地</label>
<input v-model="form.birthPlace" type="text" placeholder="请输入出生地(如:临沂市)" class="auspicious-field-input" />
</view>
</view>
</view>
<!-- 择吉目的 -->
<view class="auspicious-section">
<label class="auspicious-label">
择吉目的
</label>
<view class="auspicious-card">
<textarea v-model="form.zejiPurpose" placeholder="请描述您的择吉目的(如:选择结婚吉日,希望婚姻美满幸福)" class="auspicious-textarea"
maxlength="200" />
</view>
</view>
<!-- 期望日期范围 -->
<view class="auspicious-section">
<label class="auspicious-label">
期望日期范围
</label>
<view class="auspicious-card">
<view class="auspicious-field">
<label class="auspicious-field-label">开始日期</label>
<view class="auspicious-date-trigger" @click="showStartDatePicker = true">
<text class="auspicious-date-text" :class="{ 'is-placeholder': !form.dateRangeStartDisplay }">
{{ form.dateRangeStartDisplay || '请选择开始日期' }}
</text>
<text class="auspicious-date-arrow"></text>
</view>
</view>
<view class="auspicious-field">
<label class="auspicious-field-label">结束日期</label>
<view class="auspicious-date-trigger" @click="showEndDatePicker = true">
<text class="auspicious-date-text" :class="{ 'is-placeholder': !form.dateRangeEndDisplay }">
{{ form.dateRangeEndDisplay || '请选择结束日期' }}
</text>
<text class="auspicious-date-arrow"></text>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 日期选择器 -->
<MysticDatePicker :is-open="showDatePicker" title="请择良辰" :default-value="form.birthDateDisplay"
@close="showDatePicker = false" @confirm="handleDateConfirm" />
<!-- 开始日期不可晚于今天年份为过去至今年 -->
<MysticDatePicker :is-open="showStartDatePicker" title="选择开始日期" :default-value="form.dateRangeStartDisplay"
:min-year="zejiStartMinYear" :max-year="zejiStartMaxYear" cap-at-today
footer-tip="开始日期不可选择今天之后的日期滑动选择后自动对应农历干支"
@close="showStartDatePicker = false" @confirm="handleStartDateConfirm" />
<!-- 结束日期选择器 -->
<MysticDatePicker :is-open="showEndDatePicker" title="选择结束日期" :default-value="form.dateRangeEndDisplay"
:min-year="zejiExpectRangeMinYear" :max-year="zejiExpectRangeMaxYear"
footer-tip="期望日期区间支持选择至未来多年滑动选择后自动对应农历干支"
@close="showEndDatePicker = false" @confirm="handleEndDateConfirm" />
<!-- Footer -->
<view class="auspicious-footer">
<button class="auspicious-submit" @click="submit">
立即测算
</button>
<text class="auspicious-footer-tip">
已有 28,392 人通过壹梵择得良辰吉日
</text>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from "vue";
import MysticDatePicker from "../MysticDatePicker.vue";
import { baziZejiApi, type BaziZejiCalculateRequest } from '../../api';
declare const uni: any;
const emit = defineEmits<{
submit: [data: any];
back: [];
}>();
const form = reactive({
eventType: "wedding",
customEvent: "",
name: "",
gender: "male",
birthDateDisplay: "",
birthDateApi: "",
birthPlace: "",
zejiPurpose: "",
dateRangeStartDisplay: "",
dateRangeStart: "",
dateRangeEndDisplay: "",
dateRangeEnd: "",
});
const showDatePicker = ref(false);
const showStartDatePicker = ref(false);
const showEndDatePicker = ref(false);
/** 精准八字择吉 · 结束日期:从当年起可往后选多年 */
const ZEJI_RANGE_FORWARD_YEARS = 50;
const zejiExpectRangeMinYear = computed(() => new Date().getFullYear());
const zejiExpectRangeMaxYear = computed(() => new Date().getFullYear() + ZEJI_RANGE_FORWARD_YEARS);
/** 开始日期:至多为今天,年份列与生辰类似(当年往前若干年) */
const zejiStartMinYear = computed(() => new Date().getFullYear() - 85);
const zejiStartMaxYear = computed(() => new Date().getFullYear());
const eventTypes = [
{ id: "wedding", label: "婚嫁择吉", icon: "💒" },
{ id: "business", label: "开业择吉", icon: "🧧" },
{ id: "move", label: "搬家择吉", icon: "🏠" },
{ id: "travel", label: "出行择吉", icon: "✈️" },
{ id: "investment", label: "投资择吉", icon: "💰" },
{ id: "surgery", label: "手术择吉", icon: "🏥" },
{ id: "contract", label: "签约择吉", icon: "📝" },
{ id: "other", label: "其他择吉", icon: "✍️" },
];
const handleDateConfirm = (displayVal: string, apiVal: string) => {
form.birthDateDisplay = displayVal;
form.birthDateApi = apiVal;
showDatePicker.value = false;
};
const handleStartDateConfirm = (displayVal: string, apiVal: string) => {
form.dateRangeStartDisplay = displayVal;
// 从API格式中提取日期部分 (YYYY-MM-DD)
form.dateRangeStart = apiVal.split(' ')[0];
showStartDatePicker.value = false;
};
const handleEndDateConfirm = (displayVal: string, apiVal: string) => {
form.dateRangeEndDisplay = displayVal;
// 从API格式中提取日期部分 (YYYY-MM-DD)
form.dateRangeEnd = apiVal.split(' ')[0];
showEndDatePicker.value = false;
};
const submit = async () => {
if (form.eventType === "other" && !form.customEvent.trim()) {
uni.showToast({ title: "请输入您要求测的事项", icon: "none" });
return;
}
if (!form.name || !form.birthDateDisplay) {
uni.showToast({ title: "请填写真实信息以确保准确", icon: "none" });
return;
}
if (!form.birthPlace) {
uni.showToast({ title: "请输入出生地", icon: "none" });
return;
}
if (!form.zejiPurpose) {
uni.showToast({ title: "请输入择吉目的", icon: "none" });
return;
}
if (!form.dateRangeStart || !form.dateRangeEnd) {
uni.showToast({ title: "请选择期望日期范围", icon: "none" });
return;
}
uni.showLoading({ title: '测算中...', mask: true });
try {
const requestData: BaziZejiCalculateRequest = {
name: form.name,
gender: form.gender as 'male' | 'female',
birth_date: form.birthDateDisplay,
birth_date_api: form.birthDateApi,
birth_place: form.birthPlace,
zeji_type: form.eventType as any,
zeji_purpose: form.eventType === 'other' ? form.customEvent : form.zejiPurpose,
date_range_start: form.dateRangeStart,
date_range_end: form.dateRangeEnd,
};
const result = await baziZejiApi.calculateBaziZeji(requestData);
uni.hideLoading();
emit('submit', result);
} catch (error: any) {
uni.hideLoading();
// 如果是认证失败错误不显示toast因为API函数中已经处理了跳转
if (error.message !== '认证失败,请登录后再试') {
uni.showToast({
title: error.message || '测算失败,请稍后重试',
icon: 'none',
duration: 2000,
});
}
}
};
</script>
<style scoped>
.auspicious-form {
height: 100%;
display: flex;
flex-direction: column;
background: #f0efe9;
position: relative;
overflow: hidden;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
}
.auspicious-texture {
position: absolute;
inset: 0;
pointer-events: none;
opacity: 0.4;
mix-blend-mode: multiply;
background-image: url("https://www.transparenttextures.com/patterns/rice-paper.png");
z-index: 0;
}
/* 固定头部容器 */
.auspicious-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%;
}
/* 头部占位 */
.auspicious-header-placeholder {
height: calc(var(--status-bar-height, 0) + 100rpx);
flex-shrink: 0;
}
.auspicious-header {
position: relative;
z-index: 10;
padding: 24rpx 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #eaddcf;
}
.auspicious-back {
padding: 16rpx;
margin-left: -8rpx;
color: #5a5a5a;
background: transparent;
border: none;
}
.auspicious-title {
font-size: 18px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.3em;
}
.auspicious-header-spacer {
width: 32rpx;
}
.auspicious-scroll {
flex: 1;
position: relative;
z-index: 10;
height: 0;
}
.auspicious-container {
max-width: 700rpx;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 32rpx;
}
.auspicious-intro {
text-align: center;
margin-bottom: 48rpx;
}
.auspicious-intro-title {
font-size: 24px;
font-weight: bold;
color: #2c2c2c;
display: block;
margin-bottom: 12rpx;
letter-spacing: 0.1em;
}
.auspicious-intro-sub {
font-size: 12px;
color: #5a5a5a;
letter-spacing: 0.05em;
}
.auspicious-section {
margin-bottom: 48rpx;
}
.auspicious-label {
display: block;
font-size: 14px;
font-weight: bold;
color: #2c2c2c;
margin-bottom: 20rpx;
padding-left: 12rpx;
border-left: 4rpx solid #8b2323;
letter-spacing: 0.05em;
}
.auspicious-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
align-items: stretch;
}
.auspicious-event-card {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 24rpx 16rpx;
border-radius: 16rpx;
border: 1px solid #e5e5e5;
background: #fffdf9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
color: #5a5a5a;
transition: all 0.2s ease;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.04);
}
.auspicious-event-card.is-active {
background: #8b2323;
border-color: #8b2323;
color: #fdfbf7;
box-shadow: 0 10rpx 16rpx -4rpx rgba(139, 35, 35, 0.35);
}
.auspicious-event-icon {
font-size: 22px;
}
.auspicious-event-text {
font-size: 14px;
font-weight: bold;
letter-spacing: 0.05em;
}
.auspicious-custom {
margin-top: 16rpx;
}
.auspicious-input {
width: 100%;
background: #fffdf9;
border: 1px solid #e5e5e5;
border-radius: 14rpx;
padding: 20rpx;
font-size: 14px;
color: #2c2c2c;
outline: none;
box-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.04);
}
.auspicious-input::placeholder {
color: #bfbfbf;
}
.auspicious-card {
background: #fffdf9;
border: 1px solid #e5e5e5;
border-radius: 16rpx;
padding: 28rpx;
box-shadow: 0 6rpx 12rpx -4rpx rgba(0, 0, 0, 0.08);
display: flex;
flex-direction: column;
gap: 20rpx;
}
.auspicious-field {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.auspicious-field-label {
font-size: 12px;
color: #8a8a8a;
}
.auspicious-field-input {
width: 100%;
background: transparent;
border: none;
border-bottom: 1px solid #e5e5e5;
padding: 14rpx 0;
font-size: 14px;
color: #2c2c2c;
outline: none;
}
.auspicious-field-input::placeholder {
color: #bfbfbf;
}
.auspicious-textarea {
width: 100%;
min-height: 120rpx;
background: transparent;
border: none;
border-bottom: 1px solid #e5e5e5;
padding: 14rpx 0;
font-size: 14px;
color: #2c2c2c;
outline: none;
resize: none;
font-family: inherit;
}
.auspicious-textarea::placeholder {
color: #bfbfbf;
}
.auspicious-gender {
display: flex;
gap: 16rpx;
}
.auspicious-gender-btn {
flex: 1;
border-radius: 12rpx;
border: 1px solid #e5e5e5;
background: transparent;
color: #5a5a5a;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
}
.auspicious-gender-btn.is-active {
background: #2c2c2c;
color: #d4af37;
border-color: #2c2c2c;
}
.auspicious-date-trigger {
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid #e5e5e5;
padding: 14rpx 0;
}
.auspicious-date-text {
font-size: 14px;
color: #2c2c2c;
}
.auspicious-date-text.is-placeholder {
color: #bfbfbf;
}
.auspicious-date-arrow {
color: #8b2323;
font-size: 24rpx;
opacity: 0.6;
}
.auspicious-footer {
padding: 32rpx;
background: #fdfbf7;
border-top: 1px solid #e5e5e5;
position: relative;
z-index: 20;
}
.auspicious-submit {
width: 100%;
background: #8b2323;
color: #fdfbf7;
font-weight: bold;
padding: 18rpx 0;
border-radius: 16rpx;
box-shadow: 0 12rpx 18rpx -8rpx rgba(139, 35, 35, 0.35);
font-size: 16px;
border: none;
}
.auspicious-footer-tip {
display: block;
text-align: center;
font-size: 10px;
color: #999;
margin-top: 12rpx;
}
</style>