This commit is contained in:
Lwei
2026-06-22 11:23:05 +08:00
commit 234d126ff6
998 changed files with 355523 additions and 0 deletions
+553
View File
@@ -0,0 +1,553 @@
{extend name="PatientAPP/layout" /}
{block name="css"}
<link rel="stylesheet" href="/PatientAPP/Order/create.css" />
{/block}
{block name="app"}
<div id="app" v-cloak>
<!-- 头部区域 -->
<div class="header-section">
<div class="header-top">
<div class="header-left">
<van-icon name="arrow-left" size="20" @click="goBack" />
<span class="page-title">发布订单</span>
</div>
<div class="header-actions">
<div class="icon-btn" @click="onDraft">
<van-icon name="description" size="17" />
<span style="font-size:11px;margin-left:3px;">草稿</span>
</div>
</div>
</div>
</div>
<!-- 表单主体 -->
<div class="form-wrapper">
<!-- 服务类型选择 -->
<div class="form-card form-card-type">
<div class="fc-label"><van-icon name="apps-o" color="#018fa5" size="16" /> 选择服务类型</div>
<div class="type-grid">
<div class="type-item"
:class="{ 'type-active': form.serviceType === item.value }"
v-for="(item, index) in serviceTypes"
:key="index"
@click="selectServiceType(item.value)">
<div class="type-icon-wrap" :class="'wrap-' + item.value">
<van-icon :name="item.icon" size="24" :color="form.serviceType === item.value ? '#fff' : item.color" />
</div>
<span class="type-name">{{ item.label }}</span>
<span class="type-desc">{{ item.desc }}</span>
</div>
</div>
</div>
<!-- 患者信息 -->
<div class="form-card">
<div class="fc-header">
<div class="fc-label"><van-icon name="contact" color="#018fa5" size="16" /> 患者信息</div>
</div>
<div class="fc-body">
<van-field v-model="form.patientName" label="姓名" placeholder="请输入患者姓名" clearable />
<van-field v-model="form.patientPhone" label="联系电话" type="tel" placeholder="请输入联系电话" clearable />
<van-field v-model="form.patientAge" label="年龄" type="digit" placeholder="请输入年龄" />
<van-field v-model="form.patientGender" label="性别" readonly placeholder="请选择" is-link @click="showGenderPicker = true" />
</div>
</div>
<!-- 服务时间 -->
<div class="form-card">
<div class="fc-header">
<div class="fc-label"><van-icon name="clock-o" color="#018fa5" size="16" /> 服务时间</div>
</div>
<div class="fc-body">
<van-field
:model-value="form.startDate || ''"
label="开始日期"
readonly
placeholder="请选择开始日期"
is-link
@click="openStartDatePicker"
/>
<van-field
:model-value="form.startTime || ''"
label="开始时间"
readonly
placeholder="请选择开始时间"
is-link
@click="openStartTimePicker"
/>
<van-field
:model-value="form.endDate || ''"
label="结束日期"
readonly
placeholder="请先选开始日期"
is-link
@click="openEndDatePicker"
/>
<van-field
:model-value="form.endTime || ''"
label="结束时间"
readonly
placeholder="请先选结束日期"
is-link
@click="openEndTimePicker"
/>
<div class="duration-tip" v-if="calcDuration">
<van-icon name="info-o" size="14" />
<span>预计服务时长:<strong>{{ calcDuration }}</strong></span>
</div>
</div>
</div>
<!-- 服务地点 -->
<div class="form-card">
<div class="fc-header">
<div class="fc-label"><van-icon name="location-o" color="#018fa5" size="16" /> 服务地点</div>
<van-tag type="primary" size="medium" plain round @click="useCurrentLocation">使用当前位置</van-tag>
</div>
<div class="fc-body">
<van-field v-model="form.hospitalName" label="医院/地点" placeholder="请输入医院名称或服务地点" />
<van-field
v-model="form.detailAddress"
label="详细地址"
type="textarea"
rows="2"
autosize
placeholder="请输入详细地址,如病房号、楼层等"
/>
</div>
</div>
<!-- 护工要求 -->
<div class="form-card">
<div class="fc-header">
<div class="fc-label"><van-icon name="shield-o" color="#018fa5" size="16" /> 护工要求</div>
</div>
<div class="fc-body">
<van-field v-model="form.nurseGender" label="护工性别" readonly placeholder="不限" is-link @click="showNurseGenderPicker = true" />
<van-field v-model="form.experienceReq" label="经验要求" readonly placeholder="不限" is-link @click="showExpPicker = true" />
<div class="skill-tags-section">
<div class="skill-tags-label">特殊技能需求(可多选)</div>
<div class="skill-tags-list">
<span
class="skill-tag"
:class="{ 'skill-active': form.skills.includes(skill) }"
v-for="(skill, index) in skillOptions"
:key="index"
@click="toggleSkill(skill)"
>{{ skill }}</span>
</div>
</div>
</div>
</div>
<!-- 备注说明 -->
<div class="form-card">
<div class="fc-header">
<div class="fc-label"><van-icon name="edit" color="#018fa5" size="16" /> 备注说明</div>
</div>
<div class="fc-body">
<van-field
v-model="form.remark"
type="textarea"
rows="4"
autosize
maxlength="200"
show-word-limit
placeholder="请详细描述患者的身体状况、护理需求等特殊情况,以便匹配合适的护工..."
/>
</div>
</div>
<!-- 费用预估 -->
<div class="form-card price-preview" v-if="estimatedPrice > 0">
<div class="price-row">
<span class="price-label">费用预估</span>
<div class="price-value">
<span class="yen">&yen;</span>
<span class="amount">{{ estimatedPrice }}</span>
</div>
</div>
<div class="price-detail">
<div class="price-item">
<span>基础费用({{ basePrice }}元/小时 x {{ totalHours }}小时)</span>
<span>&yen;{{ basePrice * totalHours }}</span>
</div>
<div class="price-item" v-if="form.skills.length > 0">
<span>技能加成({{ form.skills.length }}项技能 +10%</span>
<span>&yen;{{ Math.round(basePrice * totalHours * 0.1) }}</span>
</div>
</div>
</div>
</div>
<!-- 底部操作栏 -->
<div class="bottom-bar">
<div class="bottom-left">
<div class="total-price">
<span class="tp-label">预估总价:</span>
<span class="tp-value"><span class="tp-yen">&yen;</span>{{ estimatedPrice || '0' }}</span>
</div>
<div class="tp-desc">实际价格以接单后确认为准</div>
</div>
<van-button type="primary" class="submit-btn" round :loading="submitting" @click="submitOrder">
发布订单
</van-button>
</div>
<!-- 性别选择器 -->
<van-popup v-model:show="showGenderPicker" position="bottom" round>
<van-picker :columns="genderColumns" @confirm="onGenderConfirm" @cancel="showGenderPicker = false" title="选择性别" />
</van-popup>
<!-- 护工性别选择器 -->
<van-popup v-model:show="showNurseGenderPicker" position="bottom" round>
<van-picker :columns="nurseGenderColumns" @confirm="onNurseGenderConfirm" @cancel="showNurseGenderPicker = false" title="选择护工性别" />
</van-popup>
<!-- 经验要求选择器 -->
<van-popup v-model:show="showExpPicker" position="bottom" round>
<van-picker :columns="expColumns" @confirm="onExpConfirm" @cancel="showExpPicker = false" title="选择经验要求" />
</van-popup>
<!-- 开始日期选择器 -->
<van-popup v-model:show="showStartDatePicker" position="bottom" round>
<van-date-picker
:key="'sd-' + startDateKey"
v-model="startDatePickerValue"
title="选择开始日期"
:min-date="minDate"
:max-date="maxDate"
@confirm="onStartDateConfirm"
@cancel="showStartDatePicker = false"
/>
</van-popup>
<!-- 开始时间选择器 -->
<van-popup v-model:show="showStartTimePicker" position="bottom" round>
<van-time-picker
:key="'st-' + startTimeKey"
v-model="startTimePickerValue"
title="选择开始时间"
:columns-type="['hour', 'minute']"
@confirm="onStartTimeConfirm"
@cancel="showStartTimePicker = false"
/>
</van-popup>
<!-- 结束日期选择器 -->
<van-popup v-model:show="showEndDatePicker" position="bottom" round>
<van-date-picker
:key="'ed-' + endDateKey"
v-model="endDatePickerValue"
title="选择结束日期"
:min-date="startMinEndDate"
:max-date="maxDate"
@confirm="onEndDateConfirm"
@cancel="showEndDatePicker = false"
/>
</van-popup>
<!-- 结束时间选择器 -->
<van-popup v-model:show="showEndTimePicker" position="bottom" round>
<van-time-picker
:key="'et-' + endTimeKey"
v-model="endTimePickerValue"
title="选择结束时间"
:columns-type="['hour', 'minute']"
@confirm="onEndTimeConfirm"
@cancel="showEndTimePicker = false"
/>
</van-popup>
</div>
{/block}
{block name="js"}
<script>
const app = Vue.createApp({
data() {
const now = new Date();
return {
submitting: false,
showGenderPicker: false,
showNurseGenderPicker: false,
showExpPicker: false,
minDate: new Date(),
maxDate: new Date(Date.now() + 365 * 86400000),
// 开始日期选择器
showStartDatePicker: false,
startDateKey: 0,
startDatePickerValue: ['2026', '5', '9'],
// 开始时间选择器
showStartTimePicker: false,
startTimeKey: 0,
startTimePickerValue: ['8', '0'],
// 结束日期选择器
showEndDatePicker: false,
endDateKey: 0,
endDatePickerValue: ['2026', '5', '10'],
// 结束时间选择器
showEndTimePicker: false,
endTimeKey: 0,
endTimePickerValue: ['18', '0'],
genderColumns: ['男', '女'],
nurseGenderColumns: ['不限', '男', '女'],
expColumns: ['不限', '1年以上', '3年以上', '5年以上', '8年以上'],
serviceTypes: [
{ value: 'hospital', label: '住院陪护', icon: 'hotel-o', desc: '院内专业护理', color: '#018fa5' },
{ value: 'outpatient', label: '门诊陪护', icon: 'guide-o', desc: '陪同就诊检查', color: '#e08c28' },
{ value: 'home', label: '居家陪护', icon: 'home-o', desc: '上门居家照料', color: '#2ba245' },
{ value: 'maternal', label: '母婴护理', icon: 'like-o', desc: '月子期专业照护', color: '#e91e8b' },
],
skillOptions: ['术后护理', '老年照护', '康复训练', '输液换药', '重症监护', '母婴护理', '营养配餐', '心理疏导', '行动辅助', '中医理疗'],
form: {
serviceType: '',
patientName: '',
patientPhone: '',
patientAge: '',
patientGender: '',
startDate: '',
startTime: '',
endDate: '',
endTime: '',
hospitalName: '',
detailAddress: '',
nurseGender: '',
experienceReq: '',
skills: [],
remark: '',
},
basePriceMap: {
hospital: 68,
outpatient: 58,
home: 52,
maternal: 88,
},
};
},
computed: {
startMinEndDate() {
if (!this.form.startDate) return this.minDate;
const d = new Date(this.form.startDate.replace(/-/g, '/'));
return d;
},
totalHours() {
if (!this.form.startDate || !this.form.endDate) return 0;
const start = new Date(`${this.form.startDate.replace(/-/g, '/')} ${this.form.startTime || '00:00'}`);
const end = new Date(`${this.form.endDate.replace(/-/g, '/')} ${this.form.endTime || '00:00'}`);
let diff = (end - start) / 3600000;
if (diff <= 0) diff = 0;
return Math.round(diff * 10) / 10;
},
totalDays() {
if (!this.form.startDate || !this.form.endDate) return 0;
const s = new Date(this.form.startDate.replace(/-/g, '/'));
const e = new Date(this.form.endDate.replace(/-/g, '/'));
return Math.ceil((e - s) / 86400000);
},
basePrice() {
return this.basePriceMap[this.form.serviceType] || 60;
},
estimatedPrice() {
if (this.totalHours === 0) return 0;
let price = this.basePrice * this.totalHours;
if (this.form.skills.length > 0) {
price = Math.round(price * 1.1);
}
return price;
},
calcDuration() {
if (!this.form.startDate || !this.form.endDate) return '';
const days = this.totalDays;
const hours = this.totalHours;
if (days <= 0 && hours <= 0) return '';
if (days > 0) return `${days}${hours}小时`;
return `${hours}小时`;
},
},
methods: {
goBack() { history.back(); },
onDraft() { vant.showToast('暂无草稿'); },
selectServiceType(type) { this.form.serviceType = type; },
useCurrentLocation() {
vant.showToast({ message: '正在获取位置...', loading: true, duration: 1500 });
setTimeout(() => {
this.form.hospitalName = '当前位置附近';
vant.clearToast();
vant.showToast('已获取位置');
}, 1000);
},
toggleSkill(skill) {
const idx = this.form.skills.indexOf(skill);
if (idx > -1) {
this.form.skills.splice(idx, 1);
} else {
this.form.skills.push(skill);
}
},
padZero(n) { return String(n).padStart(2, '0'); },
openStartDatePicker() {
if (this.form.startDate) {
const p = this.form.startDate.split('-');
this.startDatePickerValue = [p[0], String(parseInt(p[1])), String(parseInt(p[2]))];
} else {
const now = new Date();
this.startDatePickerValue = [
String(now.getFullYear()),
String(now.getMonth() + 1),
String(now.getDate())
];
}
this.startDateKey++;
this.showStartDatePicker = true;
},
openStartTimePicker() {
if (!this.form.startDate) {
vant.showToast('请先选择开始日期');
return;
}
if (this.form.startTime) {
const p = this.form.startTime.split(':');
this.startTimePickerValue = [String(parseInt(p[0])), String(parseInt(p[1]))];
} else {
this.startTimePickerValue = ['8', '0'];
}
this.startTimeKey++;
this.showStartTimePicker = true;
},
openEndDatePicker() {
if (!this.form.startDate) {
vant.showToast('请先选择开始日期');
return;
}
if (this.form.endDate) {
const p = this.form.endDate.split('-');
this.endDatePickerValue = [p[0], String(parseInt(p[1])), String(parseInt(p[2]))];
} else {
const start = new Date(this.form.startDate.replace(/-/g, '/'));
start.setDate(start.getDate() + 1);
this.endDatePickerValue = [
String(start.getFullYear()),
String(start.getMonth() + 1),
String(start.getDate())
];
}
this.endDateKey++;
this.showEndDatePicker = true;
},
openEndTimePicker() {
if (!this.form.endDate) {
vant.showToast('请先选择结束日期');
return;
}
if (this.form.endTime) {
const p = this.form.endTime.split(':');
this.endTimePickerValue = [String(parseInt(p[0])), String(parseInt(p[1]))];
} else {
this.endTimePickerValue = ['18', '0'];
}
this.endTimeKey++;
this.showEndTimePicker = true;
},
onStartDateConfirm({ selectedValues }) {
const v = selectedValues;
this.form.startDate = `${v[0]}-${this.padZero(v[1])}-${this.padZero(v[2])}`;
// 如果已选结束日期早于开始日期,清空结束日期
if (this.form.endDate && new Date(this.form.endDate.replace(/-/g, '/')) < new Date(this.form.startDate.replace(/-/g, '/'))) {
this.form.endDate = '';
this.form.endTime = '';
}
this.showStartDatePicker = false;
},
onStartTimeConfirm({ selectedValues }) {
const v = selectedValues;
this.form.startTime = `${this.padZero(v[0])}:${this.padZero(v[1])}`;
this.showStartTimePicker = false;
},
onEndDateConfirm({ selectedValues }) {
const v = selectedValues;
this.form.endDate = `${v[0]}-${this.padZero(v[1])}-${this.padZero(v[2])}`;
// 校验结束日期不能早于开始日期
if (new Date(this.form.endDate.replace(/-/g, '/')) < new Date(this.form.startDate.replace(/-/g, '/'))) {
vant.showToast('结束日期不能早于开始日期');
this.form.endDate = '';
return;
}
this.showEndDatePicker = false;
},
onEndTimeConfirm({ selectedValues }) {
const v = selectedValues;
const endTimeStr = `${this.padZero(v[0])}:${this.padZero(v[1])}`;
// 校验结束时间须晚于开始时间
if (this.form.endDate === this.form.startDate && this.form.startTime) {
const endFull = new Date(`${this.form.endDate.replace(/-/g, '/')} ${endTimeStr}`);
const startFull = new Date(`${this.form.startDate.replace(/-/g, '/')} ${this.form.startTime}`);
if (endFull <= startFull) {
vant.showToast('结束时间须晚于开始时间');
return;
}
}
this.form.endTime = endTimeStr;
this.showEndTimePicker = false;
},
onGenderConfirm({ selectedOptions }) { this.form.patientGender = selectedOptions[0]?.text || ''; this.showGenderPicker = false; },
onNurseGenderConfirm({ selectedOptions }) { this.form.nurseGender = selectedOptions[0]?.text || ''; this.showNurseGenderPicker = false; },
onExpConfirm({ selectedOptions }) { this.form.experienceReq = selectedOptions[0]?.text || ''; this.showExpPicker = false; },
formatDateOnly(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
},
validateForm() {
if (!this.form.serviceType) { vant.showToast('请选择服务类型'); return false; }
if (!this.form.patientName) { vant.showToast('请输入患者姓名'); return false; }
if (!this.form.patientPhone) { vant.showToast('请输入联系电话'); return false; }
if (!/^1\d{10}$/.test(this.form.patientPhone)) { vant.showToast('手机号格式不正确'); return false; }
if (!this.form.startDate || !this.form.startTime) { vant.showToast('请选择开始服务时间'); return false; }
if (!this.form.endDate || !this.form.endTime) { vant.showToast('请选择结束服务时间'); return false; }
if (this.totalHours <= 0) { vant.showToast('结束时间须晚于开始时间'); return false; }
if (!this.form.hospitalName) { vant.showToast('请输入服务地点'); return false; }
return true;
},
submitOrder() {
if (!this.validateForm()) return;
vant.showConfirmDialog({
title: '确认发布',
message: `服务类型:${this.serviceTypes.find(s => s.value === this.form.serviceType)?.label}\n服务时间:${this.form.startDate} ${this.form.startTime} ~ ${this.form.endDate} ${this.form.endTime}\n服务时长:${this.calcDuration}\n预估费用:¥${this.estimatedPrice}`,
confirmButtonText: '确认发布',
cancelButtonText: '再想想',
}).then(() => {
this.submitting = true;
setTimeout(() => {
this.submitting = false;
vant.showDialog({
title: '发布成功',
message: '订单已发布,系统将为您匹配合适的护工,请耐心等待接单。',
confirmButtonText: '查看订单',
}).then(() => {
window.location.href = '/PatientApp/Order/index';
});
}, 1500);
}).catch(() => {});
},
},
watch: {},
});
app.use(vant);
app.use(vant.Lazyload);
app.mount('#app');
</script>
{/block}