設備管理點位新增 | 密碼欄位可視 | 能源管理英文版
This commit is contained in:
parent
d2a9027869
commit
68f0af6229
@ -13,3 +13,4 @@ export const GET_ASSET_FLOOR_LIST_API = `/AssetManage/GetFloorList`;
|
||||
export const POST_ASSET_FLOOR_API = `/AssetManage/SaveFloor`;
|
||||
|
||||
export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
||||
export const GET_ASSET_SUB_POINT_API = `/AssetManage/GetSubPoint`;
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
GET_ASSET_IOT_LIST_API,
|
||||
DELETE_ASSET_ITEM_API,
|
||||
POST_ASSET_SINGLE_API,
|
||||
GET_ASSET_SUB_POINT_API,
|
||||
} from "./api";
|
||||
import instance from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
@ -98,7 +99,8 @@ export const postAssetSingle = async (data) => {
|
||||
});
|
||||
} else {
|
||||
value.forEach((element, index) => {
|
||||
formData.append(`${key}[${index}].device_guid`, element);
|
||||
formData.append(`sub_device[${index}].device_number`, element.device_number);
|
||||
formData.append(`sub_device[${index}].points`, element.points);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -141,8 +143,22 @@ export const postAssetFloor = async (formData) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getAssetIOTList = async () => {
|
||||
const res = await instance.post(GET_ASSET_IOT_LIST_API);
|
||||
export const getAssetIOTList = async (sub_system_tag, points) => {
|
||||
const res = await instance.post(GET_ASSET_IOT_LIST_API, {
|
||||
sub_system_tag,
|
||||
points,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const getAssetSubPoint = async (sub_system_tag) => {
|
||||
const res = await instance.post(GET_ASSET_SUB_POINT_API, {
|
||||
sub_system_tag,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
@layer utilities {
|
||||
.btn{
|
||||
@apply px-6 py-1;
|
||||
@apply px-4 py-1;
|
||||
text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.9);
|
||||
box-shadow: 0px 0px 5px rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ import { defineProps, computed } from "vue";
|
||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
||||
import "@vuepic/vue-datepicker/dist/main.css";
|
||||
import { zhTW } from "date-fns/locale";
|
||||
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
items: Array,
|
||||
withLine: Boolean,
|
||||
@ -62,13 +62,12 @@ const curWidth = computed(() => {
|
||||
showPreview: false,
|
||||
}"
|
||||
v-model="item.value"
|
||||
locale="zh-TW"
|
||||
:day-names="['一', '二', '三', '四', '五', '六', '日']"
|
||||
locale='en-US'
|
||||
:format="item.dateFormat"
|
||||
:enable-time-picker="false"
|
||||
:time-picker="Boolean(item.timePicker)"
|
||||
:placeholder="item.placeholder"
|
||||
selectText="確定"
|
||||
:selectText="t('button.submit')"
|
||||
:input-class-name="
|
||||
twMerge('dp-custom-input', 'btn border', inputClass)
|
||||
"
|
||||
|
@ -52,13 +52,19 @@ const changePageData = (currentPage) => {
|
||||
watch(
|
||||
() => [props.dataSource, props.sort],
|
||||
([newVal, newVal2]) => {
|
||||
// console.log(props.dataSource, newVal);
|
||||
currentPage.value = 1;
|
||||
totalPage.value =
|
||||
const totalPageNumber =
|
||||
props.totalPages || Math.ceil(props.dataSource.length / props.pageSize);
|
||||
if (currentPage.value > totalPageNumber) {
|
||||
currentPage.value = 1;
|
||||
} else {
|
||||
// 若頁數在範圍內,則保持當前頁面
|
||||
currentPage.value = currentPage.value;
|
||||
}
|
||||
|
||||
totalPage.value = totalPageNumber;
|
||||
props.onPageChange
|
||||
? current_table_data.updateDataSource(props.dataSource)
|
||||
: changePageData(1);
|
||||
: changePageData(currentPage.value || 1);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
@ -99,7 +105,7 @@ const pageInput = computed(() => {
|
||||
:key="`page${page}`"
|
||||
:class="
|
||||
twMerge(
|
||||
'w-10 h-10 mx-1 border-2 border-sub-success rounded-full flex items-center justify-center',
|
||||
'w-10 h-10 mx-1 border-2 border-sub-success rounded-full flex items-center justify-center cursor-pointer',
|
||||
currentPage === page ? 'bg-sub-success' : 'bg-transparent'
|
||||
)
|
||||
"
|
||||
@ -145,7 +151,7 @@ const pageInput = computed(() => {
|
||||
<span
|
||||
class="w-full text-center absolute -bottom-8 left-1/2 -translate-x-1/2 text-base"
|
||||
>
|
||||
{{ totalItems || dataSource.length }} {{ $t("table.in_otal") }}</span
|
||||
{{ totalItems || dataSource.length }} {{ $t("table.in_otal") }}</span
|
||||
>
|
||||
</label>
|
||||
<ul
|
||||
|
@ -163,14 +163,33 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
::v-deep .ant-menu-submenu-title:active {
|
||||
background-color: #35759d !important;
|
||||
color: #35759d !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
::v-deep .ant-menu-item:not(.ant-menu-item-selected) {
|
||||
&::before {
|
||||
@apply absolute w-[15px] h-[15px] bottom-3.5 left-7 bg-no-repeat z-10 grayscale;
|
||||
content: "";
|
||||
background: url(@ASSET/img/chart-data-background03.svg) center center;
|
||||
}
|
||||
&:active {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .ant-menu-item-selected {
|
||||
background-color: #35759d !important;
|
||||
@apply bg-transparent relative;
|
||||
|
||||
&::before {
|
||||
@apply absolute w-[15px] h-[15px] bottom-3.5 left-7 bg-no-repeat z-10;
|
||||
content: "";
|
||||
background: url(@ASSET/img/chart-data-background03.svg) center center;
|
||||
}
|
||||
&:active {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
a {
|
||||
color: #fff !important;
|
||||
color: #89d2ff !important;
|
||||
text-shadow: 0px 0px 1px #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -204,6 +204,7 @@
|
||||
"operate_text": "显示名称",
|
||||
"fill_text": "请由系统人员填写",
|
||||
"equipment_point": "设备点位",
|
||||
"point": "点位",
|
||||
"add_sensor": "新增感测器",
|
||||
"associated_device": "关联设备",
|
||||
"choose": "选择",
|
||||
|
@ -204,6 +204,7 @@
|
||||
"operate_text": "顯示名稱",
|
||||
"fill_text": "請由系統人員填寫",
|
||||
"equipment_point": "設備點位",
|
||||
"point": "點位",
|
||||
"add_sensor": "新增感測器",
|
||||
"associated_device": "關聯設備",
|
||||
"choose": "選擇",
|
||||
|
@ -204,6 +204,7 @@
|
||||
"operate_text": "Display name",
|
||||
"fill_text": "Please fill it in by system personnel",
|
||||
"equipment_point": "Equipment point",
|
||||
"point": "Point",
|
||||
"add_sensor": "Add new sensor",
|
||||
"associated_device": "Associated devices",
|
||||
"choose": "Choose",
|
||||
|
@ -53,7 +53,9 @@ import {
|
||||
faFireExtinguisher,
|
||||
faDoorOpen,
|
||||
faCar,
|
||||
faWind
|
||||
faWind,
|
||||
faEye,
|
||||
faEyeSlash,
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
/* add icons to the library */
|
||||
@ -108,7 +110,9 @@ library.add(
|
||||
faFireExtinguisher,
|
||||
faDoorOpen,
|
||||
faCar,
|
||||
faWind
|
||||
faWind,
|
||||
faEye,
|
||||
faEyeSlash
|
||||
);
|
||||
|
||||
export default library;
|
||||
|
@ -71,7 +71,7 @@ const deleteItem = async (id) => {
|
||||
>
|
||||
<template #buttonContent="{ item }">
|
||||
<span class="text-base">{{ item.title }}</span>
|
||||
<template v-if="!item.is_IOT">
|
||||
<!-- <template v-if="!item.is_IOT">
|
||||
<span
|
||||
class="ml-2 text-base text-warning"
|
||||
@click.stop.prevent="() => edit(item)"
|
||||
@ -84,7 +84,7 @@ const deleteItem = async (id) => {
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'trash-alt']" />
|
||||
</span>
|
||||
</template>
|
||||
</template> -->
|
||||
</template>
|
||||
</ButtonConnectedGroup>
|
||||
</div>
|
||||
|
@ -140,7 +140,10 @@ const edit = async (id) => {
|
||||
ext: file.saveName.split(".")[file.saveName.split(".").length - 1],
|
||||
}));
|
||||
res.data.sub_device = res.data.sub_device?.map(
|
||||
({ device_guid }) => device_guid
|
||||
({ device_number, points }) => ({
|
||||
device_number,
|
||||
points,
|
||||
})
|
||||
);
|
||||
editRecord.value = res.data;
|
||||
openModal();
|
||||
|
@ -1,6 +1,10 @@
|
||||
<script setup>
|
||||
import { getAssetIOTList } from "@/apis/asset";
|
||||
import { ref, computed, inject, watch } from "vue";
|
||||
import { ref, computed, inject, watch, onMounted } from "vue";
|
||||
import ButtonGroup from "@/components/customUI/ButtonGroup.vue";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import { getAssetSubPoint } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { formState } = inject("asset_table_modal_form");
|
||||
@ -11,65 +15,98 @@ const tableColumns = computed(() => [
|
||||
},
|
||||
{
|
||||
title: "tag",
|
||||
key: "tag",
|
||||
key: "device_name",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.system_name"),
|
||||
key: "full_name",
|
||||
title: t("assetManagement.point"),
|
||||
key: "point_name",
|
||||
},
|
||||
// {
|
||||
// title: "狀態",
|
||||
// key: "status",
|
||||
// },
|
||||
{
|
||||
title: t("assetManagement.operation"),
|
||||
key: "operation",
|
||||
width: 100,
|
||||
},
|
||||
]);
|
||||
|
||||
// 選小類
|
||||
const store = useBuildingStore();
|
||||
const {
|
||||
items: sysTagItems,
|
||||
changeActiveBtn: changeSysActiveBtn,
|
||||
setItems: setSysItems,
|
||||
selectedBtn: selectedSysItems,
|
||||
} = useActiveBtn();
|
||||
const modalColumns = computed(() => [
|
||||
{
|
||||
title: t("assetManagement.choose"),
|
||||
key: "check",
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: "tag",
|
||||
key: "tag",
|
||||
key: "device_name",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.system_name"),
|
||||
key: "full_name",
|
||||
title: t("assetManagement.point"),
|
||||
key: "point_name",
|
||||
},
|
||||
]);
|
||||
|
||||
// 設定點位
|
||||
const {
|
||||
items: points,
|
||||
changeActiveBtn: changeActivePoint,
|
||||
setItems: setPoints,
|
||||
selectedBtn: selectedPoints,
|
||||
} = useActiveBtn();
|
||||
|
||||
const getPoint = async (sub_system_tag) => {
|
||||
const res = await getAssetSubPoint(sub_system_tag);
|
||||
setPoints(
|
||||
res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.points,
|
||||
key: d.points,
|
||||
active: false,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: 抓取IOT
|
||||
const iotData = ref([]);
|
||||
const iotCheckedList = ref([]); // 初始選中的項目
|
||||
const currentCheckedList = ref([]); // 當前勾選的項目
|
||||
|
||||
const getIOTData = async () => {
|
||||
const res = await getAssetIOTList();
|
||||
const getIOTData = async (sub_system_tag = null, points = null) => {
|
||||
const res = await getAssetIOTList(sub_system_tag, points);
|
||||
let data = res.data.map((d) => ({
|
||||
...d,
|
||||
title: d.full_name,
|
||||
key: d.device_guid,
|
||||
tag: d.device_number,
|
||||
checked: formState.value.sub_device?.includes(d.device_guid),
|
||||
key: d.device_number + d.points,
|
||||
checked: formState.value.sub_device?.some(
|
||||
({ device_number, points }) =>
|
||||
device_number + points === `${d.device_number}${d.points}`
|
||||
),
|
||||
}));
|
||||
iotData.value = data;
|
||||
iotCheckedList.value = data.filter(({ checked }) => checked);
|
||||
if (!sub_system_tag && !points) {
|
||||
iotCheckedList.value = data.filter(({ checked }) => checked);
|
||||
currentCheckedList.value = [...iotCheckedList.value];
|
||||
}
|
||||
};
|
||||
const iotCheckedList = ref([]);
|
||||
|
||||
const onChange = (value, checked) => {
|
||||
const d = iotData.value.find(({ device_guid }) => device_guid === value);
|
||||
const d = iotData.value.find(
|
||||
({ device_number, points }) => `${device_number}${points}` === value
|
||||
);
|
||||
let newList = [];
|
||||
if (checked) {
|
||||
newList = [...iotCheckedList.value, d];
|
||||
newList = [...currentCheckedList.value, d];
|
||||
} else {
|
||||
newList = iotCheckedList.value.filter(
|
||||
(checked) => checked.device_guid !== d.device_guid
|
||||
newList = currentCheckedList.value.filter(
|
||||
(item) => item.device_number + item.points !== value
|
||||
);
|
||||
}
|
||||
iotCheckedList.value = newList;
|
||||
currentCheckedList.value = newList;
|
||||
return newList;
|
||||
};
|
||||
|
||||
@ -78,8 +115,12 @@ const openModal = () => {
|
||||
};
|
||||
|
||||
const onOk = () => {
|
||||
formState.value.sub_device = iotCheckedList.value.map(
|
||||
({ device_guid }) => device_guid
|
||||
iotCheckedList.value = currentCheckedList.value;
|
||||
formState.value.sub_device = currentCheckedList.value.map(
|
||||
({ device_number, points }) => ({
|
||||
device_number,
|
||||
points,
|
||||
})
|
||||
);
|
||||
onCancel();
|
||||
};
|
||||
@ -88,23 +129,57 @@ const onCancel = () => {
|
||||
asset_add_IoT_item.close();
|
||||
};
|
||||
|
||||
watch(
|
||||
[selectedSysItems, selectedPoints],
|
||||
([newSys, newPoint]) => {
|
||||
if (newSys || newPoint) {
|
||||
getIOTData(newSys?.key, newPoint?.key);
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
watch(selectedSysItems, (newVal) => {
|
||||
if (newVal) {
|
||||
getPoint(newVal.key);
|
||||
}
|
||||
});
|
||||
watch(
|
||||
formState,
|
||||
() => {
|
||||
getIOTData();
|
||||
setSysItems(
|
||||
store.subSys.map(({ full_name, sub_system_tag }, index) => ({
|
||||
title: full_name,
|
||||
key: sub_system_tag,
|
||||
active: 0,
|
||||
}))
|
||||
);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
const deleteItem = (id) => {
|
||||
console.log(formState.value.sub_device, id);
|
||||
// formState.value.device_guid = formState.value.device_guid.filter(
|
||||
// (d) => d !== id
|
||||
// );
|
||||
const newList = onChange(id, false);
|
||||
formState.value.sub_device = newList.map(({ device_guid }) => device_guid);
|
||||
const deleteItem = (value) => {
|
||||
const itemToDelete = iotCheckedList.value.find(
|
||||
({ device_number, points }) => `${device_number}${points}` === value
|
||||
);
|
||||
|
||||
if (itemToDelete) {
|
||||
iotCheckedList.value = iotCheckedList.value.filter(
|
||||
(item) => item.device_number + item.points !== value
|
||||
);
|
||||
currentCheckedList.value = iotCheckedList.value;
|
||||
|
||||
formState.value.sub_device = iotCheckedList.value.map(
|
||||
({ device_number, points }) => ({
|
||||
device_number,
|
||||
points,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -131,15 +206,11 @@ const deleteItem = (id) => {
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template
|
||||
><template v-if="column.key === 'operation'">
|
||||
<!-- <span
|
||||
class="mr-5 text-base text-warning cursor-pointer"
|
||||
@click.stop.prevent="() => edit(record)"
|
||||
>
|
||||
<FontAwesomeIcon :icon="['fas', 'pencil-alt']"></FontAwesomeIcon>
|
||||
</span> -->
|
||||
<span
|
||||
class="text-base text-error cursor-pointer"
|
||||
@click.stop.prevent="() => deleteItem(record.device_guid)"
|
||||
@click.stop.prevent="
|
||||
() => deleteItem(record.device_number + record.points)
|
||||
"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', 'trash-alt']"
|
||||
@ -155,16 +226,39 @@ const deleteItem = (id) => {
|
||||
width="900"
|
||||
>
|
||||
<template #modalContent>
|
||||
<ButtonGroup
|
||||
:items="sysTagItems"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeSysActiveBtn(item);
|
||||
}
|
||||
"
|
||||
class="my-3"
|
||||
/>
|
||||
<ButtonGroup
|
||||
v-if="selectedSysItems"
|
||||
:items="points"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeActivePoint(item);
|
||||
}
|
||||
"
|
||||
class="my-3"
|
||||
/>
|
||||
<Table :columns="modalColumns" :dataSource="iotData" :withStyle="false">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'check'">
|
||||
<Checkbox
|
||||
name="device_guid"
|
||||
:value="record.device_guid"
|
||||
name="device_number"
|
||||
:value="record.device_number + record.points"
|
||||
:onChange="onChange"
|
||||
:checked="
|
||||
iotCheckedList?.some(
|
||||
({ device_guid }) => device_guid === record.device_guid
|
||||
currentCheckedList?.some(
|
||||
({ device_number, points }) =>
|
||||
`${device_number}${points}` ===
|
||||
`${record.device_number}${record.points}`
|
||||
)
|
||||
"
|
||||
></Checkbox>
|
||||
|
@ -13,6 +13,10 @@ const props = defineProps({
|
||||
reset: Function,
|
||||
getData: Function,
|
||||
});
|
||||
const showPassword = ref(false);
|
||||
const togglePasswordVisibility = () => {
|
||||
showPassword.value = !showPassword.value;
|
||||
};
|
||||
|
||||
let userSchema = ref(
|
||||
yup.object({
|
||||
@ -143,17 +147,24 @@ const onOk = async () => {
|
||||
<Input
|
||||
v-if="!Boolean(formState?.Id)"
|
||||
:value="formState"
|
||||
class="my-2"
|
||||
class="relative my-2"
|
||||
name="Password"
|
||||
type="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
>
|
||||
<template #topLeft>{{ $t("accountManagement.password") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Password }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
>
|
||||
<template #bottomRight>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', showPassword ? 'eye-slash' : 'eye']"
|
||||
class="fa-2x absolute top-14 right-0 transform -translate-x-1/2 cursor-pointer"
|
||||
@click="togglePasswordVisibility"
|
||||
/>
|
||||
</template>
|
||||
</Input>
|
||||
<Input :value="formState" class="my-2" name="Name">
|
||||
<template #topLeft>{{ $t("accountManagement.name") }}</template>
|
||||
<template #bottomLeft
|
||||
|
@ -15,6 +15,10 @@ const props = defineProps({
|
||||
const formState = ref({
|
||||
Password: "",
|
||||
});
|
||||
const showPassword = ref(false);
|
||||
const togglePasswordVisibility = () => {
|
||||
showPassword.value = !showPassword.value;
|
||||
};
|
||||
|
||||
let userSchema = yup.object({
|
||||
Password: yup.string().required(t("button.required")),
|
||||
@ -49,7 +53,12 @@ const onOk = async () => {
|
||||
<template #modalContent>
|
||||
<p class="mt-10 text-3xl">{{ account.Name }}</p>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="Password" type="password">
|
||||
<Input
|
||||
:value="formState"
|
||||
class="relative my-2"
|
||||
name="Password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
>
|
||||
<template #topLeft>{{
|
||||
$t("accountManagement.change_password")
|
||||
}}</template>
|
||||
@ -57,8 +66,15 @@ const onOk = async () => {
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.Password }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
>
|
||||
<template #bottomRight>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', showPassword ? 'eye-slash' : 'eye']"
|
||||
class="fa-2x absolute top-14 right-0 transform -translate-x-1/2 cursor-pointer"
|
||||
@click="togglePasswordVisibility"
|
||||
/>
|
||||
</template>
|
||||
</Input>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
|
@ -20,7 +20,7 @@ const yesterdayTodayData = {
|
||||
|
||||
// 本週與上週用電比較資料
|
||||
const weekComparisonData = {
|
||||
categories: ["週五", "週六", "週日", "週一", "週二", "週三", "週四"],
|
||||
categories: ["Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu"],
|
||||
values: [
|
||||
{
|
||||
name: "This week's electricity consumption",
|
||||
@ -131,6 +131,31 @@ const generateCylinderChartOption = (categories, values) => {
|
||||
},
|
||||
z: 12,
|
||||
},
|
||||
// 圓柱底部
|
||||
{
|
||||
name: null,
|
||||
type: "pictorialBar",
|
||||
symbolSize: [barWidth, 5],
|
||||
symbolOffset: [-9, 4],
|
||||
symbolPosition: "start",
|
||||
data: values[0].value,
|
||||
z: 12,
|
||||
itemStyle: {
|
||||
color: "#0ca9d4",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: null,
|
||||
type: "pictorialBar",
|
||||
symbolSize: [barWidth, 5],
|
||||
symbolOffset: [9, 4],
|
||||
symbolPosition: "start",
|
||||
data: values[1].value,
|
||||
itemStyle: {
|
||||
color: "#ffe000",
|
||||
},
|
||||
z: 12,
|
||||
},
|
||||
],
|
||||
grid: {
|
||||
left: "3%",
|
||||
@ -180,14 +205,14 @@ const weekComparisonOption = generateCylinderChartOption(
|
||||
(kWH)
|
||||
</h2>
|
||||
<div class="w-[48%]">
|
||||
<a href="#" class="text-con">
|
||||
<div class="text-con">
|
||||
<img src="@ASSET/img/chart-title01.svg" />
|
||||
<span>{{ $t("dashboard.today_electricity_consumption") }}</span>
|
||||
</a>
|
||||
<a href="#" class="text-con">
|
||||
</div>
|
||||
<div class="text-con">
|
||||
<img src="@ASSET/img/chart-title02.svg" />
|
||||
<span>{{ $t("dashboard.yesterday_electricity_consumption") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[250px]">
|
||||
@ -208,14 +233,14 @@ const weekComparisonOption = generateCylinderChartOption(
|
||||
(kWH)
|
||||
</h2>
|
||||
<div class="w-[48%]">
|
||||
<a href="#" class="text-con">
|
||||
<div class="text-con">
|
||||
<img src="@ASSET/img/chart-title01.svg" />
|
||||
<span>{{ $t("dashboard.thisweek_electricity_consumption") }}</span>
|
||||
</a>
|
||||
<a href="#" class="text-con">
|
||||
</div>
|
||||
<div class="text-con">
|
||||
<img src="@ASSET/img/chart-title02.svg" />
|
||||
<span>{{ $t("dashboard.lastweek_electricity_consumption") }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[250px]">
|
||||
|
@ -10,10 +10,10 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ["尖峰", "半尖峰", "離峰度數"],
|
||||
data: ["Peak", "Semi-Peak", "Off-Peak"],
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
},
|
||||
orient: "horizontal",
|
||||
bottom: "0%",
|
||||
@ -28,18 +28,18 @@ const defaultChartOption = ref({
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月",
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
@ -53,7 +53,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "尖峰",
|
||||
name: "Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -65,7 +65,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "半尖峰",
|
||||
name: "Semi-Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -76,7 +76,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "離峰度數",
|
||||
name: "Off-Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
|
@ -10,10 +10,10 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ["碳排當量", "減量目標"],
|
||||
data: ["Carbon Equivalent", "Reduction Target"],
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
},
|
||||
orient: "horizontal",
|
||||
bottom: "0%",
|
||||
@ -28,18 +28,18 @@ const defaultChartOption = ref({
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月",
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
@ -53,7 +53,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "碳排當量",
|
||||
name: "Carbon Equivalent",
|
||||
type: "bar",
|
||||
data: [
|
||||
5400, 6500, 7200, 7500, 9800, 9500, 11200, 11500, 11800, 7500, 6500,
|
||||
@ -64,7 +64,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "減量目標",
|
||||
name: "Reduction Target",
|
||||
type: "bar",
|
||||
data: [
|
||||
5000, 6000, 6000, 7000, 9500, 9000, 10000, 11000, 10000, 7200, 6000,
|
||||
|
@ -14,7 +14,7 @@ const data = {
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "即時趨勢",
|
||||
name: "Real-Time Trend", // 即時趨勢
|
||||
type: "line",
|
||||
data: [320, 310, 300, 305, 310, 300],
|
||||
smooth: true,
|
||||
@ -26,7 +26,7 @@ const data = {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "契約容量",
|
||||
name: "Contract Capacity", // 契約容量
|
||||
type: "line",
|
||||
data: [400, 400, 400, 400, 400, 400],
|
||||
smooth: true,
|
||||
@ -38,7 +38,7 @@ const data = {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "警戒容量",
|
||||
name: "Alert Capacity", // 警戒容量
|
||||
type: "line",
|
||||
data: [350, 350, 350, 350, 350, 350],
|
||||
smooth: true,
|
||||
@ -50,7 +50,7 @@ const data = {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "偵測值",
|
||||
name: "Measured Value", // 偵測值
|
||||
type: "line",
|
||||
data: [280, 300, 290, 295, 300, 290],
|
||||
smooth: true,
|
||||
|
@ -19,10 +19,10 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ["尖峰", "半尖峰", "離峰度數"],
|
||||
data: ["Peak", "Semi-Peak", "Off-Peak"],
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
},
|
||||
orient: "horizontal",
|
||||
bottom: "0%",
|
||||
@ -31,7 +31,7 @@ const defaultChartOption = ref({
|
||||
top: "5%",
|
||||
left: "0%",
|
||||
right: "0%",
|
||||
bottom: "10%",
|
||||
bottom: "15%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
@ -49,7 +49,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "尖峰",
|
||||
name: "Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -61,7 +61,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "半尖峰",
|
||||
name: "Semi-Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -73,7 +73,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "離峰度數",
|
||||
name: "Off-Peak",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
|
@ -10,10 +10,10 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: [ "流動電費","基本電費", "電費總額"],
|
||||
data: ["Var. Elec. Cost", "Fixed Elec. Cost", "Total Elec. Cost"],
|
||||
textStyle: {
|
||||
color: "#ffffff",
|
||||
fontSize: 16,
|
||||
fontSize: 14,
|
||||
},
|
||||
orient: "horizontal",
|
||||
bottom: "0%",
|
||||
@ -28,18 +28,18 @@ const defaultChartOption = ref({
|
||||
xAxis: {
|
||||
type: "category",
|
||||
data: [
|
||||
"1月",
|
||||
"2月",
|
||||
"3月",
|
||||
"4月",
|
||||
"5月",
|
||||
"6月",
|
||||
"7月",
|
||||
"8月",
|
||||
"9月",
|
||||
"10月",
|
||||
"11月",
|
||||
"12月",
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
],
|
||||
axisLabel: {
|
||||
color: "#ffffff",
|
||||
@ -53,7 +53,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "流動電費",
|
||||
name: "Var. Elec. Cost",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -65,7 +65,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "基本電費",
|
||||
name: "Fixed Elec. Cost",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -78,7 +78,7 @@ const defaultChartOption = ref({
|
||||
},
|
||||
|
||||
{
|
||||
name: "電費總額",
|
||||
name: "Total Elec. Cost",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
data: [
|
||||
@ -99,7 +99,9 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="bg-slate-800 p-3">
|
||||
<div class="text-white mb-3 text-base">{{$t("energy.monthly_elec_consumption")}}</div>
|
||||
<div class="text-white mb-3 text-base">
|
||||
{{ $t("energy.monthly_elec_consumption") }}
|
||||
</div>
|
||||
<div class="bar-box">
|
||||
<BarChart
|
||||
id="electricity_bill_chart"
|
||||
|
@ -22,6 +22,10 @@ const formState = ref({
|
||||
account: "",
|
||||
password: "",
|
||||
});
|
||||
const showPassword = ref(false);
|
||||
const togglePasswordVisibility = () => {
|
||||
showPassword.value = !showPassword.value;
|
||||
};
|
||||
|
||||
const imageSrc = import.meta.env.MODE === "production" ? "./logo.svg" : Image;
|
||||
|
||||
@ -63,17 +67,22 @@ const doLogin = async () => {
|
||||
</span>
|
||||
</div>
|
||||
<div class="w-full flex flex-col items-end my-2">
|
||||
<div class="w-full flex justify-between">
|
||||
<div class="w-full flex justify-between relative">
|
||||
<label class="mr-2 text-2xl text-black" for="password">{{
|
||||
$t("password")
|
||||
}}</label>
|
||||
<input
|
||||
name="password"
|
||||
class="w-5/6 border-2 rounded-full bg-white text-black px-4 py-1"
|
||||
type="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
v-model="formState.password"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<FontAwesomeIcon
|
||||
:icon="['fas', showPassword ? 'eye-slash' : 'eye']"
|
||||
class="text-gray-400 text-2xl absolute top-1/2 right-4 transform -translate-y-1/2 cursor-pointer"
|
||||
@click="togglePasswordVisibility"
|
||||
/>
|
||||
</div>
|
||||
<span class="text-error text-base">
|
||||
{{ formErrorMsg.password }}
|
||||
|
Loading…
Reference in New Issue
Block a user