upload project source code
This commit is contained in:
590
前端源码/uni-app/components/screens/AuspiciousForm.vue
Normal file
590
前端源码/uni-app/components/screens/AuspiciousForm.vue
Normal file
@@ -0,0 +1,590 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user