feat(frontend): add inspection/replacement APIs and shared InspectionForm component
- Create inspection.ts with template CRUD and record APIs - Create vehicle-replacement.ts with full CRUD + BPM approval APIs - Update return-order.ts with new fields and 5 new endpoints - Create shared InspectionForm.vue component with category grouping, multi-input-type rendering, auto-save, and image upload - Update barrel exports in index.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
13
apps/web-antd/src/api/asset/index.ts
Normal file
13
apps/web-antd/src/api/asset/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export * from './parking';
|
||||
export * from './customer';
|
||||
export * from './supplier';
|
||||
export * from './vehicle-model';
|
||||
export * from './vehicle-registration';
|
||||
export * from './contract';
|
||||
export * from './vehicle-prepare';
|
||||
export * from './delivery-task';
|
||||
export * from './delivery-order';
|
||||
export * from './return-order';
|
||||
export * from './vehicle';
|
||||
export * from './inspection';
|
||||
export * from './vehicle-replacement';
|
||||
100
apps/web-antd/src/api/asset/inspection.ts
Normal file
100
apps/web-antd/src/api/asset/inspection.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace InspectionApi {
|
||||
export interface Template {
|
||||
id?: number;
|
||||
code: string;
|
||||
name: string;
|
||||
bizType: number;
|
||||
vehicleType?: string;
|
||||
status: number;
|
||||
remark?: string;
|
||||
items?: TemplateItem[];
|
||||
}
|
||||
|
||||
export interface TemplateItem {
|
||||
id?: number;
|
||||
category: string;
|
||||
itemName: string;
|
||||
itemCode: string;
|
||||
inputType: string;
|
||||
sort: number;
|
||||
required: number;
|
||||
}
|
||||
|
||||
export interface RecordDetail {
|
||||
id: number;
|
||||
recordCode: string;
|
||||
templateId: number;
|
||||
sourceType: number;
|
||||
sourceId: number;
|
||||
vehicleId: number;
|
||||
inspectorName?: string;
|
||||
inspectionTime?: string;
|
||||
status: number;
|
||||
overallResult?: number;
|
||||
remark?: string;
|
||||
items: RecordItem[];
|
||||
}
|
||||
|
||||
export interface RecordItem {
|
||||
id: number;
|
||||
itemCode: string;
|
||||
category: string;
|
||||
itemName: string;
|
||||
inputType: string;
|
||||
result?: number;
|
||||
value?: string;
|
||||
remark?: string;
|
||||
imageUrls?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export function getInspectionTemplatePage(params: PageParam) {
|
||||
return requestClient.get<PageResult<InspectionApi.Template>>(
|
||||
'/asset/inspection-template/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
export function getInspectionTemplate(id: number) {
|
||||
return requestClient.get<InspectionApi.Template>(
|
||||
`/asset/inspection-template/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function createInspectionTemplate(data: InspectionApi.Template) {
|
||||
return requestClient.post('/asset/inspection-template/create', data);
|
||||
}
|
||||
|
||||
export function updateInspectionTemplate(data: InspectionApi.Template) {
|
||||
return requestClient.put('/asset/inspection-template/update', data);
|
||||
}
|
||||
|
||||
export function deleteInspectionTemplate(id: number) {
|
||||
return requestClient.delete(`/asset/inspection-template/delete?id=${id}`);
|
||||
}
|
||||
|
||||
export function getInspectionRecord(id: number) {
|
||||
return requestClient.get<InspectionApi.RecordDetail>(
|
||||
`/asset/inspection-record/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function updateInspectionRecordItem(data: {
|
||||
id: number;
|
||||
result?: number;
|
||||
value?: string;
|
||||
remark?: string;
|
||||
imageUrls?: string;
|
||||
}) {
|
||||
return requestClient.put('/asset/inspection-record/update-item', data);
|
||||
}
|
||||
|
||||
export function completeInspection(id: number, inspectorName: string) {
|
||||
return requestClient.post(
|
||||
`/asset/inspection-record/complete?id=${id}&inspectorName=${inspectorName}`,
|
||||
);
|
||||
}
|
||||
121
apps/web-antd/src/api/asset/return-order.ts
Normal file
121
apps/web-antd/src/api/asset/return-order.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace AssetReturnOrderApi {
|
||||
export interface ReturnOrderVehicle {
|
||||
id?: number;
|
||||
vehicleId?: number;
|
||||
plateNo?: string;
|
||||
vin?: string;
|
||||
brand?: string;
|
||||
model?: string;
|
||||
returnMileage?: number;
|
||||
returnHydrogenLevel?: number;
|
||||
deliveryHydrogenLevel?: number;
|
||||
hydrogenDiff?: number;
|
||||
hydrogenUnitPrice?: number;
|
||||
hydrogenRefundAmount?: number;
|
||||
checkList?: string;
|
||||
defectPhotos?: string;
|
||||
vehicleDamageFee?: number;
|
||||
toolDamageFee?: number;
|
||||
unpaidMaintenanceFee?: number;
|
||||
unpaidRepairFee?: number;
|
||||
otherFee?: number;
|
||||
inspectionRecordId?: number;
|
||||
}
|
||||
|
||||
export interface ReturnOrder {
|
||||
id?: number;
|
||||
orderCode?: string;
|
||||
contractId: number;
|
||||
contractCode?: string;
|
||||
projectName?: string;
|
||||
customerId?: number;
|
||||
customerName?: string;
|
||||
returnDate: string;
|
||||
returnPerson: string;
|
||||
returnLocation?: string;
|
||||
returnReason?: string;
|
||||
returnReasonDesc?: string;
|
||||
totalRefundAmount?: number;
|
||||
depositRefund?: number;
|
||||
hydrogenRefund?: number;
|
||||
otherCharges?: number;
|
||||
returnPhotos?: string;
|
||||
sourceType?: number;
|
||||
sourceId?: number;
|
||||
deliveryOrderId?: number;
|
||||
status?: number;
|
||||
approvalStatus?: number;
|
||||
vehicles?: ReturnOrderVehicle[];
|
||||
createTime?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export function getReturnOrderPage(params: PageParam) {
|
||||
return requestClient.get<PageResult<AssetReturnOrderApi.ReturnOrder>>(
|
||||
'/asset/return-order/page',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
export function getReturnOrder(id: number) {
|
||||
return requestClient.get<AssetReturnOrderApi.ReturnOrder>(
|
||||
`/asset/return-order/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function createReturnOrder(data: AssetReturnOrderApi.ReturnOrder) {
|
||||
return requestClient.post('/asset/return-order/create', data);
|
||||
}
|
||||
|
||||
export function updateReturnOrder(data: AssetReturnOrderApi.ReturnOrder) {
|
||||
return requestClient.put('/asset/return-order/update', data);
|
||||
}
|
||||
|
||||
export function deleteReturnOrder(id: number) {
|
||||
return requestClient.delete(`/asset/return-order/delete?id=${id}`);
|
||||
}
|
||||
|
||||
export function completeReturnOrderInspection(id: number) {
|
||||
return requestClient.put(`/asset/return-order/complete-inspection?id=${id}`);
|
||||
}
|
||||
|
||||
export function settleReturnOrder(id: number) {
|
||||
return requestClient.put(`/asset/return-order/settle?id=${id}`);
|
||||
}
|
||||
|
||||
export function createReturnOrderFromDelivery(
|
||||
deliveryOrderId: number,
|
||||
vehicleIds: number[],
|
||||
) {
|
||||
return requestClient.post('/asset/return-order/create-from-delivery', {
|
||||
deliveryOrderId,
|
||||
vehicleIds,
|
||||
});
|
||||
}
|
||||
|
||||
export function startVehicleInspection(returnOrderVehicleId: number) {
|
||||
return requestClient.post(
|
||||
`/asset/return-order/start-vehicle-inspection?returnOrderVehicleId=${returnOrderVehicleId}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function completeVehicleInspection(returnOrderVehicleId: number) {
|
||||
return requestClient.post(
|
||||
`/asset/return-order/complete-vehicle-inspection?returnOrderVehicleId=${returnOrderVehicleId}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function submitReturnOrderApproval(id: number) {
|
||||
return requestClient.post(`/asset/return-order/submit-approval?id=${id}`, {});
|
||||
}
|
||||
|
||||
export function withdrawReturnOrderApproval(id: number) {
|
||||
return requestClient.post(
|
||||
`/asset/return-order/withdraw-approval?id=${id}`,
|
||||
{},
|
||||
);
|
||||
}
|
||||
82
apps/web-antd/src/api/asset/vehicle-replacement.ts
Normal file
82
apps/web-antd/src/api/asset/vehicle-replacement.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import type { PageParam, PageResult } from '@vben/request';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace AssetVehicleReplacementApi {
|
||||
export interface VehicleReplacement {
|
||||
id?: number;
|
||||
replacementCode?: string;
|
||||
replacementType: number;
|
||||
contractId?: number;
|
||||
contractCode?: string;
|
||||
projectName?: string;
|
||||
customerId?: number;
|
||||
customerName?: string;
|
||||
originalVehicleId?: number;
|
||||
originalPlateNo?: string;
|
||||
originalVin?: string;
|
||||
newVehicleId?: number;
|
||||
newPlateNo?: string;
|
||||
newVin?: string;
|
||||
deliveryOrderId?: number;
|
||||
replacementReason?: string;
|
||||
expectedDate?: string;
|
||||
actualDate?: string;
|
||||
returnDate?: string;
|
||||
status?: number;
|
||||
approvalStatus?: number;
|
||||
bpmInstanceId?: string;
|
||||
remark?: string;
|
||||
creator?: string;
|
||||
createTime?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export function getVehicleReplacementPage(params: PageParam) {
|
||||
return requestClient.get<
|
||||
PageResult<AssetVehicleReplacementApi.VehicleReplacement>
|
||||
>('/asset/vehicle-replacement/page', { params });
|
||||
}
|
||||
|
||||
export function getVehicleReplacement(id: number) {
|
||||
return requestClient.get<AssetVehicleReplacementApi.VehicleReplacement>(
|
||||
`/asset/vehicle-replacement/get?id=${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function createVehicleReplacement(
|
||||
data: AssetVehicleReplacementApi.VehicleReplacement,
|
||||
) {
|
||||
return requestClient.post('/asset/vehicle-replacement/create', data);
|
||||
}
|
||||
|
||||
export function updateVehicleReplacement(
|
||||
data: AssetVehicleReplacementApi.VehicleReplacement,
|
||||
) {
|
||||
return requestClient.put('/asset/vehicle-replacement/update', data);
|
||||
}
|
||||
|
||||
export function deleteVehicleReplacement(id: number) {
|
||||
return requestClient.delete(`/asset/vehicle-replacement/delete?id=${id}`);
|
||||
}
|
||||
|
||||
export function submitVehicleReplacementApproval(id: number) {
|
||||
return requestClient.post(
|
||||
`/asset/vehicle-replacement/submit-approval?id=${id}`,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
export function withdrawVehicleReplacementApproval(id: number) {
|
||||
return requestClient.post(
|
||||
`/asset/vehicle-replacement/withdraw-approval?id=${id}`,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
export function confirmVehicleReplacementReturn(id: number) {
|
||||
return requestClient.post(
|
||||
`/asset/vehicle-replacement/confirm-return?id=${id}`,
|
||||
{},
|
||||
);
|
||||
}
|
||||
290
apps/web-antd/src/views/asset/components/InspectionForm.vue
Normal file
290
apps/web-antd/src/views/asset/components/InspectionForm.vue
Normal file
@@ -0,0 +1,290 @@
|
||||
<script lang="ts" setup>
|
||||
import type { InspectionApi } from '#/api/asset/inspection';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Collapse,
|
||||
CollapsePanel,
|
||||
Image,
|
||||
Input,
|
||||
InputNumber,
|
||||
message,
|
||||
RadioButton,
|
||||
RadioGroup,
|
||||
Space,
|
||||
Spin,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import {
|
||||
completeInspection,
|
||||
getInspectionRecord,
|
||||
updateInspectionRecordItem,
|
||||
} from '#/api/asset/inspection';
|
||||
|
||||
const props = defineProps<{
|
||||
recordId: number;
|
||||
readonly?: boolean;
|
||||
onComplete?: () => void;
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const record = ref<InspectionApi.RecordDetail>();
|
||||
const inspectorName = ref('');
|
||||
|
||||
// Group items by category
|
||||
const groupedItems = computed(() => {
|
||||
if (!record.value?.items) return [];
|
||||
const map = new Map<string, InspectionApi.RecordItem[]>();
|
||||
for (const item of record.value.items) {
|
||||
const list = map.get(item.category) || [];
|
||||
list.push(item);
|
||||
map.set(item.category, list);
|
||||
}
|
||||
return [...map.entries()].map(([category, items]) => ({
|
||||
category,
|
||||
items,
|
||||
}));
|
||||
});
|
||||
|
||||
// Active collapse keys (all open by default)
|
||||
const activeKeys = computed(() =>
|
||||
groupedItems.value.map((g) => g.category),
|
||||
);
|
||||
|
||||
// Whether inspection is already completed
|
||||
const isCompleted = computed(() => record.value?.status === 2);
|
||||
|
||||
// Load inspection record
|
||||
async function loadRecord() {
|
||||
if (!props.recordId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
record.value = await getInspectionRecord(props.recordId);
|
||||
inspectorName.value = record.value.inspectorName || '';
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.recordId,
|
||||
() => loadRecord(),
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
// Save single item on change
|
||||
async function handleItemChange(item: InspectionApi.RecordItem) {
|
||||
if (props.readonly || isCompleted.value) return;
|
||||
try {
|
||||
await updateInspectionRecordItem({
|
||||
id: item.id,
|
||||
result: item.result,
|
||||
value: item.value,
|
||||
remark: item.remark,
|
||||
imageUrls: item.imageUrls,
|
||||
});
|
||||
} catch {
|
||||
message.error('保存检查项失败');
|
||||
}
|
||||
}
|
||||
|
||||
// Complete inspection
|
||||
async function handleComplete() {
|
||||
if (!inspectorName.value) {
|
||||
message.warning('请输入验车人姓名');
|
||||
return;
|
||||
}
|
||||
submitting.value = true;
|
||||
try {
|
||||
await completeInspection(props.recordId, inspectorName.value);
|
||||
message.success('验车完成');
|
||||
await loadRecord();
|
||||
props.onComplete?.();
|
||||
} catch {
|
||||
message.error('完成验车失败');
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Result options for checkbox type
|
||||
const resultOptions = [
|
||||
{ label: '合格', value: 1 },
|
||||
{ label: '不合格', value: 2 },
|
||||
{ label: '不适用', value: 3 },
|
||||
];
|
||||
|
||||
// Parse image URLs
|
||||
function parseImageUrls(urls?: string): string[] {
|
||||
if (!urls) return [];
|
||||
return urls.split(',').filter(Boolean);
|
||||
}
|
||||
|
||||
// Handle image upload (simplified - assumes backend returns URL)
|
||||
function handleImageUpload(item: InspectionApi.RecordItem, info: any) {
|
||||
if (info.file.status === 'done' && info.file.response) {
|
||||
const url = info.file.response.data;
|
||||
const urls = parseImageUrls(item.imageUrls);
|
||||
urls.push(url);
|
||||
item.imageUrls = urls.join(',');
|
||||
handleItemChange(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if inputs should be disabled
|
||||
const isDisabled = computed(() => props.readonly || isCompleted.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Spin :spinning="loading">
|
||||
<div class="inspection-form">
|
||||
<!-- Inspector name -->
|
||||
<div class="mb-4 flex items-center gap-4">
|
||||
<label class="font-medium">验车人:</label>
|
||||
<Input
|
||||
v-model:value="inspectorName"
|
||||
:disabled="isDisabled"
|
||||
placeholder="请输入验车人姓名"
|
||||
style="width: 200px"
|
||||
/>
|
||||
<span v-if="record?.inspectionTime" class="text-gray-500">
|
||||
验车时间:{{ record.inspectionTime }}
|
||||
</span>
|
||||
<span
|
||||
v-if="isCompleted"
|
||||
class="rounded bg-green-100 px-2 py-1 text-sm text-green-700"
|
||||
>
|
||||
已完成
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Inspection items grouped by category -->
|
||||
<Collapse :active-key="activeKeys" :bordered="true">
|
||||
<CollapsePanel
|
||||
v-for="group in groupedItems"
|
||||
:key="group.category"
|
||||
:header="group.category"
|
||||
>
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-for="item in group.items"
|
||||
:key="item.id"
|
||||
class="rounded border border-gray-200 p-3"
|
||||
>
|
||||
<div class="mb-2 flex items-start justify-between">
|
||||
<span class="font-medium">{{ item.itemName }}</span>
|
||||
<span class="text-xs text-gray-400">{{ item.itemCode }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-start gap-4">
|
||||
<!-- Checkbox type: radio group -->
|
||||
<div v-if="item.inputType === 'checkbox'" class="flex-1">
|
||||
<RadioGroup
|
||||
v-model:value="item.result"
|
||||
:disabled="isDisabled"
|
||||
button-style="solid"
|
||||
size="small"
|
||||
@change="handleItemChange(item)"
|
||||
>
|
||||
<RadioButton
|
||||
v-for="opt in resultOptions"
|
||||
:key="opt.value"
|
||||
:value="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</RadioButton>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<!-- Number type: input number -->
|
||||
<div v-else-if="item.inputType === 'number'" class="flex-1">
|
||||
<InputNumber
|
||||
v-model:value="item.value"
|
||||
:disabled="isDisabled"
|
||||
placeholder="请输入数值"
|
||||
size="small"
|
||||
style="width: 200px"
|
||||
@change="handleItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Text type: input -->
|
||||
<div v-else class="flex-1">
|
||||
<Input
|
||||
v-model:value="item.value"
|
||||
:disabled="isDisabled"
|
||||
placeholder="请输入"
|
||||
size="small"
|
||||
style="width: 300px"
|
||||
@change="handleItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Remark -->
|
||||
<div class="flex-1">
|
||||
<Input
|
||||
v-model:value="item.remark"
|
||||
:disabled="isDisabled"
|
||||
placeholder="备注"
|
||||
size="small"
|
||||
@change="handleItemChange(item)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Images -->
|
||||
<div class="mt-2">
|
||||
<Space :size="8" wrap>
|
||||
<Image
|
||||
v-for="(url, idx) in parseImageUrls(item.imageUrls)"
|
||||
:key="idx"
|
||||
:src="url"
|
||||
:width="60"
|
||||
:height="60"
|
||||
style="object-fit: cover; border-radius: 4px"
|
||||
/>
|
||||
<Upload
|
||||
v-if="!isDisabled"
|
||||
:show-upload-list="false"
|
||||
action="/admin-api/infra/file/upload"
|
||||
name="file"
|
||||
accept="image/*"
|
||||
@change="(info: any) => handleImageUpload(item, info)"
|
||||
>
|
||||
<Button size="small" type="dashed">上传图片</Button>
|
||||
</Upload>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CollapsePanel>
|
||||
</Collapse>
|
||||
|
||||
<!-- Overall remark -->
|
||||
<div v-if="record" class="mt-4">
|
||||
<label class="mb-1 block font-medium">整体备注:</label>
|
||||
<Input.TextArea
|
||||
v-model:value="record.remark"
|
||||
:disabled="isDisabled"
|
||||
:rows="3"
|
||||
placeholder="请输入整体备注"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Complete button -->
|
||||
<div v-if="!isDisabled" class="mt-4 text-right">
|
||||
<Button
|
||||
type="primary"
|
||||
:loading="submitting"
|
||||
@click="handleComplete"
|
||||
>
|
||||
完成验车
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</template>
|
||||
183
apps/web-antd/src/views/asset/return-order/index.vue
Normal file
183
apps/web-antd/src/views/asset/return-order/index.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script lang="ts" setup>
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { AssetReturnOrderApi } from '#/api/asset/return-order';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
completeReturnOrderInspection,
|
||||
deleteReturnOrder,
|
||||
getReturnOrderPage,
|
||||
settleReturnOrder,
|
||||
} from '#/api/asset/return-order';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
const [FormModal, formModalApi] = useVbenModal({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
function handleEdit(row: AssetReturnOrderApi.ReturnOrder) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
async function handleDelete(row: AssetReturnOrderApi.ReturnOrder) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.orderCode]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteReturnOrder(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.orderCode]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCompleteInspection(row: AssetReturnOrderApi.ReturnOrder) {
|
||||
await confirm('确认验车完成?完成后状态将变更为验车完成。');
|
||||
await completeReturnOrderInspection(row.id!);
|
||||
message.success('验车已完成');
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
async function handleSettle(row: AssetReturnOrderApi.ReturnOrder) {
|
||||
await confirm('确认结算还车单?结算后不可修改。');
|
||||
await settleReturnOrder(row.id!);
|
||||
message.success('还车单已结算');
|
||||
handleRefresh();
|
||||
}
|
||||
|
||||
/** 根据状态动态生成操作菜单 */
|
||||
function getRowActions(row: AssetReturnOrderApi.ReturnOrder) {
|
||||
const actions: any[] = [];
|
||||
|
||||
// 查看 - 始终可见
|
||||
actions.push({
|
||||
auth: ['asset:return-order:query'],
|
||||
icon: ACTION_ICON.VIEW,
|
||||
label: '查看',
|
||||
onClick: () => message.info('查看功能开发中'),
|
||||
type: 'link',
|
||||
});
|
||||
|
||||
// 待验车 → 可编辑
|
||||
if (row.status === 0) {
|
||||
actions.push({
|
||||
auth: ['asset:return-order:update'],
|
||||
icon: ACTION_ICON.EDIT,
|
||||
label: $t('common.edit'),
|
||||
onClick: () => handleEdit(row),
|
||||
type: 'link',
|
||||
});
|
||||
}
|
||||
|
||||
// 待验车 → 完成验车
|
||||
if (row.status === 0) {
|
||||
actions.push({
|
||||
auth: ['asset:return-order:update'],
|
||||
label: '完成验车',
|
||||
onClick: () => handleCompleteInspection(row),
|
||||
type: 'link',
|
||||
});
|
||||
}
|
||||
|
||||
// 验车完成 → 结算
|
||||
if (row.status === 1) {
|
||||
actions.push({
|
||||
auth: ['asset:return-order:update'],
|
||||
label: '结算',
|
||||
onClick: () => handleSettle(row),
|
||||
type: 'link',
|
||||
});
|
||||
}
|
||||
|
||||
// 待验车 → 可删除
|
||||
if (row.status === 0) {
|
||||
actions.push({
|
||||
auth: ['asset:return-order:delete'],
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
label: $t('common.delete'),
|
||||
popConfirm: {
|
||||
confirm: () => handleDelete(row),
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.orderCode]),
|
||||
},
|
||||
type: 'link',
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useGridColumns(),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getReturnOrderPage({
|
||||
pageNo: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
toolbarConfig: {
|
||||
refresh: true,
|
||||
search: true,
|
||||
},
|
||||
} as VxeTableGridOptions<AssetReturnOrderApi.ReturnOrder>,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="还车管理">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['还车单']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['asset:return-order:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction :actions="getRowActions(row)" />
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
Reference in New Issue
Block a user