刪除按鈕點選後新增"確認刪除" | 能源管理語言切換時即時更新 | 樓層新增與修改bug修正 | 系統監控filter暫時移除device_coordinate_3d條件

This commit is contained in:
koko 2024-11-06 17:27:03 +08:00
parent b6890e8311
commit 5cb8e7da6e
19 changed files with 199 additions and 73 deletions

View File

@ -18,15 +18,16 @@ const cancelToastOpen = () => {
}; };
}; };
const openToast = (status, content, to = "body") => { const openToast = (status, content, to = "body", confirm = null) => {
isToastOpen.value = { isToastOpen.value = {
open: true, open: true,
content, content,
status, status,
to, to,
confirm,
}; };
}; };
provide("app_toast", { openToast }); provide("app_toast", { openToast, cancelToastOpen });
</script> </script>
<template> <template>
@ -35,6 +36,7 @@ provide("app_toast", { openToast });
:open="isToastOpen.open" :open="isToastOpen.open"
:status="isToastOpen.status" :status="isToastOpen.status"
:cancel="cancelToastOpen" :cancel="cancelToastOpen"
:confirm="isToastOpen.confirm"
:to="isToastOpen.to" :to="isToastOpen.to"
/> />
<div v-if="store.user.token" class="min-h-screen"> <div v-if="store.user.token" class="min-h-screen">

View File

@ -1,12 +1,14 @@
<script setup> <script setup>
import { defineProps, ref, watch } from "vue"; import { defineProps, ref, watch } from "vue";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const props = defineProps({ const props = defineProps({
content: String, content: String,
status: String, status: String,
open: Boolean, open: Boolean,
cancel: Function, cancel: Function,
confirm: Function,
to: { to: {
default: "body", default: "body",
type: String, type: String,
@ -19,7 +21,7 @@ watch(
() => props.open, () => props.open,
(newVal) => { (newVal) => {
isOpen.value = newVal; isOpen.value = newVal;
if (newVal) { if (newVal && props.status !== "warning") {
setTimeout(() => { setTimeout(() => {
props.cancel && props.cancel(); props.cancel && props.cancel();
}, 3000); }, 3000);
@ -40,6 +42,8 @@ watch(
? 'alert-info' ? 'alert-info'
: status === 'error' : status === 'error'
? 'alert-error text-white' ? 'alert-error text-white'
: status === 'warning'
? 'alert-warning bg-yellow-400'
: 'alert-success' : 'alert-success'
) )
" "
@ -58,6 +62,10 @@ watch(
></path> ></path>
</svg> </svg>
<span>{{ content }}</span> <span>{{ content }}</span>
<div v-if="status === 'warning'">
<button @click="props.cancel && props.cancel();" className="btn btn-sm btn-outline me-2">{{$t("button.cancel")}}</button>
<button @click="props.confirm && props.confirm()" className="btn btn-sm">{{$t("button.confirm")}}</button>
</div>
</div> </div>
</template> </template>
</Teleport> </Teleport>

View File

@ -28,7 +28,7 @@ const src = import.meta.env.MODE === "production" ? "./logo.svg" : Logo;
<template> <template>
<header class="navbar bg-dark text-light-info w-full relative z-50"> <header class="navbar bg-dark text-light-info w-full relative z-50">
<div class="navbar-start"> <div class="navbar-start min-w-[480px] lg:min-w-[440px]">
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden" @click="toggleMenu"> <div tabindex="0" role="button" class="btn btn-ghost lg:hidden" @click="toggleMenu">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -269,6 +269,12 @@
"password_format": "密码长度至少8码必须包含英文及数字", "password_format": "密码长度至少8码必须包含英文及数字",
"start_time_placeholder": "请输入预计开始日期", "start_time_placeholder": "请输入预计开始日期",
"rename": "重新命名", "rename": "重新命名",
"download": "下载" "download": "下载",
"confirm": "确认"
},
"msg":{
"sure_to_delete": "是否确认删除该项目?",
"delete_success": "删除成功",
"delete_failed": "删除失败"
} }
} }

View File

@ -269,6 +269,12 @@
"password_format": "密碼長度至少8碼必須包含英文及數字", "password_format": "密碼長度至少8碼必須包含英文及數字",
"start_time_placeholder": "請輸入預計開始日期", "start_time_placeholder": "請輸入預計開始日期",
"rename": "重新命名", "rename": "重新命名",
"download": "下載" "download": "下載",
"confirm": "確認"
},
"msg":{
"sure_to_delete": "是否確認刪除該項目?",
"delete_success": "刪除成功",
"delete_failed": "刪除失敗"
} }
} }

View File

@ -269,6 +269,12 @@
"password_format": "The password must be at least 8 characters long and must contain English and numbers.", "password_format": "The password must be at least 8 characters long and must contain English and numbers.",
"start_time_placeholder": "Please enter expected start date", "start_time_placeholder": "Please enter expected start date",
"rename": "Rename", "rename": "Rename",
"download": "Download" "download": "Download",
"confirm": "Confirm"
},
"msg":{
"sure_to_delete": "Are you sure to delete this item?",
"delete_success": "Delete successfully",
"delete_failed": "Delete failed"
} }
} }

View File

@ -8,7 +8,7 @@ import { getAssetFloorList } from "@/apis/asset";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast } = inject("app_toast"); const { openToast, cancelToastOpen } = inject("app_toast");
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { searchParams, changeParams } = useSearchParam(); const { searchParams, changeParams } = useSearchParam();
@ -32,7 +32,7 @@ const getAssetData = async () => {
const res = await getAssetList(searchParams.value?.subSys_id); const res = await getAssetList(searchParams.value?.subSys_id);
if (res.isSuccess) { if (res.isSuccess) {
// device_coordinate // device_coordinate
res.data.forEach(d => { res.data.forEach((d) => {
const floorGuid = d.floor_guid; const floorGuid = d.floor_guid;
if (!totalCoordinates.value[floorGuid]) { if (!totalCoordinates.value[floorGuid]) {
totalCoordinates.value[floorGuid] = []; totalCoordinates.value[floorGuid] = [];
@ -164,10 +164,16 @@ const edit = async (id) => {
}; };
const remove = async (id) => { const remove = async (id) => {
const res = await deleteAssetItem(id); openToast("warning", t("msg.sure_to_delete"), "body", async () => {
if (res.isSuccess) { await cancelToastOpen();
getAssetData(); const res = await deleteAssetItem(id);
} if (res.isSuccess) {
getAssetData();
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
}; };
provide("asset_table_data", { provide("asset_table_data", {

View File

@ -13,6 +13,7 @@ import { twMerge } from "tailwind-merge";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { totalCoordinates } = inject("asset_table_data"); const { totalCoordinates } = inject("asset_table_data");
const { updateRightFields, formErrorMsg, formState } = inject( const { updateRightFields, formErrorMsg, formState } = inject(
@ -165,9 +166,9 @@ const {
const onOk = async () => { const onOk = async () => {
const value = handleSubmit(floorScheme, FloorFormState.value); const value = handleSubmit(floorScheme, FloorFormState.value);
const formData = new FormData(form.value); const formData = new FormData(form.value);
formData.append("floor_guid", currentFloor.value.floor_guid); formData.append("floor_guid", selectedOption.value === "add" ? null :currentFloor.value.floor_guid);
formData.append("building_tag", store.selectedBuilding.building_tag); formData.append("building_tag", store.selectedBuilding.building_tag);
formData.append("initMapName", FloorFormState.value.floorFile[0].name); formData.append("initMapName", FloorFormState.value.floorFile[0]?.name);
formData.append("mapFile", FloorFormState.value.floorFile[0]); formData.append("mapFile", FloorFormState.value.floorFile[0]);
formData.delete("floorFile"); formData.delete("floorFile");
for (let [key, value] of formData) { for (let [key, value] of formData) {
@ -181,13 +182,18 @@ const onOk = async () => {
} }
}; };
const onDelete = async () => { const onDelete = async () => {
console.log("guild", formState.value.floor_guid); openToast("warning", t("msg.sure_to_delete"), "#asset_add_table_item", async () => {
const res = await deleteAssetFloor({ await cancelToastOpen();
floor_guid: formState.value.floor_guid, const res = await deleteAssetFloor({
floor_guid: formState.value.floor_guid,
});
if (res.isSuccess) {
getFloors();
openToast("success", t("msg.delete_success"), "#asset_add_table_item");
} else {
openToast("error", res.msg, "#asset_add_table_item");
}
}); });
if (res.isSuccess) {
getFloors();
}
}; };
const onCancel = () => { const onCancel = () => {

View File

@ -10,7 +10,7 @@ import { onMounted, ref, inject, computed } from "vue";
import AccountPasswordModal from "./AccountPasswordModal.vue"; import AccountPasswordModal from "./AccountPasswordModal.vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast } = inject("app_toast"); const { openToast, cancelToastOpen } = inject("app_toast");
const columns = computed(() => [ const columns = computed(() => [
{ {
@ -123,13 +123,16 @@ const resetModalForm = () => {
}; };
const removeAccount = async (id) => { const removeAccount = async (id) => {
const res = await delAccount(id); openToast("warning", t("msg.sure_to_delete"), "body", async () => {
await cancelToastOpen();
if (res.isSuccess) { const res = await delAccount(id);
if (res.isSuccess) {
getDataSource(); getDataSource();
} else { openToast("success", t("msg.delete_success"));
openToast("error", res.msg); } else {
} openToast("error", res.msg);
}
});
}; };
</script> </script>

View File

@ -3,9 +3,10 @@ import Table from "@/components/customUI/Table.vue";
import Input from "@/components/customUI/Input.vue"; import Input from "@/components/customUI/Input.vue";
import { getAccountRoleList, delRole } from "@/apis/account"; import { getAccountRoleList, delRole } from "@/apis/account";
import RoleAuthModal from "./RoleAuthModal.vue"; import RoleAuthModal from "./RoleAuthModal.vue";
import { ref, onMounted, computed } from "vue"; import { ref, onMounted, computed, inject } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const columns = computed(() => [ const columns = computed(() => [
{ {
title: t("accountManagement.index"), title: t("accountManagement.index"),
@ -60,11 +61,18 @@ const add = () => {
}; };
const remove = async (Id) => { const remove = async (Id) => {
const res = await delRole(Id); openToast("warning", t("msg.sure_to_delete"), "body", async () => {
// getRole(); await cancelToastOpen();
dataSource.value = dataSource.value.filter( const res = await delRole(Id);
({ role_guid }) => role_guid !== Id if (res.isSuccess) {
); dataSource.value = dataSource.value.filter(
({ role_guid }) => role_guid !== Id
);
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
}; };
const cancelModal = () => { const cancelModal = () => {

View File

@ -4,7 +4,7 @@ import { getAlarmMemberList, deleteAlarmMember } from "@/apis/alert";
import AlertNotifyTableAddModal from "./AlertNotifyTableAddModal.vue"; import AlertNotifyTableAddModal from "./AlertNotifyTableAddModal.vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast } = inject("app_toast"); const { openToast, cancelToastOpen } = inject("app_toast");
const tableData = ref([]); const tableData = ref([]);
const editRecord = ref(null); const editRecord = ref(null);
const { noticeList } = inject("notify_table"); const { noticeList } = inject("notify_table");
@ -75,12 +75,16 @@ const onCancel = () => {
}; };
const remove = async (id) => { const remove = async (id) => {
const res = await deleteAlarmMember(id); openToast("warning", t("msg.sure_to_delete"), "body", async () => {
if (res.isSuccess) { await cancelToastOpen();
fetchTableData(); const res = await deleteAlarmMember(id);
} else { if (res.isSuccess) {
openToast("error", res.msg); fetchTableData();
} openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
}; };
</script> </script>

View File

@ -27,7 +27,7 @@ const mockData = [
<div <div
v-for="(item, index) in mockData" v-for="(item, index) in mockData"
:key="index" :key="index"
class="xl:w-1/4 lg:w-1/2 w-full item-data-box relative px-3" class="xl:w-1/4 md:w-1/2 w-full item-data-box relative px-3"
> >
<div :class="twMerge('item-data', index % 2 === 0 ? 'blue' : 'green')"> <div :class="twMerge('item-data', index % 2 === 0 ? 'blue' : 'green')">
<h2>{{ item.value }}</h2> <h2>{{ item.value }}</h2>

View File

@ -117,7 +117,7 @@ const orderData = ref({
} }
.state-box .title { .state-box .title {
@apply relative flex items-center mb-6; @apply relative flex items-center mb-4;
} }
.state-box .title .state-title01 { .state-box .title .state-title01 {

View File

@ -14,7 +14,7 @@ const defaultChartOption = ref({
}, },
}, },
legend: { legend: {
data: [ t("energy.peak"), t("energy.semi_peak"), t("energy.off_peak")], data: [],
textStyle: { textStyle: {
color: "#ffffff", color: "#ffffff",
fontSize: 14, fontSize: 14,
@ -44,7 +44,7 @@ const defaultChartOption = ref({
}, },
series: [ series: [
{ {
name: t("energy.peak"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -53,7 +53,7 @@ const defaultChartOption = ref({
}, },
}, },
{ {
name: t("energy.semi_peak"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -62,7 +62,7 @@ const defaultChartOption = ref({
}, },
}, },
{ {
name: t("energy.off_peak"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -73,12 +73,23 @@ const defaultChartOption = ref({
], ],
}); });
const updateChartNames = () => {
defaultChartOption.value.legend.data = [
t("energy.peak"),
t("energy.semi_peak"),
t("energy.off_peak"),
];
defaultChartOption.value.series[0].name = t("energy.peak");
defaultChartOption.value.series[1].name = t("energy.semi_peak");
defaultChartOption.value.series[2].name = t("energy.off_peak");
};
// taipower_data // taipower_data
watch( watch(
taipower_data, taipower_data,
() => { () => {
// //
const months = taipower_data.value.map(item => item.month); const months = taipower_data.value.map((item) => item.month);
const kWhPeak = taipower_data.value.map((item) => item.kWhPeak); const kWhPeak = taipower_data.value.map((item) => item.kWhPeak);
const kWhHalfPeak = taipower_data.value.map((item) => item.kWhHalfPeak); const kWhHalfPeak = taipower_data.value.map((item) => item.kWhHalfPeak);
const kWhOffPeak = taipower_data.value.map((item) => item.kWhOffPeak); const kWhOffPeak = taipower_data.value.map((item) => item.kWhOffPeak);
@ -91,6 +102,15 @@ watch(
}, },
{ deep: true } { deep: true }
); );
// locale
watch(locale, () => {
updateChartNames();
});
onMounted(() => {
updateChartNames();
});
</script> </script>
<template> <template>

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import BarChart from "@/components/chart/BarChart.vue"; import BarChart from "@/components/chart/BarChart.vue";
import { ref, inject, watch } from "vue"; import { ref, onMounted, inject, watch } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t, locale } = useI18n(); const { t, locale } = useI18n();
@ -14,7 +14,7 @@ const defaultChartOption = ref({
}, },
}, },
legend: { legend: {
data: [t("energy.carbon_equivalent")], data: [],
textStyle: { textStyle: {
color: "#ffffff", color: "#ffffff",
fontSize: 14, fontSize: 14,
@ -44,7 +44,7 @@ const defaultChartOption = ref({
}, },
series: [ series: [
{ {
name: t("energy.carbon_equivalent"), name: "",
type: "bar", type: "bar",
data: [], data: [],
itemStyle: { itemStyle: {
@ -54,6 +54,13 @@ const defaultChartOption = ref({
], ],
}); });
const updateChartNames = () => {
defaultChartOption.value.legend.data = [
t("energy.carbon_equivalent"),
];
defaultChartOption.value.series[0].name = t("energy.carbon_equivalent");
};
// taipower_data // taipower_data
watch( watch(
taipower_data, taipower_data,
@ -68,6 +75,15 @@ watch(
}, },
{ deep: true } { deep: true }
); );
// locale
watch(locale, () => {
updateChartNames();
});
onMounted(() => {
updateChartNames();
});
</script> </script>
<template> <template>

View File

@ -15,11 +15,7 @@ const defaultChartOption = ref({
}, },
}, },
legend: { legend: {
data: [ data: [],
t("energy.fixed_elec_cost"),
t("energy.var_elec_cost"),
t("energy.total_elec_cost"),
],
textStyle: { textStyle: {
color: "#ffffff", color: "#ffffff",
fontSize: 14, fontSize: 14,
@ -49,7 +45,7 @@ const defaultChartOption = ref({
}, },
series: [ series: [
{ {
name: t("energy.fixed_elec_cost"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -58,7 +54,7 @@ const defaultChartOption = ref({
}, },
}, },
{ {
name: t("energy.var_elec_cost"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -67,7 +63,7 @@ const defaultChartOption = ref({
}, },
}, },
{ {
name: t("energy.total_elec_cost"), name: "",
type: "bar", type: "bar",
stack: "total", stack: "total",
data: [], data: [],
@ -78,12 +74,23 @@ const defaultChartOption = ref({
], ],
}); });
const updateChartNames = () => {
defaultChartOption.value.legend.data = [
t("energy.fixed_elec_cost"),
t("energy.var_elec_cost"),
t("energy.total_elec_cost"),
];
defaultChartOption.value.series[0].name = t("energy.fixed_elec_cost");
defaultChartOption.value.series[1].name = t("energy.var_elec_cost");
defaultChartOption.value.series[2].name = t("energy.total_elec_cost");
};
// taipower_data // taipower_data
watch( watch(
taipower_data, taipower_data,
() => { () => {
// //
const months = taipower_data.value.map(item => item.month); const months = taipower_data.value.map((item) => item.month);
const costTotal = taipower_data.value.map((item) => item.costTotal); const costTotal = taipower_data.value.map((item) => item.costTotal);
const costBase = taipower_data.value.map((item) => item.costBase); const costBase = taipower_data.value.map((item) => item.costBase);
const costChange = taipower_data.value.map((item) => item.costChange); const costChange = taipower_data.value.map((item) => item.costChange);
@ -96,6 +103,15 @@ watch(
}, },
{ deep: true } { deep: true }
); );
// locale
watch(locale, () => {
updateChartNames();
});
onMounted(() => {
updateChartNames();
});
</script> </script>
<template> <template>

View File

@ -6,6 +6,7 @@ import GraphModal from "./GraphModal.vue";
import { downloadExcelByHref } from "@/util/downloadExcel"; import { downloadExcelByHref } from "@/util/downloadExcel";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const columns = computed(() => [ const columns = computed(() => [
@ -43,8 +44,16 @@ const updateEditRecord = (data) => {
}; };
const delRecord = async (id) => { const delRecord = async (id) => {
const res = await delGraphData(id); openToast("warning", t("msg.sure_to_delete"), "body", async () => {
getData(parseInt(searchParams.value.id)); await cancelToastOpen();
const res = await delGraphData(id);
if (res.isSuccess) {
getData(parseInt(searchParams.value.id));
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
}; };
watch( watch(

View File

@ -8,6 +8,7 @@ import {
import useSearchParam from "@/hooks/useSearchParam"; import useSearchParam from "@/hooks/useSearchParam";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
const { t } = useI18n(); const { t } = useI18n();
const { openToast, cancelToastOpen } = inject("app_toast");
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL; const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
const { searchParams } = useSearchParam(); const { searchParams } = useSearchParam();
@ -25,15 +26,21 @@ const changeData = (record) => {
}; };
const deleteItem = async (id) => { const deleteItem = async (id) => {
let res; openToast("warning", t("msg.sure_to_delete"), "body", async () => {
if (searchParams.value?.work_type < 3) { await cancelToastOpen();
res = await deleteOperationRecord(id); let res;
} else { if (searchParams.value?.work_type < 3) {
res = await deleteOperationCompany(id); res = await deleteOperationRecord(id);
} } else {
if (res.isSuccess) { res = await deleteOperationCompany(id);
search(); }
} if (res.isSuccess) {
search();
openToast("success", t("msg.delete_success"));
} else {
openToast("error", res.msg);
}
});
}; };
</script> </script>

View File

@ -56,7 +56,9 @@ const getData = async () => {
building_tag: buildingStore.selectedBuilding?.building_tag, building_tag: buildingStore.selectedBuilding?.building_tag,
}) })
const devices = res.data.map(d => ({ const devices = res.data.map(d => ({
...d, key: d.full_name, device_list: d.device_list.filter(({ device_coordinate_3d }) => device_coordinate_3d).map((dev, index) => ({ ...d,
key: d.full_name,
device_list: d.device_list.map((dev, index) => ({
...dev, ...dev,
forge_dbid: parseInt(dev.forge_dbid), forge_dbid: parseInt(dev.forge_dbid),
room_dbid: parseInt(dev.room_dbid), room_dbid: parseInt(dev.room_dbid),
@ -72,6 +74,7 @@ const getData = async () => {
raw_data.value = devices raw_data.value = devices
data.value = devices data.value = devices
console.log("devices", res.data,devices)
} }
const subscribeData = ref([]); // flat data const subscribeData = ref([]); // flat data