This commit is contained in:
Leo_Ding 2025-11-05 19:11:27 +08:00
commit 1c7f8b381d
7 changed files with 294 additions and 197 deletions

View File

@ -20,3 +20,6 @@ export const getBackWorkOrderList = (params) => request.basic.get(`/api/v1/order
// 工单详情
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,5 +1,5 @@
<template>
<a-modal :open="modal.open" :title="modal.title" :width="600" :confirm-loading="modal.confirmLoading"
<a-modal :open="modal.open" :title="modal.title" :width="750" :confirm-loading="modal.confirmLoading"
:after-close="onAfterClose" :cancel-text="cancelText" @ok="handleOk" @cancel="handleCancel">
<a-card>
<a-form ref="formRef" :model="formData" :rules="formRules">
@ -33,7 +33,7 @@
<template #addonAfter>
<a-select v-model:value="formData.priceUnit" style="width: 100px">
<a-select-option value="次">/</a-select-option>
<a-select-option value="时">/</a-select-option>
<a-select-option value="时">/</a-select-option>
</a-select>
</template>
</a-input-number>
@ -52,9 +52,9 @@
<a-form-item label="服务频次" name="frequency">
<a-input-number v-model:value="formData.frequency" style="width: 100%;">
<template #addonAfter>
<a-select v-model:value="formData.frequencyUnit" style="width: 80px">
<a-select-option value="元/次">/</a-select-option>
<a-select-option value="元/台">/</a-select-option>
<a-select v-model:value="formData.frequencyUnit" style="width: 120px">
<a-select-option value="元/次">小时/</a-select-option>
<a-select-option value="元/台">小时/</a-select-option>
</a-select>
</template>
</a-input-number>

View File

@ -50,6 +50,10 @@
<template v-if="column.key === 'categoryType'">
<span>{{ dicsStore.getDictLabel('PROJECT_TYPE', record.categoryType) }}</span>
</template>
<template v-if="column.key === 'price'">
<span>{{ record.price + record.frequencyUnit }}</span>
</template>
<template v-if="'action' === column.key">
<x-action-button @click="$refs.editDialogRef.handleEdit(record)">
@ -85,7 +89,7 @@ const columns = [
{ title: '分类名称', dataIndex: 'name', key: 'name', align: 'center', width: 180, },
{ title: '项目名称', dataIndex: 'name', key: 'name', align: 'center', },
{ title: '价格', dataIndex: 'price', key: 'price', align: 'center', },
{ title: '简介', dataIndex: 'remark', key: 'remark', align: 'center', },
{ title: '简介', dataIndex: 'content', key: 'content', align: 'center', },
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 120, fixed: 'right', }
];

View File

@ -28,12 +28,17 @@
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :label="'组织机构代码'" name="orgCode" :required="true">
<a-input v-model:value="formData.orgCode" placeholder="统一社会信用代码或组织机构代码" allow-clear />
<a-form-item :label="'服务组织编号'" name="orgCode" :required="true">
<a-input v-model:value="formData.orgCode" placeholder="请输入服务组织编号" allow-clear />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item :label="'组织机构代码'" name="orgCode33" :required="false">
<a-input v-model:value="formData.orgCode33" placeholder="请输入服务组织编号" allow-clear />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item :label="'组织等级'" name="orgLv" :required="true">
<a-select v-model:value="formData.orgLv" placeholder="请选择组织等级" allow-clear>
@ -242,6 +247,7 @@ const editingId = ref(null);
const formData = reactive({
name: '',
orgCode: '',
orgCode33: '',
orgLv: '',
concatName: '',
concatPhone: '',

View File

@ -119,7 +119,7 @@
<a-button type="link" size="small" @click="handleDetail(record)">详情</a-button>
<a-divider type="vertical" />
<a-button type="link" size="small" @click="handleToggleStatus(record)">
{{ record.status === 'enabled' ? '停用' : '启用' }}
{{ record.status === '1' ? '停用' : '启用' }}
</a-button>
<a-divider type="vertical" />
<!-- <a-button type="link" size="small" @click="handleDeviceManagement(record)">设备管理</a-button> -->

View File

@ -1,28 +1,12 @@
<!-- src/components/DispatchDrawer.vue -->
<template>
<a-drawer
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-drawer title="派单" :open="open" :width="600" @close="handleCancel" :destroy-on-close="true">
<a-form :rules="rules" ref="formRef" layout="vertical" class="form-mode" :model="formModel">
<!-- 服务对象 -->
<a-row :gutter="16">
<a-col :xs="12">
<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-col>
</a-row>
@ -31,11 +15,7 @@
<a-row :gutter="16">
<a-col :xs="12">
<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>
@ -44,12 +24,8 @@
</a-col>
<a-col :xs="12">
<a-form-item label="服务费用">
<a-input-number
v-model:value="formModel.serviceFee"
placeholder="由关联服务项目计算得出"
style="width: 100%"
:min="0"
/>
<a-input-number v-model:value="formModel.serviceFee" placeholder="由关联服务项目计算得出" style="width: 100%"
:min="0" />
</a-form-item>
</a-col>
</a-row>
@ -58,15 +34,8 @@
<a-row :gutter="16">
<a-col :xs="12">
<a-form-item label="服务人员" name="serviceStaff">
<a-select
v-model:value="formModel.serviceStaff"
placeholder="请选择服务人员"
>
<a-select-option
v-for="user in assignees"
:key="user.id"
:value="user.id"
>
<a-select v-model:value="formModel.serviceStaff" placeholder="请选择服务人员">
<a-select-option v-for="user in assignees" :key="user.id" :value="user.id">
{{ user.name }}
</a-select-option>
</a-select>
@ -78,11 +47,7 @@
<a-row :gutter="16">
<a-col :xs="24">
<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-col>
</a-row>
@ -91,21 +56,13 @@
<a-row :gutter="16">
<a-col :xs="12">
<a-form-item label="计划开始时间" name="plannedStartTime">
<a-time-picker
v-model:value="formModel.plannedStartTime"
placeholder="请选择计划开始时间"
style="width: 100%"
format="HH:mm"
:minute-step="5"
/>
<a-time-picker v-model:value="formModel.plannedStartTime" placeholder="请选择计划开始时间" style="width: 100%"
format="HH:mm" :minute-step="5" />
</a-form-item>
</a-col>
<a-col :xs="12">
<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>
</a-input>
</a-form-item>
@ -116,14 +73,8 @@
<a-row :gutter="16">
<a-col :xs="24">
<a-form-item label="服务地址" name="serviceArea">
<AreaCascader
v-model:value="formModel.serviceArea"
style="width: 100%; margin-bottom: 8px;"
/>
<a-input
v-model:value="formModel.detailedAddress"
placeholder="详细地址"
/>
<AreaCascader v-model:value="formModel.serviceArea" style="width: 100%; margin-bottom: 8px;" />
<a-input v-model:value="formModel.detailedAddress" placeholder="详细地址" />
</a-form-item>
</a-col>
</a-row>
@ -132,10 +83,7 @@
<a-row :gutter="16">
<a-col :xs="24">
<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-col>
</a-row>
@ -143,8 +91,8 @@
<template #footer>
<div class="select-btn" style="display: flex; justify-content: flex-end; gap: 8px;">
<a-button @click="handleClose">取消</a-button>
<a-button type="primary" @click="handleSubmit">确定</a-button>
<a-button @click="handleCancel">取消</a-button>
<a-button type="primary" @click="handleOk" :loading="confirmLoading">确定</a-button>
</div>
</template>
</a-drawer>
@ -153,9 +101,13 @@
<script setup>
import { ref, reactive, watch, nextTick } from '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({
visible: {
open: {
type: Boolean,
default: false
},
@ -171,12 +123,17 @@ const props = defineProps({
const emit = defineEmits(['close', 'submit'])
//
const formRef = ref()
const confirmLoading = ref(false)
// 使 formModel formData
const formModel = reactive({
serviceTarget: '',
serviceItem: [],
serviceFee: undefined,
serviceStaff: undefined,
serviceStaffId: undefined,
plannedDate: [],
plannedStartTime: null,
requiredDuration: '',
@ -185,6 +142,7 @@ const formModel = reactive({
serviceContent: ''
})
//
const rules = {
serviceItem: [{ required: true, message: '请选择服务项目', trigger: 'change' }],
serviceStaff: [{ required: true, message: '请选择服务人员', trigger: 'change' }],
@ -195,47 +153,195 @@ const rules = {
detailedAddress: [{ required: true, message: '请输入详细地址', trigger: 'blur' }]
}
// visible
// ================== Methods ==================
/**
* 确认提交
*/
async function handleOk() {
try {
confirmLoading.value = true
await formRef.value.validate()
//
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])) {
formModel[key] = []
} else if (typeof formModel[key] === 'string') {
formModel[key] = ''
} else {
formModel[key] = undefined
}
})
// 使 clearValidate resetFields
if (formRef.value?.clearValidate) {
formRef.value.clearValidate()
}
}
/**
* 设置表单字段值
*/
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.visible,
(newVal) => {
() => props.open,
async (newVal) => {
if (newVal) {
nextTick(() => {
if (Object.keys(props.initialValues).length > 0) {
Object.assign(formModel, props.initialValues)
} else {
//
for (const key in formModel) {
if (Array.isArray(formModel[key])) {
formModel[key] = []
} else if (typeof formModel[key] === 'string') {
formModel[key] = ''
} else {
formModel[key] = undefined
}
}
formRef.value?.resetFields()
}
})
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>
<style scoped>

View File

@ -135,7 +135,7 @@
<a-button type="primary">导出记录</a-button>
</a-space>
</div>
<a-table rowKey="id" :loading="loading" :pagination="paginationConfig" :columns="columns"
<a-table rowKey="id" :loading="loading" :pagination="paginationState" :columns="columns"
:data-source="listData" :scroll="{ x: totalWidth }" @change="onTableChange">
<template #bodyCell="{ column, record, index }">
<template v-if="column.key === 'serialNumber'">
@ -161,8 +161,12 @@
</a-row>
<!-- 派单抽屉右侧弹出 -->
<DispatchDrawer :visible="showDrawer" :assignees="staffList" :initial-values="initialValues"
@close="showDrawer = false" @submit="handleDispatchSubmit" />
<DispatchDrawer
:open="showDrawer"
:assignees="staffList"
:initial-values="drawerInitialValues"
@close="handleDrawerClose"
@submit="handleDispatchSubmit" />
</template>
<script setup>
@ -172,14 +176,18 @@ import { message } from 'ant-design-vue'
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import DispatchDrawer from './components/DispatchDrawer.vue';
import apis from '@/apis'
import { usePagination } from '@/hooks'
import storage from '@/utils/storage'
defineOptions({
name: 'allocation',
})
const { t } = useI18n()
const { listData, loading, showLoading, hideLoading, paginationState, resetPagination, searchFormData } = usePagination()
// columns
//
const columns = [
{ title: '序号', key: 'serialNumber', align: 'center', width: 60 },
{ title: '工单号', dataIndex: 'orderNum', key: 'orderNum', align: 'center', width: 120 },
@ -197,80 +205,56 @@ const columns = [
{ 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 totalWidth = computed(() => {
return columns.reduce((sum, col) => sum + (col.width || 100), 0)
})
// a-table
const paginationConfig = computed(() => ({
current: paginationState.value.current,
pageSize: paginationState.value.pageSize,
total: paginationState.value.total,
showSizeChanger: true,
showQuickJumper: true,
}))
// ====== ======
const showDrawer = ref(false)
const currentRecord = ref(null)
const staffList = ref([
{ id: '1', name: '张三' },
{ id: '2', name: '李四' },
{ id: '3', name: '王五' },
])
//
function mockData() {
return Array.from({ length: 5 }, (_, i) => ({
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',
}))
}
//
const drawerInitialValues = computed(() => {
if (!currentRecord.value) return {}
console.log('当前记录传递给抽屉:', currentRecord.value)
// 使 mock
return {
id: currentRecord.value.id
}
})
//
async function getPageList() {
loading.value = true
try {
//
await new Promise(resolve => setTimeout(resolve, 300))
listData.value = mockData()
paginationState.value.total = listData.value.length
showLoading()
const { pageSize, current } = paginationState
const params = {
status:'Initialize',
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) {
console.error('获取列表失败:', error)
message.error('加载数据失败')
console.error('获取工单列表失败:', error)
message.error('获取数据失败')
} finally {
loading.value = false
hideLoading()
}
}
@ -318,26 +302,14 @@ function formatDate(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) {
currentRecord.value = record
showDrawer.value = true
console.log('打开派单抽屉记录ID:', record.id)
}
function closeDispatchDrawer() {
function handleDrawerClose() {
showDrawer.value = false
currentRecord.value = null
}
@ -346,15 +318,21 @@ function handleDispatchSubmit(values) {
console.log('派单数据:', values)
console.log('派给工单:', currentRecord.value)
//
// await apis.workOrder.dispatchWorkOrder({
// workOrderId: currentRecord.value.id,
// ...values
// })
message.success('派单成功')
closeDispatchDrawer()
handleDrawerClose()
//
getPageList()
}
//
getPageList()
</script>
<style lang="less" scoped></style>
<style lang="less" scoped></style>