JewelryMall/miniprogram/pages/calculator/index.vue

255 lines
6.7 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="calculator-page">
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="custom-navbar__content" :style="{ height: navBarHeight + 'px' }">
<image class="custom-navbar__back" src="/static/ic_back.png" mode="aspectFit" @click="goBack" />
<text class="custom-navbar__title">钻戒计算器</text>
<view class="custom-navbar__placeholder" />
</view>
</view>
<view :style="{ height: (statusBarHeight + navBarHeight) + 'px' }" />
<view class="section">
<view class="section__title">基础参数</view>
<view class="form-item" v-for="field in basicFields" :key="field.key">
<text class="form-item__label">{{ field.label }}{{ field.unit }}</text>
<input
class="form-item__input"
type="digit"
:placeholder="'请输入' + field.label"
:value="String(form[field.key])"
@input="onInput(field.key, $event)"
/>
<text v-if="errors[field.key]" class="form-item__error">{{ errors[field.key] }}</text>
</view>
</view>
<view class="section">
<view class="section__title">费用参数</view>
<view class="form-item" v-for="field in feeFields" :key="field.key">
<text class="form-item__label">{{ field.label }}{{ field.unit }}</text>
<input
class="form-item__input"
type="digit"
:placeholder="'请输入' + field.label"
:value="String(form[field.key])"
@input="onInput(field.key, $event)"
/>
<text v-if="errors[field.key]" class="form-item__error">{{ errors[field.key] }}</text>
</view>
</view>
<view class="section result-section">
<view class="section__title">计算结果</view>
<view class="result-row" v-for="r in resultRows" :key="r.label">
<text class="result-row__label">{{ r.label }}</text>
<text class="result-row__value">{{ r.value }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue'
import type { RingCalculatorInput, RingCalculatorResult } from '../../types/calculator'
import { calculateRing } from '../../utils/calculator'
const statusBarHeight = ref(20)
const navBarHeight = ref(44)
try {
const sysInfo = uni.getSystemInfoSync()
statusBarHeight.value = sysInfo.statusBarHeight || 20
// #ifdef MP-WEIXIN
const menuBtn = uni.getMenuButtonBoundingClientRect()
navBarHeight.value = (menuBtn.top - (sysInfo.statusBarHeight || 20)) * 2 + menuBtn.height
// #endif
} catch { /* fallback */ }
function goBack() {
uni.navigateBack({ delta: 1 })
}
interface FieldDef {
key: keyof RingCalculatorInput
label: string
unit: string
}
const basicFields: FieldDef[] = [
{ key: 'goldWeight', label: '金重', unit: 'g' },
{ key: 'mainStoneWeight', label: '主石重', unit: 'ct' },
{ key: 'sideStoneWeight', label: '副石重', unit: 'ct' },
{ key: 'lossRate', label: '损耗', unit: '倍率' },
{ key: 'moldGoldPrice', label: '倒模金价', unit: '' },
]
const feeFields: FieldDef[] = [
{ key: 'mainStoneUnitPrice', label: '主石单价', unit: '' },
{ key: 'sideStoneUnitPrice', label: '副石单价', unit: '' },
{ key: 'sideStoneCount', label: '副石粒数', unit: 'p' },
{ key: 'microSettingFee', label: '微镶费', unit: '/' },
{ key: 'mainStoneSettingFee', label: '主石镶费', unit: '' },
{ key: 'threeDFee', label: '3D起板费', unit: '' },
{ key: 'basicLaborCost', label: '基本工费', unit: '' },
{ key: 'otherCost', label: '其他费用', unit: '' },
]
const form = reactive<RingCalculatorInput>({
goldWeight: 0,
mainStoneWeight: 0,
sideStoneWeight: 0,
lossRate: 1,
moldGoldPrice: 0,
mainStoneUnitPrice: 0,
sideStoneUnitPrice: 0,
sideStoneCount: 0,
microSettingFee: 0,
mainStoneSettingFee: 0,
threeDFee: 0,
basicLaborCost: 0,
otherCost: 0,
})
const errors = reactive<Partial<Record<keyof RingCalculatorInput, string>>>({})
function onInput(key: keyof RingCalculatorInput, event: { detail: { value: string } }) {
const raw = event.detail.value
const num = Number(raw)
if (raw === '' || raw === '-') {
form[key] = 0
delete errors[key]
return
}
if (isNaN(num)) {
errors[key] = '请输入有效数字'
return
}
if (num < 0) {
errors[key] = '不能为负数'
return
}
delete errors[key]
form[key] = num
}
const result = computed<RingCalculatorResult | null>(() => {
const hasErrors = Object.keys(errors).length > 0
if (hasErrors) return null
try {
return calculateRing({ ...form })
} catch {
return null
}
})
const resultRows = computed(() => {
const r = result.value
if (!r) return []
return [
{ label: '净金重', value: r.netGoldWeight.toFixed(4) + ' g' },
{ label: '含耗重', value: r.weightWithLoss.toFixed(4) + ' g' },
{ label: '金值', value: '¥' + r.goldValue.toFixed(2) },
{ label: '主石总价', value: '¥' + r.mainStoneTotal.toFixed(2) },
{ label: '副石总价', value: '¥' + r.sideStoneTotal.toFixed(2) },
{ label: '微镶总价', value: '¥' + r.microSettingTotal.toFixed(2) },
{ label: '总价', value: '¥' + r.totalPrice.toFixed(2) },
]
})
</script>
<style scoped>
.custom-navbar {
background: linear-gradient(to right, #FFCFDE, #FFA6C4);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
.custom-navbar__content {
display: flex;
align-items: center;
padding: 0 24rpx;
}
.custom-navbar__back {
width: 44rpx;
height: 44rpx;
}
.custom-navbar__title {
flex: 1;
text-align: center;
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.custom-navbar__placeholder {
width: 44rpx;
}
.calculator-page {
min-height: 100vh;
background: #f5f5f5;
padding: 16rpx;
}
.section {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 16rpx;
}
.section__title {
font-size: 30rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.form-item {
margin-bottom: 20rpx;
}
.form-item__label {
font-size: 26rpx;
color: #666;
display: block;
margin-bottom: 8rpx;
}
.form-item__input {
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 16rpx;
font-size: 28rpx;
color: #333;
}
.form-item__error {
font-size: 22rpx;
color: #e4393c;
margin-top: 4rpx;
display: block;
}
.result-section {
background: #fff8f0;
}
.result-row {
display: flex;
justify-content: space-between;
padding: 12rpx 0;
border-bottom: 1rpx solid #f0e8e0;
}
.result-row:last-child {
border-bottom: none;
font-weight: bold;
font-size: 32rpx;
color: #e4393c;
}
.result-row__label {
font-size: 26rpx;
color: #666;
}
.result-row__value {
font-size: 26rpx;
color: #333;
}
</style>