ils_front/src/views/alert/components/AlertQuery/AlertTableModal.vue

403 lines
11 KiB
Vue

<script setup>
import {
ref,
defineProps,
watch,
inject,
nextTick,
onMounted,
toRaw,
} from "vue";
import dayjs from "dayjs";
import { postOperationRecord } from "@/apis/alert";
import * as yup from "yup";
import "yup-phone-lite";
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const props = defineProps({
editRecord: { type: Object, default: null },
});
const form = ref(null);
const dateItem = ref([
{
key: "start_time",
name: "start_time",
value: dayjs(),
dateFormat: "yyyy-MM-dd",
placeholder: t("alert.start_time"),
},
]);
const formState = ref({
formId: null,
uuid: null,
work_type: 2,
fix_do: "",
fix_do_code: "",
fix_firm: "",
status: 0,
device_number: "",
work_person_id: "",
start_time: dayjs().format("YYYY-MM-DD"),
notice: "",
description: "",
lorf: [],
});
const { model_data, updateEditRecord, search } = inject("alert_modal") || {
model_data: [],
updateEditRecord: null,
search: null,
};
let alertSchema = yup.object({
start_time: yup.date().required(t("button.required")),
fix_do: yup.string().required(t("button.required")),
fix_firm: yup.string().required(t("button.required")),
status: yup.number().required(t("button.required")),
work_person_id: yup.string().required(t("button.required")),
notice: yup.string().nullable(true),
description: yup.string().nullable(true),
});
const { formErrorMsg, handleSubmit, handleErrorReset } =
useFormErrorMessage(alertSchema);
const updateFileList = (files) => {
formState.value.lorf = files;
};
// ---------------------- 告警影片儲存位置:顯示 API 的 video_url ----------------------
const videoLocation = ref({ videoLocation: "" });
const showTooltip = ref(false);
async function copyToClipboard() {
const text = videoLocation.value.videoLocation || "";
try {
await navigator.clipboard.writeText(text);
// 觸發 tooltip 動畫
showTooltip.value = false;
await nextTick();
showTooltip.value = true;
setTimeout(() => {
showTooltip.value = false;
}, 1500);
} catch (err) {
console.error("複製失敗:", err);
}
}
const onOk = async () => {
const formData = new FormData(form.value);
formData.delete("oriFile");
(formState.value?.lorf ?? []).forEach((file, index) => {
formData.append(`lorf[${index}].id`, file?.id ? file.id : "");
// 只有新檔案才上傳檔案本體
if (!file?.id && file) {
formData.append(`lorf[${index}].file`, file);
}
formData.append(
`lorf[${index}].save_file_name`,
file?.id ? file.save_file_name : ""
);
formData.append(`lorf[${index}].ori_file_name`, file?.name ?? "");
});
formData.append(
"start_time",
dayjs(dateItem.value[0].value).format("YYYY-MM-DD")
);
if (props.editRecord.id) formData.append("id", props.editRecord.id);
if (props.editRecord.uuid)
formData.append("error_code", props.editRecord.uuid);
formData.append("work_type", 2);
formData.append(
"fix_do_code",
props.editRecord.main_id
? props.editRecord.main_id
: props.editRecord.fix_do_code
);
try {
await handleSubmit(alertSchema, formState.value);
const res = await postOperationRecord(formData);
if (res.isSuccess) {
search?.();
onCancel();
}
} catch (err) {
console.error("Form submission failed:", err);
}
};
const onCancel = () => {
formState.value = {
formId: null,
uuid: null,
work_type: 2,
fix_do: "",
fix_do_code: "",
fix_firm: "",
status: 0,
device_number: "",
work_person_id: "",
start_time: dayjs().format("YYYY-MM-DD"),
notice: "",
description: "",
lorf: [],
};
// 重置顯示用的影片路徑
videoLocation.value.videoLocation = "";
handleErrorReset();
updateEditRecord?.(null);
alert_action_item.close();
};
// 同步 props.editRecord -> formState / 日期 / 維修項目 / 影片網址
watch(
() => props.editRecord,
(newVal) => {
if (newVal) {
for (let [key, value] of Object.entries(newVal)) {
if (key in formState.value) {
formState.value[key] = value;
}
if (key === "start_time") {
const d = value ? dayjs(value) : dayjs();
formState.value.start_time = d.format("YYYY-MM-DD");
dateItem.value[0].value = d; // 一律用 dayjs 物件
}
if (key === "full_name") {
formState.value.fix_do = value ?? "";
}
}
// 取 API 回傳的影片位址(與 device_number 同來源物件)
videoLocation.value.videoLocation =
newVal?.video_url ?? newVal?.videoUrl ?? newVal?.video_path ?? "";
}
},
{ immediate: true }
);
</script>
<template>
<Modal
id="alert_action_item"
:title="t('alert.repair_order')"
:onCancel="onCancel"
:width="710"
>
<template #modalContent>
<form
ref="form"
class="mt-5 w-full flex flex-wrap justify-between"
@submit.prevent
>
<Input
v-if="formState && formState.formId"
class="my-2"
:value="formState"
name="formId"
readonly
>
<template #topLeft>{{ $t("alert.form_number") }}</template>
</Input>
<Input :value="formState" class="my-2" name="uuid" readonly>
<template #topLeft>{{ $t("alert.uuid") }}</template>
</Input>
<DateGroup
class="my-2"
:items="dateItem"
inputClass="w-full shadow-none"
:required="true"
>
<template #topLeft>{{ $t("alert.start_time") }}</template>
<template #bottomLeft>
<span class="text-error text-base">
{{ formErrorMsg.start_time }}
</span>
</template>
</DateGroup>
<Select
:value="formState"
class="my-2"
selectClass="border-info focus-within:border-info"
name="work_type"
Attribute="title"
:options="[
{
key: 1,
value: 1,
title: $t('alert.maintenance'),
},
{
key: 2,
value: 2,
title: $t('alert.repair'),
},
]"
:required="true"
:disabled="true"
>
<template #topLeft>{{ $t("alert.item") }}</template>
</Select>
<Input class="my-2" :value="formState" name="fix_do" :required="true">
<template #topLeft>{{ $t("alert.repair_item") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.fix_do }}
</span>
</template>
</Input>
<Input
class="my-2"
:value="formState"
name="device_number"
:required="true"
:disabled="true"
>
<template #topLeft>{{ $t("alert.repair_item_code") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.fix_do_code }}
</span></template
>
</Input>
<Select
:value="formState"
class="my-2"
selectClass="border-info focus-within:border-info"
name="fix_firm"
Attribute="name"
:options="model_data.model_companyList"
:required="true"
>
<template #topLeft>{{ $t("alert.responsible_vendor") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.fix_firm }}
</span></template
>
</Select>
<RadioGroup
class="my-2"
name="status"
:value="formState"
:items="[
{
key: 0,
value: 0,
title: $t('alert.not_completed'),
},
{
key: 1,
value: 1,
title: $t('alert.completed'),
},
]"
:required="true"
>
<template #topLeft>{{ $t("alert.status") }}</template>
</RadioGroup>
<Select
:value="formState"
class="my-2"
selectClass="border-info focus-within:border-info"
name="work_person_id"
Attribute="full_name"
:options="model_data.model_userList"
:required="true"
>
<template #topLeft>{{ $t("alert.worker_id") }}</template>
<template #bottomLeft
><span class="text-error text-base">
{{ formErrorMsg.work_person_id }}
</span></template
>
</Select>
<!-- 注意事項 -->
<Textarea :value="formState" name="notice" class="w-full my-2">
<template #topLeft>{{ $t("alert.notice") }}</template>
</Textarea>
<!-- 告警影片儲存位置-->
<div class="my-4 w-full">
<label class="text-lg">
{{ $t("alert.video_storage_location") }}
</label>
<div class="flex items-center gap-3">
<Input
class="flex-1"
name="videoLocation"
:value="videoLocation"
readonly
/>
<div class="relative inline-flex items-center">
<button
type="button"
class="btn btn-success"
@click.stop="copyToClipboard"
>
{{ $t("alert.copy") }}
</button>
<transition name="fade">
<span
v-if="showTooltip"
class="absolute left-full ml-4 top-1/2 -translate-y-1/2 text-white text-xs px-2 py-1 bg-gray-800 rounded shadow whitespace-nowrap"
role="status"
aria-live="polite"
>
{{ $t("alert.copied") }}
</span>
</transition>
</div>
</div>
</div>
<!-- 結果描述 -->
<Textarea :value="formState" name="description" class="w-full my-2">
<template #topLeft>{{ $t("alert.result_description") }}</template>
</Textarea>
<Upload
class="my-2"
name="oriFile"
:fileList="formState?.lorf"
:getFileList="updateFileList"
:multiple="true"
:baseUrl="`${FILE_BASEURL}/upload/operation`"
>
<template #topLeft>{{ $t("alert.upload_file") }}</template>
</Upload>
</form>
</template>
<template #modalAction>
<button
type="reset"
class="btn btn-outline-success mr-2"
@click.prevent="onCancel"
>
{{ $t("button.cancel") }}
</button>
<button
type="submit"
class="btn btn-outline-success"
@click.stop.prevent="onOk"
>
{{ $t("button.submit") }}
</button>
</template>
</Modal>
</template>
<style lang="scss" scoped></style>