Files
zyt/TUICallKit-Vue3/pages/Card/edit_card.vue
T
2026-03-20 13:56:40 +08:00

2442 lines
56 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="edit-container">
<!-- 加载状态 -->
<view v-if="loading" class="loading-state">
<text>加载中...</text>
</view>
<!-- 编辑表单 -->
<view v-else class="form-wrapper">
<!-- 患者基本信息 -->
<view class="form-section">
<text class="section-title">填写就诊人信息</text>
<!-- 姓名和性别在同一行 -->
<view class="form-row">
<view class="form-col-left">
<text class="form-label">姓名</text>
<input
v-model="formData.patient_name"
class="form-input-inline"
placeholder="请输入姓名"
maxlength="50"
/>
</view>
<view class="form-col-right">
<text class="form-label"><text class="required">*</text>性别</text>
<view class="gender-inline">
<view
class="gender-radio"
:class="{ active: formData.gender === 1 }"
@click="formData.gender = 1"
>
<text class="radio-icon">{{ formData.gender === 1 ? '✓' : '' }}</text>
<text></text>
</view>
<view
class="gender-radio"
:class="{ active: formData.gender === 0 }"
@click="formData.gender = 0"
>
<text class="radio-icon">{{ formData.gender === 0 ? '✓' : '' }}</text>
<text></text>
</view>
</view>
</view>
</view>
<!-- 证件类型 -->
<view class="form-row-single">
<text class="form-label">证件类型</text>
<text class="form-value">身份证号</text>
</view>
<!-- 证件号码和年龄在同一行 -->
<view class="form-row">
<view class="form-col-large">
<text class="form-label">证件号码</text>
<input
v-model="formData.id_card"
class="form-input-inline"
placeholder="需实名就诊"
maxlength="18"
style="width: 339rpx;"
@blur="handleIdCardChange"
/>
</view>
<view class="form-col-small">
<text class="form-label">年龄</text>
<input
v-model="formData.age"
class="form-input-inline"
style="padding: 12rpx 6rpx;"
disabled
placeholder=""
/>
</view>
</view>
<view class="form-tip">仅自己可见,会隐藏保护</view>
</view>
<!-- 生命体征 -->
<view class="form-section">
<!-- 体重 -->
<view class="form-row-single">
<text class="form-label"><text class="required">*</text>就诊人体重</text>
<input
v-model.number="formData.weight"
class="form-input-inline"
type="digit"
placeholder="请填写体重(数值)"
/>
<text class="form-unit">KG(千克)</text>
</view>
<!-- 身高 -->
<view class="form-row-single">
<text class="form-label"><text class="required">*</text>就诊人身高</text>
<input
v-model.number="formData.height"
class="form-input-inline"
type="digit"
placeholder="请填写身高(数值)"
/>
<text class="form-unit">CM(厘米)</text>
</view>
<!-- 高压 -->
<view class="form-row-single">
<text class="form-label">就诊人血压(高压)</text>
<input
v-model.number="formData.systolic_pressure"
class="form-input-inline"
type="number"
placeholder="请填写高压(数值)"
/>
<text class="form-unit">mmHg</text>
</view>
<!-- 低压 -->
<view class="form-row-single">
<text class="form-label">就诊人血压(低压)</text>
<input
v-model.number="formData.diastolic_pressure"
class="form-input-inline"
type="number"
placeholder="请填写低压(数值)"
/>
<text class="form-unit">mmHg</text>
</view>
<!-- 空腹血糖 -->
<view class="form-row-single">
<text class="form-label"><text class="required">*</text>空腹血糖</text>
<input
v-model.number="formData.fasting_blood_sugar"
class="form-input-inline"
type="digit"
placeholder="请填写空腹血糖(数值)"
required
/>
<text class="form-unit">mmol/L</text>
</view>
</view>
<!-- 主诉 -->
<view class="form-section">
<!-- 发现糖尿病病史 -->
<view class="form-row-single">
<text class="form-label-number">1</text>
<text class="form-labels"><text class="required">*</text>发现糖尿病患病史</text>
<input
v-model.number="formData.diabetes_discovery_year"
class="form-input-inline"
type="number"
placeholder="请输入年数"
/>
<text class="form-unit"></text>
</view>
<!-- 当地医院诊断结果 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">2</text>
<text class="form-labels"><text class="required">*</text>当地医院诊断结果</text>
</view>
<view class="button-grid">
<view
class="grid-button"
:class="{ active: formData.local_hospital_diagnosis.includes('糖尿病') }"
@click="toggleMultiCheckbox('local_hospital_diagnosis', '糖尿病')"
>
糖尿病
</view>
<view
class="grid-button"
:class="{ active: formData.local_hospital_diagnosis.includes('消渴病') }"
@click="toggleMultiCheckbox('local_hospital_diagnosis', '消渴病')"
>
消渴病
</view>
</view>
<view class="button-grid-single">
<view
class="grid-button"
:class="{ active: formData.local_hospital_diagnosis.includes('糖尿病前期') }"
@click="toggleMultiCheckbox('local_hospital_diagnosis', '糖尿病前期')"
>
糖尿病前期
</view>
</view>
</view>
<!-- 当地就诊医院名称 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">3</text>
<text class="form-labels"><text class="required">*</text>当地就诊医院名称</text>
</view>
<input
v-model="formData.local_hospital_name"
class="form-input-full"
placeholder="请填写当地就诊医院名称(全称)"
maxlength="100"
/>
</view>
<!-- 当地医院就诊时间 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">4</text>
<text class="form-labels"><text class="required">*</text>当地医院就诊时间</text>
</view>
<view class="date-input-full" @click="showDatePicker = true">
<text class="date-icon-left">🕐</text>
<text class="date-text">{{ formData.local_hospital_visit_date || '请选择确诊时间' }}</text>
<text class="date-arrow"></text>
</view>
</view>
</view>
<!-- 现病史 -->
<view class="form-section">
<!-- <text class="section-title">现病史</text> -->
<!-- 5口腔感觉 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">5</text>
<text class="form-labels">口腔感觉</text>
</view>
<view class="button-grid-three">
<view
v-for="item in appetiteOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.appetite === item.value }"
@click="formData.appetite = item.value"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 6饮食情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">6</text>
<text class="form-labels">饮食情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in dietConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.diet_condition.includes(item.value) }"
@click="toggleMultiCheckbox('diet_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 7每日饮水量 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">7</text>
<text class="form-labels">每日饮水量</text>
</view>
<view class="button-grid-three">
<view
v-for="item in waterIntakeOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.water_intake === item.value }"
@click="formData.water_intake = item.value"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 8体重变化 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">8</text>
<text class="form-labels">体重变化</text>
</view>
<view class="button-grid-three">
<view
v-for="item in weightChangeOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.weight_change === item.value }"
@click="formData.weight_change = item.value"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 9脂肪肝程度 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">9</text>
<text class="form-labels">脂肪肝程度</text>
</view>
<view class="button-grid-three">
<view
v-for="item in fattyLiverDegreeOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.fatty_liver_degree === item.value }"
@click="formData.fatty_liver_degree = item.value"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 10肢体感觉 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">10</text>
<text class="form-labels">肢体感觉</text>
</view>
<view class="button-grid-three">
<view
v-for="item in bodyFeelingOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.body_feeling.includes(item.value) }"
@click="toggleMultiCheckbox('body_feeling', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 11睡眠情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">11</text>
<text class="form-labels">睡眠情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in sleepConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.sleep_condition.includes(item.value) }"
@click="toggleMultiCheckbox('sleep_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 12眼睛情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">12</text>
<text class="form-labels">眼睛情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in eyeConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.eye_condition.includes(item.value) }"
@click="toggleMultiCheckbox('eye_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 13头部感觉 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">13</text>
<text class="form-labels">头部感觉</text>
</view>
<view class="button-grid-three">
<view
v-for="item in headFeelingOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.head_feeling.includes(item.value) }"
@click="toggleMultiCheckbox('head_feeling', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 14出汗情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">14</text>
<text class="form-labels">出汗情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in sweatConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.sweat_condition.includes(item.value) }"
@click="toggleMultiCheckbox('sweat_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 15皮肤情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">15</text>
<text class="form-labels">皮肤情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in skinConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.skin_condition.includes(item.value) }"
@click="toggleMultiCheckbox('skin_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 16小便情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">16</text>
<text class="form-labels">小便情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in urineConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.urine_condition.includes(item.value) }"
@click="toggleMultiCheckbox('urine_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 17大便情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">17</text>
<text class="form-labels">大便情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in stoolConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.stool_condition.includes(item.value) }"
@click="toggleMultiCheckbox('stool_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<!-- 18腰肾情况 -->
<view class="form-item-block">
<view class="form-label-block">
<text class="form-label-number">18</text>
<text class="form-labels">腰肾情况</text>
</view>
<view class="button-grid-three">
<view
v-for="item in kidneyConditionOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.kidney_condition.includes(item.value) }"
@click="toggleMultiCheckbox('kidney_condition', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
</view>
<!-- 既往史 -->
<view class="form-section">
<view class="form-label-block">
<text class="form-label-number">20</text>
<text class="form-labels">既往史</text>
</view>
<view class="form-group">
<view class="button-grid-three">
<view
v-for="item in pastHistoryOptions"
:key="item.value"
class="grid-button"
:class="{ active: formData.past_history.includes(item.value) }"
@click="toggleMultiCheckbox('past_history', item.value)"
>
{{ item.name }}
</view>
</view>
</view>
<view class="form-group">
<view class="form-label-block">
<text class="form-label-number">21</text>
<text class="form-labels">外伤史</text>
</view>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.trauma_history === 1 }"
@click="formData.trauma_history = 1"
>
</view>
<view
class="radio-item"
:class="{ active: formData.trauma_history === 0 }"
@click="formData.trauma_history = 0"
>
</view>
</view>
</view>
<view class="form-group">
<view class="form-label-block">
<text class="form-label-number">22</text>
<text class="form-labels">手术史</text>
</view>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.surgery_history === 1 }"
@click="formData.surgery_history = 1"
>
</view>
<view
class="radio-item"
:class="{ active: formData.surgery_history === 0 }"
@click="formData.surgery_history = 0"
>
</view>
</view>
</view>
<view class="form-group">
<view class="form-label-block">
<text class="form-label-number">23</text>
<text class="form-labels">过敏史</text>
</view>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.allergy_history === 1 }"
@click="formData.allergy_history = 1"
>
</view>
<view
class="radio-item"
:class="{ active: formData.allergy_history === 0 }"
@click="formData.allergy_history = 0"
>
</view>
</view>
</view>
<view class="form-group">
<view class="form-label-block">
<text class="form-label-number">24</text>
<text class="form-labels">家族病史</text>
</view>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.family_history === 1 }"
@click="formData.family_history = 1"
>
</view>
<view
class="radio-item"
:class="{ active: formData.family_history === 0 }"
@click="formData.family_history = 0"
>
</view>
</view>
</view>
<view class="form-group">
<view class="form-label-block">
<text class="form-label-number">25</text>
<text class="form-labels">妊娠哺乳史</text>
</view>
<view class="radio-group">
<view
class="radio-item"
:class="{ active: formData.pregnancy_history === 1 }"
@click="formData.pregnancy_history = 1"
>
</view>
<view
class="radio-item"
:class="{ active: formData.pregnancy_history === 0 }"
@click="formData.pregnancy_history = 0"
>
</view>
</view>
</view>
</view>
<!-- 舌苔照片 -->
<view class="form-section">
<view class="form-label-block">
<text class="form-label-number">26</text>
<text class="form-labels">舌苔照片</text>
</view>
<view class="upload-layout">
<view class="upload-left">
<view v-if="formData.tongue_images && formData.tongue_images.length > 0" class="image-list">
<view v-for="(img, index) in formData.tongue_images" :key="index" class="image-preview">
<image :src="img" mode="widthFix" class="preview-image" />
<view class="image-actions">
<text class="action-btn" @click="previewImages(formData.tongue_images, index)">预览</text>
<text class="action-btn delete" @click="deleteTonguePhoto(index)">删除</text>
</view>
</view>
</view>
<view v-if="formData.tongue_images.length <9" class="upload-btn-single" @click="uploadTonguePhoto">
<uni-icons type="camera" size="30"></uni-icons>
</view>
</view>
</view>
</view>
<!-- 检查报告 -->
<view class="form-section">
<view class="form-label-block">
<text class="form-label-number">27</text>
<text class="form-labels">检查报告</text>
</view>
<text class="form-label-tip">请上传线下就诊检查报告病历彩超等彩像科检查图片</text>
<view class="upload-layout">
<view class="upload-left">
<view v-if="formData.report_files && formData.report_files.length > 0" class="image-list">
<view v-for="(img, index) in formData.report_files" :key="index" class="image-preview">
<image :src="img" mode="widthFix" class="preview-image" />
<view class="image-actions">
<text class="action-btn" @click="previewImages(formData.report_files, index)">预览</text>
<text class="action-btn delete" @click="deleteExaminationReport(index)">删除</text>
</view>
</view>
</view>
<view class="upload-btn-single" @click="uploadExaminationReport" v-if="formData.report_files.length <9">
<uni-icons type="camera" size="30"></uni-icons>
</view>
</view>
</view>
</view>
<!-- 备注 -->
<view class="form-section">
<view class="form-group">
<text class="form-label">备注</text>
<textarea
v-model="formData.remark"
class="form-textarea"
placeholder="请输入备注"
maxlength="500"
/>
</view>
</view>
<!-- 操作按钮 -->
<view class="button-fixed">
<!-- 协议勾选 -->
<view class="agreement-section">
<view class="agreement-radio-group">
<view class="radio-option" @click="agreedToTerms = true">
<view class="radio-icon" :class="{ checked: agreedToTerms }">
<view v-if="agreedToTerms" class="radio-dot"></view>
</view>
<text class="radio-label"></text>
<view class="radio-option" @click="agreedToTerms = false">
<view class="radio-icon" :class="{ checked: !agreedToTerms }">
<view v-if="!agreedToTerms" class="radio-dot"></view>
</view>
<text class="radio-label"></text>
</view>
<text class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click.stop="openAgreement('service')">互联网诊疗知情同意书</text>
<text class="agreement-link" @click.stop="openAgreement('privacy')">中药委托加工和配送协议</text>
</text>
</view>
</view>
</view>
<!-- 提交按钮 -->
<button class="btn btn-submit" @click="submitForm" :disabled="submitting || !agreedToTerms">
{{ submitting ? '保存中...' : '我已确认无误,提交给医生' }}
</button>
</view>
</view>
<!-- 日期选择器 -->
<view v-if="showDatePicker" class="date-picker-overlay">
<view class="date-picker-container">
<view class="date-picker-header">
<text class="date-btn-cancel" @click="showDatePicker = false">取消</text>
<text class="date-btn-confirm" @click="confirmDate">确定</text>
</view>
<picker-view
:value="datePickerValue"
@change="onDateChange"
class="date-picker-view"
>
<picker-view-column>
<view v-for="year in yearRange" :key="year" class="picker-item">{{ year }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="month in monthRange" :key="month" class="picker-item">{{ String(month).padStart(2, '0') }}</view>
</picker-view-column>
<picker-view-column>
<view v-for="day in dayRange" :key="day" class="picker-item">{{ String(day).padStart(2, '0') }}</view>
</picker-view-column>
</picker-view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue'
const { proxy } = getCurrentInstance()
// 数据
const loading = ref(false)
const submitting = ref(false)
const agreedToTerms = ref(true) // 默认选中"是"
const showDatePicker = ref(false)
const diagnosisId = ref(null)
const patientId = ref(null)
const isAddMode = ref(false) // 新建模式
// 表单数据
const formData = ref({
patient_name: '',
id_card: '',
gender: 1,
age: '',
// 生命体征
height: '',
weight: '',
systolic_pressure: '',
diastolic_pressure: '',
fasting_blood_sugar: '',
// 主诉
diabetes_discovery_year: '',
local_hospital_diagnosis: [],
local_hospital_name: '',
local_hospital_visit_date: '',
// 诊断信息
diagnosis_date: '',
diagnosis_type: '',
syndrome_type: '',
diabetes_type: '',
// 现病史
appetite: '',
water_intake: '',
diet_condition: [],
weight_change: '',
body_feeling: [],
sleep_condition: [],
eye_condition: [],
head_feeling: [],
sweat_condition: [],
skin_condition: [],
urine_condition: [],
stool_condition: [],
kidney_condition: [],
fatty_liver_degree: '',
// 既往史
past_history: [],
trauma_history: 0,
surgery_history: 0,
allergy_history: 0,
family_history: 0,
pregnancy_history: 0,
// 临床信息
symptoms: '',
tongue_coating: '',
tongue_images: [],
report_files: [],
pulse: '',
treatment_principle: '',
prescription: '',
doctor_advice: '',
remark: ''
})
// 字典选项
const diagnosisTypeOptions = ref([])
const syndromeTypeOptions = ref([])
const diabetesTypeOptions = ref([])
const pastHistoryOptions = ref([])
// 现病史选项
const appetiteOptions = ref([])
const waterIntakeOptions = ref([])
const dietConditionOptions = ref([])
const weightChangeOptions = ref([])
const bodyFeelingOptions = ref([])
const sleepConditionOptions = ref([])
const eyeConditionOptions = ref([])
const headFeelingOptions = ref([])
const sweatConditionOptions = ref([])
const skinConditionOptions = ref([])
const urineConditionOptions = ref([])
const stoolConditionOptions = ref([])
const kidneyConditionOptions = ref([])
const fattyLiverDegreeOptions = ref([])
// 日期选择器
const datePickerValue = ref([0, 0, 0])
const yearRange = ref([])
const monthRange = ref(Array.from({ length: 12 }, (_, i) => i + 1))
const dayRange = ref(Array.from({ length: 31 }, (_, i) => i + 1))
// 页面加载
onMounted(() => {
loadDictOptions()
loadCardDetail()
initDatePicker()
})
// 获取字典选项
const loadDictOptions = async () => {
try {
const dictTypes = [
'diagnosis_type',
'syndrome_type',
'diabetes_type',
'past_history',
'appetite',
'water_intake',
'diet_condition',
'weight_change',
'body_feeling',
'sleep_condition',
'eye_condition',
'head_feeling',
'sweat_condition',
'skin_condition',
'urine_condition',
'stool_condition',
'kidney_condition',
'fatty_liver_degree'
]
// 并行请求所有字典数据
const results = await Promise.all(
dictTypes.map(type =>
proxy.apiUrl({
url: '/api/tcm/getDict',
method: 'GET',
data: { type }
})
)
)
// 处理返回的数据
results.forEach((res, index) => {
const type = dictTypes[index]
if (res.code === 1 && res.data) {
const data = res.data[type] || []
// 转换数据格式
const options = Array.isArray(data) ? data : Object.entries(data).map(([key, value]) => ({
value: key,
name: value
}))
// 根据类型赋值
switch (type) {
case 'diagnosis_type':
diagnosisTypeOptions.value = options
break
case 'syndrome_type':
syndromeTypeOptions.value = options
break
case 'diabetes_type':
diabetesTypeOptions.value = options
break
case 'past_history':
pastHistoryOptions.value = options
break
case 'appetite':
appetiteOptions.value = options
break
case 'water_intake':
waterIntakeOptions.value = options
break
case 'diet_condition':
dietConditionOptions.value = options
break
case 'weight_change':
weightChangeOptions.value = options
break
case 'body_feeling':
bodyFeelingOptions.value = options
break
case 'sleep_condition':
sleepConditionOptions.value = options
break
case 'eye_condition':
eyeConditionOptions.value = options
break
case 'head_feeling':
headFeelingOptions.value = options
break
case 'sweat_condition':
sweatConditionOptions.value = options
break
case 'skin_condition':
skinConditionOptions.value = options
break
case 'urine_condition':
urineConditionOptions.value = options
break
case 'stool_condition':
stoolConditionOptions.value = options
break
case 'kidney_condition':
kidneyConditionOptions.value = options
break
case 'fatty_liver_degree':
fattyLiverDegreeOptions.value = options
break
}
}
})
} catch (err) {
console.error('加载字典数据失败:', err)
}
}
// 初始化日期选择器
const initDatePicker = () => {
const currentYear = new Date().getFullYear()
yearRange.value = Array.from({ length: 100 }, (_, i) => currentYear - 50 + i)
}
// 根据日期字符串初始化日期选择器的值
const initDatePickerValue = (dateStr) => {
if (!dateStr) return
try {
// 解析日期字符串 (格式: YYYY-MM-DD)
const parts = dateStr.split('-')
if (parts.length !== 3) return
const year = parseInt(parts[0])
const month = parseInt(parts[1])
const day = parseInt(parts[2])
// 找到年份在yearRange中的索引
const yearIndex = yearRange.value.indexOf(year)
if (yearIndex === -1) return
// 月份索引 (0-11)
const monthIndex = month - 1
// 日期索引 (0-30)
const dayIndex = day - 1
datePickerValue.value = [yearIndex, monthIndex, dayIndex]
} catch (err) {
console.error('初始化日期选择器失败:', err)
}
}
// 加载就诊卡详情
const loadCardDetail = async () => {
loading.value = true
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const options = currentPage.options || {}
diagnosisId.value = options.id
isAddMode.value = options.add === '1' || options.add === 1
const userData = uni.getStorageSync('userData')
patientId.value = userData?.diagnosis?.patient_id
// 新建模式:patient_id 可选(创建后自动生成),仅需登录
if (isAddMode.value) {
const token = uni.getStorageSync('token')
if (!token) {
uni.showToast({ title: '请先登录', icon: 'none' })
setTimeout(() => uni.redirectTo({ url: '/pages/login/login' }), 1500)
return
}
loading.value = false
return
}
if (!patientId.value) {
uni.showToast({ title: '请先登录', icon: 'none' })
setTimeout(() => uni.redirectTo({ url: '/pages/login/login' }), 1500)
return
}
if (!diagnosisId.value) {
uni.showToast({ title: '诊单ID不存在', icon: 'none' })
setTimeout(() => uni.navigateBack(), 1500)
return
}
const res = await proxy.apiUrl({
url: '/api/tcm/diagnosisDetail',
method: 'GET',
data: {
id: diagnosisId.value,
user_id: patientId.value
}
})
if (res.code === 1) {
const diagnosis = res.data
if (diagnosis.patient_id !== patientId.value) {
uni.showToast({ title: '无权限编辑此诊单', icon: 'none' })
setTimeout(() => uni.navigateBack(), 1500)
return
}
// 处理数组字段
const arrayFields = ['diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition', 'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition', 'stool_condition', 'kidney_condition', 'past_history', 'local_hospital_diagnosis', 'report_files', 'tongue_images']
arrayFields.forEach(field => {
if (typeof diagnosis[field] === 'string' && diagnosis[field]) {
diagnosis[field] = diagnosis[field].split(',')
} else if (!diagnosis[field]) {
diagnosis[field] = []
}
})
formData.value = diagnosis
// 如果没有local_hospital_visit_date但有diagnosis_date,则使用diagnosis_date
if (!diagnosis.local_hospital_visit_date && diagnosis.diagnosis_date) {
formData.value.local_hospital_visit_date = diagnosis.diagnosis_date
}
// 初始化日期选择器的值
if (formData.value.local_hospital_visit_date) {
initDatePickerValue(formData.value.local_hospital_visit_date)
}
} else {
uni.showToast({ title: res.msg || '获取失败', icon: 'none' })
setTimeout(() => uni.navigateBack(), 1500)
}
} catch (err) {
console.error('加载诊单详情失败:', err)
uni.showToast({ title: '加载失败', icon: 'none' })
setTimeout(() => uni.navigateBack(), 1500)
} finally {
loading.value = false
}
}
// 多选框切换
const toggleMultiCheckbox = (field, value) => {
const index = formData.value[field].indexOf(value)
if (index > -1) {
formData.value[field].splice(index, 1)
} else {
formData.value[field].push(value)
}
}
// 根据身份证号计算年龄
const calculateAgeFromIdCard = (idCard) => {
if (!idCard || idCard.length !== 18) {
return ''
}
// 从身份证号中提取出生日期
const year = parseInt(idCard.substring(6, 10))
const month = parseInt(idCard.substring(10, 12))
const day = parseInt(idCard.substring(12, 14))
// 获取当前日期
const today = new Date()
const currentYear = today.getFullYear()
const currentMonth = today.getMonth() + 1
const currentDay = today.getDate()
// 计算年龄
let age = currentYear - year
// 如果还没到生日,年龄减1
if (currentMonth < month || (currentMonth === month && currentDay < day)) {
age--
}
return age
}
// 身份证号变化时自动计算年龄
const handleIdCardChange = () => {
if (formData.value.id_card && formData.value.id_card.length === 18) {
const age = calculateAgeFromIdCard(formData.value.id_card)
if (age) {
formData.value.age = age
}
}
}
const onDiagnosisTypeChange = (e) => {
formData.value.diagnosis_type = diagnosisTypeOptions.value[e.detail.value].value
}
const onSyndromeTypeChange = (e) => {
formData.value.syndrome_type = syndromeTypeOptions.value[e.detail.value].value
}
const onDiabetesTypeChange = (e) => {
formData.value.diabetes_type = diabetesTypeOptions.value[e.detail.value].value
}
const getDiagnosisTypeName = (type) => {
const item = diagnosisTypeOptions.value.find(i => i.value === type)
return item ? item.name : ''
}
const getSyndromeTypeName = (type) => {
const item = syndromeTypeOptions.value.find(i => i.value === type)
return item ? item.name : ''
}
const getDiabetesTypeName = (type) => {
const item = diabetesTypeOptions.value.find(i => i.value === type)
return item ? item.name : ''
}
const onDateChange = (e) => {
datePickerValue.value = e.detail.value
}
const confirmDate = () => {
const year = yearRange.value[datePickerValue.value[0]]
const month = String(monthRange.value[datePickerValue.value[1]]).padStart(2, '0')
const day = String(dayRange.value[datePickerValue.value[2]]).padStart(2, '0')
formData.value.local_hospital_visit_date = `${year}-${month}-${day}`
showDatePicker.value = false
}
// 切换协议同意状态
const toggleAgreement = () => {
agreedToTerms.value = !agreedToTerms.value
}
// 打开协议页面
const openAgreement = (type) => {
// 根据类型打开不同的协议页面
uni.navigateTo({
url: '/pages/Card/contact?type='+type
})
}
const submitForm = async () => {
// 检查是否同意协议
if (!agreedToTerms.value) {
uni.showToast({ title: '请先阅读并同意相关协议', icon: 'none' })
return
}
// 必填项验证
if (!formData.value.patient_name) {
uni.showToast({ title: '患者姓名不能为空', icon: 'none' })
return
}
if (formData.value.gender === null || formData.value.gender === undefined) {
uni.showToast({ title: '请选择性别', icon: 'none' })
return
}
if (!formData.value.weight) {
uni.showToast({ title: '体重不能为空', icon: 'none' })
return
}
if (!formData.value.height) {
uni.showToast({ title: '身高不能为空', icon: 'none' })
return
}
if (!formData.value.fasting_blood_sugar) {
uni.showToast({ title: '空腹血糖不能为空', icon: 'none' })
return
}
if (!formData.value.diabetes_discovery_year) {
uni.showToast({ title: '发现糖尿病患病史不能为空', icon: 'none' })
return
}
if (!formData.value.local_hospital_diagnosis || formData.value.local_hospital_diagnosis.length === 0) {
uni.showToast({ title: '当地医院诊断结果不能为空', icon: 'none' })
return
}
if (!formData.value.local_hospital_name) {
uni.showToast({ title: '当地就诊医院名称不能为空', icon: 'none' })
return
}
if (!formData.value.local_hospital_visit_date) {
uni.showToast({ title: '当地医院就诊时间不能为空', icon: 'none' })
return
}
submitting.value = true
try {
// 处理数组字段,转换为逗号分隔的字符串
const submitData = { ...formData.value }
const arrayFields = ['diet_condition', 'body_feeling', 'sleep_condition', 'eye_condition', 'head_feeling', 'sweat_condition', 'skin_condition', 'urine_condition', 'stool_condition', 'kidney_condition', 'past_history', 'local_hospital_diagnosis', 'report_files', 'tongue_images']
arrayFields.forEach(field => {
if (Array.isArray(submitData[field])) {
submitData[field] = submitData[field].join(',')
}
})
// 如果local_hospital_visit_date有值,也设置为diagnosis_date
if (submitData.local_hospital_visit_date) {
submitData.diagnosis_date = submitData.local_hospital_visit_date
}
const apiUrl = isAddMode.value ? '/api/tcm/addCard' : '/api/tcm/updateCard'
const apiData = isAddMode.value
? { ...submitData, ...(patientId.value ? { patient_id: patientId.value } : {}) }
: { id: diagnosisId.value, patient_id: patientId.value, ...submitData }
const res = await proxy.apiUrl({
url: apiUrl,
method: 'POST',
data: apiData
})
if (res.code === 1) {
// 新建成功:若有返回 patient_id,写入 userData 供后续列表查询
if (isAddMode.value && res.data?.patient_id) {
const userData = uni.getStorageSync('userData') || {}
if (!userData.diagnosis) userData.diagnosis = {}
userData.diagnosis.patient_id = res.data.patient_id
uni.setStorageSync('userData', userData)
}
uni.showToast({ title: isAddMode.value ? '创建成功' : '保存成功', icon: 'success' })
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({ title: res.msg || '保存失败', icon: 'none' })
}
} catch (err) {
console.error('保存失败:', err)
uni.showToast({ title: '保存失败', icon: 'none' })
} finally {
submitting.value = false
}
}
// 上传舌苔照片
const uploadTonguePhoto = () => {
const maxCount = 9 - (formData.value.tongue_images?.length || 0)
uni.chooseImage({
count: maxCount,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async (res) => {
const tempFilePaths = res.tempFilePaths
try {
uni.showLoading({ title: '上传中...' })
// 获取token
const token = uni.getStorageSync('token')
const $url = proxy.$url
// 初始化数组
if (!formData.value.tongue_images) {
formData.value.tongue_images = []
}
// 逐个上传图片
for (let i = 0; i < tempFilePaths.length; i++) {
const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({
url: $url + '/api/upload/image',
filePath: tempFilePaths[i],
name: 'file',
header: {
token: token,
'content-type': 'multipart/form-data'
},
success: (res) => {
const data = JSON.parse(res.data)
resolve(data)
},
fail: (err) => {
reject(err)
}
})
})
if (uploadRes.code === 1) {
formData.value.tongue_images.push(uploadRes.data.uri || uploadRes.data)
console.log('-',formData)
}
}
uni.hideLoading()
uni.showToast({ title: '上传成功', icon: 'success' })
} catch (err) {
uni.hideLoading()
console.error('上传照片失败:', err)
uni.showToast({ title: '上传失败', icon: 'none' })
}
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({ title: '选择图片失败', icon: 'none' })
}
})
}
// 删除舌苔照片
const deleteTonguePhoto = (index) => {
uni.showModal({
title: '提示',
content: '确定要删除这张舌苔照片吗?',
success: (res) => {
if (res.confirm) {
formData.value.tongue_images.splice(index, 1)
uni.showToast({ title: '已删除', icon: 'success' })
}
}
})
}
// 预览图片
const previewImage = (url) => {
uni.previewImage({
urls: [url],
current: url
})
}
// 上传检查报告
const uploadExaminationReport = () => {
const maxCount = 9 - (formData.value.report_files?.length || 0)
uni.chooseImage({
count: maxCount,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: async (res) => {
const tempFilePaths = res.tempFilePaths
try {
uni.showLoading({ title: '上传中...' })
// 获取token
const token = uni.getStorageSync('token')
const $url = proxy.$url
// 初始化数组
if (!formData.value.report_files) {
formData.value.report_files = []
}
// 逐个上传图片
for (let i = 0; i < tempFilePaths.length; i++) {
const uploadRes = await new Promise((resolve, reject) => {
uni.uploadFile({
url: $url + '/api/upload/image',
filePath: tempFilePaths[i],
name: 'file',
header: {
token: token,
'content-type': 'multipart/form-data'
},
success: (res) => {
const data = JSON.parse(res.data)
resolve(data)
},
fail: (err) => {
reject(err)
}
})
})
if (uploadRes.code === 1) {
formData.value.report_files.push(uploadRes.data.uri || uploadRes.data)
}
}
uni.hideLoading()
uni.showToast({ title: '上传成功', icon: 'success' })
} catch (err) {
uni.hideLoading()
console.error('上传检查报告失败:', err)
uni.showToast({ title: '上传失败', icon: 'none' })
}
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({ title: '选择图片失败', icon: 'none' })
}
})
}
// 删除检查报告
const deleteExaminationReport = (index) => {
uni.showModal({
title: '提示',
content: '确定要删除这张图片吗?',
success: (res) => {
if (res.confirm) {
formData.value.report_files.splice(index, 1)
uni.showToast({ title: '已删除', icon: 'success' })
}
}
})
}
// 预览多张图片
const previewImages = (urls, current) => {
uni.previewImage({
urls: urls,
current: current
})
}
const goBack = () => {
uni.navigateBack()
}
</script>
<style scoped>
.edit-container {
min-height: 100vh;
background: #f5f5f5;
}
.loading-state {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 36rpx;
color: #999999;
}
.form-wrapper {
padding: 20rpx;
padding-bottom: 280rpx; /* 为固定按钮留出空间 */
}
.form-section {
background: #ffffff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.section-title {
font-size: 34rpx;
font-weight: 600;
color: #333333;
margin-bottom: 20rpx;
display: block;
padding-bottom: 12rpx;
border-bottom: 1rpx solid #e8e8e8;
}
/* 行布局 */
.form-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
gap: 20rpx;
}
.form-row-single {
display: flex;
align-items: center;
margin-bottom: 20rpx;
flex-wrap: wrap;
}
.form-col-left {
flex: 1;
display: flex;
align-items: center;
}
.form-col-right {
flex: 1;
display: flex;
align-items: center;
}
.form-col-large {
flex: 2;
display: flex;
align-items: center;
}
.form-col-small {
flex: 1;
display: flex;
align-items: center;
}
/* 标签样式 */
.form-label {
font-size: 28rpx;
color: #666666;
white-space: nowrap;
margin-right: 8rpx;
}
.form-labels {
font-size: 32rpx;
color: #222222;
white-space: nowrap;
margin-right: 8rpx;
font-weight: 700;
}
.form-label-number {
font-size: 32rpx;
color: #333333;
font-weight: 600;
margin-right: 4rpx;
}
.required {
color: #ff4d4f;
margin-right: 4rpx;
font-size: 32rpx;
}
.form-value {
font-size: 28rpx;
color: #333333;
}
/* 行内输入框 */
.form-input-inline {
flex: 1;
padding: 12rpx 16rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
font-size: 28rpx;
color: #333333;
background: transparent;
}
.form-input-inline:disabled {
color: #999999;
background: #f5f5f5;
}
.form-input-inline::placeholder {
color: #cccccc;
font-size: 30rpx;
}
.form-unit {
font-size: 30rpx;
color: #999999;
margin-left: 8rpx;
white-space: nowrap;
}
/* 性别选择 - 行内样式 */
.gender-inline {
display: flex;
gap: 30rpx;
margin-left: 10rpx;
}
.gender-radio {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 32rpx;
color: #666666;
}
.gender-radio.active {
color: #386641;
}
.radio-icon {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #ffffff;
background: #ffffff;
}
.gender-radio.active .radio-icon {
background: #386641;
border-color: #386641;
}
.form-tip {
font-size: 28rpx;
color: #ff4d4f;
margin-top: 8rpx;
}
/* 块级表单项 */
.form-item-block {
margin-bottom: 30rpx;
}
.form-label-block {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
/* 按钮网格布局 */
.button-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
margin-bottom: 16rpx;
}
.button-grid-single {
display: grid;
grid-template-columns: 1fr;
}
.button-grid-three {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0;
}
.grid-button {
padding: 4rpx 20rpx;
border: 2rpx solid #e8e8e8;
border-radius: 0;
text-align: center;
font-size: 32rpx;
color: #555555;
background: #ffffff;
transition: all 0.2s;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: -2rpx;
margin-bottom: 10rpx;
position: relative;
}
.grid-button:first-child {
margin-left: 0;
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
}
.grid-button:last-child {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.button-grid-three .grid-button:nth-child(3n+1) {
border-top-left-radius: 8rpx;
border-bottom-left-radius: 8rpx;
margin-left: 0;
}
.button-grid-three .grid-button:nth-child(3n) {
border-top-right-radius: 8rpx;
border-bottom-right-radius: 8rpx;
}
.button-grid-three .grid-button:nth-child(3n+2) {
border-radius: 0;
}
.grid-button.active {
border-color: #386641;
background: #386641;
color: #ffffff;
font-weight: 500;
z-index: 1;
}
/* 选中按钮右侧的白色分隔线 */
.grid-button.active::after {
content: '';
position: absolute;
right: -2rpx;
top: 0;
bottom: 0;
width: 2rpx;
background: #ffffff;
z-index: 2;
}
/* 选中按钮左侧的白色分隔线 */
.grid-button.active::before {
content: '';
position: absolute;
left: -2rpx;
top: 0;
bottom: 0;
width: 2rpx;
background: #ffffff;
z-index: 2;
}
/* 如果是第一个按钮被选中,不显示左侧白线 */
.grid-button.active:first-child::before,
.button-grid-three .grid-button.active:nth-child(3n+1)::before {
display: none;
}
/* 如果是最后一个按钮被选中,不显示右侧白线 */
.grid-button.active:last-child::after,
.button-grid-three .grid-button.active:nth-child(3n)::after {
display: none;
}
/* 全宽输入框 */
.form-input-full {
width: 100%;
padding: 20rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
font-size: 32rpx;
color: #333333;
background: transparent;
box-sizing: border-box;
min-height: 80rpx;
display: block;
}
.form-input-full::placeholder {
color: #cccccc;
font-size: 30rpx;
}
/* 全宽日期选择器 */
.date-input-full {
display: flex;
align-items: center;
padding: 20rpx;
border: none;
border-bottom: 1rpx solid #e8e8e8;
background: transparent;
min-height: 80rpx;
box-sizing: border-box;
}
.date-icon-left {
font-size: 36rpx;
color: #999999;
margin-right: 12rpx;
flex-shrink: 0;
}
.date-text {
flex: 1;
font-size: 32rpx;
color: #333333;
word-break: break-word;
}
.date-arrow {
font-size: 40rpx;
color: #cccccc;
margin-left: 12rpx;
flex-shrink: 0;
}
/* 原有表单组样式 - 保留用于其他部分 */
.form-group {
margin-bottom: 24rpx;
}
.form-group:last-child {
margin-bottom: 0;
}
.form-input {
width: 100%;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
background: #fffaf5;
box-sizing: border-box;
display: block;
height: auto;
min-height: 80rpx;
}
.form-input::placeholder {
color: #cccccc;
font-size: 32rpx;
}
.form-textarea {
width: 100%;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
font-size: 34rpx;
line-height: 1.6;
color: #333333;
background: #fffaf5;
box-sizing: border-box;
min-height: 180rpx;
display: block;
font-family: inherit;
}
.form-textarea::placeholder {
color: #cccccc;
font-size: 32rpx;
}
.gender-group {
display: flex;
gap: 16rpx;
}
.gender-btn {
flex: 1;
padding: 22rpx 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
text-align: center;
font-size: 34rpx;
line-height: 1.5;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
min-height: 80rpx;
}
.gender-btn.active {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.radio-group {
display: flex;
gap: 16rpx;
}
.radio-item {
flex: 1;
padding: 20rpx 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
text-align: center;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
min-height: 70rpx;
display: flex;
align-items: center;
justify-content: center;
}
.radio-item.active {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.checkbox-item {
padding: 18rpx 28rpx;
border: 2rpx solid #386641;
border-radius: 24rpx;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
}
.checkbox-item.active {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.multi-checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.multi-checkbox-item {
padding: 18rpx 28rpx;
border: 2rpx solid #386641;
border-radius: 24rpx;
font-size: 32rpx;
color: #666666;
background: #fffaf5;
transition: all 0.2s;
}
.multi-checkbox-item.active {
border-color: #386641;
background: #fff4e6;
color: #386641;
font-weight: 600;
}
.date-input {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
background: #fffaf5;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
min-height: 80rpx;
}
.date-icon {
font-size: 40rpx;
margin-left: 12rpx;
color: #386641;
}
.picker-item {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
font-size: 34rpx;
color: #333333;
}
/* 上传组件样式 */
.upload-container {
margin-top: 16rpx;
}
.upload-layout {
display: flex;
gap: 20rpx;
margin-top: 20rpx;
align-items: flex-start;
}
.upload-left {
flex-shrink: 0;
width: 600rpx;
}
.upload-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
gap: 12rpx;
}
.upload-label {
font-size: 32rpx;
color: #333333;
font-weight: 600;
}
.upload-desc {
font-size: 28rpx;
color: #999999;
line-height: 1.5;
}
.more-images {
display: flex;
gap: 12rpx;
margin-top: 12rpx;
flex-wrap: wrap;
}
.more-item {
width: 80rpx;
height: 80rpx;
border-radius: 8rpx;
overflow: hidden;
border: 2rpx solid #386641;
}
.more-item image {
width: 100%;
height: 100%;
}
.upload-btn-single {
width: 200rpx;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
transition: all 0.2s;
}
.upload-btn-single:active {
background: #fff4e6;
border-color: #386641;
}
.upload-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60rpx 40rpx;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
transition: all 0.2s;
}
.upload-btn:active {
background: #fff4e6;
border-color: #386641;
}
.upload-icon {
font-size: 80rpx;
margin-bottom: 16rpx;
}
.upload-text {
font-size: 28rpx;
color: #999999;
}
.upload-tip {
font-size: 24rpx;
color: #cccccc;
margin-top: 8rpx;
}
.form-label-tip {
font-size: 24rpx;
color: #999999;
margin-bottom: 16rpx;
display: block;
line-height: 1.5;
}
.image-preview {
position: relative;
border-radius: 12rpx;
overflow: hidden;
border: 2rpx solid #386641;
width: 100%;
height: 100%;
}
.preview-image {
width: 200rpx;
height: 200rpx;
display: block;
}
.image-actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
padding: 12rpx;
background: rgba(0, 0, 0, 0.6);
gap: 8rpx;
}
.action-btn {
padding: 8rpx 16rpx;
border-radius: 6rpx;
font-size: 24rpx;
color: #ffffff;
background: rgba(255, 140, 0, 0.8);
border: none;
transition: all 0.2s;
flex: 1;
text-align: center;
}
.action-btn:active {
background: rgba(255, 140, 0, 1);
}
.action-btn.delete {
color: #ffffff;
background: rgba(255, 77, 79, 0.8);
}
.action-btn.delete:active {
background: rgba(255, 77, 79, 1);
}
/* 多图片列表样式 */
.image-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16rpx;
margin-bottom: 20rpx;
}
.image-item {
position: relative;
width: 100%;
padding-bottom: 100%;
border-radius: 12rpx;
overflow: hidden;
border: 2rpx solid #386641;
}
.item-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.delete-icon {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 48rpx;
height: 48rpx;
background: rgba(255, 77, 79, 0.9);
color: #ffffff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
line-height: 1;
font-weight: bold;
z-index: 10;
}
.upload-btn-small {
width: 100%;
padding-bottom: 100%;
position: relative;
border: 2rpx dashed #386641;
border-radius: 12rpx;
background: #fffaf5;
display: flex;
align-items: center;
justify-content: center;
}
.upload-icon-small {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 60rpx;
color: #386641;
}
.picker-input {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
border: 2rpx solid #386641;
border-radius: 12rpx;
background: #fffaf5;
font-size: 34rpx;
line-height: 1.5;
color: #333333;
min-height: 80rpx;
}
.picker-icon {
font-size: 28rpx;
color: #386641;
margin-left: 12rpx;
}
.button-group {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
margin-bottom: 40rpx;
padding: 0 20rpx;
}
.button-fixed {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 12rpx 20rpx 16rpx 20rpx;
background: #ffffff;
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.1);
z-index: 100;
box-sizing: border-box;
}
.agreement-section {
margin-bottom: 16rpx;
}
.agreement-radio-group {
display: flex;
align-items: center;
gap: 12rpx;
flex-wrap: wrap;
}
.radio-option {
display: flex;
align-items: center;
gap: 8rpx;
cursor: pointer;
}
.radio-icon {
width: 36rpx;
height: 36rpx;
border: 2rpx solid #d9d9d9;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.2s;
background: #ffffff;
}
.radio-icon.checked {
border-color: #52c41a;
}
.radio-dot {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background: #52c41a;
}
.radio-label {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.agreement-text {
font-size: 26rpx;
color: #333333;
line-height: 1.6;
}
.agreement-link {
color: #1890ff;
text-decoration: underline;
}
.agreement-link:active {
opacity: 0.7;
}
.btn {
width: 100%;
padding: 10rpx;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 600;
border: none;
cursor: pointer;
transition: all 0.2s;
min-height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30rpx;
}
.btn:active {
transform: scale(0.98);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-cancel {
background: #f5f5f5;
color: #666666;
border: 2rpx solid #e8e8e8;
}
.btn-submit {
background: #389e0d;
color: #ffffff;
}
.btn-submit:active:not(:disabled) {
background: linear-gradient(135deg, #389e0d 0%, #237804 100%);
}
.date-picker-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
justify-content: flex-end;
z-index: 999;
}
.date-picker-container {
background: #ffffff;
border-radius: 20rpx 20rpx 0 0;
overflow: hidden;
}
.date-picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
border-bottom: 2rpx solid #ffe4cc;
}
.date-btn-cancel,
.date-btn-confirm {
font-size: 34rpx;
color: #386641;
font-weight: 600;
}
.date-picker-view {
height: 320rpx;
}
.picker-item {
display: flex;
justify-content: center;
align-items: center;
height: 50px;
font-size: 34rpx;
color: #333333;
}
</style>