csm修改

master
2358328281@qq.com 5 days ago
parent 5d9626f55b
commit c9efdb0e65

@ -97,6 +97,7 @@ export default {
close: "关闭", close: "关闭",
detail: "详情", detail: "详情",
process: "处理", process: "处理",
modify: "修改",
submit: "提交工单", submit: "提交工单",
save: "保存修改", save: "保存修改",
cancelModal: "取消", cancelModal: "取消",
@ -142,11 +143,12 @@ export default {
category: "客户分类", category: "客户分类",
signDate: "签约时间", signDate: "签约时间",
acceptDate: "验收时间", acceptDate: "验收时间",
years: "年限", years: "免费维保年限",
maintenanceEndDate: "维保截止时间", maintenanceEndDate: "维保截止时间",
manager: "客户经理", manager: "客户经理",
acceptanceReport: "请上传验收报告", acceptanceReport: "请上传验收报告",
acceptanceReportTip: "支持上传PDF格式文件必填", acceptanceReportTip: "支持上传PDF格式文件必填",
attachmentTip: "不超过50M",
account: "账号", account: "账号",
accountType: "账号类型", accountType: "账号类型",
nameAccount: "姓名/账号", nameAccount: "姓名/账号",
@ -252,7 +254,7 @@ export default {
phone: "联系电话", phone: "联系电话",
manager: "客户经理", manager: "客户经理",
signDate: "签约日期", signDate: "签约日期",
usageYears: "客户使用年限", usageYears: "累计使用年限",
maintenanceEnd: "维保截止时间", maintenanceEnd: "维保截止时间",
acceptanceReport: "验收报告", acceptanceReport: "验收报告",
account: "账号", account: "账号",

@ -142,7 +142,7 @@ export default {
category: '客戶分類', category: '客戶分類',
signDate: '簽約時間', signDate: '簽約時間',
acceptDate: '驗收時間', acceptDate: '驗收時間',
years: '年限', years: '免费维保年限',
maintenanceEndDate: '維保截止時間', maintenanceEndDate: '維保截止時間',
manager: '客戶經理', manager: '客戶經理',
acceptanceReport: '驗收報告', acceptanceReport: '驗收報告',

@ -24,7 +24,7 @@
<el-descriptions-item :label="$t('label.contactPhone')">{{ detail.contactPhone }}</el-descriptions-item> <el-descriptions-item :label="$t('label.contactPhone')">{{ detail.contactPhone }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.signDate')">{{ formatDate(detail.signDate) }}</el-descriptions-item> <el-descriptions-item :label="$t('table.signDate')">{{ formatDate(detail.signDate) }}</el-descriptions-item>
<el-descriptions-item :label="$t('label.acceptDate')">{{ formatDate(detail.acceptDate) }}</el-descriptions-item> <el-descriptions-item :label="$t('label.acceptDate')">{{ formatDate(detail.acceptDate) }}</el-descriptions-item>
<el-descriptions-item :label="$t('label.years')">{{ formatUsageYears(detail.signDate) }}</el-descriptions-item> <el-descriptions-item :label="$t('label.years')">{{ detail.contractYears }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.maintenanceEnd')">{{ formatDate(detail.maintenanceEnd) }}</el-descriptions-item> <el-descriptions-item :label="$t('table.maintenanceEnd')">{{ formatDate(detail.maintenanceEnd) }}</el-descriptions-item>
<el-descriptions-item :label="$t('table.manager')">{{ detail.managerName }}</el-descriptions-item> <el-descriptions-item :label="$t('table.manager')">{{ detail.managerName }}</el-descriptions-item>
</el-descriptions> </el-descriptions>

@ -39,7 +39,7 @@
</div> </div>
<div class="section"> <div class="section">
<div class="section-title">{{ $t('label.attachment') }} ({{ (detail.attachments || []).length }})</div> <div class="section-title">{{ $t('label.attachment') }}</div>
<el-empty v-if="!detail.attachments?.length" :description="$t('orderDetail.noAttachment')" :image-size="80" /> <el-empty v-if="!detail.attachments?.length" :description="$t('orderDetail.noAttachment')" :image-size="80" />
<div v-else class="files"> <div v-else class="files">
<el-button <el-button

@ -85,22 +85,38 @@
</el-form> </el-form>
<!-- 工单总览 --> <!-- 工单总览 -->
<div class="order-stats"> <div class="order-stats">
<div class="stat-card stat-total"> <div
class="stat-card stat-total"
:class="{ active: activeStat === 'total' }"
@click="onStatClick('total')"
>
<div class="stat-icon">📋</div> <div class="stat-icon">📋</div>
<div class="stat-value">{{ stats.total }}</div> <div class="stat-value">{{ stats.total }}</div>
<div class="stat-label">{{ $t("stat.totalOrders") }}</div> <div class="stat-label">{{ $t("stat.totalOrders") }}</div>
</div> </div>
<div class="stat-card stat-pending"> <div
class="stat-card stat-pending"
:class="{ active: activeStat === 'pending' }"
@click="onStatClick('pending')"
>
<div class="stat-icon"></div> <div class="stat-icon"></div>
<div class="stat-value">{{ stats.pending }}</div> <div class="stat-value">{{ stats.pending }}</div>
<div class="stat-label">{{ $t("stat.pendingOrders") }}</div> <div class="stat-label">{{ $t("stat.pendingOrders") }}</div>
</div> </div>
<div class="stat-card stat-completed"> <div
class="stat-card stat-completed"
:class="{ active: activeStat === 'completed' }"
@click="onStatClick('completed')"
>
<div class="stat-icon"></div> <div class="stat-icon"></div>
<div class="stat-value">{{ stats.completed }}</div> <div class="stat-value">{{ stats.completed }}</div>
<div class="stat-label">{{ $t("stat.completedOrders") }}</div> <div class="stat-label">{{ $t("stat.completedOrders") }}</div>
</div> </div>
<div class="stat-card stat-high"> <div
class="stat-card stat-high"
:class="{ active: activeStat === 'high' }"
@click="onStatClick('high')"
>
<div class="stat-icon">🔴</div> <div class="stat-icon">🔴</div>
<div class="stat-value">{{ stats.highPriority }}</div> <div class="stat-value">{{ stats.highPriority }}</div>
<div class="stat-label">{{ $t("stat.highPriorityOrders") }}</div> <div class="stat-label">{{ $t("stat.highPriorityOrders") }}</div>
@ -114,10 +130,17 @@
min-width="160" min-width="160"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column :label="$t('table.priority')" width="90">
<template #default="{ row }">
<el-tag :type="priorityType(row.priority)" size="small">{{
row.priority
}}</el-tag>
</template>
</el-table-column>
<el-table-column <el-table-column
:label="$t('table.title')" :label="$t('table.title')"
prop="title" prop="title"
min-width="200" min-width="300"
show-overflow-tooltip show-overflow-tooltip
/> />
<el-table-column <el-table-column
@ -155,14 +178,7 @@
width="170" width="170"
> >
<template #default="{ row }"> <template #default="{ row }">
{{ row.completedAt || '-' }} {{ row.completedAt || "-" }}
</template>
</el-table-column>
<el-table-column :label="$t('table.priority')" width="90">
<template #default="{ row }">
<el-tag :type="priorityType(row.priority)" size="small">{{
row.priority
}}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('table.status')" width="100"> <el-table-column :label="$t('table.status')" width="100">
@ -184,10 +200,11 @@
}}</el-button> }}</el-button>
<el-button <el-button
link link
type="success" :type="row.status === '已完成' ? 'warning' : 'success'"
v-if="row.status !== '已完成'"
@click="goProcess(row.id)" @click="goProcess(row.id)"
>{{ $t("btn.process") }}</el-button >{{
row.status === "已完成" ? $t("btn.modify") : $t("btn.process")
}}</el-button
> >
</template> </template>
</el-table-column> </el-table-column>
@ -290,18 +307,18 @@
<el-radio-button label="低"></el-radio-button> <el-radio-button label="低"></el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.name')">
<el-input
v-model="quickForm.submitter"
:placeholder="$t('placeholder.name')"
/>
</el-form-item>
<el-form-item :label="$t('label.dept')"> <el-form-item :label="$t('label.dept')">
<el-input <el-input
v-model="quickForm.department" v-model="quickForm.department"
:placeholder="$t('placeholder.dept')" :placeholder="$t('placeholder.dept')"
/> />
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.name')">
<el-input
v-model="quickForm.submitter"
:placeholder="$t('placeholder.name')"
/>
</el-form-item>
<el-form-item :label="$t('label.description')" prop="description"> <el-form-item :label="$t('label.description')" prop="description">
<div class="rich-editor"> <div class="rich-editor">
<Toolbar <Toolbar
@ -321,28 +338,21 @@
</div> </div>
</el-form-item> </el-form-item>
<el-form-item :label="$t('label.attachment')"> <el-form-item :label="$t('label.attachment')">
<div class="attachment-pick"> <el-upload
<el-input v-model:file-list="quickForm.files"
:model-value="quickForm.files[0]?.name || ''" :auto-upload="false"
:placeholder="$t('upload.text')" :limit="1"
readonly :on-exceed="handleAttachmentExceed"
clearable :on-remove="handleAttachmentRemove"
@click="triggerFilePicker" :on-change="handleAttachmentChange"
@clear="clearFile"
> >
<template #append> <el-button type="primary" plain>{{ $t("btn.upload") }}</el-button>
<el-button @click.stop="triggerFilePicker">{{ <template #tip>
$t("btn.upload") <div class="el-upload__tip">
}}</el-button> {{ $t("label.attachmentTip") }}
</template>
</el-input>
<input
ref="fileInputRef"
type="file"
class="attachment-hidden-input"
@change="onFileChange"
/>
</div> </div>
</template>
</el-upload>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -361,6 +371,7 @@
</template> </template>
<script setup> <script setup>
import dayjs from "dayjs";
import { import {
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
@ -422,6 +433,8 @@ const stats = reactive({
completed: 0, completed: 0,
highPriority: 0, highPriority: 0,
}); });
// total | pending | completed | high
const activeStat = ref("total");
const parseStats = (res) => { const parseStats = (res) => {
stats.total = res?.total || 0; stats.total = res?.total || 0;
let payload = res?.data; let payload = res?.data;
@ -449,10 +462,12 @@ const loadList = async () => {
list.value = res?.list || []; list.value = res?.list || [];
list.value.forEach((item) => { list.value.forEach((item) => {
if (item.createdAt) { if (item.createdAt) {
item.createdAt = item.createdAt.split("T").join(" "); item.createdAt = dayjs(item.createdAt).format("YYYY-MM-DD HH:mm:ss");
} }
if (item.completedAt) { if (item.completedAt) {
item.completedAt = item.completedAt.split("T").join(" "); item.completedAt = dayjs(item.completedAt).format(
"YYYY-MM-DD HH:mm:ss",
);
} }
}); });
total.value = res?.total || 0; total.value = res?.total || 0;
@ -462,6 +477,26 @@ const loadList = async () => {
} }
}; };
//
const onStatClick = (type) => {
activeStat.value = type;
if (type === "total") {
filters.status = "";
filters.priority = "";
} else if (type === "pending") {
filters.status = "待处理";
filters.priority = "";
} else if (type === "completed") {
filters.status = "已完成";
filters.priority = "";
} else if (type === "high") {
filters.status = "";
filters.priority = "高";
}
page.value = 1;
loadList();
};
const onSearch = () => { const onSearch = () => {
page.value = 1; page.value = 1;
loadList(); loadList();
@ -475,6 +510,7 @@ const onReset = () => {
keyword: "", keyword: "",
}); });
dateRange.value = []; dateRange.value = [];
activeStat.value = "total";
onSearch(); onSearch();
}; };
@ -522,22 +558,27 @@ const quickForm = reactive({
submitter: "", submitter: "",
files: [], files: [],
}); });
const fileInputRef = ref();
const MAX_FILE_SIZE = 50 * 1024 * 1024; const MAX_FILE_SIZE = 50 * 1024 * 1024;
// base64 HTML // base64 HTML
const MAX_PASTE_IMAGE_SIZE = 5 * 1024 * 1024; const MAX_PASTE_IMAGE_SIZE = 5 * 1024 * 1024;
const triggerFilePicker = () => {
fileInputRef.value?.click(); // el-upload
const handleAttachmentChange = (file) => {
if (file?.size > MAX_FILE_SIZE) {
ElMessage.warning("文件不能超过 50MB");
//
quickForm.files = quickForm.files.filter(
(f) => f.uid !== file.uid || f.size <= MAX_FILE_SIZE,
);
}
}; };
const onFileChange = (e) => { // el-upload limit
const file = e.target.files?.[0]; const handleAttachmentExceed = () => {
e.target.value = ""; ElMessage.warning("只能上传 1 个文件");
if (!file) return;
if (file.size > MAX_FILE_SIZE) return;
quickForm.files = [{ name: file.name, size: file.size, raw: file }];
}; };
const clearFile = () => { // el-upload
quickForm.files = []; const handleAttachmentRemove = () => {
// el-upload v-model:file-list
}; };
const quickRules = { const quickRules = {
hospitalId: [ hospitalId: [
@ -620,7 +661,7 @@ const collectPastedImages = (clipboardData) => {
return files; return files;
}; };
// customPaste //
const handleEditorPasteImage = (editor, event) => { const handleEditorPasteImage = (editor, event) => {
if (event.__wpsImageHandled) return; if (event.__wpsImageHandled) return;
const imageFiles = collectPastedImages(event.clipboardData); const imageFiles = collectPastedImages(event.clipboardData);
@ -630,15 +671,13 @@ const handleEditorPasteImage = (editor, event) => {
if (typeof event.stopPropagation === "function") event.stopPropagation(); if (typeof event.stopPropagation === "function") event.stopPropagation();
if (typeof event.stopImmediatePropagation === "function") if (typeof event.stopImmediatePropagation === "function")
event.stopImmediatePropagation(); event.stopImmediatePropagation();
// ElMessage.info(` ${imageFiles.length} ...`);
// selection FileReader
if (typeof editor.focus === "function") editor.focus(); if (typeof editor.focus === "function") editor.focus();
const savedSelection = editor.selection; const savedSelection = editor.selection;
imageFiles.forEach((file) => { imageFiles.forEach((file) => {
if (file.size > MAX_PASTE_IMAGE_SIZE) { if (file.size > MAX_PASTE_IMAGE_SIZE) {
ElMessage.warning("粘贴的图片不能超过 5MB,请使用下方附件上传"); ElMessage.warning("粘贴的图片不能超过 5MB");
return; return;
} }
const reader = new FileReader(); const reader = new FileReader();
@ -649,7 +688,6 @@ const handleEditorPasteImage = (editor, event) => {
return; return;
} }
try { try {
//
if (savedSelection) editor.select(savedSelection); if (savedSelection) editor.select(savedSelection);
} catch (_) { } catch (_) {
if (typeof editor.focus === "function") editor.focus(); if (typeof editor.focus === "function") editor.focus();
@ -658,32 +696,26 @@ const handleEditorPasteImage = (editor, event) => {
editor.dangerouslyInsertHtml( editor.dangerouslyInsertHtml(
`<img src="${dataUrl}" style="max-width:100%;height:auto;" />`, `<img src="${dataUrl}" style="max-width:100%;height:auto;" />`,
); );
// v-model
if (typeof editor.updateView === "function") editor.updateView(); if (typeof editor.updateView === "function") editor.updateView();
// ElMessage.success("");
}; };
reader.onerror = () => { reader.onerror = () => {
ElMessage.error("图片处理失败"); ElMessage.error("图片处理失败");
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}); });
return false; // wangEditor customPaste return false;
}; };
// customPaste default-config wangEditor for Vue
// <Editor> @custom-paste + contentEditable
const editorConfig = { const editorConfig = {
placeholder: $t("placeholder.description"), placeholder: $t("placeholder.description"),
}; };
let pasteHandlerRef = null; let pasteHandlerRef = null;
const handleEditorCreated = (editor) => { const handleEditorCreated = (editor) => {
editorRef.value = editor; editorRef.value = editor;
// contentEditable capture customPaste
nextTick(() => { nextTick(() => {
const root = typeof editor.$el === "function" ? editor.$el() : editor.$el; const root = typeof editor.$el === "function" ? editor.$el() : editor.$el;
if (!root) return; if (!root) return;
const editable = const editable = root.querySelector?.('[contenteditable="true"]') || root;
root.querySelector?.('[contenteditable="true"]') || root;
if (pasteHandlerRef) { if (pasteHandlerRef) {
editable.removeEventListener("paste", pasteHandlerRef, { capture: true }); editable.removeEventListener("paste", pasteHandlerRef, { capture: true });
} }
@ -728,8 +760,9 @@ const submitQuick = async () => {
} }
} }
formData.append("registrarName", userStore.userInfo?.userName || ""); formData.append("registrarName", userStore.userInfo?.userName || "");
// el-upload file-list raw File
for (const f of files || []) { for (const f of files || []) {
formData.append("files", f.raw, f.name); if (f.raw) formData.append("files", f.raw, f.name);
} }
await createAdminOrder(formData); await createAdminOrder(formData);
ElMessage.success($t("msg.submitSuccess")); ElMessage.success($t("msg.submitSuccess"));
@ -888,14 +921,6 @@ onBeforeUnmount(() => {
transform: translate(-50%, -50%) rotate(-45deg); transform: translate(-50%, -50%) rotate(-45deg);
} }
//
.attachment-pick {
width: 100%;
}
.attachment-hidden-input {
display: none;
}
// //
.order-stats { .order-stats {
display: grid; display: grid;
@ -915,13 +940,32 @@ onBeforeUnmount(() => {
border-left: 4px solid #d9d9d9; border-left: 4px solid #d9d9d9;
transition: transition:
transform 0.2s ease, transform 0.2s ease,
box-shadow 0.2s ease; box-shadow 0.2s ease,
cursor: default; background 0.2s ease;
cursor: pointer;
user-select: none;
} }
.stat-card:hover { .stat-card:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
} }
.stat-card.active {
background: #f0f7ff;
box-shadow: 0 4px 14px rgba(24, 144, 255, 0.18);
transform: translateY(-2px);
}
.stat-card.active.stat-pending {
background: #fff7e6;
box-shadow: 0 4px 14px rgba(250, 140, 22, 0.18);
}
.stat-card.active.stat-completed {
background: #f6ffed;
box-shadow: 0 4px 14px rgba(82, 196, 26, 0.18);
}
.stat-card.active.stat-high {
background: #fff1f0;
box-shadow: 0 4px 14px rgba(255, 77, 79, 0.18);
}
.stat-total { .stat-total {
border-left-color: #1890ff; border-left-color: #1890ff;
} }

@ -1,14 +1,19 @@
<template> <template>
<div class="process" v-loading="loading"> <div class="process" v-loading="loading">
<el-page-header :icon="ArrowLeft" :content="$t('common.back')" @back="goBack" class="page-header"> <el-page-header
:icon="ArrowLeft"
:content="$t('common.back')"
@back="goBack"
class="page-header"
>
<template #content> <template #content>
<span class="page-title">{{ $t('btn.process') }}</span> <span class="page-title">{{ $t("btn.process") }}</span>
</template> </template>
</el-page-header> </el-page-header>
<el-card shadow="never" v-if="detail.id" class="form-card"> <el-card shadow="never" v-if="detail.id" class="form-card">
<template #header> <template #header>
<span class="card-title">{{ $t('btn.process') }}</span> <span class="card-title">{{ $t("btn.process") }}</span>
</template> </template>
<el-form <el-form
@ -61,8 +66,10 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :loading="submitting" @click="submit">{{ $t('btn.confirm') }}</el-button> <el-button type="primary" :loading="submitting" @click="submit">{{
<el-button @click="goBack">{{ $t('common.cancel') }}</el-button> $t("btn.confirm")
}}</el-button>
<el-button @click="goBack">{{ $t("common.cancel") }}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
@ -70,14 +77,25 @@
</template> </template>
<script setup> <script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, shallowRef } from "vue"; import {
computed,
nextTick,
onBeforeUnmount,
onMounted,
reactive,
ref,
shallowRef,
} from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { ArrowLeft } from "@element-plus/icons-vue"; import { ArrowLeft } from "@element-plus/icons-vue";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue"; import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import "@wangeditor/editor/dist/css/style.css"; import "@wangeditor/editor/dist/css/style.css";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { getAdminOrderDetail, processAdminOrder } from "@/service/modular/admin"; import {
getAdminOrderDetail,
processAdminOrder,
} from "@/service/modular/admin";
import { useUserStore } from "@/stores/api/user"; import { useUserStore } from "@/stores/api/user";
const { t: $t } = useI18n(); const { t: $t } = useI18n();
@ -92,7 +110,11 @@ const formRef = ref();
// //
const handlerName = computed( const handlerName = computed(
() => userStore.userInfo?.name || userStore.userInfo?.userName || userStore.userInfo?.account || '管理员', () =>
userStore.userInfo?.name ||
userStore.userInfo?.userName ||
userStore.userInfo?.account ||
"管理员",
); );
// "" // ""
@ -102,7 +124,9 @@ const form = reactive({
}); });
const rules = { const rules = {
status: [{ required: true, message: () => $t('table.status'), trigger: "change" }], status: [
{ required: true, message: () => $t("table.status"), trigger: "change" },
],
processRemark: [ processRemark: [
{ {
required: true, required: true,
@ -112,7 +136,8 @@ const rules = {
.replace(/<[^>]*>/g, "") .replace(/<[^>]*>/g, "")
.replace(/&nbsp;/gi, "") .replace(/&nbsp;/gi, "")
.trim(); .trim();
if (!text && !hasImage) return callback(new Error($t('placeholder.remark'))); if (!text && !hasImage)
return callback(new Error($t("placeholder.remark")));
callback(); callback();
}, },
trigger: "change", trigger: "change",
@ -133,7 +158,7 @@ const toolbarConfig = {
], ],
}; };
const editorConfig = { const editorConfig = {
placeholder: $t('placeholder.remark'), placeholder: $t("placeholder.remark"),
}; };
// base64 HTML // base64 HTML
@ -215,8 +240,7 @@ const handleEditorCreated = (editor) => {
nextTick(() => { nextTick(() => {
const root = typeof editor.$el === "function" ? editor.$el() : editor.$el; const root = typeof editor.$el === "function" ? editor.$el() : editor.$el;
if (!root) return; if (!root) return;
const editable = const editable = root.querySelector?.('[contenteditable="true"]') || root;
root.querySelector?.('[contenteditable="true"]') || root;
if (pasteHandlerRef) { if (pasteHandlerRef) {
editable.removeEventListener("paste", pasteHandlerRef, { capture: true }); editable.removeEventListener("paste", pasteHandlerRef, { capture: true });
} }
@ -229,6 +253,7 @@ const loadDetail = async () => {
loading.value = true; loading.value = true;
try { try {
detail.value = await getAdminOrderDetail(route.params.id); detail.value = await getAdminOrderDetail(route.params.id);
form.processRemark = detail.value.processRemark || "";
} finally { } finally {
loading.value = false; loading.value = false;
} }
@ -244,7 +269,7 @@ const submit = async () => {
status: form.status, status: form.status,
processRemark: form.processRemark, processRemark: form.processRemark,
}); });
ElMessage.success($t('msg.editSuccess')); ElMessage.success($t("msg.editSuccess"));
router.replace("/admin/orders"); router.replace("/admin/orders");
} finally { } finally {
submitting.value = false; submitting.value = false;

Loading…
Cancel
Save