設備管理:部門與樓層管理移至設定、小類新稱icon | 能源管理:能匯出excel、報表日期設置 | 運維管理: 廠商管理移至設定
This commit is contained in:
parent
a91c4397a4
commit
80fcfda16c
@ -26,3 +26,5 @@ export const POST_ASSET_DEPARTMENT_API = `/AssetManage/SaveDepartment`;
|
||||
export const DELETE_ASSET_DEPARTMENT_API = `/AssetManage/DeleteDepartment`;
|
||||
|
||||
export const GET_ASSET_ELECTYPE_API = `/AssetManage/GetElecType`;
|
||||
export const POST_ASSET_ELECTYPE_API = `/AssetManage/SaveElecType`;
|
||||
export const DELETE_ASSET_ELECTYPE_API = `/AssetManage/DeleteElecType`;
|
||||
|
@ -19,6 +19,8 @@ import {
|
||||
POST_ASSET_DEPARTMENT_API,
|
||||
DELETE_ASSET_DEPARTMENT_API,
|
||||
GET_ASSET_ELECTYPE_API,
|
||||
POST_ASSET_ELECTYPE_API,
|
||||
DELETE_ASSET_ELECTYPE_API
|
||||
} from "./api";
|
||||
import instance from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
@ -251,3 +253,24 @@ export const getElecTypeList = async () => {
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const postElecTypeList = async ({ name, id }) => {
|
||||
const res = await instance.post(POST_ASSET_ELECTYPE_API, {
|
||||
name,
|
||||
id,
|
||||
});
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteElecTypeItem = async (id) => {
|
||||
const res = await instance.post(DELETE_ASSET_ELECTYPE_API, { id });
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
});
|
||||
};
|
@ -2,8 +2,9 @@ export const GET_REALTIME_DIST_API = `/api/Energe/GetRealTimeDistribution`;
|
||||
export const GET_ELECUSE_DAY_API = `/api/Energe/GetElecUseDay`;
|
||||
export const GET_TAI_POWER_API = `/api/Energe/GetTaipower`;
|
||||
|
||||
export const GET_SIDEBAR_API = `/api/Energe/GetEnergySideBar`;
|
||||
export const GET_SIDEBAR_API = `/api/GetSideBar`;
|
||||
export const GET_SEARCH_API = `/api/Energe/GetFilter`;
|
||||
|
||||
export const GET_REPORT_API = `/api/Energe/GetReport`;
|
||||
export const GET_Excel_API = `/api/Energe/GetReportExcel`;
|
||||
|
||||
|
@ -5,9 +5,11 @@ import {
|
||||
GET_SIDEBAR_API,
|
||||
GET_SEARCH_API,
|
||||
GET_REPORT_API,
|
||||
GET_Excel_API,
|
||||
} from "./api";
|
||||
import instance from "@/util/request";
|
||||
import instance, { fileInstance } from "@/util/request";
|
||||
import apihandler from "@/util/apihandler";
|
||||
import downloadExcel from "@/util/downloadExcel";
|
||||
|
||||
export const getRealTimeDist = async () => {
|
||||
const res = await instance.post(GET_REALTIME_DIST_API);
|
||||
@ -36,8 +38,8 @@ export const getTaipower = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const getEnergySideBar = async () => {
|
||||
const res = await instance.post(GET_SIDEBAR_API);
|
||||
export const getSideBar = async (system_type) => {
|
||||
const res = await instance.post(GET_SIDEBAR_API, { system_type });
|
||||
|
||||
return apihandler(res.code, res.data, {
|
||||
msg: res.msg,
|
||||
@ -76,3 +78,35 @@ export const getReport = async ({
|
||||
code: res.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const getExcel = async ({
|
||||
department,
|
||||
elecType,
|
||||
floor,
|
||||
start_time,
|
||||
end_time,
|
||||
type,
|
||||
}) => {
|
||||
const res = await fileInstance.post(
|
||||
GET_Excel_API,
|
||||
{
|
||||
department,
|
||||
elecType,
|
||||
floor,
|
||||
start_time,
|
||||
end_time,
|
||||
type,
|
||||
},
|
||||
{ responseType: "blob" }
|
||||
);
|
||||
|
||||
return apihandler(
|
||||
res.code,
|
||||
res,
|
||||
{
|
||||
msg: res.msg,
|
||||
code: res.code,
|
||||
},
|
||||
downloadExcel
|
||||
);
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { onMounted, ref, watch, computed } from "vue";
|
||||
import { AUTHPAGES } from "@/constant";
|
||||
import { getAuth, getAllSysSidebar } from "@/apis/building";
|
||||
import { getEnergySideBar } from "@/apis/energy";
|
||||
import { getSideBar } from "@/apis/energy";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@ -42,15 +42,15 @@ const getSubMonitorPage = async (building) => {
|
||||
buildingStore.mainSubSys = res.data.history_Main_Systems;
|
||||
menu_array.value = res.data.history_Main_Systems;
|
||||
};
|
||||
const getSubEnergyPage = async () => {
|
||||
const res = await getEnergySideBar();
|
||||
const getSubPage = async (system_type) => {
|
||||
const res = await getSideBar(system_type);
|
||||
menu_array.value = res.data;
|
||||
};
|
||||
const showDrawer = (authCode) => {
|
||||
if (authCode === "PF1") {
|
||||
getSubMonitorPage();
|
||||
} else if (authCode === "PF2") {
|
||||
getSubEnergyPage();
|
||||
} else if (authCode === "PF2" || authCode === "PF11") {
|
||||
getSubPage(authCode === "PF2" ? "Energy" : "Setting");
|
||||
}
|
||||
currentAuthCode.value = authCode;
|
||||
open.value = true;
|
||||
@ -105,7 +105,11 @@ onMounted(() => {
|
||||
:key="page.authCode"
|
||||
>
|
||||
<a
|
||||
v-if="page.authCode === 'PF1' || page.authCode === 'PF2'"
|
||||
v-if="
|
||||
page.authCode === 'PF1' ||
|
||||
page.authCode === 'PF2' ||
|
||||
page.authCode === 'PF11'
|
||||
"
|
||||
@click="showDrawer(page.authCode)"
|
||||
:class="
|
||||
twMerge(
|
||||
@ -115,6 +119,9 @@ onMounted(() => {
|
||||
: '',
|
||||
page.authCode === 'PF2' &&
|
||||
route.fullPath.includes('/energyManagement')
|
||||
? 'router-link-active router-link-exact-active'
|
||||
: '',
|
||||
page.authCode === 'PF11' && route.fullPath.includes('/setting')
|
||||
? 'router-link-active router-link-exact-active'
|
||||
: ''
|
||||
)
|
||||
@ -168,18 +175,26 @@ onMounted(() => {
|
||||
<a-menu-item
|
||||
v-for="sub in currentAuthCode === 'PF1'
|
||||
? main.history_Sub_systems
|
||||
: currentAuthCode === 'PF2'
|
||||
? main.sub
|
||||
: main.sub"
|
||||
:key="sub.sub_system_tag+`_`+sub.type"
|
||||
:key="sub.sub_system_tag + `_` + sub.type"
|
||||
@click="() => onClose()"
|
||||
>
|
||||
<router-link
|
||||
:to="{
|
||||
name:
|
||||
currentAuthCode === 'PF2' ? 'energyManagement' : 'sub_system',
|
||||
currentAuthCode === 'PF2'
|
||||
? 'energyManagement'
|
||||
: currentAuthCode === 'PF11'
|
||||
? 'setting'
|
||||
: 'sub_system',
|
||||
params: {
|
||||
main_system_id: main.main_system_tag,
|
||||
sub_system_id: sub.sub_system_tag,
|
||||
...(currentAuthCode === 'PF2' ? { type: sub.type } : { floor_id: 'main' }),
|
||||
...(currentAuthCode === 'PF2' || currentAuthCode === 'PF11'
|
||||
? { type: sub.type }
|
||||
: { floor_id: 'main' }),
|
||||
},
|
||||
}"
|
||||
>
|
||||
|
@ -61,4 +61,10 @@ export const AUTHPAGES = [
|
||||
pageName: "ProductSetting",
|
||||
navigate: "/productSetting",
|
||||
},
|
||||
{
|
||||
authCode: "PF11",
|
||||
icon: "cog",
|
||||
pageName: "Setting",
|
||||
navigate: "/Setting",
|
||||
},
|
||||
];
|
||||
|
@ -8,6 +8,7 @@ import AssetManagement from "@/views/AssetManagement/AssetManagement.vue";
|
||||
import AlertManagement from "@/views/alert/AlertManagement.vue";
|
||||
import ProductSetting from "@/views/productSetting/ProductSetting.vue";
|
||||
import EnergyManagement from "@/views/energyManagement/EnergyManagement.vue";
|
||||
import SettingManagement from "@/views/setting/SettingManagement.vue";
|
||||
import Login from "@/views/login/Login.vue";
|
||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||
import useGetCookie from "@/hooks/useGetCookie";
|
||||
@ -84,6 +85,11 @@ const router = createRouter({
|
||||
name: "energyManagement",
|
||||
component: EnergyManagement,
|
||||
},
|
||||
{
|
||||
path: "/setting/:main_system_id/:sub_system_id/:type",
|
||||
name: "setting",
|
||||
component: SettingManagement,
|
||||
},
|
||||
{
|
||||
path: "/mytestfile/mjm",
|
||||
name: "mytestfile",
|
||||
|
@ -4,12 +4,14 @@ import AssetMainList from "./components/AssetMainList.vue";
|
||||
import AssetSubList from "./components/AssetSubList.vue";
|
||||
import AssetTable from "./components/AssetTable.vue";
|
||||
import { getOperationCompanyList } from "@/apis/operation";
|
||||
import { getIOTSchema, getElecTypeList } from "@/apis/asset";
|
||||
import { getDepartmentList, getIOTSchema, getElecTypeList, getAssetFloorList } from "@/apis/asset";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
const { searchParams, changeParams } = useSearchParam();
|
||||
const companyOptions = ref([]);
|
||||
const iotSchemaOptions = ref([]);
|
||||
const elecTypeOptions = ref([]);
|
||||
const departmentList = ref([]);
|
||||
const floors = ref([]);
|
||||
const getCompany = async () => {
|
||||
const res = await getOperationCompanyList();
|
||||
companyOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
@ -22,10 +24,20 @@ const getElecType = async () => {
|
||||
const res = await getElecTypeList();
|
||||
elecTypeOptions.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
};
|
||||
const getDepartment = async () => {
|
||||
const res = await getDepartmentList();
|
||||
departmentList.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
};
|
||||
const getFloors = async () => {
|
||||
const res = await getAssetFloorList();
|
||||
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getCompany();
|
||||
getElecType();
|
||||
getDepartment();
|
||||
getFloors();
|
||||
});
|
||||
|
||||
watch(
|
||||
@ -44,6 +56,8 @@ provide("asset_modal_options", {
|
||||
companyOptions,
|
||||
iotSchemaOptions,
|
||||
elecTypeOptions,
|
||||
departmentList,
|
||||
floors,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -15,6 +15,9 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const mainSystem = ref([]);
|
||||
const updateFileList = (files) => {
|
||||
formState.value.icon = files;
|
||||
};
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
mainSystem.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
@ -33,6 +36,7 @@ const formState = ref({
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
icon: [],
|
||||
});
|
||||
|
||||
const resetForm = () => {
|
||||
@ -40,6 +44,7 @@ const resetForm = () => {
|
||||
system_key: "",
|
||||
system_value: "",
|
||||
system_parent_id: 0,
|
||||
icon: [],
|
||||
};
|
||||
};
|
||||
|
||||
@ -120,6 +125,15 @@ const onOk = async () => {
|
||||
</span></template
|
||||
>
|
||||
</Select>
|
||||
<Upload
|
||||
name="icon"
|
||||
:fileList="formState.icon"
|
||||
:getFileList="updateFileList"
|
||||
:multiple="false"
|
||||
:baseUrl="`${BASEURL}/upload/graph_manage`"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.upload_file") }}</template>
|
||||
</Upload>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
|
@ -4,7 +4,6 @@ import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import AssetTableModalLeftInfoIoT from "./AssetTableModalLeftInfoIoT.vue";
|
||||
import AssetTableModalLeftInfoDept from "./AssetTableModalLeftInfoDept.vue";
|
||||
import AssetTableModalLeftInfoGraph from "./AssetTableModalLeftInfoGraph.vue";
|
||||
import AssetTableModalLeftInfoMQTT from "./AssetTableModalLeftInfoMQTT.vue";
|
||||
import useUserInfoStore from "@/stores/useUserInfoStore";
|
||||
@ -16,7 +15,7 @@ const { searchParams, changeParams } = useSearchParam();
|
||||
const { updateLeftFields, formErrorMsg, formState } = inject(
|
||||
"asset_table_modal_form"
|
||||
);
|
||||
const { companyOptions, iotSchemaOptions, elecTypeOptions } = inject("asset_modal_options");
|
||||
const { companyOptions, iotSchemaOptions, elecTypeOptions, departmentList } = inject("asset_modal_options");
|
||||
const store = useUserInfoStore();
|
||||
let schema = {
|
||||
full_name: yup.string().nullable(true),
|
||||
@ -53,7 +52,7 @@ onBeforeMount(() => {
|
||||
operation_id: 0,
|
||||
response_schema_id: 0,
|
||||
department_id: 0,
|
||||
elec_id: null,
|
||||
elec_type_id: null,
|
||||
asset_number: "",
|
||||
topic: "",
|
||||
sub_device: [],
|
||||
@ -115,7 +114,17 @@ watch(
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<AssetTableModalLeftInfoDept />
|
||||
<div class="flex items-center w-72">
|
||||
<Select
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="department_id"
|
||||
Attribute="name"
|
||||
:options="departmentList"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.department") }}</template>
|
||||
</Select>
|
||||
</div>
|
||||
<Input
|
||||
:value="formState"
|
||||
width="290"
|
||||
@ -147,7 +156,7 @@ watch(
|
||||
<Select
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="elec_id"
|
||||
name="elec_type_id"
|
||||
Attribute="name"
|
||||
:options="elecTypeOptions"
|
||||
:required="true"
|
||||
|
@ -1,21 +1,15 @@
|
||||
<script setup>
|
||||
import { onMounted, ref, inject, onBeforeMount, watch, computed } from "vue";
|
||||
import EffectScatter from "@/components/chart/EffectScatter.vue";
|
||||
import {
|
||||
getAssetFloorList,
|
||||
postAssetFloor,
|
||||
deleteAssetFloor,
|
||||
} from "@/apis/asset";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import useBuildingStore from "@/stores/useBuildingStore";
|
||||
import * as yup from "yup";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const { totalCoordinates } = inject("asset_table_data");
|
||||
const { floors } = inject("asset_modal_options");
|
||||
const { updateRightFields, formErrorMsg, formState } = inject(
|
||||
"asset_table_modal_form"
|
||||
);
|
||||
@ -34,7 +28,6 @@ onBeforeMount(() => {
|
||||
|
||||
const asset_floor_chart = ref(null);
|
||||
const currentFloor = ref(null);
|
||||
const selectedOption = ref("add");
|
||||
const parsedCoordinates = ref(null);
|
||||
|
||||
const defaultOption = (map, data = []) => {
|
||||
@ -45,7 +38,10 @@ const defaultOption = (map, data = []) => {
|
||||
name: coordString,
|
||||
value: coordinate,
|
||||
itemStyle: {
|
||||
color: coordString === formState.value.device_coordinate ? "#0000FF" : "#b02a02",
|
||||
color:
|
||||
coordString === formState.value.device_coordinate
|
||||
? "#0000FF"
|
||||
: "#b02a02",
|
||||
},
|
||||
};
|
||||
});
|
||||
@ -75,13 +71,14 @@ const defaultOption = (map, data = []) => {
|
||||
watch(currentFloor, (newValue) => {
|
||||
if (newValue?.floor_map_name) {
|
||||
const coordinates =
|
||||
(totalCoordinates.value?.[newValue.floor_guid]?.filter(
|
||||
totalCoordinates.value?.[newValue.floor_guid]?.filter(
|
||||
(coord) => coord !== ""
|
||||
) || []);
|
||||
) || [];
|
||||
|
||||
parsedCoordinates.value = coordinates.length > 0
|
||||
? coordinates.map((coord) => JSON.parse(coord))
|
||||
: [];
|
||||
parsedCoordinates.value =
|
||||
coordinates.length > 0
|
||||
? coordinates.map((coord) => JSON.parse(coord))
|
||||
: [];
|
||||
|
||||
asset_floor_chart.value.updateSvg(
|
||||
{
|
||||
@ -92,12 +89,6 @@ watch(currentFloor, (newValue) => {
|
||||
);
|
||||
}
|
||||
});
|
||||
const floors = ref([]);
|
||||
|
||||
const getFloors = async () => {
|
||||
const res = await getAssetFloorList();
|
||||
floors.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||
};
|
||||
|
||||
watch(
|
||||
formState,
|
||||
@ -120,136 +111,31 @@ const getCoordinate = (position) => {
|
||||
formState.value.device_coordinate = JSON.stringify(position);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getFloors();
|
||||
});
|
||||
|
||||
// modal
|
||||
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();
|
||||
};
|
||||
|
||||
const form = ref(null);
|
||||
const FloorFormState = ref({
|
||||
full_name: "",
|
||||
floorFile: [],
|
||||
});
|
||||
|
||||
const floorScheme = yup.object({
|
||||
full_name: yup.string().required(t("button.required")),
|
||||
floorFile: yup.array(),
|
||||
});
|
||||
|
||||
const updateFileList = (files) => {
|
||||
console.log("file", files);
|
||||
FloorFormState.value.floorFile = files;
|
||||
};
|
||||
|
||||
const {
|
||||
formErrorMsg: floorFormErrorMsg,
|
||||
handleSubmit,
|
||||
handleErrorReset,
|
||||
updateScheme,
|
||||
} = useFormErrorMessage(floorScheme);
|
||||
const onOk = async () => {
|
||||
const value = handleSubmit(floorScheme, FloorFormState.value);
|
||||
const formData = new FormData(form.value);
|
||||
formData.append("floor_guid", selectedOption.value === "add" ? null :currentFloor.value.floor_guid);
|
||||
formData.append("building_tag", store.selectedBuilding.building_tag);
|
||||
formData.append("initMapName", FloorFormState.value.floorFile[0]?.name);
|
||||
formData.append("mapFile", FloorFormState.value.floorFile[0]);
|
||||
formData.delete("floorFile");
|
||||
for (let [key, value] of formData) {
|
||||
console.log(key, value);
|
||||
}
|
||||
|
||||
const res = await postAssetFloor(formData);
|
||||
if (res.isSuccess) {
|
||||
getFloors();
|
||||
onCancel();
|
||||
}
|
||||
};
|
||||
const onDelete = async () => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "#asset_add_table_item", async () => {
|
||||
await cancelToastOpen();
|
||||
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");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
FloorFormState.value = {
|
||||
full_name: "",
|
||||
floorFile: [],
|
||||
};
|
||||
asset_add_floor.close();
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 平面圖 -->
|
||||
|
||||
<div class="flex gap-4 mb-5">
|
||||
<div className="join w-80 mb-4">
|
||||
<Select
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info rounded-r-none"
|
||||
name="floor_guid"
|
||||
Attribute="full_name"
|
||||
:options="floors"
|
||||
:isBottomLabelExist="false"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
||||
</Select>
|
||||
<select
|
||||
v-model="selectedOption"
|
||||
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>
|
||||
</div>
|
||||
<Select
|
||||
:value="formState"
|
||||
selectClass="border-info focus-within:border-info"
|
||||
name="floor_guid"
|
||||
Attribute="full_name"
|
||||
:options="floors"
|
||||
:isBottomLabelExist="false"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.floor") }}</template>
|
||||
</Select>
|
||||
<Input
|
||||
:value="formState"
|
||||
width="270"
|
||||
name="device_coordinate"
|
||||
:disabled="true"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.device_coordinate") }}</template>
|
||||
<template #topLeft>{{
|
||||
$t("assetManagement.device_coordinate")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.device_coordinate }}
|
||||
@ -276,47 +162,6 @@ const onCancel = () => {
|
||||
<p class="text-2xl">{{ $t("assetManagement.add_floor_text") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Modal
|
||||
id="asset_add_floor"
|
||||
:title="t('assetManagement.floor_plan')"
|
||||
:onCancel="onCancel"
|
||||
width="400"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form">
|
||||
<Input :value="FloorFormState" width="270" name="full_name">
|
||||
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ floorFormErrorMsg.full_name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Upload
|
||||
name="floorFile"
|
||||
:fileList="FloorFormState.floorFile"
|
||||
:getFileList="updateFileList"
|
||||
:multiple="false"
|
||||
class="col-span-2"
|
||||
formats="svg"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||
</Upload>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-outline-success" @click="onOk">
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template></Modal
|
||||
>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
getAssetFloorList,
|
||||
getElecTypeList,
|
||||
} from "@/apis/asset";
|
||||
import { getReport } from "@/apis/energy";
|
||||
import { getReport, getExcel } from "@/apis/energy";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
@ -83,6 +83,10 @@ const onSearch = async () => {
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
const onExcel = async () => {
|
||||
const res = await getExcel(formState.value);
|
||||
};
|
||||
|
||||
// 監聽部門選取
|
||||
watch(
|
||||
selectedDeptItems,
|
||||
@ -176,7 +180,7 @@ onMounted(() => {
|
||||
<font-awesome-icon :icon="['fas', 'search']" class="text-lg" />
|
||||
{{ $t("button.search") }}
|
||||
</button>
|
||||
<button class="btn btn-export" @click.stop.prevent="search">
|
||||
<button class="btn btn-export" @click.stop.prevent="onExcel">
|
||||
<font-awesome-icon :icon="['fas', 'download']" class="text-lg" />
|
||||
{{ $t("button.export") }}
|
||||
</button>
|
||||
|
@ -1,11 +1,12 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import { inject, computed, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import dayjs from "dayjs";
|
||||
const { t } = useI18n();
|
||||
const { tableData, loading } = inject("energy_table_data");
|
||||
|
||||
const route = useRoute();
|
||||
// 預設的欄位
|
||||
const defaultColumns = ref([
|
||||
{
|
||||
@ -29,59 +30,64 @@ const defaultColumns = ref([
|
||||
|
||||
// 動態計算欄位
|
||||
const columns = computed(() => {
|
||||
if (!tableData.value || tableData.value.length === 0) {
|
||||
return [...defaultColumns.value, {
|
||||
title: t("energy.subtotal"),
|
||||
key: "subtotal",
|
||||
}];
|
||||
}
|
||||
// 複製預設欄位
|
||||
const dynamicColumns = [...defaultColumns.value];
|
||||
|
||||
// 取得第一個資料的 data 屬性,以此為準
|
||||
const firstDataItem = tableData.value[0];
|
||||
if (firstDataItem && firstDataItem.data) {
|
||||
// 根據 data 屬性動態生成欄位
|
||||
firstDataItem.data.forEach((item, index) => {
|
||||
// 使用 dayjs 和 format 格式化日期
|
||||
const formattedTime = dayjs(item.time).format("MM-DD");
|
||||
dynamicColumns.push({
|
||||
title: formattedTime,
|
||||
key: `data[${index}].value`,
|
||||
if (tableData.value && tableData.value.length > 0) {
|
||||
const firstDataItem = tableData.value[0];
|
||||
if (firstDataItem && firstDataItem.data) {
|
||||
firstDataItem.data.forEach((item, index) => {
|
||||
let formatString = "MM/DD"; // 預設格式
|
||||
switch (route.params.type) {
|
||||
case "3":
|
||||
formatString = "YYYY/MM";
|
||||
break;
|
||||
case "4":
|
||||
formatString = "YYYY";
|
||||
break;
|
||||
default:
|
||||
formatString = "MM/DD"; // case 1 和 case 2 都是 "MM-DD"
|
||||
break;
|
||||
}
|
||||
const formattedTime = dayjs(item.time).format(formatString);
|
||||
// 為每個 data 欄位創建一個唯一的 key,例如 "data_0", "data_1" 等
|
||||
dynamicColumns.push({
|
||||
title: formattedTime,
|
||||
key: `data_${index}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
dynamicColumns.push({
|
||||
title: t("energy.subtotal"),
|
||||
key: "subtotal",
|
||||
});
|
||||
|
||||
dynamicColumns.push({
|
||||
title: t("energy.subtotal"),
|
||||
key: "subtotal",
|
||||
});
|
||||
|
||||
return dynamicColumns;
|
||||
});
|
||||
|
||||
|
||||
// 動態調整 subtotal 值
|
||||
// 動態調整 subtotal 值和新增 data 屬性
|
||||
const dataSource = computed(() => {
|
||||
if (!tableData.value || tableData.value.length === 0) return [];
|
||||
|
||||
if(!tableData.value || tableData.value.length === 0) return [];
|
||||
|
||||
return tableData.value.map(item => {
|
||||
let subtotalValue = 0;
|
||||
if(item.data && item.data.length > 0) {
|
||||
item.data.forEach((dataItem) => {
|
||||
subtotalValue += parseFloat(dataItem.value || 0);
|
||||
});
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
subtotal: subtotalValue.toFixed(2)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
return tableData.value.map((item) => {
|
||||
let subtotalValue = 0;
|
||||
const newData = {}; // 用於儲存 data 的值,方便 Table 組件讀取
|
||||
if (item.data && item.data.length > 0) {
|
||||
item.data.forEach((dataItem, index) => {
|
||||
const value = parseFloat(dataItem.value || 0);
|
||||
subtotalValue += value;
|
||||
// 將值儲存在 newData 中,key 與 columns 中的 key 對應
|
||||
newData[`data_${index}`] = dataItem.value;
|
||||
});
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
...newData, // 將 data 的值合併到 item 中
|
||||
subtotal: subtotalValue.toFixed(2),
|
||||
};
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -26,9 +26,7 @@ const updateDataSource = (data) => {
|
||||
finish_time: d?.finish_time
|
||||
? dayjs(d?.finish_time).format("YYYY-MM-DD")
|
||||
: "",
|
||||
updated_at: d?.updated_at
|
||||
? dayjs(d?.updated_at).format("YYYY-MM-DD")
|
||||
: "",
|
||||
updated_at: d?.updated_at ? dayjs(d?.updated_at).format("YYYY-MM-DD") : "",
|
||||
}));
|
||||
};
|
||||
|
||||
@ -105,7 +103,7 @@ const getAllOptions = async (id = "") => {
|
||||
|
||||
const openModal = async (id = "") => {
|
||||
console.log("open modal", id);
|
||||
if(searchParams.value?.work_type < 3){
|
||||
if (searchParams.value?.work_type < 3) {
|
||||
getAllOptions(id);
|
||||
}
|
||||
operation_action_item.showModal();
|
||||
@ -115,11 +113,7 @@ const tableLoading = ref(false);
|
||||
const search = async () => {
|
||||
tableLoading.value = true;
|
||||
let res;
|
||||
if (searchParams.value?.work_type < 3) {
|
||||
res = await getOperationRecord(searchParams.value);
|
||||
} else {
|
||||
res = await getOperationCompanyList();
|
||||
}
|
||||
res = await getOperationRecord(searchParams.value);
|
||||
updateDataSource(res.data);
|
||||
tableLoading.value = false;
|
||||
};
|
||||
|
@ -61,10 +61,7 @@ watch(
|
||||
([newValue, newSelected]) => {
|
||||
const keys = Object.keys(newValue).filter((v) => v !== "search_type");
|
||||
|
||||
if (newValue.work_type === "3") {
|
||||
isSearchDisabled.value = false;
|
||||
initSubmit(true);
|
||||
} else if (newSelected) {
|
||||
if (newSelected) {
|
||||
isSearchDisabled.value = !searchParams.value.sub_system_tag;
|
||||
initSubmit(searchParams.value.sub_system_tag);
|
||||
}
|
||||
@ -81,7 +78,6 @@ watch(
|
||||
:items="submitBtns"
|
||||
:withLine="false"
|
||||
:withBtnClass="true"
|
||||
v-if="selectedWorkType?.work_type !== 3"
|
||||
/>
|
||||
|
||||
<button class="btn btn-add" @click.stop.prevent="() => openModal()">
|
||||
|
@ -42,17 +42,7 @@ const setButtonItems = () => {
|
||||
active: searchParams.value.work_type === "1",
|
||||
work_type: 1,
|
||||
params: ["work_type", "sub_system_tag"],
|
||||
},
|
||||
{
|
||||
title: t("operation.company_info"),
|
||||
key: "company_info",
|
||||
active: false,
|
||||
active:
|
||||
searchParams.value.work_type === null ||
|
||||
searchParams.value.work_type === "3",
|
||||
work_type: 3,
|
||||
params: [],
|
||||
},
|
||||
}
|
||||
]);
|
||||
|
||||
setSearchTypesItems([
|
||||
@ -85,21 +75,17 @@ watch(
|
||||
[selectedWorkType, selectedSearchType],
|
||||
([newWorkType, newSearchType]) => {
|
||||
if (newWorkType && newSearchType) {
|
||||
if (newWorkType.work_type === 3) {
|
||||
changeParams({ work_type: newWorkType?.work_type });
|
||||
} else {
|
||||
const oldSearchParams = Object.fromEntries(
|
||||
[...newWorkType?.params, ...newSearchType?.params].map((key) => [
|
||||
key,
|
||||
searchParams.value[key] ?? null,
|
||||
])
|
||||
);
|
||||
changeParams({
|
||||
...oldSearchParams,
|
||||
work_type: newWorkType?.work_type,
|
||||
search_type: newSearchType?.key,
|
||||
});
|
||||
}
|
||||
const oldSearchParams = Object.fromEntries(
|
||||
[...newWorkType?.params, ...newSearchType?.params].map((key) => [
|
||||
key,
|
||||
searchParams.value[key] ?? null,
|
||||
])
|
||||
);
|
||||
changeParams({
|
||||
...oldSearchParams,
|
||||
work_type: newWorkType?.work_type,
|
||||
search_type: newSearchType?.key,
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -119,12 +105,9 @@ watch(
|
||||
}
|
||||
"
|
||||
/>
|
||||
<OperationSearchSubSys
|
||||
:class="twMerge(selectedWorkType?.work_type === 3 ? 'hidden' : 'block')"
|
||||
/>
|
||||
<OperationSearchSubSys />
|
||||
<div class="w-full flex flex-wrap items-center justify-start">
|
||||
<ButtonGroup
|
||||
v-if="selectedWorkType?.work_type !== 3"
|
||||
:class="twMerge('mr-3')"
|
||||
:items="searchTypesItems"
|
||||
:withLine="true"
|
||||
@ -135,8 +118,7 @@ watch(
|
||||
"
|
||||
/>
|
||||
|
||||
<OperationSearchType :selected="selectedSearchType"
|
||||
v-if="selectedWorkType?.work_type !== 3" />
|
||||
<OperationSearchType :selected="selectedSearchType" />
|
||||
<OperationActionButton :selectedWorkType="selectedWorkType" />
|
||||
</div>
|
||||
</form>
|
||||
|
@ -128,7 +128,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<ButtonGroup
|
||||
v-if="selected?.key === 'date' && searchParams?.work_type !== '3'"
|
||||
v-if="selected?.key === 'date' "
|
||||
:items="DateItems"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
@ -140,7 +140,7 @@ watch(
|
||||
<DateGroup
|
||||
:isTopLabelExist="false"
|
||||
:isBottomLabelExist="false"
|
||||
v-if="selected?.key === 'date' && searchParams?.work_type !== '3'"
|
||||
v-if="selected?.key === 'date' "
|
||||
:items="searchDateItems"
|
||||
:withLine="true"
|
||||
/>
|
||||
|
@ -1,9 +1,8 @@
|
||||
<script setup>
|
||||
import { getFIX_COL, getCOM_COL } from "../constant/OperationTableColumns";
|
||||
import { getFIX_COL } from "../constant/OperationTableColumns";
|
||||
import { defineProps, computed, inject, watch } from "vue";
|
||||
import {
|
||||
deleteOperationRecord,
|
||||
deleteOperationCompany,
|
||||
deleteOperationRecord
|
||||
} from "@/apis/operation";
|
||||
import useSearchParam from "@/hooks/useSearchParam";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@ -16,7 +15,7 @@ const { dataSource, openModal, updateEditRecord, search, tableLoading } =
|
||||
inject("operation_table");
|
||||
|
||||
const columns = computed(() =>
|
||||
searchParams.value?.work_type < 3 ? getFIX_COL(t) : getCOM_COL(t)
|
||||
getFIX_COL(t)
|
||||
);
|
||||
|
||||
const changeData = (record) => {
|
||||
@ -29,11 +28,7 @@ const deleteItem = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
let res;
|
||||
if (searchParams.value?.work_type < 3) {
|
||||
res = await deleteOperationRecord(id);
|
||||
} else {
|
||||
res = await deleteOperationCompany(id);
|
||||
}
|
||||
res = await deleteOperationRecord(id);
|
||||
if (res.isSuccess) {
|
||||
search();
|
||||
openToast("success", t("msg.delete_success"));
|
||||
|
@ -4,8 +4,6 @@ import useSearchParam from "@/hooks/useSearchParam";
|
||||
import dayjs from "dayjs";
|
||||
import {
|
||||
postOperationRecord,
|
||||
postOperationCompany,
|
||||
updateOperationCompany,
|
||||
} from "@/apis/operation";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
@ -71,17 +69,6 @@ const formState = ref([
|
||||
},
|
||||
]);
|
||||
|
||||
let companySchema = yup.object({
|
||||
name: yup.string().required(t("button.required")),
|
||||
contact_person: yup.string().nullable(true),
|
||||
email: yup.string().email().nullable(true),
|
||||
phone: yup.string().nullable(true),
|
||||
city: yup.string().nullable(true),
|
||||
address: yup.string().nullable(true),
|
||||
tax_id_number: yup.string().nullable(true),
|
||||
remark: yup.string().nullable(true),
|
||||
});
|
||||
|
||||
let maintainSchema = yup.object({
|
||||
formId: yup.string().required(t("button.required")),
|
||||
work_type: yup.string().required(t("button.required")),
|
||||
@ -168,36 +155,9 @@ const saveMaintain = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
formErrorMsg: companyFormErrorMsg,
|
||||
handleSubmit: handleCompanySubmit,
|
||||
handleErrorReset: handleCompanyErrorReset,
|
||||
} = useFormErrorMessage(companySchema);
|
||||
const saveCompany = async () => {
|
||||
const value = await handleCompanySubmit(
|
||||
companySchema,
|
||||
formState.value[searchParams.value?.work_type - 1]
|
||||
);
|
||||
let res;
|
||||
if (props.editRecord) {
|
||||
res = await updateOperationCompany(value);
|
||||
} else {
|
||||
res = await postOperationCompany(value);
|
||||
}
|
||||
if (res.isSuccess) {
|
||||
search();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#operation_action_item");
|
||||
}
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
if (searchParams.value.work_type < 3) {
|
||||
saveMaintain();
|
||||
} else {
|
||||
saveCompany();
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
@ -221,7 +181,6 @@ const onCancel = () => {
|
||||
updateEditRecord?.(null);
|
||||
reset();
|
||||
handleMaintainErrorReset();
|
||||
handleCompanyErrorReset();
|
||||
operation_action_item.close();
|
||||
};
|
||||
|
||||
@ -469,139 +428,6 @@ watch(
|
||||
<template #topLeft>{{ $t("operation.upload_file") }}</template>
|
||||
</Upload>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'name')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="name"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.name }}
|
||||
</span></template
|
||||
>
|
||||
</Input>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(
|
||||
formState[searchParams?.work_type - 1],
|
||||
'contact_person'
|
||||
)
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="contact_person"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.contact_person") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.contact_person }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'phone')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="phone"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.phone") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.phone }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'email')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="email"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.email") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.email }}
|
||||
</span></template
|
||||
>
|
||||
</Input>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'city')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2 w-[250px]"
|
||||
name="city"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.city") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.city }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'address')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2 w-4/5"
|
||||
name="address"
|
||||
>
|
||||
<template #topLeft>{{ $t("operation.address") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.address }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(
|
||||
formState[searchParams?.work_type - 1],
|
||||
'tax_id_number'
|
||||
)
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="tax_id_number"
|
||||
>
|
||||
<template #topLeft> {{ $t("operation.tax_id_number") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ companyFormErrorMsg.tax_id_number }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input
|
||||
v-if="
|
||||
searchParams?.work_type &&
|
||||
Object.hasOwn(formState[searchParams?.work_type - 1], 'remark')
|
||||
"
|
||||
:value="formState[searchParams?.work_type - 1]"
|
||||
class="my-2"
|
||||
name="remark"
|
||||
>
|
||||
<template #topLeft>
|
||||
{{ $t("operation.remark") }}
|
||||
</template></Input
|
||||
>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
|
@ -55,31 +55,3 @@ export const getFIX_COL = (t) => [
|
||||
width: 250,
|
||||
},
|
||||
];
|
||||
|
||||
export const getCOM_COL = (t) => [
|
||||
{
|
||||
title: t("operation.vendor"),
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: t("operation.contact_person"),
|
||||
key: "contact_person",
|
||||
},
|
||||
{
|
||||
title: t("operation.phone"),
|
||||
key: "phone",
|
||||
},
|
||||
{
|
||||
title: t("operation.email"),
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: t("operation.created_at"),
|
||||
key: "created_at",
|
||||
},
|
||||
{
|
||||
title: t("operation.operation"),
|
||||
key: "operation",
|
||||
width: 250,
|
||||
},
|
||||
];
|
||||
|
47
src/views/setting/SettingManagement.vue
Normal file
47
src/views/setting/SettingManagement.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import Dept from "./components/Dept.vue";
|
||||
import ElecType from "./components/ElecType.vue";
|
||||
import Vendor from "./components/Vendor.vue";
|
||||
import Floors from "./components/Floors.vue";
|
||||
import MITTList from "./components/MITTList.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const currentComponent = ref(null);
|
||||
|
||||
const updateComponent = () => {
|
||||
const { main_system_id, sub_system_id } = route.params;
|
||||
|
||||
if (main_system_id === "Setting") {
|
||||
if (sub_system_id === "Department") {
|
||||
currentComponent.value = Dept;
|
||||
} else if(sub_system_id === "ElecType") {
|
||||
currentComponent.value = ElecType;
|
||||
} else if(sub_system_id === "Vendor") {
|
||||
currentComponent.value = Vendor;
|
||||
} else if(sub_system_id === "Floor") {
|
||||
currentComponent.value = Floors;
|
||||
}else{
|
||||
currentComponent.value = MITTList;
|
||||
}
|
||||
} else if (main_system_id === "MQTT") {
|
||||
currentComponent.value = MITTList;
|
||||
} else {
|
||||
currentComponent.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(updateComponent);
|
||||
|
||||
watch(
|
||||
() => route.params,
|
||||
() => {
|
||||
updateComponent();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<component :is="currentComponent" />
|
||||
</template>
|
104
src/views/setting/components/Dept.vue
Normal file
104
src/views/setting/components/Dept.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import DeptModal from "./DeptModal.vue";
|
||||
import { getDepartmentList, deleteDepartmentItem } from "@/apis/asset";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.department_name"),
|
||||
key: "name",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: t("accountManagement.operation"),
|
||||
key: "operation",
|
||||
},
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
const res = await getDepartmentList();
|
||||
dataSource.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
id: 0,
|
||||
name: "",
|
||||
});
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record.id) {
|
||||
formState.value = { ...record };
|
||||
} else {
|
||||
formState.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
};
|
||||
}
|
||||
dept_modal.showModal();
|
||||
};
|
||||
|
||||
const remove = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteDepartmentItem(id);
|
||||
if (res.isSuccess) {
|
||||
getDataSource();
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">{{ $t("assetManagement.department") }}</h3>
|
||||
<DeptModal
|
||||
:formState="formState"
|
||||
:getData="getDataSource"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.id)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
84
src/views/setting/components/DeptModal.vue
Normal file
84
src/views/setting/components/DeptModal.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps, inject, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postDepartmentList } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
openModal: Function
|
||||
});
|
||||
|
||||
const deptScheme = yup.object({
|
||||
name: yup.string().required(t("button.required")),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(deptScheme);
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
dept_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(deptScheme, props.formState);
|
||||
const res = await postDepartmentList(value);
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#dept_modal");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-add " @click.stop.prevent="props.openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="dept_modal"
|
||||
:title="props.formState?.id ? t('button.edit') : t('button.add')"
|
||||
:onCancel="onCancel"
|
||||
width="400"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="name">
|
||||
<template #topLeft>{{
|
||||
$t("assetManagement.department_name")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
104
src/views/setting/components/ElecType.vue
Normal file
104
src/views/setting/components/ElecType.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import ElecTypeModal from "./ElecTypeModal.vue";
|
||||
import { getElecTypeList, deleteElecTypeItem } from "@/apis/asset";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: t("energy.electricity_classification"),
|
||||
key: "name",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: t("accountManagement.operation"),
|
||||
key: "operation",
|
||||
},
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
const res = await getElecTypeList();
|
||||
dataSource.value = res.data.map((d) => ({ ...d, key: d.id }));
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
id: 0,
|
||||
name: "",
|
||||
});
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record.id) {
|
||||
formState.value = { ...record };
|
||||
} else {
|
||||
formState.value = {
|
||||
id: 0,
|
||||
name: "",
|
||||
};
|
||||
}
|
||||
elec_modal.showModal();
|
||||
};
|
||||
|
||||
const remove = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteElecTypeItem(id);
|
||||
if (res.isSuccess) {
|
||||
getDataSource();
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">{{ $t("energy.electricity_classification") }}</h3>
|
||||
<ElecTypeModal
|
||||
:formState="formState"
|
||||
:getData="getDataSource"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.id)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
84
src/views/setting/components/ElecTypeModal.vue
Normal file
84
src/views/setting/components/ElecTypeModal.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps, inject, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postElecTypeList } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
openModal: Function
|
||||
});
|
||||
|
||||
const deptScheme = yup.object({
|
||||
name: yup.string().required(t("button.required")),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(deptScheme);
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
elec_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(deptScheme, props.formState);
|
||||
const res = await postElecTypeList(value);
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#elec_modal");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-add " @click.stop.prevent="props.openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="elec_modal"
|
||||
:title="props.formState?.id ? t('button.edit') : t('button.add')"
|
||||
:onCancel="onCancel"
|
||||
width="400"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="name">
|
||||
<template #topLeft>{{
|
||||
$t("energy.electricity_classification")
|
||||
}}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
114
src/views/setting/components/Floors.vue
Normal file
114
src/views/setting/components/Floors.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import FloorsModal from "./FloorsModal.vue";
|
||||
import { getAssetFloorList, deleteAssetFloor } from "@/apis/asset";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const FILE_BASEURL = import.meta.env.VITE_FILE_API_BASEURL;
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("accountManagement.index"),
|
||||
key: "index",
|
||||
},
|
||||
{
|
||||
title: t("energy.floor"),
|
||||
key: "full_name",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: t("assetManagement.floor_plan"),
|
||||
key: "floor_plan",
|
||||
},
|
||||
{
|
||||
title: t("accountManagement.operation"),
|
||||
key: "operation",
|
||||
},
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
const res = await getAssetFloorList();
|
||||
dataSource.value = res.data[0]?.floors.map((d) => ({ ...d, key: d.floor_guid }));
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
full_name: "",
|
||||
floorFile: [],
|
||||
});
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record.floor_guid) {
|
||||
formState.value = { ...record };
|
||||
} else {
|
||||
formState.value = {
|
||||
full_name: "",
|
||||
floorFile: [],
|
||||
};
|
||||
}
|
||||
floor_modal.showModal();
|
||||
};
|
||||
|
||||
const remove = async () => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteAssetFloor({
|
||||
floor_guid: formState.value.floor_guid,
|
||||
});
|
||||
if (res.isSuccess) {
|
||||
getDataSource();
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">{{ $t("energy.floor") }}</h3>
|
||||
<FloorsModal
|
||||
:formState="formState"
|
||||
:getData="getDataSource"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove()"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'floor_plan'">
|
||||
<img :src="`${FILE_BASEURL}/${record.floor_map_url}.svg`" class="w-[200px] bg-white mx-auto" />
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
108
src/views/setting/components/FloorsModal.vue
Normal file
108
src/views/setting/components/FloorsModal.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps, inject, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import { postAssetFloor } from "@/apis/asset";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
openModal: Function,
|
||||
});
|
||||
const form = ref(null);
|
||||
|
||||
const floorScheme = yup.object({
|
||||
full_name: yup.string().required(t("button.required")),
|
||||
floorFile: yup.array(),
|
||||
});
|
||||
|
||||
const updateFileList = (files) => {
|
||||
console.log("file", files);
|
||||
FloorFormState.value.floorFile = files;
|
||||
};
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(floorScheme);
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
floor_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(floorScheme, props.formState);
|
||||
const formData = new FormData(form.value);
|
||||
formData.append(
|
||||
"floor_guid",
|
||||
selectedOption.value === "add" ? null : currentFloor.value.floor_guid
|
||||
);
|
||||
formData.append("building_tag", store.selectedBuilding.building_tag);
|
||||
formData.append("initMapName", FloorFormState.value.floorFile[0]?.name);
|
||||
formData.append("mapFile", FloorFormState.value.floorFile[0]);
|
||||
formData.delete("floorFile");
|
||||
const res = await postAssetFloor(formData);
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#floor_modal");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-add" @click.stop.prevent="props.openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="floor_modal"
|
||||
:title="props.formState?.floor_guid ? t('button.edit') : t('button.add')"
|
||||
:onCancel="onCancel"
|
||||
width="400"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="full_name">
|
||||
<template #topLeft>{{ $t("assetManagement.system_name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Upload
|
||||
name="floorFile"
|
||||
:fileList="formState.floorFile"
|
||||
:getFileList="updateFileList"
|
||||
:multiple="false"
|
||||
class="col-span-2"
|
||||
formats="svg"
|
||||
>
|
||||
<template #topLeft>{{ $t("assetManagement.oriFile") }}</template>
|
||||
</Upload>
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
144
src/views/setting/components/MITTList.vue
Normal file
144
src/views/setting/components/MITTList.vue
Normal file
@ -0,0 +1,144 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, computed } from "vue";
|
||||
import { getAssetMainList, getAssetSubList, getIOTSchema } from "@/apis/asset";
|
||||
import useActiveBtn from "@/hooks/useActiveBtn";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const formState = ref({});
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
// 系統類別
|
||||
const {
|
||||
items: mainSysItems,
|
||||
changeActiveBtn: changeMainSysActiveBtn,
|
||||
setItems: setMainSysItems,
|
||||
selectedBtn: selectedMainSysItems,
|
||||
} = useActiveBtn();
|
||||
// 設備類別
|
||||
const {
|
||||
items: subSysItems,
|
||||
changeActiveBtn: changeSubSysActiveBtn,
|
||||
setItems: setSubSysItems,
|
||||
selectedBtn: selectedSubSysItems,
|
||||
} = useActiveBtn();
|
||||
|
||||
const getMainSystems = async () => {
|
||||
const res = await getAssetMainList();
|
||||
const cate = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.system_key,
|
||||
key: d.id,
|
||||
active: index === 0,
|
||||
}));
|
||||
setMainSysItems(cate);
|
||||
};
|
||||
|
||||
const getSubSystems = async (id) => {
|
||||
const res = await getAssetSubList(id);
|
||||
const sub = res.data.map((d, index) => ({
|
||||
...d,
|
||||
title: d.system_key,
|
||||
key: d.id,
|
||||
active: index === 0,
|
||||
}));
|
||||
setSubSysItems(sub);
|
||||
};
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: "schema",
|
||||
key: "schema_name",
|
||||
},
|
||||
{
|
||||
title: "point",
|
||||
key: "pointOrg",
|
||||
filter: true,
|
||||
},
|
||||
{
|
||||
title: "Description",
|
||||
key: "description",
|
||||
},
|
||||
]);
|
||||
|
||||
const getDataSource = async (id) => {
|
||||
loading.value = true;
|
||||
const res = await getIOTSchema(id);
|
||||
dataSource.value = res.data.flatMap((d) =>
|
||||
d.details.map((detail) => ({
|
||||
key: `${d.id}-${detail.pointOrg}`,
|
||||
schema_name: d.name,
|
||||
pointOrg: detail.pointOrg,
|
||||
description: detail.describe,
|
||||
}))
|
||||
);
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
selectedMainSysItems,
|
||||
(newValue) => {
|
||||
if (newValue && newValue.key) {
|
||||
getSubSystems(parseInt(newValue.key));
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
selectedSubSysItems,
|
||||
(newValue) => {
|
||||
if (newValue && newValue.key) {
|
||||
getDataSource(parseInt(newValue.key));
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
getMainSystems();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<h2 class="text-lg font-bold whitespace-nowrap">
|
||||
{{ $t("history.system_category") }} :
|
||||
</h2>
|
||||
<ButtonGroup
|
||||
:items="mainSysItems"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeMainSysActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<h2 class="text-lg font-bold whitespace-nowrap">
|
||||
{{ $t("history.device_category") }} :
|
||||
</h2>
|
||||
<ButtonGroup
|
||||
:items="subSysItems"
|
||||
:withLine="true"
|
||||
:onclick="
|
||||
(e, item) => {
|
||||
changeSubSysActiveBtn(item);
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:columns="columns"
|
||||
:dataSource="dataSource"
|
||||
:loading="loading"
|
||||
></Table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
128
src/views/setting/components/Vendor.vue
Normal file
128
src/views/setting/components/Vendor.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<script setup>
|
||||
import Table from "@/components/customUI/Table.vue";
|
||||
import VendorModal from "./VendorModal.vue";
|
||||
import { getOperationCompanyList,deleteOperationCompany } from "@/apis/operation";
|
||||
import { onMounted, ref, inject, computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast, cancelToastOpen } = inject("app_toast");
|
||||
|
||||
const columns = computed(() => [
|
||||
{
|
||||
title: t("operation.vendor"),
|
||||
key: "name",
|
||||
},
|
||||
{
|
||||
title: t("operation.contact_person"),
|
||||
key: "contact_person",
|
||||
},
|
||||
{
|
||||
title: t("operation.phone"),
|
||||
key: "phone",
|
||||
},
|
||||
{
|
||||
title: t("operation.email"),
|
||||
key: "email",
|
||||
},
|
||||
{
|
||||
title: t("operation.created_at"),
|
||||
key: "created_at",
|
||||
},
|
||||
{
|
||||
title: t("operation.operation"),
|
||||
key: "operation",
|
||||
width: 250,
|
||||
},
|
||||
]);
|
||||
|
||||
const dataSource = ref([]);
|
||||
const loading = ref(false);
|
||||
|
||||
const getDataSource = async () => {
|
||||
loading.value = true;
|
||||
const res = await getOperationCompanyList();
|
||||
dataSource.value = res.data;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getDataSource();
|
||||
});
|
||||
|
||||
const formState = ref({
|
||||
contact_person: "",
|
||||
email: "",
|
||||
name: "",
|
||||
phone: "",
|
||||
remark: "",
|
||||
tax_id_number: "",
|
||||
city: "",
|
||||
address: "",
|
||||
});
|
||||
|
||||
const openModal = (record) => {
|
||||
if (record.id) {
|
||||
formState.value = { ...record };
|
||||
} else {
|
||||
formState.value = {
|
||||
contact_person: "",
|
||||
email: "",
|
||||
name: "",
|
||||
phone: "",
|
||||
remark: "",
|
||||
tax_id_number: "",
|
||||
city: "",
|
||||
address: "",
|
||||
};
|
||||
}
|
||||
company_modal.showModal();
|
||||
};
|
||||
|
||||
const remove = async (id) => {
|
||||
openToast("warning", t("msg.sure_to_delete"), "body", async () => {
|
||||
await cancelToastOpen();
|
||||
const res = await deleteOperationCompany(id);
|
||||
if (res.isSuccess) {
|
||||
getDataSource();
|
||||
openToast("success", t("msg.delete_success"));
|
||||
} else {
|
||||
openToast("error", res.msg);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-start items-center mt-10 mb-5">
|
||||
<h3 class="text-xl mr-5">{{ $t("assetManagement.company") }}</h3>
|
||||
<VendorModal
|
||||
:formState="formState"
|
||||
:getData="getDataSource"
|
||||
:openModal="openModal"
|
||||
/>
|
||||
</div>
|
||||
<Table :columns="columns" :dataSource="dataSource" :loading="loading">
|
||||
<template #bodyCell="{ record, column, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.key === 'operation'">
|
||||
<button
|
||||
class="btn btn-sm btn-success text-white mr-2"
|
||||
@click.stop.prevent="() => openModal(record)"
|
||||
>
|
||||
{{ $t("button.edit") }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-error text-white"
|
||||
@click.stop.prevent="() => remove(record.id)"
|
||||
>
|
||||
{{ $t("button.delete") }}
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ record[column.key] }}
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped></style>
|
153
src/views/setting/components/VendorModal.vue
Normal file
153
src/views/setting/components/VendorModal.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, defineProps, inject, watch } from "vue";
|
||||
import * as yup from "yup";
|
||||
import "yup-phone-lite";
|
||||
import useFormErrorMessage from "@/hooks/useFormErrorMessage";
|
||||
import {
|
||||
postOperationCompany,
|
||||
updateOperationCompany,
|
||||
} from "@/apis/operation";
|
||||
import { useI18n } from "vue-i18n";
|
||||
const { t } = useI18n();
|
||||
const { openToast } = inject("app_toast");
|
||||
|
||||
const props = defineProps({
|
||||
formState: Object,
|
||||
getData: Function,
|
||||
openModal: Function
|
||||
});
|
||||
|
||||
const deptScheme = yup.object({
|
||||
name: yup.string().required(t("button.required")),
|
||||
contact_person: yup.string().nullable(true),
|
||||
email: yup.string().email().nullable(true),
|
||||
phone: yup.string().nullable(true),
|
||||
city: yup.string().nullable(true),
|
||||
address: yup.string().nullable(true),
|
||||
tax_id_number: yup.string().nullable(true),
|
||||
remark: yup.string().nullable(true),
|
||||
});
|
||||
|
||||
const { formErrorMsg, handleSubmit, handleErrorReset, updateScheme } =
|
||||
useFormErrorMessage(deptScheme);
|
||||
|
||||
const onCancel = () => {
|
||||
handleErrorReset();
|
||||
company_modal.close();
|
||||
};
|
||||
|
||||
const onOk = async () => {
|
||||
const value = await handleSubmit(deptScheme, props.formState);
|
||||
if (props.formState?.id) {
|
||||
res = await updateOperationCompany(value);
|
||||
} else {
|
||||
res = await postOperationCompany(value);
|
||||
}
|
||||
if (res.isSuccess) {
|
||||
props.getData();
|
||||
onCancel();
|
||||
} else {
|
||||
openToast("error", res.msg, "#company_modal");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="btn btn-sm btn-add " @click.stop.prevent="props.openModal">
|
||||
<font-awesome-icon :icon="['fas', 'plus']" />{{ $t("button.add") }}
|
||||
</button>
|
||||
<Modal
|
||||
id="company_modal"
|
||||
:title="props.formState?.id ? t('button.edit') : t('button.add')"
|
||||
:onCancel="onCancel"
|
||||
width="710"
|
||||
>
|
||||
<template #modalContent>
|
||||
<form ref="form" class="mt-5 w-full flex flex-wrap justify-between">
|
||||
<Input :value="formState" class="my-2" name="name">
|
||||
<template #topLeft>{{ $t("operation.name") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.name }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="contact_person">
|
||||
<template #topLeft>{{ $t("operation.contact_person") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.contact_person }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="phone">
|
||||
<template #topLeft>{{ $t("operation.phone") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.phone }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="email">
|
||||
<template #topLeft>{{ $t("operation.email") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.email }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="city">
|
||||
<template #topLeft>{{ $t("operation.city") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.city }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="address">
|
||||
<template #topLeft>{{ $t("operation.address") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.address }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="tax_id_number">
|
||||
<template #topLeft>{{ $t("operation.tax_id_number") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.tax_id_number }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
<Input :value="formState" class="my-2" name="remark">
|
||||
<template #topLeft>{{ $t("operation.remark") }}</template>
|
||||
<template #bottomLeft
|
||||
><span class="text-error text-base">
|
||||
{{ formErrorMsg.remark }}
|
||||
</span></template
|
||||
></Input
|
||||
>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
<template #modalAction>
|
||||
<button
|
||||
type="reset"
|
||||
class="btn btn-outline-success mr-2"
|
||||
@click.prevent="onCancel"
|
||||
>
|
||||
{{ $t("button.cancel") }}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-outline-success"
|
||||
@click.stop.prevent="onOk"
|
||||
>
|
||||
{{ $t("button.submit") }}
|
||||
</button>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
Loading…
Reference in New Issue
Block a user