refactor(@vben/web-antd): 重写升级优先级和取消工单弹窗,统一布局风格
- 移除 useVbenForm 避免弹窗内布局冲突,改用原生 Input.TextArea - 统一为图标+标签+值的信息行布局,与安保派单弹窗风格一致 - 提交时增加 confirmLoading 状态反馈及 maxLength 保护校验 - 支持暗色模式样式适配 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,120 +4,183 @@ import { ref } from 'vue';
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Alert, message } from 'ant-design-vue';
|
||||
import { Alert, Input, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { cancelOrder } from '#/api/ops/order-center';
|
||||
|
||||
defineOptions({ name: 'CancelOrderForm' });
|
||||
|
||||
const emit = defineEmits<{ success: [] }>();
|
||||
|
||||
interface ModalData {
|
||||
orderId: number;
|
||||
orderCode: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const modalData = ref<ModalData>({
|
||||
orderId: 0,
|
||||
orderCode: '',
|
||||
title: '',
|
||||
});
|
||||
const reason = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
onOpenChange: (isOpen) => {
|
||||
if (isOpen) {
|
||||
const data = modalApi.getData<{
|
||||
orderCode: string;
|
||||
orderId: number;
|
||||
title: string;
|
||||
}>();
|
||||
const data = modalApi.getData<ModalData>();
|
||||
if (data) {
|
||||
orderId.value = data.orderId;
|
||||
orderCode.value = data.orderCode;
|
||||
orderTitle.value = data.title;
|
||||
modalData.value = data;
|
||||
}
|
||||
reason.value = '';
|
||||
}
|
||||
},
|
||||
onConfirm: handleSubmit,
|
||||
});
|
||||
|
||||
const orderId = ref<number>();
|
||||
const orderCode = ref<string>('');
|
||||
const orderTitle = ref<string>('');
|
||||
const loading = ref(false);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'reason',
|
||||
label: '取消原因',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder: '请输入取消工单的原因',
|
||||
rows: 4,
|
||||
maxLength: 200,
|
||||
showCount: true,
|
||||
},
|
||||
rules: z
|
||||
.string()
|
||||
.min(2, '原因至少2个字符')
|
||||
.max(200, '原因不能超过200字符'),
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid, values } = await formApi.validate();
|
||||
if (!valid) return;
|
||||
const val = reason.value.trim();
|
||||
if (val.length < 2) {
|
||||
message.warning('取消原因至少2个字符');
|
||||
return;
|
||||
}
|
||||
if (val.length > 200) {
|
||||
message.warning('取消原因不能超过200字符');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
modalApi.setState({ confirmLoading: true });
|
||||
try {
|
||||
await cancelOrder({
|
||||
id: orderId.value!,
|
||||
reason: values.reason,
|
||||
id: modalData.value.orderId,
|
||||
reason: val,
|
||||
});
|
||||
message.success('工单已取消');
|
||||
modalApi.close();
|
||||
emit('success');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
modalApi.setState({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="取消工单" class="w-[480px]">
|
||||
<div class="cancel-form">
|
||||
<!-- 警告提示 -->
|
||||
<Alert
|
||||
type="error"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
message="取消后工单将无法恢复,请确认操作"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="lucide:alert-circle" class="text-red-500" />
|
||||
</template>
|
||||
</Alert>
|
||||
<!-- 警告提示 -->
|
||||
<Alert
|
||||
type="error"
|
||||
show-icon
|
||||
class="cf-alert"
|
||||
message="取消后工单将无法恢复,请确认操作"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="lucide:alert-circle" class="text-red-500" />
|
||||
</template>
|
||||
</Alert>
|
||||
|
||||
<!-- 工单信息 -->
|
||||
<div class="mb-4 rounded-lg bg-gray-50 p-4 dark:bg-gray-800">
|
||||
<div class="mb-2 text-sm text-gray-500">
|
||||
工单编号:<span
|
||||
class="font-medium text-gray-700 dark:text-gray-300"
|
||||
>{{ orderCode }}</span
|
||||
>
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
工单标题:<span
|
||||
class="font-medium text-gray-700 dark:text-gray-300"
|
||||
>{{ orderTitle }}</span
|
||||
>
|
||||
</div>
|
||||
<!-- 工单信息 -->
|
||||
<div class="cf-order-info">
|
||||
<div class="cf-info-row">
|
||||
<IconifyIcon
|
||||
icon="solar:document-text-bold-duotone"
|
||||
class="cf-info-icon"
|
||||
/>
|
||||
<span class="cf-info-label">工单编号</span>
|
||||
<span class="cf-info-value">{{ modalData.orderCode }}</span>
|
||||
</div>
|
||||
<div v-if="modalData.title" class="cf-info-row">
|
||||
<IconifyIcon
|
||||
icon="solar:notes-bold-duotone"
|
||||
class="cf-info-icon"
|
||||
/>
|
||||
<span class="cf-info-label">工单标题</span>
|
||||
<span class="cf-info-value cf-info-desc">{{ modalData.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表单 -->
|
||||
<Form />
|
||||
<!-- 取消原因 -->
|
||||
<div class="cf-section">
|
||||
<div class="cf-section-title">取消原因</div>
|
||||
<Input.TextArea
|
||||
v-model:value="reason"
|
||||
placeholder="请输入取消工单的原因"
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.cancel-form {
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.cf-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cf-order-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
background: #fafafa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.cf-info-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: flex-start;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.cf-info-icon {
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.cf-info-label {
|
||||
flex-shrink: 0;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.cf-info-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.cf-info-desc {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.cf-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.cf-section-title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* dark mode */
|
||||
html.dark .cf-order-info {
|
||||
background: rgb(255 255 255 / 6%);
|
||||
}
|
||||
|
||||
html.dark .cf-info-value {
|
||||
color: rgb(255 255 255 / 85%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,60 +4,41 @@ import { ref } from 'vue';
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import { Alert, message } from 'ant-design-vue';
|
||||
import { Alert, Input, message } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm, z } from '#/adapter/form';
|
||||
import { upgradePriority } from '#/api/ops/cleaning';
|
||||
|
||||
defineOptions({ name: 'UpgradePriorityForm' });
|
||||
|
||||
const emit = defineEmits<{ success: [] }>();
|
||||
|
||||
interface ModalData {
|
||||
orderId: number;
|
||||
orderCode: string;
|
||||
currentPriority: number;
|
||||
}
|
||||
|
||||
const modalData = ref<ModalData>({
|
||||
orderId: 0,
|
||||
orderCode: '',
|
||||
currentPriority: 2,
|
||||
});
|
||||
const reason = ref('');
|
||||
const loading = ref(false);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
onOpenChange: (isOpen) => {
|
||||
if (isOpen) {
|
||||
const data = modalApi.getData<{
|
||||
currentPriority: number;
|
||||
orderCode: string;
|
||||
orderId: number;
|
||||
}>();
|
||||
const data = modalApi.getData<ModalData>();
|
||||
if (data) {
|
||||
orderId.value = data.orderId;
|
||||
orderCode.value = data.orderCode;
|
||||
currentPriority.value = data.currentPriority;
|
||||
modalData.value = data;
|
||||
}
|
||||
reason.value = '';
|
||||
}
|
||||
},
|
||||
onConfirm: handleSubmit,
|
||||
});
|
||||
|
||||
const orderId = ref<number>();
|
||||
const orderCode = ref<string>('');
|
||||
const currentPriority = ref<number>(2);
|
||||
const loading = ref(false);
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
schema: [
|
||||
{
|
||||
fieldName: 'reason',
|
||||
label: '升级原因',
|
||||
component: 'Textarea',
|
||||
componentProps: {
|
||||
placeholder:
|
||||
'请输入升级为P0紧急工单的原因,例如:领导临时检查、VIP客户投诉等',
|
||||
rows: 4,
|
||||
maxLength: 200,
|
||||
showCount: true,
|
||||
},
|
||||
rules: z
|
||||
.string()
|
||||
.min(5, '原因至少5个字符')
|
||||
.max(200, '原因不能超过200字符'),
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
});
|
||||
|
||||
/** 获取优先级文本 */
|
||||
function getPriorityText(priority: number) {
|
||||
const map: Record<number, string> = {
|
||||
@@ -70,67 +51,162 @@ function getPriorityText(priority: number) {
|
||||
|
||||
/** 提交表单 */
|
||||
async function handleSubmit() {
|
||||
const { valid, values } = await formApi.validate();
|
||||
if (!valid || !values) return;
|
||||
const val = reason.value.trim();
|
||||
if (val.length < 5) {
|
||||
message.warning('升级原因至少5个字符');
|
||||
return;
|
||||
}
|
||||
if (val.length > 200) {
|
||||
message.warning('升级原因不能超过200字符');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
modalApi.setState({ confirmLoading: true });
|
||||
try {
|
||||
await upgradePriority({
|
||||
orderId: orderId.value!,
|
||||
reason: values.reason,
|
||||
orderId: modalData.value.orderId,
|
||||
reason: val,
|
||||
});
|
||||
message.success('已升级为P0紧急工单');
|
||||
modalApi.close();
|
||||
emit('success');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
modalApi.setState({ confirmLoading: false });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal title="升级优先级" class="w-[500px]">
|
||||
<div class="upgrade-form">
|
||||
<!-- 警告提示 -->
|
||||
<Alert
|
||||
type="warning"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
message="升级为P0紧急工单后,该工单将插队优先处理"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="lucide:zap" class="text-orange-500" />
|
||||
</template>
|
||||
</Alert>
|
||||
<!-- 警告提示 -->
|
||||
<Alert
|
||||
type="warning"
|
||||
show-icon
|
||||
class="uf-alert"
|
||||
message="升级为P0紧急工单后,该工单将优先处理"
|
||||
>
|
||||
<template #icon>
|
||||
<IconifyIcon icon="lucide:zap" class="text-orange-500" />
|
||||
</template>
|
||||
</Alert>
|
||||
|
||||
<!-- 工单信息 -->
|
||||
<div class="mb-4 rounded-lg bg-gray-50 p-4 dark:bg-gray-800">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<span class="text-sm text-gray-500">工单编号</span>
|
||||
<span class="font-medium">{{ orderCode }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="text-sm text-gray-500">当前优先级</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-gray-600 dark:text-gray-300">
|
||||
{{ getPriorityText(currentPriority) }}
|
||||
</span>
|
||||
<IconifyIcon icon="lucide:arrow-right" class="text-gray-400" />
|
||||
<span class="font-bold text-red-500">P0 (紧急)</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 工单信息 -->
|
||||
<div class="uf-order-info">
|
||||
<div class="uf-info-row">
|
||||
<IconifyIcon
|
||||
icon="solar:document-text-bold-duotone"
|
||||
class="uf-info-icon"
|
||||
/>
|
||||
<span class="uf-info-label">工单编号</span>
|
||||
<span class="uf-info-value">{{ modalData.orderCode }}</span>
|
||||
</div>
|
||||
<div class="uf-info-row">
|
||||
<IconifyIcon
|
||||
icon="solar:ranking-bold-duotone"
|
||||
class="uf-info-icon"
|
||||
/>
|
||||
<span class="uf-info-label">优先级变更</span>
|
||||
<span class="uf-info-value uf-priority">
|
||||
<span class="uf-priority-from">
|
||||
{{ getPriorityText(modalData.currentPriority) }}
|
||||
</span>
|
||||
<IconifyIcon icon="lucide:arrow-right" class="uf-arrow" />
|
||||
<span class="uf-priority-to">P0 (紧急)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表单 -->
|
||||
<Form />
|
||||
<!-- 升级原因 -->
|
||||
<div class="uf-section">
|
||||
<div class="uf-section-title">升级原因</div>
|
||||
<Input.TextArea
|
||||
v-model:value="reason"
|
||||
placeholder="请输入升级为P0紧急工单的原因,例如:领导临时检查、VIP客户投诉等"
|
||||
:rows="4"
|
||||
:maxlength="200"
|
||||
show-count
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.upgrade-form {
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.uf-alert {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.uf-order-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
margin-bottom: 12px;
|
||||
background: #fafafa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.uf-info-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.uf-info-icon {
|
||||
flex-shrink: 0;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.uf-info-label {
|
||||
flex-shrink: 0;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.uf-info-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.uf-priority {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.uf-priority-from {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.uf-arrow {
|
||||
color: #bfbfbf;
|
||||
}
|
||||
|
||||
.uf-priority-to {
|
||||
font-weight: 600;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.uf-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.uf-section-title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* dark mode */
|
||||
html.dark .uf-order-info {
|
||||
background: rgb(255 255 255 / 6%);
|
||||
}
|
||||
|
||||
html.dark .uf-info-value {
|
||||
color: rgb(255 255 255 / 85%);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user