Compare commits

..

No commits in common. "120742819a7ef73e2d21f68ab43314966421ef89" and "66514a1a0855914be35731b1cb4338d988073ad5" have entirely different histories.

3 changed files with 188 additions and 275 deletions

View File

@ -20,6 +20,3 @@ export const getBackWorkOrderList = (params) => request.basic.get(`/api/v1/order
// 工单详情 // 工单详情
export const getWorkOrderDetail = (id) => request.basic.get(`/api/v1/orders/${id}`) export const getWorkOrderDetail = (id) => request.basic.get(`/api/v1/orders/${id}`)
// 派单
export const sendWorkOrder = (id, params) => request.basic.put(`/api/v1/orders${id}`, params)

View File

@ -1,12 +1,28 @@
<!-- src/components/DispatchDrawer.vue --> <!-- src/components/DispatchDrawer.vue -->
<template> <template>
<a-drawer title="派单" :open="open" :width="600" @close="handleCancel" :destroy-on-close="true"> <a-drawer
<a-form :rules="rules" ref="formRef" layout="vertical" class="form-mode" :model="formModel"> title="派单"
:open="visible"
:width="600"
@close="handleClose"
:destroy-on-close="true"
>
<a-form
:model="formModel"
:rules="rules"
ref="formRef"
layout="vertical"
class="form-mode"
>
<!-- 服务对象 --> <!-- 服务对象 -->
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="服务对象"> <a-form-item label="服务对象">
<a-input v-model:value="formModel.serviceTarget" placeholder="请输入服务对象名称" disabled /> <a-input
v-model:value="formModel.serviceTarget"
placeholder="请输入服务对象名称"
disabled
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -15,7 +31,11 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="服务项目" name="serviceItem"> <a-form-item label="服务项目" name="serviceItem">
<a-select v-model:value="formModel.serviceItem" placeholder="请选择服务项目" mode="multiple"> <a-select
v-model:value="formModel.serviceItem"
placeholder="请选择服务项目"
mode="multiple"
>
<a-select-option value="兴趣活动">兴趣活动</a-select-option> <a-select-option value="兴趣活动">兴趣活动</a-select-option>
<a-select-option value="居家照护">居家照护</a-select-option> <a-select-option value="居家照护">居家照护</a-select-option>
<a-select-option value="医疗护理">医疗护理</a-select-option> <a-select-option value="医疗护理">医疗护理</a-select-option>
@ -24,8 +44,12 @@
</a-col> </a-col>
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="服务费用"> <a-form-item label="服务费用">
<a-input-number v-model:value="formModel.serviceFee" placeholder="由关联服务项目计算得出" style="width: 100%" <a-input-number
:min="0" /> v-model:value="formModel.serviceFee"
placeholder="由关联服务项目计算得出"
style="width: 100%"
:min="0"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -34,8 +58,15 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="服务人员" name="serviceStaff"> <a-form-item label="服务人员" name="serviceStaff">
<a-select v-model:value="formModel.serviceStaff" placeholder="请选择服务人员"> <a-select
<a-select-option v-for="user in assignees" :key="user.id" :value="user.id"> v-model:value="formModel.serviceStaff"
placeholder="请选择服务人员"
>
<a-select-option
v-for="user in assignees"
:key="user.id"
:value="user.id"
>
{{ user.name }} {{ user.name }}
</a-select-option> </a-select-option>
</a-select> </a-select>
@ -47,7 +78,11 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="24"> <a-col :xs="24">
<a-form-item label="计划日期" name="plannedDate"> <a-form-item label="计划日期" name="plannedDate">
<a-range-picker v-model:value="formModel.plannedDate" style="width: 100%" :placeholder="['开始日期', '结束日期']" /> <a-range-picker
v-model:value="formModel.plannedDate"
style="width: 100%"
:placeholder="['开始日期', '结束日期']"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -56,13 +91,21 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="计划开始时间" name="plannedStartTime"> <a-form-item label="计划开始时间" name="plannedStartTime">
<a-time-picker v-model:value="formModel.plannedStartTime" placeholder="请选择计划开始时间" style="width: 100%" <a-time-picker
format="HH:mm" :minute-step="5" /> v-model:value="formModel.plannedStartTime"
placeholder="请选择计划开始时间"
style="width: 100%"
format="HH:mm"
:minute-step="5"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :xs="12"> <a-col :xs="12">
<a-form-item label="要求工单时长" name="requiredDuration"> <a-form-item label="要求工单时长" name="requiredDuration">
<a-input v-model:value="formModel.requiredDuration" placeholder="请输入要求工单时长"> <a-input
v-model:value="formModel.requiredDuration"
placeholder="请输入要求工单时长"
>
<template #suffix>分钟</template> <template #suffix>分钟</template>
</a-input> </a-input>
</a-form-item> </a-form-item>
@ -73,8 +116,14 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="24"> <a-col :xs="24">
<a-form-item label="服务地址" name="serviceArea"> <a-form-item label="服务地址" name="serviceArea">
<AreaCascader v-model:value="formModel.serviceArea" style="width: 100%; margin-bottom: 8px;" /> <AreaCascader
<a-input v-model:value="formModel.detailedAddress" placeholder="详细地址" /> v-model:value="formModel.serviceArea"
style="width: 100%; margin-bottom: 8px;"
/>
<a-input
v-model:value="formModel.detailedAddress"
placeholder="详细地址"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -83,7 +132,10 @@
<a-row :gutter="16"> <a-row :gutter="16">
<a-col :xs="24"> <a-col :xs="24">
<a-form-item label="服务内容"> <a-form-item label="服务内容">
<a-input v-model:value="formModel.serviceContent" placeholder="填写服务内容充当备注作用此字段数据会在app工单中显示" /> <a-input
v-model:value="formModel.serviceContent"
placeholder="填写服务内容充当备注作用此字段数据会在app工单中显示"
/>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
@ -91,8 +143,8 @@
<template #footer> <template #footer>
<div class="select-btn" style="display: flex; justify-content: flex-end; gap: 8px;"> <div class="select-btn" style="display: flex; justify-content: flex-end; gap: 8px;">
<a-button @click="handleCancel">取消</a-button> <a-button @click="handleClose">取消</a-button>
<a-button type="primary" @click="handleOk" :loading="confirmLoading">确定</a-button> <a-button type="primary" @click="handleSubmit">确定</a-button>
</div> </div>
</template> </template>
</a-drawer> </a-drawer>
@ -101,13 +153,9 @@
<script setup> <script setup>
import { ref, reactive, watch, nextTick } from 'vue' import { ref, reactive, watch, nextTick } from 'vue'
import AreaCascader from '@/components/AreaCascader/index.vue' import AreaCascader from '@/components/AreaCascader/index.vue'
import { message } from 'ant-design-vue'
import apis from '@/apis'
import dayjs from 'dayjs'
// props
const props = defineProps({ const props = defineProps({
open: { visible: {
type: Boolean, type: Boolean,
default: false default: false
}, },
@ -123,17 +171,12 @@ const props = defineProps({
const emit = defineEmits(['close', 'submit']) const emit = defineEmits(['close', 'submit'])
//
const formRef = ref() const formRef = ref()
const confirmLoading = ref(false)
// 使 formModel formData
const formModel = reactive({ const formModel = reactive({
serviceTarget: '', serviceTarget: '',
serviceItem: [], serviceItem: [],
serviceFee: undefined, serviceFee: undefined,
serviceStaff: undefined, serviceStaff: undefined,
serviceStaffId: undefined,
plannedDate: [], plannedDate: [],
plannedStartTime: null, plannedStartTime: null,
requiredDuration: '', requiredDuration: '',
@ -142,7 +185,6 @@ const formModel = reactive({
serviceContent: '' serviceContent: ''
}) })
//
const rules = { const rules = {
serviceItem: [{ required: true, message: '请选择服务项目', trigger: 'change' }], serviceItem: [{ required: true, message: '请选择服务项目', trigger: 'change' }],
serviceStaff: [{ required: true, message: '请选择服务人员', trigger: 'change' }], serviceStaff: [{ required: true, message: '请选择服务人员', trigger: 'change' }],
@ -153,69 +195,17 @@ const rules = {
detailedAddress: [{ required: true, message: '请输入详细地址', trigger: 'blur' }] detailedAddress: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
} }
// ================== Methods ================== // visible
watch(
/** () => props.visible,
* 确认提交 (newVal) => {
*/ if (newVal) {
async function handleOk() { nextTick(() => {
try { if (Object.keys(props.initialValues).length > 0) {
confirmLoading.value = true Object.assign(formModel, props.initialValues)
await formRef.value.validate() } else {
//
// for (const key in formModel) {
let plannedStart = null
let plannedEnd = null
if (formModel.plannedDate[0] && formModel.plannedStartTime) {
const datePart = formModel.plannedDate[0].format('YYYY-MM-DD')
const timePart = formModel.plannedStartTime.format('HH:mm')
plannedStart = dayjs(`${datePart} ${timePart}`)
plannedEnd = plannedStart.add(Number(formModel.requiredDuration) || 0, 'minute')
}
const submitData = {
customerId: props.initialValues.id,
projects: formModel.serviceItem,
price: formModel.serviceFee,
servicePerson: formModel.serviceStaff,
servicePersonId: formModel.serviceStaffId,
plannedStartDate: plannedStart?.toISOString() || null,
plannedEndDate: plannedEnd?.toISOString() || null,
plannedServiceStartDate: Number(formModel.requiredDuration) || 0,
areaLabels: formModel.serviceArea,
areaCodes: formModel.serviceArea.join(','),
detailAddress: formModel.detailedAddress,
content: formModel.serviceContent
}
console.log('提交数据:', submitData)
const { success } = await apis.workOrder.sendWorkOrder(submitData)
if (success) {
message.success('派单成功')
emit('submit', submitData)
}
} catch (error) {
console.log('表单校验或提交失败:', error)
// message.error('')
} finally {
confirmLoading.value = false
}
}
/**
* 取消/关闭弹框
*/
function handleCancel() {
resetFormModel()
emit('close')
}
/**
* 重置表单数据
*/
function resetFormModel() {
Object.keys(formModel).forEach(key => {
if (Array.isArray(formModel[key])) { if (Array.isArray(formModel[key])) {
formModel[key] = [] formModel[key] = []
} else if (typeof formModel[key] === 'string') { } else if (typeof formModel[key] === 'string') {
@ -223,125 +213,29 @@ function resetFormModel() {
} else { } else {
formModel[key] = undefined formModel[key] = undefined
} }
})
// 使 clearValidate resetFields
if (formRef.value?.clearValidate) {
formRef.value.clearValidate()
} }
} formRef.value?.resetFields()
/**
* 设置表单字段值
*/
function setFormFields(values) {
//
for (const key in formModel) {
if (key in values) {
formModel[key] = values[key]
} else {
//
formModel[key] = Array.isArray(formModel[key]) ? [] : typeof formModel[key] === 'string' ? '' : undefined
}
}
nextTick(() => {
if (formRef.value?.clearValidate) {
formRef.value.clearValidate()
} }
}) })
}
/**
* 加载工单详情并填充表单
* @param {string} id - 工单ID
*/
async function loadWorkOrderDetail(id) {
console.log('开始加载工单详情ID:', id)
try {
const { success, data } = await apis.workOrder.getWorkOrderDetail(id)
console.log('工单详情接口响应:', { success, data })
if (success && data) {
const detailData = data
console.log('工单详情数据:', detailData)
let projects = detailData.projects.map(item => item.name)
//
const formData = {
serviceTarget: detailData.customer.name || '',
serviceItem: projects,
serviceFee: detailData.price,
serviceStaff: detailData.servicePerson || undefined,
servicePersonId: detailData.servicePersonId,
plannedDate: detailData.plannedEndDate ? [
dayjs(detailData.plannedEndDate),
dayjs(detailData.plannedEndDate)
] : [],
// Day.js
plannedStartTime: detailData.plannedStartDate
? dayjs(detailData.plannedStartDate)
: null,
requiredDuration: detailData.plannedServiceStartDate ?
String(detailData.plannedServiceStartDate) : '',
serviceArea: (detailData.areaLabels || []).join('/'),
detailedAddress: detailData.detailAddress || '',
serviceContent: detailData.content || ''
}
console.log('构建的表单数据:', formData)
//
setFormFields(formData)
console.log('设置后的 formModel:', formModel)
} else {
message.error('获取工单详情失败')
}
} catch (error) {
console.error('获取工单详情失败:', error)
// message.error('')
}
}
/**
* 辅助函数将区域字符串转换为数组格式
* @param {string} areaString - 区域字符串
* @returns {Array} 区域数组
*/
function parseAreaToArray(areaString) {
if (!areaString) return []
return areaString.split('/') || []
}
// open
watch(
() => props.open,
async (newVal) => {
if (newVal) {
console.log('抽屉打开,重置表单')
resetFormModel()
// DOM
await nextTick()
if (props.initialValues && props.initialValues.id) {
console.log('加载工单详情ID:', props.initialValues.id)
await loadWorkOrderDetail(props.initialValues.id)
} else {
console.log('新建派单,无初始值')
//
setFormFields({
serviceTarget: '新建测试数据',
serviceItem: ['兴趣活动'],
serviceFee: 100,
requiredDuration: '60'
})
}
} }
} }
) )
const handleClose = () => {
formRef.value?.resetFields()
emit('close')
}
const handleSubmit = async () => {
try {
const values = await formRef.value.validateFields()
// model
emit('submit', { ...formModel })
handleClose()
} catch (error) {
console.log('校验失败:', error)
}
}
</script> </script>
<style scoped> <style scoped>

View File

@ -135,7 +135,7 @@
<a-button type="primary">导出记录</a-button> <a-button type="primary">导出记录</a-button>
</a-space> </a-space>
</div> </div>
<a-table rowKey="id" :loading="loading" :pagination="paginationState" :columns="columns" <a-table rowKey="id" :loading="loading" :pagination="paginationConfig" :columns="columns"
:data-source="listData" :scroll="{ x: totalWidth }" @change="onTableChange"> :data-source="listData" :scroll="{ x: totalWidth }" @change="onTableChange">
<template #bodyCell="{ column, record, index }"> <template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'serialNumber'"> <template v-if="column.key === 'serialNumber'">
@ -161,12 +161,8 @@
</a-row> </a-row>
<!-- 派单抽屉右侧弹出 --> <!-- 派单抽屉右侧弹出 -->
<DispatchDrawer <DispatchDrawer :visible="showDrawer" :assignees="staffList" :initial-values="initialValues"
:open="showDrawer" @close="showDrawer = false" @submit="handleDispatchSubmit" />
:assignees="staffList"
:initial-values="drawerInitialValues"
@close="handleDrawerClose"
@submit="handleDispatchSubmit" />
</template> </template>
<script setup> <script setup>
@ -176,18 +172,14 @@ import { message } from 'ant-design-vue'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import DispatchDrawer from './components/DispatchDrawer.vue'; import DispatchDrawer from './components/DispatchDrawer.vue';
import apis from '@/apis'
import { usePagination } from '@/hooks'
import storage from '@/utils/storage'
defineOptions({ defineOptions({
name: 'allocation', name: 'allocation',
}) })
const { t } = useI18n() const { t } = useI18n()
const { listData, loading, showLoading, hideLoading, paginationState, resetPagination, searchFormData } = usePagination()
// // columns
const columns = [ const columns = [
{ title: '序号', key: 'serialNumber', align: 'center', width: 60 }, { title: '序号', key: 'serialNumber', align: 'center', width: 60 },
{ title: '工单号', dataIndex: 'orderNum', key: 'orderNum', align: 'center', width: 120 }, { title: '工单号', dataIndex: 'orderNum', key: 'orderNum', align: 'center', width: 120 },
@ -205,56 +197,80 @@ const columns = [
{ title: '操作', key: 'action', width: 100, fixed: 'right' } { title: '操作', key: 'action', width: 100, fixed: 'right' }
] ]
//
const paginationState = ref({
current: 1,
pageSize: 10,
total: 0,
})
const loading = ref(false)
const listData = ref([])
//
const searchFormData = ref({
name: '',
idCard: '',
exceptionType: undefined,
serviceOrderNo: '',
serviceOrg: undefined,
satisfaction: undefined,
staffSatisfaction: undefined,
isVisited: undefined,
plannedDate: [],
serviceDuration: undefined,
currentNode: [],
})
// //
const advancedSearchVisible = ref([]) const advancedSearchVisible = ref([])
// //
const totalWidth = computed(() => { const totalWidth = computed(() => {
return columns.reduce((sum, col) => sum + (col.width || 100), 0) return columns.reduce((sum, col) => sum + (col.width || 100), 0)
}) })
// ====== ====== // a-table
const showDrawer = ref(false) const paginationConfig = computed(() => ({
const currentRecord = ref(null) current: paginationState.value.current,
const staffList = ref([ pageSize: paginationState.value.pageSize,
{ id: '1', name: '张三' }, total: paginationState.value.total,
{ id: '2', name: '李四' }, showSizeChanger: true,
{ id: '3', name: '王五' }, showQuickJumper: true,
]) }))
// //
const drawerInitialValues = computed(() => { function mockData() {
if (!currentRecord.value) return {} return Array.from({ length: 5 }, (_, i) => ({
console.log('当前记录传递给抽屉:', currentRecord.value) id: i + 1,
orderNum: `GD20251021${String(i + 1).padStart(4, '0')}`,
customerName: `客户${i + 1}`,
idCard: `11010119900101${String(i + 1).padStart(4, '0')}`,
otherPhone1: `1380013800${i}`,
otherPhone2: i % 2 === 0 ? `1390013900${i}` : null,
plannedServiceStartDate: '2025-10-25 09:00:00',
serviceDurationMinutes: 120 + i * 10,
serviceName: '居家照护',
area: '北京市朝阳区',
serviceAddress: '朝阳区某某街道XX号',
creator: '管理员',
createTime: '2025-10-20 14:30:00',
}))
}
return { // 使 mock
id: currentRecord.value.id
}
})
//
async function getPageList() { async function getPageList() {
loading.value = true loading.value = true
try { try {
showLoading() //
const { pageSize, current } = paginationState await new Promise(resolve => setTimeout(resolve, 300))
const params = { listData.value = mockData()
status:'Initialize', paginationState.value.total = listData.value.length
stationId: storage.local.getItem('stationId'),
companyId: storage.local.getItem('companyId'),
pageSize,
current,
...searchFormData.value
}
const { success, data, total } = await apis.workOrder.getWorkOrderList(params)
listData.value = data
paginationState.total = total
} catch (error) { } catch (error) {
console.error('获取工单列表失败:', error) console.error('获取列表失败:', error)
message.error('获取数据失败') message.error('加载数据失败')
} finally { } finally {
hideLoading() loading.value = false
} }
} }
@ -302,14 +318,26 @@ function formatDate(dateStr) {
return dateStr || '—' return dateStr || '—'
} }
// ====== ====== // ====== ======
const showDrawer = ref(false)
const currentRecord = ref(null)
const staffList = ref([
{ id: '1', name: '张三' },
{ id: '2', name: '李四' },
{ id: '3', name: '王五' },
])
const initialValues = computed(() => {
return {}
})
// ====== ======
function openDispatchDrawer(record) { function openDispatchDrawer(record) {
currentRecord.value = record currentRecord.value = record
showDrawer.value = true showDrawer.value = true
console.log('打开派单抽屉记录ID:', record.id)
} }
function handleDrawerClose() { function closeDispatchDrawer() {
showDrawer.value = false showDrawer.value = false
currentRecord.value = null currentRecord.value = null
} }
@ -318,17 +346,8 @@ function handleDispatchSubmit(values) {
console.log('派单数据:', values) console.log('派单数据:', values)
console.log('派给工单:', currentRecord.value) console.log('派给工单:', currentRecord.value)
//
// await apis.workOrder.dispatchWorkOrder({
// workOrderId: currentRecord.value.id,
// ...values
// })
message.success('派单成功') message.success('派单成功')
handleDrawerClose() closeDispatchDrawer()
//
getPageList()
} }
// //
@ -336,3 +355,6 @@ getPageList()
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>