feat(schedule): 生成工单 UI 入口(打通计划→派单闭环)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
abf76e22d7
commit
2ae7aec861
@ -4,6 +4,7 @@
|
|||||||
<div class="flex items-center gap-sm">
|
<div class="flex items-center gap-sm">
|
||||||
<el-date-picker v-model="date" type="date" value-format="YYYY-MM-DD" :clearable="false" style="width: 160px" />
|
<el-date-picker v-model="date" type="date" value-format="YYYY-MM-DD" :clearable="false" style="width: 160px" />
|
||||||
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
|
<el-button type="primary" :loading="loading" @click="refresh">刷新</el-button>
|
||||||
|
<el-button v-permission="'order:generate'" @click="openGenerate">生成工单</el-button>
|
||||||
<el-button v-permission="'order:assign'" @click="recommendVisible = true">批量推荐</el-button>
|
<el-button v-permission="'order:assign'" @click="recommendVisible = true">批量推荐</el-button>
|
||||||
<span class="ml-auto text-xs text-text-placeholder">横轴时段 08:00–18:00 · 纵轴护理员 · 冲突红框 · 空缺橙框</span>
|
<span class="ml-auto text-xs text-text-placeholder">横轴时段 08:00–18:00 · 纵轴护理员 · 冲突红框 · 空缺橙框</span>
|
||||||
</div>
|
</div>
|
||||||
@ -116,6 +117,50 @@
|
|||||||
<span class="ml-xs text-xs text-text-placeholder">选择工单后自动带入老人与时段,逐条确认后才派</span>
|
<span class="ml-xs text-xs text-text-placeholder">选择工单后自动带入老人与时段,逐条确认后才派</span>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-dialog v-model="generateVisible" title="生成工单" width="520px">
|
||||||
|
<el-form :model="generateForm" label-width="90px">
|
||||||
|
<el-form-item label="老人">
|
||||||
|
<el-select v-model="generateForm.elderId" filterable placeholder="选择老人" style="width: 100%" @change="onGenerateElderChange">
|
||||||
|
<el-option v-for="e in elders" :key="e.id" :label="`${e.name}(${e.idCardNo})`" :value="e.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="服务计划">
|
||||||
|
<div style="width: 100%">
|
||||||
|
<el-select
|
||||||
|
v-model="generateForm.planId"
|
||||||
|
filterable
|
||||||
|
placeholder="选择生效中的计划"
|
||||||
|
style="width: 100%"
|
||||||
|
:disabled="!generateForm.elderId"
|
||||||
|
:loading="generatePlansLoading"
|
||||||
|
>
|
||||||
|
<el-option v-for="p in activePlans" :key="p.id" :label="planLabel(p)" :value="p.id" />
|
||||||
|
</el-select>
|
||||||
|
<el-alert
|
||||||
|
v-if="generateForm.elderId && !generatePlansLoading && activePlans.length === 0"
|
||||||
|
class="mt-xs"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
title="该老人无生效中的计划"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="服务日期">
|
||||||
|
<el-date-picker v-model="generateForm.serviceDate" type="date" value-format="YYYY-MM-DD" :clearable="false" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="时段起">
|
||||||
|
<el-time-picker v-model="generateForm.timeSlotStart" value-format="HH:mm:ss" format="HH:mm" :clearable="false" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="时段止">
|
||||||
|
<el-time-picker v-model="generateForm.timeSlotEnd" value-format="HH:mm:ss" format="HH:mm" :clearable="false" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="generateVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :disabled="!generateForm.planId" :loading="generating" @click="confirmGenerate">生成工单</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -124,10 +169,12 @@ import { computed, onMounted, reactive, ref } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { ApiError } from '@/api/http'
|
import { ApiError } from '@/api/http'
|
||||||
import { listElders, type Elder } from '@/api/elder'
|
import { listElders, type Elder } from '@/api/elder'
|
||||||
|
import { listPlansByElder, type Plan } from '@/api/plan'
|
||||||
import { listStaff, type Staff } from '@/api/staff'
|
import { listStaff, type Staff } from '@/api/staff'
|
||||||
import {
|
import {
|
||||||
assignOrder,
|
assignOrder,
|
||||||
batchRecommend,
|
batchRecommend,
|
||||||
|
generateOrder,
|
||||||
listOrders,
|
listOrders,
|
||||||
reassignOrder,
|
reassignOrder,
|
||||||
type BatchRecommendItem,
|
type BatchRecommendItem,
|
||||||
@ -164,6 +211,22 @@ const recommending = ref(false)
|
|||||||
const recommendations = ref<BatchRecommendItem[]>([])
|
const recommendations = ref<BatchRecommendItem[]>([])
|
||||||
const recommendForm = reactive({ elderId: '', timeSlotStart: '', timeSlotEnd: '', orderId: '' })
|
const recommendForm = reactive({ elderId: '', timeSlotStart: '', timeSlotEnd: '', orderId: '' })
|
||||||
|
|
||||||
|
// ---- 生成工单 ----
|
||||||
|
const generateVisible = ref(false)
|
||||||
|
const generating = ref(false)
|
||||||
|
const generatePlans = ref<Plan[]>([])
|
||||||
|
const generatePlansLoading = ref(false)
|
||||||
|
const generateForm = reactive({
|
||||||
|
elderId: '',
|
||||||
|
planId: '',
|
||||||
|
serviceDate: todayStr(),
|
||||||
|
timeSlotStart: '09:00:00',
|
||||||
|
timeSlotEnd: '10:00:00',
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 老人名下生效中的计划(plan_status === active)。 */
|
||||||
|
const activePlans = computed(() => generatePlans.value.filter((p) => p.planStatus === 'active'))
|
||||||
|
|
||||||
/** 当日待派(to_assign)工单,供「待派工单」下拉选择。 */
|
/** 当日待派(to_assign)工单,供「待派工单」下拉选择。 */
|
||||||
const toAssignOrders = computed(() => orders.value.filter((o) => o.orderStatus === 'to_assign'))
|
const toAssignOrders = computed(() => orders.value.filter((o) => o.orderStatus === 'to_assign'))
|
||||||
|
|
||||||
@ -182,6 +245,12 @@ function fmtTime(ms?: number | null): string {
|
|||||||
return `${pad(d.getHours())}:${pad(d.getMinutes())}`
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fmtDate(ms?: number | null): string {
|
||||||
|
if (ms == null) return '-'
|
||||||
|
const d = new Date(ms)
|
||||||
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`
|
||||||
|
}
|
||||||
|
|
||||||
/** 毫秒时间戳 → 'YYYY-MM-DDTHH:mm:ss'(用于 datetime picker 的 value-format)。 */
|
/** 毫秒时间戳 → 'YYYY-MM-DDTHH:mm:ss'(用于 datetime picker 的 value-format)。 */
|
||||||
function toDateTimeStr(ms?: number | null): string {
|
function toDateTimeStr(ms?: number | null): string {
|
||||||
if (ms == null) return ''
|
if (ms == null) return ''
|
||||||
@ -365,6 +434,64 @@ async function confirmRecommend(staffId: string, name: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- 生成工单 ----
|
||||||
|
function planLabel(p: Plan): string {
|
||||||
|
return `${fmtDate(p.planPeriodStart)} ~ ${fmtDate(p.planPeriodEnd)} · ${p.careLevel}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGenerate(): void {
|
||||||
|
generateForm.elderId = ''
|
||||||
|
generateForm.planId = ''
|
||||||
|
generateForm.serviceDate = todayStr()
|
||||||
|
generateForm.timeSlotStart = '09:00:00'
|
||||||
|
generateForm.timeSlotEnd = '10:00:00'
|
||||||
|
generatePlans.value = []
|
||||||
|
generateVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 选中老人后加载其计划列表,仅 active 计划可生成工单。 */
|
||||||
|
async function onGenerateElderChange(elderId: string): Promise<void> {
|
||||||
|
generateForm.planId = ''
|
||||||
|
generatePlans.value = []
|
||||||
|
if (!elderId) return
|
||||||
|
generatePlansLoading.value = true
|
||||||
|
try {
|
||||||
|
generatePlans.value = await listPlansByElder(elderId)
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ApiError) ElMessage.error(e.message)
|
||||||
|
} finally {
|
||||||
|
generatePlansLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmGenerate(): Promise<void> {
|
||||||
|
if (!generateForm.elderId) {
|
||||||
|
ElMessage.warning('请选择老人')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!generateForm.planId) {
|
||||||
|
ElMessage.warning('请选择生效中的计划')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
generating.value = true
|
||||||
|
try {
|
||||||
|
await generateOrder({
|
||||||
|
elderId: generateForm.elderId,
|
||||||
|
planId: generateForm.planId,
|
||||||
|
serviceDate: generateForm.serviceDate,
|
||||||
|
timeSlotStart: `${generateForm.serviceDate}T${generateForm.timeSlotStart}`,
|
||||||
|
timeSlotEnd: `${generateForm.serviceDate}T${generateForm.timeSlotEnd}`,
|
||||||
|
})
|
||||||
|
ElMessage.success('已生成待派工单')
|
||||||
|
generateVisible.value = false
|
||||||
|
await refresh()
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof ApiError) ElMessage.error(e.message)
|
||||||
|
} finally {
|
||||||
|
generating.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
void refresh()
|
void refresh()
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user