Merge branch 'feature/assetMgmt'
This commit is contained in:
commit
3c876607ef
@ -11,6 +11,7 @@ export const DELETE_ASSET_ITEM_API = `/AssetManage/DeleteAsset`;
|
|||||||
export const GET_ASSET_FLOOR_LIST_API = `/AssetManage/GetFloorList`;
|
export const GET_ASSET_FLOOR_LIST_API = `/AssetManage/GetFloorList`;
|
||||||
|
|
||||||
export const POST_ASSET_FLOOR_API = `/AssetManage/SaveFloor`;
|
export const POST_ASSET_FLOOR_API = `/AssetManage/SaveFloor`;
|
||||||
|
export const DELETE_ASSET_FLOOR_API = `/AssetManage/DeleteFloor`;
|
||||||
|
|
||||||
export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
export const GET_ASSET_IOT_LIST_API = `/AssetManage/GetIOTList`;
|
||||||
export const GET_ASSET_SUB_POINT_API = `/AssetManage/GetSubPoint`;
|
export const GET_ASSET_SUB_POINT_API = `/AssetManage/GetSubPoint`;
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
GET_ASSET_SINGLE_API,
|
GET_ASSET_SINGLE_API,
|
||||||
GET_ASSET_FLOOR_LIST_API,
|
GET_ASSET_FLOOR_LIST_API,
|
||||||
POST_ASSET_FLOOR_API,
|
POST_ASSET_FLOOR_API,
|
||||||
|
DELETE_ASSET_FLOOR_API,
|
||||||
GET_ASSET_IOT_LIST_API,
|
GET_ASSET_IOT_LIST_API,
|
||||||
DELETE_ASSET_ITEM_API,
|
DELETE_ASSET_ITEM_API,
|
||||||
POST_ASSET_SINGLE_API,
|
POST_ASSET_SINGLE_API,
|
||||||
@ -143,6 +144,15 @@ export const postAssetFloor = async (formData) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteAssetFloor = async (formData) => {
|
||||||
|
const res = await instance.post(DELETE_ASSET_FLOOR_API, formData);
|
||||||
|
|
||||||
|
return apihandler(res.code, res.data, {
|
||||||
|
msg: res.msg,
|
||||||
|
code: res.code,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const getAssetIOTList = async (sub_system_tag, points) => {
|
export const getAssetIOTList = async (sub_system_tag, points) => {
|
||||||
const res = await instance.post(GET_ASSET_IOT_LIST_API, {
|
const res = await instance.post(GET_ASSET_IOT_LIST_API, {
|
||||||
sub_system_tag,
|
sub_system_tag,
|
||||||
|
@ -10,7 +10,7 @@ const props = defineProps({
|
|||||||
svg: Object,
|
svg: Object,
|
||||||
getCoordinate: {
|
getCoordinate: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: null
|
default: null,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ async function updateSvg(svg, option) {
|
|||||||
if (!chart.value && dom.value && svg) {
|
if (!chart.value && dom.value && svg) {
|
||||||
init();
|
init();
|
||||||
} else {
|
} else {
|
||||||
clear()
|
clear();
|
||||||
}
|
}
|
||||||
axios.get(svg.path).then(({ data }) => {
|
axios.get(svg.path).then(({ data }) => {
|
||||||
echarts.registerMap(svg.full_name, { svg: data });
|
echarts.registerMap(svg.full_name, { svg: data });
|
||||||
@ -30,24 +30,33 @@ async function updateSvg(svg, option) {
|
|||||||
if (props.getCoordinate) {
|
if (props.getCoordinate) {
|
||||||
chart.value.getZr().on("click", function (params) {
|
chart.value.getZr().on("click", function (params) {
|
||||||
var pixelPoint = [params.offsetX, params.offsetY];
|
var pixelPoint = [params.offsetX, params.offsetY];
|
||||||
var dataPoint = chart.value.convertFromPixel({ geoIndex: 0 }, pixelPoint);
|
var dataPoint = chart.value.convertFromPixel(
|
||||||
|
{ geoIndex: 0 },
|
||||||
|
pixelPoint
|
||||||
|
);
|
||||||
currentClickPosition.value = dataPoint;
|
currentClickPosition.value = dataPoint;
|
||||||
props.getCoordinate(dataPoint);
|
props.getCoordinate(dataPoint);
|
||||||
|
const updatedData = option.series.data
|
||||||
|
.filter(
|
||||||
|
(point) => !(point.itemStyle && point.itemStyle.color === "#0000FF")
|
||||||
|
)
|
||||||
|
.concat({
|
||||||
|
value: dataPoint, // 當前座標值
|
||||||
|
itemStyle: { color: "#0000FF" }, // 設為藍色
|
||||||
|
});
|
||||||
chart.value.setOption({
|
chart.value.setOption({
|
||||||
series: {
|
series: {
|
||||||
data: [dataPoint],
|
data: updatedData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
console.log("updateSvg", svg.path);
|
console.log("updateSvg", svg.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
chart.value.clear()
|
chart.value.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
35
src/components/customUI/Menu.vue
Normal file
35
src/components/customUI/Menu.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<script setup>
|
||||||
|
import Checkbox from "@/components/customUI/Checkbox.vue";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { computed, defineProps, onMounted, ref, watch } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
item: Object,
|
||||||
|
selectedId: String,
|
||||||
|
menuCheck:Function,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<li>
|
||||||
|
<!-- 項目無子層級 -->
|
||||||
|
<a v-if="item.children.length === 0">
|
||||||
|
<Checkbox
|
||||||
|
:title="item.name"
|
||||||
|
:checked="selectedId === item.id"
|
||||||
|
@click="() => menuCheck(item.id)"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<!-- 項目有子層級 -->
|
||||||
|
<details v-else open>
|
||||||
|
<summary>{{ item.name }}</summary>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(child, index) in item.children" :key="index">
|
||||||
|
<Menu :item="child" :selectedId="selectedId" :menuCheck="menuCheck"></Menu>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="css" scoped></style>
|
@ -161,12 +161,12 @@ watch(
|
|||||||
<div :class="withStyle ? 'content-box' : 'py-5'">
|
<div :class="withStyle ? 'content-box' : 'py-5'">
|
||||||
<div class="content-decoration">
|
<div class="content-decoration">
|
||||||
<slot name="beforeTable"></slot>
|
<slot name="beforeTable"></slot>
|
||||||
<form ref="form " class="overflow-x-auto">
|
<form ref="form" class="overflow-x-auto">
|
||||||
<table
|
<table
|
||||||
:class="
|
:class="
|
||||||
twMerge(
|
twMerge(
|
||||||
withStyle ? 'table' : 'table border',
|
withStyle ? 'table' : 'table border',
|
||||||
currentDataSource.length === 0 ? 'h-96' : ''
|
currentDataSource.length === 0 ? 'h-28' : ''
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
@ -3,9 +3,6 @@ import { getBuildings } from "@/apis/building";
|
|||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
|
|
||||||
// const buildings = ref(null);
|
|
||||||
// const selectedBuilding = ref(null);
|
|
||||||
|
|
||||||
const store = useBuildingStore();
|
const store = useBuildingStore();
|
||||||
|
|
||||||
const getBui = async () => {
|
const getBui = async () => {
|
||||||
@ -15,6 +12,10 @@ const getBui = async () => {
|
|||||||
store.selectedBuilding = res?.data[0];
|
store.selectedBuilding = res?.data[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectBuilding = (bui) => {
|
||||||
|
store.selectedBuilding = bui;
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getBui();
|
getBui();
|
||||||
});
|
});
|
||||||
@ -25,10 +26,10 @@ onMounted(() => {
|
|||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="text-white ml-8 text-lg font-semiLight "
|
class="text-white ml-8 text-lg font-semiLight"
|
||||||
>
|
>
|
||||||
{{ store.selectedBuilding?.full_name }}
|
{{ store.selectedBuilding?.full_name }}
|
||||||
<font-awesome-icon :icon="['fas', 'angle-down']" class="ml-1"/>
|
<font-awesome-icon :icon="['fas', 'angle-down']" class="ml-1" />
|
||||||
</div>
|
</div>
|
||||||
<ul
|
<ul
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@ -38,6 +39,7 @@ onMounted(() => {
|
|||||||
class="text-white my-1 text-base"
|
class="text-white my-1 text-base"
|
||||||
v-for="bui in store.buildings"
|
v-for="bui in store.buildings"
|
||||||
:key="bui.building_tag"
|
:key="bui.building_tag"
|
||||||
|
@click="selectBuilding(bui)"
|
||||||
>
|
>
|
||||||
{{ bui.full_name }}
|
{{ bui.full_name }}
|
||||||
</li>
|
</li>
|
||||||
|
@ -41,6 +41,7 @@ export default function useForgeSprite() {
|
|||||||
console.log("onSpriteClicked", event.target);
|
console.log("onSpriteClicked", event.target);
|
||||||
console.log("onSpriteClicked", data);
|
console.log("onSpriteClicked", data);
|
||||||
// modalContent.value = data;
|
// modalContent.value = data;
|
||||||
|
// debugger;
|
||||||
if (data) {
|
if (data) {
|
||||||
getCurrentInfoModalData(
|
getCurrentInfoModalData(
|
||||||
event,
|
event,
|
||||||
|
@ -38,6 +38,9 @@ instance.interceptors.response.use(
|
|||||||
function (error) {
|
function (error) {
|
||||||
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
// Any status codes that falls outside the range of 2xx cause this function to trigger
|
||||||
// Do something with response error
|
// Do something with response error
|
||||||
|
if (response && response.status === 401) {
|
||||||
|
window.location.href = "/logout";
|
||||||
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { getAssetList, getAssetSingle, deleteAssetItem } from "@/apis/asset";
|
import { getAssetList, getAssetSingle, deleteAssetItem } from "@/apis/asset";
|
||||||
import { onMounted, ref, watch, inject, computed } from "vue";
|
import { onMounted, ref, watch, inject, provide, computed } from "vue";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import AssetTableAddModal from "./AssetTableAddModal.vue";
|
import AssetTableAddModal from "./AssetTableAddModal.vue";
|
||||||
import { getOperationCompanyList } from "@/apis/operation";
|
import { getOperationCompanyList } from "@/apis/operation";
|
||||||
@ -20,6 +20,7 @@ const getCompany = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const floors = ref([]);
|
const floors = ref([]);
|
||||||
|
const totalCoordinates = ref({});
|
||||||
const getFloors = async () => {
|
const getFloors = async () => {
|
||||||
const res = await getAssetFloorList();
|
const res = await getAssetFloorList();
|
||||||
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||||
@ -27,8 +28,18 @@ const getFloors = async () => {
|
|||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([]);
|
||||||
const getAssetData = async () => {
|
const getAssetData = async () => {
|
||||||
|
totalCoordinates.value = {}; // 在獲取新數據之前清空 totalCoordinates
|
||||||
const res = await getAssetList(searchParams.value?.subSys_id);
|
const res = await getAssetList(searchParams.value?.subSys_id);
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
|
// 將 device_coordinate 推入對應樓層的座標陣列
|
||||||
|
res.data.forEach(d => {
|
||||||
|
const floorGuid = d.floor_guid;
|
||||||
|
if (!totalCoordinates.value[floorGuid]) {
|
||||||
|
totalCoordinates.value[floorGuid] = [];
|
||||||
|
}
|
||||||
|
totalCoordinates.value[floorGuid].push(d.device_coordinate);
|
||||||
|
});
|
||||||
|
|
||||||
tableData.value = res.data.map((d) => ({
|
tableData.value = res.data.map((d) => ({
|
||||||
...d,
|
...d,
|
||||||
key: d.id,
|
key: d.id,
|
||||||
@ -90,10 +101,10 @@ const columns = computed(() => [
|
|||||||
key: "buying_date",
|
key: "buying_date",
|
||||||
sort: true,
|
sort: true,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
title: t("assetManagement.oriFile"),
|
// title: t("assetManagement.oriFile"),
|
||||||
key: "oriFile",
|
// key: "oriFile",
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: t("assetManagement.created_at"),
|
title: t("assetManagement.created_at"),
|
||||||
key: "created_at",
|
key: "created_at",
|
||||||
@ -158,6 +169,10 @@ const remove = async (id) => {
|
|||||||
getAssetData();
|
getAssetData();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
provide("asset_table_data", {
|
||||||
|
totalCoordinates,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -3,6 +3,7 @@ import { ref, inject, onBeforeMount, onMounted, watch } from "vue";
|
|||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import "yup-phone-lite";
|
import "yup-phone-lite";
|
||||||
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
|
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
|
||||||
|
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
|
||||||
import { getOperationCompanyList } from "@/apis/operation";
|
import { getOperationCompanyList } from "@/apis/operation";
|
||||||
import useSearchParam from "@/hooks/useSearchParam";
|
import useSearchParam from "@/hooks/useSearchParam";
|
||||||
import OperationTableModal from "@/views/operation/components/OperationTableModal.vue";
|
import OperationTableModal from "@/views/operation/components/OperationTableModal.vue";
|
||||||
@ -200,7 +201,8 @@ const openCompanyAddModal = () => {
|
|||||||
{{ $t("button.add") }}
|
{{ $t("button.add") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<Upload
|
<AssetTableModalLeftInfoGraph />
|
||||||
|
<!-- <Upload
|
||||||
name="oriFile"
|
name="oriFile"
|
||||||
:fileList="formState.oriFile"
|
:fileList="formState.oriFile"
|
||||||
:getFileList="updateFileList"
|
:getFileList="updateFileList"
|
||||||
@ -209,7 +211,7 @@ const openCompanyAddModal = () => {
|
|||||||
:baseUrl="FILE_BASEURL"
|
:baseUrl="FILE_BASEURL"
|
||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||||
</Upload>
|
</Upload> -->
|
||||||
<AssetTableModalLeftInfoIoT />
|
<AssetTableModalLeftInfoIoT />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -0,0 +1,173 @@
|
|||||||
|
<script setup>
|
||||||
|
import { getSideBar, getGraphData } from "@/apis/graph";
|
||||||
|
import { ref, computed, inject, watch, onMounted } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import Menu from "@/components/customUI/Menu.vue";
|
||||||
|
const { t } = useI18n();
|
||||||
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
|
const { formState } = inject("asset_table_modal_form");
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
title: t("assetManagement.index"),
|
||||||
|
key: "index",
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("graphManagement.oriOrgName"),
|
||||||
|
key: "oriOrgName",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const graphData = ref([]);
|
||||||
|
const menuData = ref([]);
|
||||||
|
const selectedId = ref(null);
|
||||||
|
const dataSource = ref([]);
|
||||||
|
|
||||||
|
const getMenuData = async () => {
|
||||||
|
const res = await getSideBar();
|
||||||
|
graphData.value = res.data;
|
||||||
|
console.log(" graphData", graphData.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async (id) => {
|
||||||
|
const res = await getGraphData(id);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
dataSource.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildMenuTree = (data) => {
|
||||||
|
const map = new Map();
|
||||||
|
const menu = [];
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
map.set(item.id, { ...item, children: [] });
|
||||||
|
});
|
||||||
|
|
||||||
|
data.forEach((item) => {
|
||||||
|
const parent = map.get(item.parent_id);
|
||||||
|
if (item.parent_id === 0) {
|
||||||
|
menu.push(map.get(item.id));
|
||||||
|
} else {
|
||||||
|
if (parent) {
|
||||||
|
parent.children.push(map.get(item.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return menu;
|
||||||
|
};
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
asset_add_graph_item.showModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
formState.value.graph_tree_id = selectedId.value;
|
||||||
|
onCancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
selectedId.value = null;
|
||||||
|
asset_add_graph_item.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
const menuCheck = (id) => {
|
||||||
|
selectedId.value = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(selectedId, (newId) => {
|
||||||
|
if (newId) {
|
||||||
|
getData(newId);
|
||||||
|
} else {
|
||||||
|
getData(formState.value.graph_tree_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
formState,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue?.graph_tree_id) {
|
||||||
|
selectedId.value = newValue?.graph_tree_id;
|
||||||
|
} else {
|
||||||
|
selectedId.value = null;
|
||||||
|
dataSource.value = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getMenuData();
|
||||||
|
menuData.value = buildMenuTree(graphData.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex flex-col mt-8 col-span-2">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<span class="label-text-alt text-lg">{{
|
||||||
|
$t("graphManagement.title")
|
||||||
|
}}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm btn-success"
|
||||||
|
@click.stop.prevent="openModal"
|
||||||
|
>
|
||||||
|
{{ $t("assetManagement.choose") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<Table :columns="columns" :dataSource="dataSource" :withStyle="false">
|
||||||
|
<template #bodyCell="{ record, column, index }">
|
||||||
|
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||||
|
<template v-else-if="column.key === 'oriOrgName'">
|
||||||
|
<a
|
||||||
|
:href="`${FILE_BASEURL}/upload/graph_manage/${record.oriSavName}`"
|
||||||
|
target="_blank"
|
||||||
|
class="btn-link no-underline hover:no-underline :focus:outline-0"
|
||||||
|
>
|
||||||
|
{{ record.oriOrgName }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
id="asset_add_graph_item"
|
||||||
|
:title="t('graphManagement.title')"
|
||||||
|
:onCancel="onCancel"
|
||||||
|
width="500"
|
||||||
|
>
|
||||||
|
<template #modalContent>
|
||||||
|
<ul class="menu bg-base-200 rounded-box text-lg w-full mt-3">
|
||||||
|
<li v-for="(item, index) in menuData" :key="index">
|
||||||
|
<Menu
|
||||||
|
:item="item"
|
||||||
|
:selectedId="selectedId"
|
||||||
|
:menuCheck="menuCheck"
|
||||||
|
></Menu>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</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.prevent="onOk"
|
||||||
|
>
|
||||||
|
{{ $t("button.submit") }}
|
||||||
|
</button>
|
||||||
|
</template></Modal
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -1,18 +1,24 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
||||||
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
||||||
import { getAssetFloorList, postAssetFloor } from "@/apis/asset";
|
import {
|
||||||
|
getAssetFloorList,
|
||||||
|
postAssetFloor,
|
||||||
|
deleteAssetFloor,
|
||||||
|
} from "@/apis/asset";
|
||||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||||
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
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 { updateRightFields, formErrorMsg, formState } = inject(
|
const { updateRightFields, formErrorMsg, formState } = inject(
|
||||||
"asset_table_modal_form"
|
"asset_table_modal_form"
|
||||||
);
|
);
|
||||||
|
const store = useBuildingStore();
|
||||||
let schema = {
|
let schema = {
|
||||||
floor_guid: yup.string().nullable(true),
|
floor_guid: yup.string().nullable(true),
|
||||||
device_coordinate: yup.string().nullable(true),
|
device_coordinate: yup.string().nullable(true),
|
||||||
@ -27,41 +33,56 @@ onBeforeMount(() => {
|
|||||||
|
|
||||||
const asset_floor_chart = ref(null);
|
const asset_floor_chart = ref(null);
|
||||||
const currentFloor = ref(null);
|
const currentFloor = ref(null);
|
||||||
const defaultOption = (map, data = []) => ({
|
const selectedOption = ref("add");
|
||||||
tooltip: {},
|
|
||||||
geo: {
|
const defaultOption = (map, data = []) => {
|
||||||
tooltip: {
|
// 生成坐標數據,根據坐標值的不同設置不同顏色
|
||||||
show: false,
|
const formattedData = data.map((coordinate) => {
|
||||||
|
const coordString = JSON.stringify(coordinate);
|
||||||
|
return {
|
||||||
|
name: coordString,
|
||||||
|
value: coordinate,
|
||||||
|
itemStyle: {
|
||||||
|
color: coordString === formState.value.device_coordinate ? "#0000FF" : "#b02a02",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
tooltip: {},
|
||||||
|
geo: {
|
||||||
|
tooltip: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
roam: true,
|
||||||
},
|
},
|
||||||
map,
|
series: {
|
||||||
roam: true, // 一定要关闭拖拽
|
type: "effectScatter",
|
||||||
},
|
coordinateSystem: "geo",
|
||||||
series: {
|
geoIndex: 0,
|
||||||
type: "effectScatter",
|
symbolSize: 10,
|
||||||
coordinateSystem: "geo",
|
encode: {
|
||||||
geoIndex: 0,
|
tooltip: 2,
|
||||||
symbolSize: 10,
|
},
|
||||||
itemStyle: {
|
data: formattedData, // 使用格式化的數據
|
||||||
color: "#b02a02",
|
|
||||||
},
|
},
|
||||||
encode: {
|
};
|
||||||
tooltip: 2,
|
};
|
||||||
},
|
|
||||||
data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
watch(currentFloor, (newValue) => {
|
watch(currentFloor, (newValue) => {
|
||||||
if (newValue?.floor_map_name) {
|
if (newValue?.floor_map_name) {
|
||||||
|
const coordinates = totalCoordinates.value[newValue.floor_guid] || [];
|
||||||
|
const parsedCoordinates = coordinates.map((coord) => {
|
||||||
|
return JSON.parse(coord);
|
||||||
|
});
|
||||||
|
|
||||||
asset_floor_chart.value.updateSvg(
|
asset_floor_chart.value.updateSvg(
|
||||||
{
|
{
|
||||||
full_name: newValue?.floor_map_name,
|
full_name: newValue?.floor_map_name,
|
||||||
path: `${FILE_BASEURL}/${newValue?.floor_map_url}.svg`,
|
path: `${FILE_BASEURL}/${newValue?.floor_map_url}.svg`,
|
||||||
},
|
},
|
||||||
defaultOption(newValue?.floor_map_name, [
|
defaultOption(newValue?.floor_map_name, parsedCoordinates)
|
||||||
formState.value.device_coordinate
|
|
||||||
? JSON.parse(formState.value.device_coordinate)
|
|
||||||
: [],
|
|
||||||
])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -99,23 +120,39 @@ onMounted(() => {
|
|||||||
|
|
||||||
// modal
|
// modal
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
|
if (selectedOption.value === "add") {
|
||||||
|
FloorFormState.value = {
|
||||||
|
full_name: "",
|
||||||
|
floorFile: [],
|
||||||
|
};
|
||||||
|
} else if (selectedOption.value === "edit") {
|
||||||
|
const floor = floors.value.find(
|
||||||
|
(f) => f.floor_guid === formState.value.floor_guid
|
||||||
|
);
|
||||||
|
if (floor) {
|
||||||
|
console.log("floor", floor);
|
||||||
|
FloorFormState.value = {
|
||||||
|
full_name: floor.full_name,
|
||||||
|
floorFile: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
asset_add_floor.showModal();
|
asset_add_floor.showModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const form = ref(null);
|
const form = ref(null);
|
||||||
const FloorFormState = ref({
|
const FloorFormState = ref({
|
||||||
building_tag: "F3",
|
|
||||||
full_name: "",
|
full_name: "",
|
||||||
floorFile: [],
|
floorFile: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const floorScheme = yup.object({
|
const floorScheme = yup.object({
|
||||||
building_tag: yup.string().required(t("button.required")),
|
|
||||||
full_name: yup.string().required(t("button.required")),
|
full_name: yup.string().required(t("button.required")),
|
||||||
floorFile: yup.array(),
|
floorFile: yup.array(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateFileList = (files) => {
|
const updateFileList = (files) => {
|
||||||
|
console.log("file", files);
|
||||||
FloorFormState.value.floorFile = files;
|
FloorFormState.value.floorFile = files;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -127,9 +164,9 @@ const {
|
|||||||
} = useFormErrorMessage(floorScheme);
|
} = useFormErrorMessage(floorScheme);
|
||||||
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", "");
|
formData.append("floor_guid", currentFloor.value.floor_guid);
|
||||||
|
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");
|
||||||
@ -143,25 +180,34 @@ const onOk = async () => {
|
|||||||
onCancel();
|
onCancel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const onDelete = async () => {
|
||||||
|
console.log("guild", formState.value.floor_guid);
|
||||||
|
const res = await deleteAssetFloor({
|
||||||
|
floor_guid: formState.value.floor_guid,
|
||||||
|
});
|
||||||
|
if (res.isSuccess) {
|
||||||
|
getFloors();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
FloorFormState.value = {
|
FloorFormState.value = {
|
||||||
building_tag: "F3",
|
|
||||||
full_name: "",
|
full_name: "",
|
||||||
floorFile: [],
|
floorFile: [],
|
||||||
};
|
};
|
||||||
asset_add_floor.close();
|
asset_add_floor.close();
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- 平面圖 -->
|
<!-- 平面圖 -->
|
||||||
|
|
||||||
<div class="flex items-center justify-between mb-5">
|
<div class="flex items-center justify-between mb-5">
|
||||||
<div class="flex justify-start">
|
<div className="join">
|
||||||
<Select
|
<Select
|
||||||
:value="formState"
|
:value="formState"
|
||||||
selectClass="border-info focus-within:border-info"
|
selectClass="border-info focus-within:border-info rounded-r-none"
|
||||||
name="floor_guid"
|
name="floor_guid"
|
||||||
Attribute="full_name"
|
Attribute="full_name"
|
||||||
:options="floors"
|
:options="floors"
|
||||||
@ -169,12 +215,23 @@ const onCancel = () => {
|
|||||||
>
|
>
|
||||||
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
||||||
</Select>
|
</Select>
|
||||||
<button type="button" class="btn btn-add mt-10 ml-5" @click="openModal">
|
<select
|
||||||
<font-awesome-icon :icon="['fas', 'plus']" />
|
v-model="selectedOption"
|
||||||
{{ $t("assetManagement.add_floor") }}
|
className="select border-info focus-within:border-info join-item mt-11"
|
||||||
|
>
|
||||||
|
<option value="add" selected>{{ $t("button.add") }}</option>
|
||||||
|
<option value="edit">{{ $t("button.edit") }}</option>
|
||||||
|
<option value="delete">{{ $t("button.delete") }}</option>
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-success join-item mt-11"
|
||||||
|
@click="selectedOption === 'delete' ? onDelete() : openModal()"
|
||||||
|
:aria-label="$t('button.submit')"
|
||||||
|
>
|
||||||
|
{{ $t("button.submit") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- <button type="button" class="btn btn-success mt-10">確認座標</button> -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<EffectScatter
|
<EffectScatter
|
||||||
|
@ -73,11 +73,11 @@ const getProgressValue = (group) => {
|
|||||||
<img src="@ASSET/img/state-ul.svg" />
|
<img src="@ASSET/img/state-ul.svg" />
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="mark">
|
<div class="mark">
|
||||||
<span>{{ group[0].value }}</span>
|
<span class="w-10">{{ group[0].value }}</span>
|
||||||
<span
|
<span
|
||||||
><img class="w-[50px]" src="@ASSET/img/state-ul-text.svg" />
|
><img class="w-[50px]" src="@ASSET/img/state-ul-text.svg" />
|
||||||
</span>
|
</span>
|
||||||
<span>{{ group[1].value }}</span>
|
<span class="w-10">{{ group[1].value }}</span>
|
||||||
</div>
|
</div>
|
||||||
<progress
|
<progress
|
||||||
class="progress [&::-webkit-progress-value]:bg-red-600 [&::-moz-progress-bar]:bg-red-600"
|
class="progress [&::-webkit-progress-value]:bg-red-600 [&::-moz-progress-bar]:bg-red-600"
|
||||||
|
@ -43,9 +43,9 @@ const specOptions = computed(() => {
|
|||||||
return options.value.find(({ selected }) => selected)?.spec || [];
|
return options.value.find(({ selected }) => selected)?.spec || [];
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
// onMounted(() => {
|
||||||
getOption();
|
// getOption();
|
||||||
});
|
// });
|
||||||
|
|
||||||
const openModal = () => {
|
const openModal = () => {
|
||||||
graph_add_item.showModal();
|
graph_add_item.showModal();
|
||||||
|
@ -130,8 +130,8 @@ const getNewFilename = async (e) => {
|
|||||||
});
|
});
|
||||||
getData(res);
|
getData(res);
|
||||||
} else if (
|
} else if (
|
||||||
e.target.value !== "" &&
|
e?.target.value !== "" &&
|
||||||
e.target.value !== selectedItem.value?.title
|
e?.target.value !== selectedItem.value?.title
|
||||||
) {
|
) {
|
||||||
// 編輯
|
// 編輯
|
||||||
const res = await updateSideBarTreeName({
|
const res = await updateSideBarTreeName({
|
||||||
|
@ -5,12 +5,15 @@ import SystemFloorBar from './components/SystemFloorBar.vue';
|
|||||||
import useBuildingStore from "@/stores/useBuildingStore";
|
import useBuildingStore from "@/stores/useBuildingStore";
|
||||||
import ForgeForSystem from "@/components/forge/ForgeForSystem.vue";
|
import ForgeForSystem from "@/components/forge/ForgeForSystem.vue";
|
||||||
import { getSystemDevices, getSystemRealTime } from "@/apis/system";
|
import { getSystemDevices, getSystemRealTime } from "@/apis/system";
|
||||||
|
import { getOperationCompanyList } from "@/apis/operation";
|
||||||
|
import { getAssetSingle, getAssetFloorList } from "@/apis/asset";
|
||||||
import SystemSubBar from './components/SystemSubBar.vue';
|
import SystemSubBar from './components/SystemSubBar.vue';
|
||||||
import SystemInfoModal from './components/SystemInfoModal.vue';
|
import SystemInfoModal from './components/SystemInfoModal.vue';
|
||||||
import SystemMain from "./SystemMain.vue";
|
import SystemMain from "./SystemMain.vue";
|
||||||
import SystemMode from './components/SystemMode.vue';
|
import SystemMode from './components/SystemMode.vue';
|
||||||
import SystemFloor from './SystemFloor.vue';
|
import SystemFloor from './SystemFloor.vue';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
const buildingStore = useBuildingStore()
|
const buildingStore = useBuildingStore()
|
||||||
|
|
||||||
@ -30,9 +33,21 @@ const statusList = computed(() => {
|
|||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
|
|
||||||
const raw_data = ref([])
|
const raw_data = ref([]);
|
||||||
const data = ref([]) // filter data
|
const data = ref([]); // filter data
|
||||||
const route = useRoute()
|
const route = useRoute();
|
||||||
|
const floors = ref([]);
|
||||||
|
const companyOptions = ref([]);
|
||||||
|
|
||||||
|
const getFloors = async () => {
|
||||||
|
const res = await getAssetFloorList();
|
||||||
|
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCompany = async () => {
|
||||||
|
const res = await getOperationCompanyList();
|
||||||
|
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||||
|
};
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
if (!route.params.sub_system_id) return
|
if (!route.params.sub_system_id) return
|
||||||
@ -90,7 +105,7 @@ const updateDataByGas = (gas) => {
|
|||||||
} else {
|
} else {
|
||||||
update_values = raw_data.value.map((d) => (
|
update_values = raw_data.value.map((d) => (
|
||||||
{
|
{
|
||||||
...d,
|
...d,
|
||||||
device_list: d.device_list.filter(({ points }) => points.some(({ points: p }) => p === gas))
|
device_list: d.device_list.filter(({ points }) => points.some(({ points: p }) => p === gas))
|
||||||
}
|
}
|
||||||
))
|
))
|
||||||
@ -100,23 +115,24 @@ const updateDataByGas = (gas) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(() => buildingStore.selectedBuilding, (newBuilding) => {
|
watch(() => buildingStore.selectedBuilding, (newBuilding) => {
|
||||||
if (Boolean(newBuilding)) {
|
if (Boolean(newBuilding)) {
|
||||||
getData()
|
getData()
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
deep: true,
|
deep: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => route.params.sub_system_id, (newRoute, oldRoute) => {
|
watch(() => route.params.sub_system_id, (newRoute, oldRoute) => {
|
||||||
if (buildingStore.selectedBuilding && newRoute !== oldRoute) {
|
if (buildingStore.selectedBuilding && newRoute !== oldRoute) {
|
||||||
getData()
|
getData()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getData()
|
getFloors();
|
||||||
|
getCompany();
|
||||||
})
|
getData();
|
||||||
|
});
|
||||||
|
|
||||||
const currentFloor = ref(null);
|
const currentFloor = ref(null);
|
||||||
|
|
||||||
@ -142,6 +158,7 @@ watch(subscribeData, (newValue) => {
|
|||||||
provide("system_deviceList", { data, subscribeData, currentFloor, updateCurrentFloor, updateDataByGas, realtimeData })
|
provide("system_deviceList", { data, subscribeData, currentFloor, updateCurrentFloor, updateDataByGas, realtimeData })
|
||||||
|
|
||||||
const selectedDevice = ref(null);
|
const selectedDevice = ref(null);
|
||||||
|
const selectedDeviceCog = ref({});
|
||||||
// const selectedDevice = computed(() => {
|
// const selectedDevice = computed(() => {
|
||||||
// return {
|
// return {
|
||||||
// ...currentInfo.value,
|
// ...currentInfo.value,
|
||||||
@ -152,13 +169,35 @@ const selectedDevice = ref(null);
|
|||||||
const isMobile = (e) => {
|
const isMobile = (e) => {
|
||||||
return e.pointerType !== "mouse" && e.type !== "click"; // is mobile
|
return e.pointerType !== "mouse" && e.type !== "click"; // is mobile
|
||||||
};
|
};
|
||||||
const getCurrentInfoModalData = (e, position, value) => {
|
|
||||||
|
const getCurrentInfoModalData = async (e, position, value) => {
|
||||||
|
if (value.main_id) {
|
||||||
|
const res = await getAssetSingle(value.main_id);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
selectedDeviceCog.value = {
|
||||||
|
...res.data,
|
||||||
|
key: res.data.id,
|
||||||
|
floor: floors.value.find(
|
||||||
|
({ floor_guid }) => res.data.floor_guid === floor_guid
|
||||||
|
)?.full_name,
|
||||||
|
company: companyOptions.value.find(
|
||||||
|
({ id }) => res.data.operation_id === id
|
||||||
|
)?.name,
|
||||||
|
buying_date: res.data?.buying_date
|
||||||
|
? dayjs(res.data.buying_date).format("YYYY-MM-DD")
|
||||||
|
: "",
|
||||||
|
created_at: res.data?.created_at
|
||||||
|
? dayjs(res.data.created_at).format("YYYY-MM-DD")
|
||||||
|
: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
const mobile = isMobile(e);
|
const mobile = isMobile(e);
|
||||||
selectedDevice.value = {
|
selectedDevice.value = {
|
||||||
initPos: { left: `50%`, top: `50%` },
|
initPos: { left: `50%`, top: `50%` },
|
||||||
value,
|
value,
|
||||||
isMobile: mobile,
|
isMobile: mobile,
|
||||||
};;
|
};
|
||||||
document.getElementById('system_info_modal').showModal();
|
document.getElementById('system_info_modal').showModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -169,7 +208,7 @@ const clearSelectedDeviceInfo = () => {
|
|||||||
selectedDevice.value.value = null;
|
selectedDevice.value.value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
provide("system_selectedDevice", { selectedDeviceRealtime, selectedDevice, getCurrentInfoModalData, clearSelectedDeviceInfo })
|
provide("system_selectedDevice", { selectedDeviceRealtime, selectedDevice, getCurrentInfoModalData, clearSelectedDeviceInfo, selectedDeviceCog, })
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ const fitToView = (forge_dbid) => {
|
|||||||
<p class="title">{{ d.full_name }}</p>
|
<p class="title">{{ d.full_name }}</p>
|
||||||
<div class="grid grid-cols-3 gap-5">
|
<div class="grid grid-cols-3 gap-5">
|
||||||
<div class="col-auto relative" v-for="device in d.device_list" :key="device.device_guid">
|
<div class="col-auto relative" v-for="device in d.device_list" :key="device.device_guid">
|
||||||
<div class="item" @click="() => fitToView(device.forge_dbid)">
|
<div class="item h-full">
|
||||||
<div class="left h-full flex flex-wrap justify-center">
|
<div class="left h-full flex flex-wrap justify-center">
|
||||||
<div class="sec02 w-full">
|
<div class="sec02 w-full">
|
||||||
<img v-if="device.device_image_url" :src="device.device_image_url" alt="" class="w-8 h-8">
|
<img v-if="device.device_image_url" :src="device.device_image_url" alt="" class="w-8 h-8">
|
||||||
@ -34,7 +34,7 @@ const fitToView = (forge_dbid) => {
|
|||||||
<div class="flex justify-between w-full self-end">
|
<div class="flex justify-between w-full self-end">
|
||||||
<div class="sec03">
|
<div class="sec03">
|
||||||
<span class="w-5 h-5 rounded-full" :style="{ backgroundColor: device.device_normal_color }"></span>
|
<span class="w-5 h-5 rounded-full" :style="{ backgroundColor: device.device_normal_color }"></span>
|
||||||
<span class="mx-2">{{ $t("system.status") }}:</span>
|
<span class="mx-2">{{ $t("system.status") }}:</span>
|
||||||
<span>{{ device.device_status }}</span>
|
<span>{{ device.device_status }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-text border-0 "
|
<button class="btn-text border-0 "
|
||||||
|
@ -1,5 +1,71 @@
|
|||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { computed, inject, watch, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { selectedDeviceCog } = inject("system_selectedDevice");
|
||||||
|
const tableData = ref({});
|
||||||
|
|
||||||
<template>Cog</template>
|
watch(
|
||||||
|
selectedDeviceCog,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
tableData.value = newValue;
|
||||||
|
} else {
|
||||||
|
tableData.value = {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<template>
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t("assetManagement.device_number") }}</td>
|
||||||
|
<td>{{ tableData.device_number }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t("assetManagement.device_name") }}</td>
|
||||||
|
<td>{{ tableData.full_name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t("assetManagement.floor") }}</td>
|
||||||
|
<td>{{ tableData.floor }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t("assetManagement.device_coordinate") }}</td>
|
||||||
|
<td>{{ tableData.device_coordinate }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ $t("assetManagement.brand_and_modal") }}</td>
|
||||||
|
<td>{{ tableData.brand }} / {{ tableData.device_model }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ t("assetManagement.company_and_contact") }}</td>
|
||||||
|
<td>
|
||||||
|
{{ tableData.company?.name }} /
|
||||||
|
{{ tableData.company?.contact_person }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ t("assetManagement.buying_date") }}</td>
|
||||||
|
<td>{{ tableData.buying_date }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ t("assetManagement.created_at") }}</td>
|
||||||
|
<td>{{ tableData.created_at }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
td {
|
||||||
|
border: 1px solid #ddd
|
||||||
|
}
|
||||||
|
|
||||||
|
td:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { defineProps, inject, ref, watch } from "vue";
|
import { defineProps, inject, ref, watch } from "vue";
|
||||||
import SystemInfoModalDesktop from "./SystemInfoModalDesktop.vue";
|
import SystemInfoModalDesktop from "./SystemInfoModalDesktop.vue";
|
||||||
import SystemInfoModalCog from "./SystemInfoModalCog.vue";
|
import SystemInfoModalCog from "./SystemInfoModalCog.vue";
|
||||||
|
import SystemInfoModalImage from "./SystemInfoModalImage.vue";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ const currentTab = ref("desktop");
|
|||||||
const tabs = {
|
const tabs = {
|
||||||
desktop: SystemInfoModalDesktop,
|
desktop: SystemInfoModalDesktop,
|
||||||
cog: SystemInfoModalCog,
|
cog: SystemInfoModalCog,
|
||||||
|
image: SystemInfoModalImage
|
||||||
};
|
};
|
||||||
|
|
||||||
const changeOpenKey = (key) => {
|
const changeOpenKey = (key) => {
|
||||||
@ -34,6 +36,10 @@ const onCancel = () => {
|
|||||||
<font-awesome-icon :icon="['fas', 'desktop']" size="lg"
|
<font-awesome-icon :icon="['fas', 'desktop']" size="lg"
|
||||||
:class="twMerge(currentTab === 'desktop' ? 'text-success' : 'text-[#a5abb1]')" />
|
:class="twMerge(currentTab === 'desktop' ? 'text-success' : 'text-[#a5abb1]')" />
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button type="link" class="btn-link btn-text-without-border px-2" @click="() => changeOpenKey('image')">
|
||||||
|
<font-awesome-icon :icon="['fas', 'image']" size="lg"
|
||||||
|
:class="twMerge(currentTab === 'image' ? 'text-success' : 'text-[#a5abb1]')" />
|
||||||
|
</Button>
|
||||||
<Button type="link" class="btn-link btn-text-without-border px-2" @click="() => changeOpenKey('cog')">
|
<Button type="link" class="btn-link btn-text-without-border px-2" @click="() => changeOpenKey('cog')">
|
||||||
<font-awesome-icon :icon="['fas', 'cog']" size="lg"
|
<font-awesome-icon :icon="['fas', 'cog']" size="lg"
|
||||||
:class="twMerge(currentTab === 'cog' ? 'text-success' : 'text-[#a5abb1]')" />
|
:class="twMerge(currentTab === 'cog' ? 'text-success' : 'text-[#a5abb1]')" />
|
||||||
|
76
src/views/system/components/SystemInfoModalImage.vue
Normal file
76
src/views/system/components/SystemInfoModalImage.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, inject, watch, ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
const { t } = useI18n();
|
||||||
|
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||||
|
const { selectedDeviceCog } = inject("system_selectedDevice");
|
||||||
|
const imgData = ref([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
selectedDeviceCog,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
imgData.value = newValue.oriFile.map(file => file.file_url);
|
||||||
|
} else {
|
||||||
|
imgData.value = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<a-carousel arrows class="mt-5 shadow-lg">
|
||||||
|
<template #prevArrow>
|
||||||
|
<div class="custom-slick-arrow" style="left: 10px; z-index: 1">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'chevron-left']"
|
||||||
|
size="lg"
|
||||||
|
class="text-[#1b1b1b]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #nextArrow>
|
||||||
|
<div class="custom-slick-arrow" style="right: 10px">
|
||||||
|
<font-awesome-icon
|
||||||
|
:icon="['fas', 'chevron-right']"
|
||||||
|
size="lg"
|
||||||
|
class="text-[#1b1b1b]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-for="(url, index) in imgData" :key="index">
|
||||||
|
<img :src="`${FILE_BASEURL}/${url}`" alt="Image" />
|
||||||
|
</div>
|
||||||
|
</a-carousel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* For demo */
|
||||||
|
:deep(.slick-slide) {
|
||||||
|
text-align: center;
|
||||||
|
background: #c5c5c5;
|
||||||
|
height: 400px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow) {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
font-size: 25px;
|
||||||
|
transition: ease all 0.3s;
|
||||||
|
opacity: 0.3;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow:before) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
:deep(.slick-arrow.custom-slick-arrow:hover) {
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.slick-slide img) {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue
Block a user