feat: 巡檢設定切版 | 公用組件: Alert、PaginatedTable
This commit is contained in:
parent
0e33a0b64f
commit
4237875453
29
package-lock.json
generated
29
package-lock.json
generated
@ -16,7 +16,8 @@
|
|||||||
"leaflet-tilelayer-mbtiles": "^1.4.1",
|
"leaflet-tilelayer-mbtiles": "^1.4.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1",
|
||||||
|
"vue3-signature": "^0.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
@ -1759,6 +1760,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/default-passive-events": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/default-passive-events/-/default-passive-events-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@ -3081,6 +3088,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/signature_pad": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-zYmjddQDolKgJnzYRoaMYaGezKaZbwjNBBwk1W7uVY0cyNWW30Izeu9BNVAGEgXvqB6APDJmf783oWTU7W67LQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@ -3474,6 +3487,20 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue3-signature": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue3-signature/-/vue3-signature-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-RvU0AqmFmZ/kTc90qbCPCGO5oavr8hidtcstLFwBZMyEpYKOvujHGzX9JO0eKkTVMEhV2gNzqtl/D9mOIHQctg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"default-passive-events": "^2.0.0",
|
||||||
|
"signature_pad": "^5.1.1",
|
||||||
|
"vue": "^3.2.37"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/webpack-virtual-modules": {
|
"node_modules/webpack-virtual-modules": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||||
|
|||||||
@ -17,7 +17,8 @@
|
|||||||
"leaflet-tilelayer-mbtiles": "^1.4.1",
|
"leaflet-tilelayer-mbtiles": "^1.4.1",
|
||||||
"pinia": "^3.0.3",
|
"pinia": "^3.0.3",
|
||||||
"vue": "^3.5.22",
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1",
|
||||||
|
"vue3-signature": "^0.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "^6.0.1",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
|
|||||||
10
src/App.vue
10
src/App.vue
@ -2,6 +2,8 @@
|
|||||||
import { ref, onMounted, onUnmounted } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
import { ElConfigProvider } from "element-plus";
|
import { ElConfigProvider } from "element-plus";
|
||||||
|
import zhTW from 'element-plus/es/locale/lang/zh-tw'
|
||||||
|
import GlobalAlerts from './components/Common/GlobalAlerts.vue';
|
||||||
import Navbar from "./components/Navbar.vue";
|
import Navbar from "./components/Navbar.vue";
|
||||||
import Sidebar from "./components/Sidebar.vue";
|
import Sidebar from "./components/Sidebar.vue";
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -30,24 +32,24 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-config-provider :size="size" :z-index="zIndex">
|
<el-config-provider :size="size" :z-index="zIndex" :locale="zhTW">
|
||||||
|
<GlobalAlerts placement="top-right" />
|
||||||
<div class="common-layout">
|
<div class="common-layout">
|
||||||
<el-container>
|
<el-container>
|
||||||
<template v-if="!isMobile">
|
<template v-if="!isMobile">
|
||||||
<el-aside :width="isCollapse ? '0px' : '240px'">
|
<el-aside :width="isCollapse ? '0px' : '240px'">
|
||||||
<Sidebar :isCollapse="isCollapse" />
|
<Sidebar v-model:isCollapse="isCollapse" :isMobile="isMobile" />
|
||||||
</el-aside>
|
</el-aside>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<el-drawer
|
<el-drawer
|
||||||
v-if="isMobile"
|
|
||||||
v-model="isCollapse"
|
v-model="isCollapse"
|
||||||
direction="ltr"
|
direction="ltr"
|
||||||
:with-header="false"
|
:with-header="false"
|
||||||
size="70%"
|
size="70%"
|
||||||
body-class="mobile-sidebar-drawer"
|
body-class="mobile-sidebar-drawer"
|
||||||
>
|
>
|
||||||
<Sidebar :isCollapse="isCollapse" />
|
<Sidebar v-model:isCollapse="isCollapse" :isMobile="isMobile" />
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
</template>
|
</template>
|
||||||
<el-container>
|
<el-container>
|
||||||
|
|||||||
9
src/components.d.ts
vendored
9
src/components.d.ts
vendored
@ -8,27 +8,35 @@ export {}
|
|||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
ElAside: typeof import('element-plus/es')['ElAside']
|
ElAside: typeof import('element-plus/es')['ElAside']
|
||||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
|
||||||
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
|
||||||
ElCard: typeof import('element-plus/es')['ElCard']
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
ElContainer: typeof import('element-plus/es')['ElContainer']
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
|
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||||
|
ElForm: typeof import('element-plus/es')['ElForm']
|
||||||
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||||
ElHeader: typeof import('element-plus/es')['ElHeader']
|
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||||
ElIcon: typeof import('element-plus/es')['ElIcon']
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
ElInput: typeof import('element-plus/es')['ElInput']
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||||
ElMain: typeof import('element-plus/es')['ElMain']
|
ElMain: typeof import('element-plus/es')['ElMain']
|
||||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||||
ElOption: typeof import('element-plus/es')['ElOption']
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
|
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
ElRow: typeof import('element-plus/es')['ElRow']
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
@ -39,6 +47,7 @@ declare module 'vue' {
|
|||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||||
|
ElTag: typeof import('element-plus/es')['ElTag']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/components/Common/GlobalAlerts.vue
Normal file
55
src/components/Common/GlobalAlerts.vue
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="alerts-container" :class="position">
|
||||||
|
<transition-group name="alert-slide" tag="div">
|
||||||
|
<el-alert
|
||||||
|
v-for="a in alerts"
|
||||||
|
:key="a.id"
|
||||||
|
:title="a.title"
|
||||||
|
:type="a.type"
|
||||||
|
:description="a.description"
|
||||||
|
:closable="a.closable"
|
||||||
|
:effect="a.effect || 'light'"
|
||||||
|
show-icon
|
||||||
|
@close="remove(a.id)"
|
||||||
|
class="alert-item"
|
||||||
|
/>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useAlert } from '../../composables/useAlert';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 位置:'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'
|
||||||
|
placement: { type: String, default: 'top-right' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { alerts, remove } = useAlert();
|
||||||
|
|
||||||
|
const position = computed(() => props.placement);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.alerts-container {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 3000;
|
||||||
|
width: 380px;
|
||||||
|
pointer-events: none; /* 讓下層可點擊;單個 alert 仍可關閉 */
|
||||||
|
}
|
||||||
|
.alerts-container :deep(.el-alert) {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.alert-item { margin-bottom: 10px; }
|
||||||
|
|
||||||
|
.top-right { top: 16px; right: 16px; }
|
||||||
|
.top-left { top: 16px; left: 16px; }
|
||||||
|
.bottom-right { bottom: 16px; right: 16px; }
|
||||||
|
.bottom-left { bottom: 16px; left: 16px; }
|
||||||
|
|
||||||
|
.alert-slide-enter-active,
|
||||||
|
.alert-slide-leave-active { transition: all .2s ease; }
|
||||||
|
.alert-slide-enter-from { opacity: 0; transform: translateY(-8px); }
|
||||||
|
.alert-slide-leave-to { opacity: 0; transform: translateY(-8px); }
|
||||||
|
</style>
|
||||||
120
src/components/Common/PaginatedTable.vue
Normal file
120
src/components/Common/PaginatedTable.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div class="paginated-table">
|
||||||
|
<div class="table-toolbar">
|
||||||
|
<slot name="toolbar" />
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
ref="tableRef"
|
||||||
|
:data="displayData"
|
||||||
|
v-bind="tableProps"
|
||||||
|
:row-key="rowKey"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<template #append>
|
||||||
|
<slot name="append" />
|
||||||
|
</template>
|
||||||
|
<template #empty>
|
||||||
|
<slot name="empty" />
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
<div class="table-pagination" v-if="showPagination">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="innerCurrentPage"
|
||||||
|
v-model:page-size="innerPageSize"
|
||||||
|
:total="resolvedTotal"
|
||||||
|
:page-sizes="pageSizes"
|
||||||
|
:layout="layout"
|
||||||
|
:background="background"
|
||||||
|
:size="size"
|
||||||
|
@current-change="onCurrentChange"
|
||||||
|
@size-change="onSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, ref, watch } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: { type: Array, required: true },
|
||||||
|
currentPage: { type: Number, default: 1 },
|
||||||
|
pageSize: { type: Number, default: 10 },
|
||||||
|
total: { type: Number, default: undefined },
|
||||||
|
pageSizes: { type: Array, default: () => [5, 10, 20, 50] },
|
||||||
|
layout: { type: String, default: "total, sizes, prev, pager, next, jumper" },
|
||||||
|
background: { type: Boolean, default: true },
|
||||||
|
size: { type: String, default: "small" },
|
||||||
|
rowKey: { type: [String, Function], default: undefined },
|
||||||
|
tableProps: { type: Object, default: () => ({}) },
|
||||||
|
remote: { type: Boolean, default: false },
|
||||||
|
showPagination: { type: Boolean, default: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
"update:currentPage",
|
||||||
|
"update:pageSize",
|
||||||
|
"page-change",
|
||||||
|
"size-change",
|
||||||
|
"fetch", // 遠端分頁時觸發
|
||||||
|
]);
|
||||||
|
|
||||||
|
const innerCurrentPage = ref(props.currentPage);
|
||||||
|
const innerPageSize = ref(props.pageSize);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.currentPage,
|
||||||
|
(v) => {
|
||||||
|
if (v !== innerCurrentPage.value) innerCurrentPage.value = v;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.pageSize,
|
||||||
|
(v) => {
|
||||||
|
if (v !== innerPageSize.value) innerPageSize.value = v;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const resolvedTotal = computed(() =>
|
||||||
|
typeof props.total === "number" ? props.total : props.data.length
|
||||||
|
);
|
||||||
|
|
||||||
|
const displayData = computed(() => {
|
||||||
|
if (props.remote) return props.data;
|
||||||
|
const start = (innerCurrentPage.value - 1) * innerPageSize.value;
|
||||||
|
return props.data.slice(start, start + innerPageSize.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onCurrentChange(page) {
|
||||||
|
emit("update:currentPage", page);
|
||||||
|
emit("page-change", { page, pageSize: innerPageSize.value });
|
||||||
|
if (props.remote) emit("fetch", { page, pageSize: innerPageSize.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSizeChange(size) {
|
||||||
|
emit("update:pageSize", size);
|
||||||
|
// 變更頁大小時通常回到第 1 頁
|
||||||
|
if (innerCurrentPage.value !== 1) {
|
||||||
|
innerCurrentPage.value = 1;
|
||||||
|
emit("update:currentPage", 1);
|
||||||
|
}
|
||||||
|
emit("size-change", { page: innerCurrentPage.value, pageSize: size });
|
||||||
|
if (props.remote)
|
||||||
|
emit("fetch", { page: innerCurrentPage.value, pageSize: size });
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableRef = ref();
|
||||||
|
defineExpose({ tableRef });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.table-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
.table-toolbar:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
109
src/components/PatrolSetting/AddTaskDialog.vue
Normal file
109
src/components/PatrolSetting/AddTaskDialog.vue
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增樣板"
|
||||||
|
modal-penetrable
|
||||||
|
style="max-width: 800px; width: 90%"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<!-- 選擇樣板 -->
|
||||||
|
<TemplateSelectDialog
|
||||||
|
v-model:visible="showTemplateDialog"
|
||||||
|
:templates="filteredTemplates"
|
||||||
|
@select="onTemplatePicked"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-form :model="form">
|
||||||
|
<el-form-item label="電廠選擇">
|
||||||
|
<el-select v-model="form.factory">
|
||||||
|
<el-option label="四磺子坪" value="四磺子坪" />
|
||||||
|
<el-option label="硫磺子坪" value="硫磺子坪" />
|
||||||
|
<el-option label="大清水" value="大清水" />
|
||||||
|
<el-option label="小清水" value="小清水" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="樣板名稱">
|
||||||
|
<el-input v-model="form.templateName">
|
||||||
|
<template #append>
|
||||||
|
<el-button type="success" @click="onSetTemplate">設定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="任務名稱">
|
||||||
|
<el-input v-model="form.taskName" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="填寫頻率">
|
||||||
|
<el-select v-model="form.frequency" placeholder="請選擇頻率">
|
||||||
|
<el-option label="每日" value="每日" />
|
||||||
|
<el-option label="每月" value="每月" />
|
||||||
|
<el-option label="單次" value="單次" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="填寫次數">
|
||||||
|
<el-input-number v-model="form.count" :min="1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="審查方式">
|
||||||
|
<el-select v-model="form.reviewType" placeholder="請選擇審查方式">
|
||||||
|
<el-option label="1關" value="1關" />
|
||||||
|
<el-option label="2關" value="2關" />
|
||||||
|
<el-option label="3關" value="3關" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="onClose">取消</el-button>
|
||||||
|
<el-button type="primary" @click="onConfirm">確定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import TemplateSelectDialog from './TemplateSelectDialog.vue';
|
||||||
|
import { useAlert } from '../../composables/useAlert';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
form: { type: Object, required: true },
|
||||||
|
templates: { type: Array, default: () => [] },
|
||||||
|
});
|
||||||
|
const emit = defineEmits([
|
||||||
|
"update:visible",
|
||||||
|
"confirm",
|
||||||
|
"close",
|
||||||
|
]);
|
||||||
|
|
||||||
|
const showTemplateDialog = ref(false);
|
||||||
|
const filteredTemplates = ref([]);
|
||||||
|
const alert = useAlert()
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
function onConfirm() {
|
||||||
|
emit("confirm");
|
||||||
|
}
|
||||||
|
function onSetTemplate() {
|
||||||
|
if (!props.form.factory) {
|
||||||
|
alert.error('請先選擇電廠');
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
filteredTemplates.value = props.templates.filter(t => t.factory === props.form.factory);
|
||||||
|
showTemplateDialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function onTemplatePicked(row) {
|
||||||
|
// 直接回填到父層傳入的表單物件
|
||||||
|
props.form.templateName = row.templateName;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-dialog__footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
284
src/components/PatrolSetting/AddTemplateDialog.vue
Normal file
284
src/components/PatrolSetting/AddTemplateDialog.vue
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增樣板"
|
||||||
|
modal-penetrable
|
||||||
|
style="max-width: 800px; width: 90%"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<DutyLogItemDialog v-model:visible="showDutyLogDialog" @add="addDutyLog" />
|
||||||
|
<HandoverItemDialog
|
||||||
|
v-model:visible="showHandoverDialog"
|
||||||
|
@add="addHandover"
|
||||||
|
/>
|
||||||
|
<VerificationSettingDialog
|
||||||
|
v-model:visible="showVerificationDialog"
|
||||||
|
@add="addVerification"
|
||||||
|
/>
|
||||||
|
<InspectionItemDialog
|
||||||
|
v-model:visible="showInspectionDialog"
|
||||||
|
@add="addInspection"
|
||||||
|
/>
|
||||||
|
<el-form :model="form">
|
||||||
|
<el-divider content-position="left">樣板資訊</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="電廠">
|
||||||
|
<el-select v-model="form.factory" placeholder="請選擇電廠">
|
||||||
|
<el-option label="四磺子坪" value="四磺子坪" />
|
||||||
|
<el-option label="硫磺子坪" value="硫磺子坪" />
|
||||||
|
<el-option label="大清水" value="大清水" />
|
||||||
|
<el-option label="小清水" value="小清水" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="模板">
|
||||||
|
<el-select v-model="form.template" placeholder="請選擇模板類型">
|
||||||
|
<el-option label="巡檢表" value="巡檢表" />
|
||||||
|
<el-option label="點檢表" value="點檢表" />
|
||||||
|
<el-option
|
||||||
|
label="高壓特每日自動檢查表"
|
||||||
|
value="高壓特每日自動檢查表"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="高壓特每月自動檢查表"
|
||||||
|
value="高壓特每月自動檢查表"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="一班作業安全許可申請表"
|
||||||
|
value="一班作業安全許可申請表"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="侷限作業安全許可申請表"
|
||||||
|
value="侷限作業安全許可申請表"
|
||||||
|
/>
|
||||||
|
<el-option
|
||||||
|
label="動火作業安全許可申請表"
|
||||||
|
value="動火作業安全許可申請表"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="母版">
|
||||||
|
<el-select v-model="form.isParent" placeholder="請選擇">
|
||||||
|
<el-option label="是" value="是" />
|
||||||
|
<el-option label="否" value="否" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24">
|
||||||
|
<el-form-item label="樣板名稱">
|
||||||
|
<el-input
|
||||||
|
v-model="form.templateName"
|
||||||
|
placeholder="請輸入樣板名稱"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">樣板內容</el-divider>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :xs="24" :sm="24">
|
||||||
|
<el-form-item label="案場名稱">
|
||||||
|
<el-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="值班人員">
|
||||||
|
<el-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="班別">
|
||||||
|
<el-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="天氣">
|
||||||
|
<el-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="環境溫度/濕度">
|
||||||
|
<el-input />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :align="'middle'" justify="space-between">
|
||||||
|
<h3>值班日誌</h3>
|
||||||
|
<el-button type="primary" size="small" @click="showDutyLogDialog = true"
|
||||||
|
>新增日誌項目</el-button
|
||||||
|
>
|
||||||
|
<el-table :data="dutyLogTableData" :border="true">
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column prop="change" label="變更內容" />
|
||||||
|
<el-table-column prop="continue" label="持續" />
|
||||||
|
<el-table-column prop="result" label="結案" />
|
||||||
|
<el-table-column prop="remark" label="備註" />
|
||||||
|
<el-table-column
|
||||||
|
label="功能"
|
||||||
|
min-width="80"
|
||||||
|
width="140"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="primary" plain>修改</el-button>
|
||||||
|
<el-button size="small" type="danger" plain>刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :align="'middle'" justify="space-between">
|
||||||
|
<h3>交接事項</h3>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="showHandoverDialog = true"
|
||||||
|
>新增交接事項</el-button
|
||||||
|
>
|
||||||
|
<el-table :data="handoverTableData" :border="true">
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column prop="content" label="工作內容" />
|
||||||
|
<el-table-column prop="continue" label="持續" />
|
||||||
|
<el-table-column prop="result" label="結案" />
|
||||||
|
<el-table-column prop="remark" label="備註" />
|
||||||
|
<el-table-column
|
||||||
|
label="功能"
|
||||||
|
min-width="80"
|
||||||
|
width="140"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="primary" plain>修改</el-button>
|
||||||
|
<el-button size="small" type="danger" plain>刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :align="'middle'" justify="space-between">
|
||||||
|
<h3>巡檢表</h3>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="showInspectionDialog = true"
|
||||||
|
>新增巡檢表</el-button
|
||||||
|
>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<PaginatedTable
|
||||||
|
:data="inspectionTableData"
|
||||||
|
:page-sizes="[5, 10]"
|
||||||
|
row-key="index"
|
||||||
|
>
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column prop="system1" label="系統1" />
|
||||||
|
<el-table-column prop="system2" label="系統2" />
|
||||||
|
<el-table-column prop="deviceId" label="設備編號" />
|
||||||
|
<el-table-column prop="item" label="項目" />
|
||||||
|
<el-table-column prop="unit" label="單位" />
|
||||||
|
<el-table-column label="功能" width="140" fixed="right">
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="primary" plain>修改</el-button>
|
||||||
|
<el-button size="small" type="danger" plain>刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</PaginatedTable>
|
||||||
|
|
||||||
|
<el-divider content-position="left">樣板查證</el-divider>
|
||||||
|
<el-row :align="'middle'" justify="space-between">
|
||||||
|
<h3>查證</h3>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="showVerificationDialog = true"
|
||||||
|
>新增查證設定</el-button
|
||||||
|
>
|
||||||
|
<el-table :data="verificationTableData" :border="true">
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column prop="fieldName" label="欄位名稱" />
|
||||||
|
<el-table-column prop="type" label="類型" />
|
||||||
|
<el-table-column
|
||||||
|
label="功能"
|
||||||
|
min-width="80"
|
||||||
|
width="140"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="primary" plain>修改</el-button>
|
||||||
|
<el-button size="small" type="danger" plain>刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="onClose">取消</el-button>
|
||||||
|
<el-button type="primary" @click="onConfirm">確定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import PaginatedTable from "../Common/PaginatedTable.vue";
|
||||||
|
import DutyLogItemDialog from "./DutyLogItemDialog.vue";
|
||||||
|
import HandoverItemDialog from "./HandoverItemDialog.vue";
|
||||||
|
import VerificationSettingDialog from "./VerificationSettingDialog.vue";
|
||||||
|
import InspectionItemDialog from "./InspectionItemDialog.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
form: { type: Object, required: true },
|
||||||
|
dutyLogTableData: { type: Array, default: () => [] },
|
||||||
|
handoverTableData: { type: Array, default: () => [] },
|
||||||
|
inspectionTableData: { type: Array, default: () => [] },
|
||||||
|
verificationTableData: { type: Array, default: () => [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:visible", "confirm", "close"]);
|
||||||
|
|
||||||
|
function onClose() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
function onConfirm() {
|
||||||
|
emit("confirm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 子項目 Dialog 顯示狀態
|
||||||
|
import { ref } from "vue";
|
||||||
|
const showDutyLogDialog = ref(false);
|
||||||
|
const showHandoverDialog = ref(false);
|
||||||
|
const showVerificationDialog = ref(false);
|
||||||
|
const showInspectionDialog = ref(false);
|
||||||
|
|
||||||
|
// 取得父層提供的陣列並推入新項目
|
||||||
|
function addDutyLog(item) {
|
||||||
|
const arr = props.dutyLogTableData;
|
||||||
|
arr.push({ index: arr.length + 1, ...item });
|
||||||
|
}
|
||||||
|
function addHandover(item) {
|
||||||
|
const arr = props.handoverTableData;
|
||||||
|
arr.push({ index: arr.length + 1, ...item });
|
||||||
|
}
|
||||||
|
function addVerification(item) {
|
||||||
|
const arr = props.verificationTableData;
|
||||||
|
arr.push({ index: arr.length + 1, ...item });
|
||||||
|
}
|
||||||
|
function addInspection(item) {
|
||||||
|
const arr = props.inspectionTableData;
|
||||||
|
arr.push({ index: arr.length + 1, ...item });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-divider--horizontal {
|
||||||
|
margin-top: 30px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
src/components/PatrolSetting/DutyLogItemDialog.vue
Normal file
50
src/components/PatrolSetting/DutyLogItemDialog.vue
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增值班日誌項目"
|
||||||
|
width="480px"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
|
<el-form :model="form" label-width="90px">
|
||||||
|
<el-form-item label="變更內容">
|
||||||
|
<el-input v-model="form.change" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="持續">
|
||||||
|
<el-input v-model="form.continue" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="結案">
|
||||||
|
<el-input v-model="form.result" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="備註">
|
||||||
|
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirm">加入</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
});
|
||||||
|
const emit = defineEmits(["update:visible", "add"]);
|
||||||
|
import { reactive } from "vue";
|
||||||
|
const form = reactive({ change: "", continue: "", result: "", remark: "" });
|
||||||
|
function reset() {
|
||||||
|
form.change = "";
|
||||||
|
form.continue = "";
|
||||||
|
form.result = "";
|
||||||
|
form.remark = "";
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
function confirm() {
|
||||||
|
emit("add", { ...form });
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
48
src/components/PatrolSetting/HandoverItemDialog.vue
Normal file
48
src/components/PatrolSetting/HandoverItemDialog.vue
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增交接事項"
|
||||||
|
width="480px"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
|
<el-form :model="form" label-width="90px">
|
||||||
|
<el-form-item label="工作內容">
|
||||||
|
<el-input v-model="form.content" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="持續">
|
||||||
|
<el-input v-model="form.continue" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="結案">
|
||||||
|
<el-input v-model="form.result" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="備註">
|
||||||
|
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirm">加入</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({ visible: { type: Boolean, default: false } });
|
||||||
|
const emit = defineEmits(["update:visible", "add"]);
|
||||||
|
import { reactive } from "vue";
|
||||||
|
const form = reactive({ content: "", continue: "", result: "", remark: "" });
|
||||||
|
function reset() {
|
||||||
|
form.content = "";
|
||||||
|
form.continue = "";
|
||||||
|
form.result = "";
|
||||||
|
form.remark = "";
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
function confirm() {
|
||||||
|
emit("add", { ...form });
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
128
src/components/PatrolSetting/InspectionItemDialog.vue
Normal file
128
src/components/PatrolSetting/InspectionItemDialog.vue
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增巡檢表項目"
|
||||||
|
width="520px"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
|
<el-form :model="form" label-width="70px">
|
||||||
|
<el-form-item label="系統1">
|
||||||
|
<el-input v-model="form.system1" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="系統2">
|
||||||
|
<el-input v-model="form.system2" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="設備編號">
|
||||||
|
<el-input v-model="form.deviceId" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="項目">
|
||||||
|
<el-input v-model="form.item" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="單位">
|
||||||
|
<el-input v-model="form.unit" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-divider content-position="left">下限</el-divider>
|
||||||
|
<el-row :gutter="20" >
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="類型">
|
||||||
|
<el-select v-model="form.lowerType" >
|
||||||
|
<el-option label="數值輸入" value="" />
|
||||||
|
<el-option label="數值判斷(下限)" value="lower" />
|
||||||
|
<el-option label="數值判斷(上限)" value="" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="數值">
|
||||||
|
<el-input v-model="form.lowerValue" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">上限</el-divider>
|
||||||
|
<el-row :gutter="20" >
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="類型">
|
||||||
|
<el-select v-model="form.upperType" >
|
||||||
|
<el-option label="數值輸入" value="" />
|
||||||
|
<el-option label="數值判斷(下限)" value="lower" />
|
||||||
|
<el-option label="數值判斷(上限)" value="" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="數值">
|
||||||
|
<el-input v-model="form.upperValue" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-divider content-position="left">DCS</el-divider>
|
||||||
|
<el-row :gutter="20" align="middle">
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="系統">
|
||||||
|
<el-select v-model="form.dcsSystem" >
|
||||||
|
<el-option label="生產井" value="生產井" />
|
||||||
|
<el-option label="潤滑系統" value="潤滑系統" />
|
||||||
|
<el-option label="空氣系統" value="空氣系統" />
|
||||||
|
<el-option label="渦輪系統" value="渦輪系統" />
|
||||||
|
<el-option label="冷凝器系統" value="冷凝器系統" />
|
||||||
|
<el-option label="蒸發器系統" value="蒸發器系統" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="設備">
|
||||||
|
<el-select v-model="form.dcsDevice">
|
||||||
|
<el-option label="TE 1004" value="TE 1004" />
|
||||||
|
<el-option label="TG 1005" value="TG 1004" />
|
||||||
|
<el-option label="PT 1004" value="PT 1004" />
|
||||||
|
<el-option label="PG 1004" value="PG 1004" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12">
|
||||||
|
<el-form-item label="點位">
|
||||||
|
<el-select v-model="form.dcsPoint" >
|
||||||
|
<el-option label="溫度" value="溫度" />
|
||||||
|
<el-option label="壓力" value="壓力" />
|
||||||
|
<el-option label="流量" value="流量" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirm">加入</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({ visible: { type: Boolean, default: false } });
|
||||||
|
const emit = defineEmits(["update:visible", "add"]);
|
||||||
|
import { reactive } from "vue";
|
||||||
|
const form = reactive({
|
||||||
|
system1: "",
|
||||||
|
system2: "",
|
||||||
|
deviceId: "",
|
||||||
|
item: "",
|
||||||
|
unit: "",
|
||||||
|
});
|
||||||
|
function reset() {
|
||||||
|
form.system1 = "";
|
||||||
|
form.system2 = "";
|
||||||
|
form.deviceId = "";
|
||||||
|
form.item = "";
|
||||||
|
form.unit = "";
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
function confirm() {
|
||||||
|
emit("add", { ...form });
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
239
src/components/PatrolSetting/TaskManager.vue
Normal file
239
src/components/PatrolSetting/TaskManager.vue
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row justify="end">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="showDialog = true"
|
||||||
|
>新增任務</el-button
|
||||||
|
>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 新增任務 -->
|
||||||
|
<AddTaskDialog
|
||||||
|
v-model:visible="showDialog"
|
||||||
|
:form="taskForm"
|
||||||
|
:templates="templateList"
|
||||||
|
@confirm="handleAddTask"
|
||||||
|
@close="resetTaskForm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PaginatedTable
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:data="tableData"
|
||||||
|
:page-sizes="[5, 10]"
|
||||||
|
row-key="index"
|
||||||
|
>
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column
|
||||||
|
prop="factory"
|
||||||
|
label="電廠"
|
||||||
|
:filters="factoryFilters"
|
||||||
|
:filter-method="filterFactory"
|
||||||
|
filter-placement="bottom-end"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="category"
|
||||||
|
label="類別"
|
||||||
|
:filters="categoryFilters"
|
||||||
|
:filter-method="filterCategory"
|
||||||
|
filter-placement="bottom-end"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="taskName" label="任務名稱" />
|
||||||
|
<el-table-column prop="frequency" label="巡檢頻率" />
|
||||||
|
<el-table-column prop="count" label="巡檢次數" />
|
||||||
|
<el-table-column prop="reviewType" label="審查方式" />
|
||||||
|
<el-table-column prop="creator" label="建立者" />
|
||||||
|
<el-table-column prop="createdAt" label="建立時間" />
|
||||||
|
<el-table-column label="功能" width="180" fixed="right">
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="primary" :icon="Edit">修改</el-button>
|
||||||
|
<el-button size="small" type="danger" :icon="Delete">刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</PaginatedTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { templateList } from "../../constants/templateList.js";
|
||||||
|
import PaginatedTable from "../Common/PaginatedTable.vue";
|
||||||
|
import AddTaskDialog from "./AddTaskDialog.vue";
|
||||||
|
import { Plus, Delete, Edit } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
const tableData = [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "巡檢表",
|
||||||
|
taskName: "四磺子坪1.2MW地熱能ORC發電機",
|
||||||
|
frequency: "每日",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "2關",
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: "2025-11-01 08:23:41",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "巡檢表",
|
||||||
|
taskName: "四磺子坪蒸汽機",
|
||||||
|
frequency: "每日",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "1關",
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: "2025-11-02 14:55:09",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "巡檢表",
|
||||||
|
taskName: "四磺子坪設備01",
|
||||||
|
frequency: "每日",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "1關",
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: "2025-11-03 09:12:36",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "巡檢表",
|
||||||
|
taskName: "四磺子坪設備02",
|
||||||
|
frequency: "每日",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "1關",
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: "2025-11-04 16:47:52",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 5,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "點檢表",
|
||||||
|
taskName: "四磺子坪1.2MW先導地熱能ORC發電機",
|
||||||
|
frequency: "每月",
|
||||||
|
count: 4,
|
||||||
|
reviewType: "2關",
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: "2025-11-06 10:31:18",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 6,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "高壓特每日自動檢查表",
|
||||||
|
taskName: "四磺子坪高壓特每日檢點臥式蒸發器",
|
||||||
|
frequency: "每日",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "1關",
|
||||||
|
creator: "管理員02",
|
||||||
|
createdAt: "2025-11-08 13:05:44",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 7,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "高壓特每月自動檢查表",
|
||||||
|
taskName: "四磺子坪高壓特每月檢點臥式蒸發器",
|
||||||
|
frequency: "每月",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "2關",
|
||||||
|
creator: "管理員02",
|
||||||
|
createdAt: "2025-11-09 11:26:57",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 8,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "一般作業安全許可申請表",
|
||||||
|
taskName: "廠房01維修工程",
|
||||||
|
frequency: "單次",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "1關",
|
||||||
|
creator: "管理員02",
|
||||||
|
createdAt: "2025-11-10 15:42:33",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 9,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "侷限作業安全許可申請表",
|
||||||
|
taskName: "廠房02維修工程",
|
||||||
|
frequency: "單次",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "2關",
|
||||||
|
creator: "管理員03",
|
||||||
|
createdAt: "2025-11-12 09:58:21",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 10,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
category: "動火作業安全許可申請表",
|
||||||
|
taskName: "廠房03維修工程",
|
||||||
|
frequency: "單次",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "2關",
|
||||||
|
creator: "管理員03",
|
||||||
|
createdAt: "2025-11-13 17:14:05",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 篩選選項
|
||||||
|
const factoryFilters = computed(() =>
|
||||||
|
Array.from(new Set(tableData.map((item) => item.factory))).map((factory) => ({
|
||||||
|
text: factory,
|
||||||
|
value: factory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
const categoryFilters = computed(() =>
|
||||||
|
Array.from(new Set(tableData.map((item) => item.category))).map(
|
||||||
|
(category) => ({
|
||||||
|
text: category,
|
||||||
|
value: category,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const filterFactory = (value, row) => row.factory === value;
|
||||||
|
const filterCategory = (value, row) => row.category === value;
|
||||||
|
|
||||||
|
// dialog 狀態與表單
|
||||||
|
const showDialog = ref(false);
|
||||||
|
const taskForm = ref({
|
||||||
|
factory: "",
|
||||||
|
templateName: "",
|
||||||
|
taskName: "",
|
||||||
|
frequency: "",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetTaskForm() {
|
||||||
|
taskForm.value = {
|
||||||
|
factory: "",
|
||||||
|
templateName: "",
|
||||||
|
taskName: "",
|
||||||
|
frequency: "",
|
||||||
|
count: 1,
|
||||||
|
reviewType: "",
|
||||||
|
};
|
||||||
|
showDialog.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddTask() {
|
||||||
|
// 新增到 tableData
|
||||||
|
tableData.push({
|
||||||
|
index: tableData.length + 1,
|
||||||
|
factory: taskForm.value.factory,
|
||||||
|
category: taskForm.value.templateName
|
||||||
|
? taskForm.value.templateName.split("每日").length > 1
|
||||||
|
? "點檢表"
|
||||||
|
: "巡檢表"
|
||||||
|
: "",
|
||||||
|
taskName: taskForm.value.taskName,
|
||||||
|
frequency: taskForm.value.frequency,
|
||||||
|
count: taskForm.value.count,
|
||||||
|
reviewType: taskForm.value.reviewType,
|
||||||
|
creator: "管理員01",
|
||||||
|
createdAt: new Date().toISOString().slice(0, 19).replace("T", " "),
|
||||||
|
});
|
||||||
|
resetTaskForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 樣板選擇由 AddTaskDialog 內部處理
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
235
src/components/PatrolSetting/TemplateManager.vue
Normal file
235
src/components/PatrolSetting/TemplateManager.vue
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 樣板管理內容 -->
|
||||||
|
<el-row justify="end">
|
||||||
|
<el-button type="primary" :icon="Plus" @click="showDialog = true"
|
||||||
|
>新增樣板</el-button
|
||||||
|
>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<AddTemplateDialog
|
||||||
|
v-model:visible="showDialog"
|
||||||
|
:form="addTemplateForm"
|
||||||
|
:duty-log-table-data="dutyLogTableData"
|
||||||
|
:handover-table-data="handoverTableData"
|
||||||
|
:inspection-table-data="inspectionTableData"
|
||||||
|
:verification-table-data="verificationTableData"
|
||||||
|
@confirm="handleAddTemplate"
|
||||||
|
@close="resetAddTemplateForm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 樣板列表 table(共用分頁表格元件) -->
|
||||||
|
<PaginatedTable
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:data="tableData"
|
||||||
|
:page-sizes="[5, 10]"
|
||||||
|
row-key="index"
|
||||||
|
>
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column
|
||||||
|
prop="factory"
|
||||||
|
label="電廠"
|
||||||
|
width="200"
|
||||||
|
:filters="factoryFilters"
|
||||||
|
:filter-method="filterFactory"
|
||||||
|
filter-placement="bottom-end"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="template"
|
||||||
|
label="模板"
|
||||||
|
:filters="templateFilters"
|
||||||
|
:filter-method="filterTemplate"
|
||||||
|
filter-placement="bottom-end"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="templateName" label="樣板名稱" />
|
||||||
|
<el-table-column prop="isParent" label="母版" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag
|
||||||
|
:type="scope.row.isParent === '是' ? 'success' : 'danger'"
|
||||||
|
disable-transitions
|
||||||
|
>
|
||||||
|
{{ scope.row.isParent }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="功能" width="320" fixed="right">
|
||||||
|
<template #default>
|
||||||
|
<el-button size="small" type="info" :icon="View">預覽</el-button>
|
||||||
|
<el-button size="small" type="success" :icon="CopyDocument"
|
||||||
|
>複製</el-button
|
||||||
|
>
|
||||||
|
<el-button size="small" type="primary" :icon="Edit">修改</el-button>
|
||||||
|
<el-button size="small" type="danger" :icon="Delete">刪除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</PaginatedTable>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import {
|
||||||
|
Plus,
|
||||||
|
Delete,
|
||||||
|
Edit,
|
||||||
|
CopyDocument,
|
||||||
|
View,
|
||||||
|
} from "@element-plus/icons-vue";
|
||||||
|
import PaginatedTable from "../Common/PaginatedTable.vue";
|
||||||
|
import AddTemplateDialog from "./AddTemplateDialog.vue";
|
||||||
|
import { templateList } from "../../constants/templateList.js";
|
||||||
|
|
||||||
|
// 對話框顯示
|
||||||
|
const showDialog = ref(false);
|
||||||
|
|
||||||
|
// 新增樣板 表單
|
||||||
|
const addTemplateForm = ref({
|
||||||
|
factory: "",
|
||||||
|
template: "",
|
||||||
|
templateName: "",
|
||||||
|
isParent: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
function resetAddTemplateForm() {
|
||||||
|
addTemplateForm.value = {
|
||||||
|
factory: "",
|
||||||
|
template: "",
|
||||||
|
templateName: "",
|
||||||
|
isParent: "",
|
||||||
|
};
|
||||||
|
showDialog.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAddTemplate() {
|
||||||
|
// 可依需求推入 tableData 或呼叫 API
|
||||||
|
resetAddTemplateForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 假資料:巡檢表
|
||||||
|
const inspectionTableData = ref([
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#2生產井",
|
||||||
|
deviceId: "TE_1004",
|
||||||
|
item: "#2地熱井溫度",
|
||||||
|
unit: "°C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#2生產井",
|
||||||
|
deviceId: "TG_1004",
|
||||||
|
item: "#2地熱井溫度表",
|
||||||
|
unit: "°C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#2生產井",
|
||||||
|
deviceId: "PT_1004",
|
||||||
|
item: "#2地熱井壓力",
|
||||||
|
unit: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#2生產井",
|
||||||
|
deviceId: "PG_1004",
|
||||||
|
item: "#2地熱井壓力表",
|
||||||
|
unit: "kg/cm2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 5,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#2生產井",
|
||||||
|
deviceId: "",
|
||||||
|
item: "H2S洩漏偵測",
|
||||||
|
unit: "ppm",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 6,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#3生產井",
|
||||||
|
deviceId: "TE_1005",
|
||||||
|
item: "#3地熱井溫度",
|
||||||
|
unit: "°C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 7,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#3生產井",
|
||||||
|
deviceId: "TG_1005",
|
||||||
|
item: "#3地熱井溫度表",
|
||||||
|
unit: "°C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 8,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#3生產井",
|
||||||
|
deviceId: "PT_1005",
|
||||||
|
item: "#3地熱井壓力",
|
||||||
|
unit: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 9,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#3生產井",
|
||||||
|
deviceId: "PG_1005",
|
||||||
|
item: "#3地熱井壓力表",
|
||||||
|
unit: "kg/cm2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 10,
|
||||||
|
system1: "熱源系統",
|
||||||
|
system2: "#3生產井",
|
||||||
|
deviceId: "",
|
||||||
|
item: "H2S洩漏偵測",
|
||||||
|
unit: "ppm",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 值班日誌 table 資料
|
||||||
|
const dutyLogTableData = ref([
|
||||||
|
{ index: 1, change: "", continue: "", result: "", remark: "" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 交接事項 table 資料
|
||||||
|
const handoverTableData = ref([
|
||||||
|
{ index: 1, content: "", continue: "", result: "", remark: "" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 查證 table 資料
|
||||||
|
const verificationTableData = ref([
|
||||||
|
{ index: 1, fieldName: "現場", type: "數值輸入" },
|
||||||
|
{ index: 2, fieldName: "比對結果", type: "單一選擇" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 分頁狀態
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
|
||||||
|
// 樣板列表資料(共用)
|
||||||
|
const tableData = templateList;
|
||||||
|
|
||||||
|
// 篩選選項
|
||||||
|
const factoryFilters = computed(() =>
|
||||||
|
Array.from(new Set(tableData.map((item) => item.factory))).map((factory) => ({
|
||||||
|
text: factory,
|
||||||
|
value: factory,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const templateFilters = computed(() =>
|
||||||
|
Array.from(new Set(tableData.map((item) => item.template))).map(
|
||||||
|
(template) => ({ text: template, value: template })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 篩選方法
|
||||||
|
const filterFactory = (value, row) => row.factory === value;
|
||||||
|
const filterTemplate = (value, row) => row.template === value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
77
src/components/PatrolSetting/TemplateSelectDialog.vue
Normal file
77
src/components/PatrolSetting/TemplateSelectDialog.vue
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="選擇樣板"
|
||||||
|
width="900px"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<PaginatedTable
|
||||||
|
:data="templates"
|
||||||
|
:page-sizes="[5, 10]"
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
row-key="index"
|
||||||
|
>
|
||||||
|
<el-table-column prop="index" label="項次" width="60" />
|
||||||
|
<el-table-column
|
||||||
|
prop="template"
|
||||||
|
label="模板"
|
||||||
|
:filters="templateFilters"
|
||||||
|
:filter-method="filterTemplate"
|
||||||
|
/>
|
||||||
|
<el-table-column prop="templateName" label="模板名稱" />
|
||||||
|
<el-table-column prop="isParent" label="母版" width="80">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ scope.row.isParent }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="功能" width="100" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click="selectTemplate(scope.row)"
|
||||||
|
>選擇</el-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</PaginatedTable>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import PaginatedTable from "../Common/PaginatedTable.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: { type: Boolean, default: false },
|
||||||
|
templates: { type: Array, default: () => [] },
|
||||||
|
});
|
||||||
|
const emit = defineEmits(["update:visible", "select", "close"]);
|
||||||
|
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
|
||||||
|
function selectTemplate(row) {
|
||||||
|
emit("select", row);
|
||||||
|
emit("update:visible", false);
|
||||||
|
}
|
||||||
|
function onClose() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
emit("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateFilters = computed(() =>
|
||||||
|
Array.from(new Set(props.templates.map((item) => item.template))).map(
|
||||||
|
(template) => ({ text: template, value: template })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const filterTemplate = (value, row) => row.template === value;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* .el-dialog__body {
|
||||||
|
padding: 16px 24px 0 24px;
|
||||||
|
} */
|
||||||
|
</style>
|
||||||
44
src/components/PatrolSetting/VerificationSettingDialog.vue
Normal file
44
src/components/PatrolSetting/VerificationSettingDialog.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:model-value="visible"
|
||||||
|
title="新增查證設定"
|
||||||
|
width="480px"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
|
<el-form :model="form" label-width="90px">
|
||||||
|
<el-form-item label="欄位名稱">
|
||||||
|
<el-input v-model="form.fieldName" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="類型">
|
||||||
|
<el-select v-model="form.type" placeholder="選擇類型">
|
||||||
|
<el-option label="數值輸入" value="數值輸入" />
|
||||||
|
<el-option label="文字輸入" value="文字輸入" />
|
||||||
|
<el-option label="單一選擇" value="單一選擇" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="primary" @click="confirm">加入</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({ visible: { type: Boolean, default: false } });
|
||||||
|
const emit = defineEmits(["update:visible", "add"]);
|
||||||
|
import { reactive } from "vue";
|
||||||
|
const form = reactive({ fieldName: "", type: "" });
|
||||||
|
function reset() {
|
||||||
|
form.fieldName = "";
|
||||||
|
form.type = "";
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
emit("update:visible", false);
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
function confirm() {
|
||||||
|
emit("add", { ...form });
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped></style>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from "vue";
|
import { computed, defineProps, defineEmits } from "vue";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { menuConfig } from "../constants/menuConfig.js";
|
import { menuConfig } from "../constants/menuConfig.js";
|
||||||
import {
|
import {
|
||||||
@ -53,6 +53,12 @@ import {
|
|||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const props = defineProps({
|
||||||
|
isCollapse: { type: Boolean, default: false },
|
||||||
|
isMobile: { type: Boolean, default: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:isCollapse"]);
|
||||||
|
|
||||||
// 圖標組件映射
|
// 圖標組件映射
|
||||||
const iconComponents = {
|
const iconComponents = {
|
||||||
@ -74,7 +80,14 @@ const getIconComponent = (iconName) => {
|
|||||||
// 處理選單點擊
|
// 處理選單點擊
|
||||||
const handleMenuClick = (item) => {
|
const handleMenuClick = (item) => {
|
||||||
if (item.routeName) {
|
if (item.routeName) {
|
||||||
router.push({ name: item.routeName, params: item.params || {} });
|
Promise.resolve(
|
||||||
|
router.push({ name: item.routeName, params: item.params || {} })
|
||||||
|
).finally(() => {
|
||||||
|
// 手機模式下點擊選單後自動關閉側邊欄
|
||||||
|
if (props.isMobile) {
|
||||||
|
emit("update:isCollapse", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
54
src/composables/useAlert.js
Normal file
54
src/composables/useAlert.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { reactive, readonly } from 'vue';
|
||||||
|
|
||||||
|
// 單例狀態:全專案共用
|
||||||
|
const state = reactive({
|
||||||
|
// { id, title, type, description, closable, duration, effect }
|
||||||
|
alerts: [],
|
||||||
|
seq: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
function normalizeType(type) {
|
||||||
|
// Element Plus Alert 支援: success | info | warning | error
|
||||||
|
if (type === 'primary') return 'info';
|
||||||
|
return ['success', 'info', 'warning', 'error'].includes(type) ? type : 'info';
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAlert({ title, type = 'info', description = '', closable = true, duration = 3000, effect = 'light' }) {
|
||||||
|
const id = ++state.seq;
|
||||||
|
const alert = { id, title, type: normalizeType(type), description, closable, duration, effect };
|
||||||
|
state.alerts.push(alert);
|
||||||
|
if (duration && duration > 0) {
|
||||||
|
setTimeout(() => removeAlert(id), duration);
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAlert(id) {
|
||||||
|
const idx = state.alerts.findIndex(a => a.id === id);
|
||||||
|
if (idx !== -1) state.alerts.splice(idx, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAlerts() {
|
||||||
|
state.alerts.splice(0, state.alerts.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAlert() {
|
||||||
|
const notify = (title, options = {}) => addAlert({ title, ...options });
|
||||||
|
const success = (title, options = {}) => addAlert({ title, type: 'success', ...options });
|
||||||
|
const info = (title, options = {}) => addAlert({ title, type: 'info', ...options });
|
||||||
|
const warning = (title, options = {}) => addAlert({ title, type: 'warning', ...options });
|
||||||
|
const error = (title, options = {}) => addAlert({ title, type: 'error', ...options });
|
||||||
|
const primary = (title, options = {}) => addAlert({ title, type: 'primary', effect: 'dark', ...options });
|
||||||
|
|
||||||
|
return {
|
||||||
|
alerts: readonly(state.alerts),
|
||||||
|
notify,
|
||||||
|
success,
|
||||||
|
info,
|
||||||
|
warning,
|
||||||
|
error,
|
||||||
|
primary,
|
||||||
|
remove: removeAlert,
|
||||||
|
clear: clearAlerts,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,104 +1,104 @@
|
|||||||
export const menuConfig = [
|
export const menuConfig = [
|
||||||
{
|
{
|
||||||
title: '總覽',
|
title: "總覽",
|
||||||
icon: 'Location',
|
icon: "Location",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '地圖總覽',
|
title: "地圖總覽",
|
||||||
routeName: 'PlantsMap'
|
routeName: "PlantsMap",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '電廠總覽',
|
title: "電廠總覽",
|
||||||
routeName: 'PlantsOverview'
|
routeName: "PlantsOverview",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '電廠資訊',
|
title: "電廠資訊",
|
||||||
icon: 'Postcard',
|
icon: "Postcard",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '四磺子坪',
|
title: "四磺子坪",
|
||||||
routeName: 'PlantInfo',
|
routeName: "PlantInfo",
|
||||||
params: { id: 1 }
|
params: { id: 1 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '宜蘭大清水',
|
title: "宜蘭大清水",
|
||||||
routeName: 'PlantInfo',
|
routeName: "PlantInfo",
|
||||||
params: { id: 2 }
|
params: { id: 2 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '宜蘭小清水',
|
title: "宜蘭小清水",
|
||||||
routeName: 'PlantInfo',
|
routeName: "PlantInfo",
|
||||||
params: { id: 3 }
|
params: { id: 3 },
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '巡檢系統',
|
title: "巡檢系統",
|
||||||
icon: 'DocumentChecked',
|
icon: "DocumentChecked",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '巡檢任務',
|
title: "巡檢設定",
|
||||||
routeName: 'PatrolMission',
|
routeName: "PatrolSetting",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '巡檢設定',
|
title: "巡檢任務",
|
||||||
routeName: 'PatrolSetting',
|
routeName: "PatrolMission",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '報表查詢',
|
title: "報表查詢",
|
||||||
icon: 'DataLine',
|
icon: "DataLine",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '電廠報表',
|
title: "電廠報表",
|
||||||
routeName: 'PlantsReport'
|
routeName: "PlantsReport",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '即時告警',
|
title: "即時告警",
|
||||||
icon: 'Bell',
|
icon: "Bell",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '異常事件查詢'
|
title: "異常事件查詢",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '備料管理',
|
title: "備料管理",
|
||||||
icon: 'MessageBox',
|
icon: "MessageBox",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '備品料件管理'
|
title: "備品料件管理",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '倉庫櫃位管理'
|
title: "倉庫櫃位管理",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '智慧安防',
|
title: "智慧安防",
|
||||||
icon: 'VideoCamera',
|
icon: "VideoCamera",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '安防系統'
|
title: "安防系統",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '系統設定',
|
title: "系統設定",
|
||||||
icon: 'Setting',
|
icon: "Setting",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: '電廠設定',
|
title: "電廠設定",
|
||||||
routeName: 'PlantSetting'
|
routeName: "PlantSetting",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '帳號設定'
|
title: "帳號設定",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
72
src/constants/templateList.js
Normal file
72
src/constants/templateList.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
export const templateList = [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "巡檢表",
|
||||||
|
templateName: "樣板-巡檢表-四磺子坪地熱能發電機組v1.0",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "巡檢表",
|
||||||
|
templateName: "樣板-巡檢表-四磺子坪地熱能發電機組v1.1",
|
||||||
|
isParent: "否",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 3,
|
||||||
|
factory: "小清水",
|
||||||
|
template: "巡檢表",
|
||||||
|
templateName: "樣板-巡檢表-小清水地熱能發電機組",
|
||||||
|
isParent: "否",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 4,
|
||||||
|
factory: "硫磺子坪",
|
||||||
|
template: "巡檢表",
|
||||||
|
templateName: "樣板-測試03",
|
||||||
|
isParent: "否",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 5,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "點檢表",
|
||||||
|
templateName: "樣板-四磺子坪先導地熱發電站每日點檢點表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 6,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "高壓特每日自動檢查表",
|
||||||
|
templateName: "樣板-四磺子坪先導地熱發電站高壓特每日點檢點表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 7,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "高壓特每月自動檢查表",
|
||||||
|
templateName: "樣板-四磺子坪先導地熱發電站高壓特每月定檢表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 8,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "一般作業安全許可申請表",
|
||||||
|
templateName: "樣板-一般施工安全許可申請表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 9,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "侷限作業安全許可申請表",
|
||||||
|
templateName: "樣板-局限空間缺氧危險作業進入許可申請表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 10,
|
||||||
|
factory: "四磺子坪",
|
||||||
|
template: "動火作業安全許可申請表",
|
||||||
|
templateName: "樣板-動火作業安全許可申請表",
|
||||||
|
isParent: "是",
|
||||||
|
},
|
||||||
|
];
|
||||||
@ -1,10 +1,12 @@
|
|||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import "./styles/style.css";
|
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
|
import Vue3Signature from "vue3-signature";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import { createPinia } from "pinia";
|
import { createPinia } from "pinia";
|
||||||
|
import "./styles/style.css";
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
app.use(createPinia());
|
app.use(createPinia());
|
||||||
|
app.use(Vue3Signature);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.mount("#app");
|
app.mount("#app");
|
||||||
|
|||||||
@ -4,14 +4,20 @@
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color-scheme: light dark;
|
color-scheme: light dark;
|
||||||
color: #213547;
|
color: #213547;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
/* --el-color-primary: #df2cff;
|
||||||
|
--el-color-primary-light-3: #b266ff;
|
||||||
|
--el-color-primary-light-5: #c299ff;
|
||||||
|
--el-color-primary-light-7: #d1ccff;
|
||||||
|
--el-color-primary-light-8: #e0e0ff;
|
||||||
|
--el-color-primary-light-9: #f0f3ff;
|
||||||
|
--el-color-primary-dark-2: #a600e6; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@ -26,16 +32,15 @@ body {
|
|||||||
/* 統一 el-card 樣式 */
|
/* 統一 el-card 樣式 */
|
||||||
.el-card.custom-card {
|
.el-card.custom-card {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.08);
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.08);
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border: none;
|
border: none;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-card.custom-card .el-card__header{
|
.el-card.custom-card .el-card__header {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
letter-spacing: 0.8px;
|
letter-spacing: 0.8px;
|
||||||
color: #213547;
|
color: #213547;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,85 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<el-row :gutter="20" style="margin-top: 20px">
|
||||||
|
<el-col :xs="24">
|
||||||
|
<el-card shadow="always" class="custom-card">
|
||||||
|
<template #header>
|
||||||
|
<el-radio-group v-model="radio" size="large" fill="#409eff">
|
||||||
|
<el-radio-button label="待完成" value="pending" />
|
||||||
|
<el-radio-button label="已完成" value="completed" />
|
||||||
|
</el-radio-group>
|
||||||
|
</template>
|
||||||
|
<div v-if="radio === 'pending'">
|
||||||
|
<!-- 待完成內容 -->
|
||||||
|
<el-button-group>
|
||||||
|
<el-button :icon="TakeawayBox" @click="save('image/png')"
|
||||||
|
>保存PNG</el-button
|
||||||
|
>
|
||||||
|
<el-button :icon="TakeawayBox" @click="save('image/jpeg')"
|
||||||
|
>保存JPEG</el-button
|
||||||
|
>
|
||||||
|
<el-button :icon="Loading" @click="clear">清除</el-button>
|
||||||
|
<el-button :icon="RefreshLeft" @click="undo">返回</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
<Vue3Signature
|
||||||
|
ref="signature"
|
||||||
|
:sigOption="options"
|
||||||
|
:w="'300px'"
|
||||||
|
:h="'150px'"
|
||||||
|
class="signature-pad"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="radio === 'completed'">
|
||||||
|
<!-- 已完成內容 -->
|
||||||
|
已完成任務列表
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed, reactive } from "vue";
|
||||||
|
import { TakeawayBox, Loading, RefreshLeft } from "@element-plus/icons-vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
const radio = ref("pending");
|
||||||
|
const signature = ref(null);
|
||||||
|
|
||||||
|
const options = reactive({
|
||||||
|
penColor: "rgb(0, 0, 0)",
|
||||||
|
backgroundColor: "rgb(255, 255, 255)",
|
||||||
|
});
|
||||||
|
|
||||||
|
const save = (format) => {
|
||||||
|
if (signature.value.isEmpty()) {
|
||||||
|
alert("Please provide a signature first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataUrl = signature.value.save(format);
|
||||||
|
|
||||||
|
// Download the image
|
||||||
|
const link = document.createElement("a");
|
||||||
|
const extension = format === "image/jpeg" ? "jpg" : "png";
|
||||||
|
// 取得當下時間字串
|
||||||
|
const nowStr = dayjs().format("YYYYMMDD_HHmmss");
|
||||||
|
link.download = `signature_${nowStr}.${extension}`;
|
||||||
|
link.href = dataUrl;
|
||||||
|
link.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = () => {
|
||||||
|
signature.value.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const undo = () => {
|
||||||
|
signature.value.undo();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
.signature-pad {
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -7,14 +7,13 @@
|
|||||||
<el-radio-button label="樣板管理" value="template" />
|
<el-radio-button label="樣板管理" value="template" />
|
||||||
<el-radio-button label="任務管理" value="task" /> </el-radio-group
|
<el-radio-button label="任務管理" value="task" /> </el-radio-group
|
||||||
></template>
|
></template>
|
||||||
|
<!-- 樣板管理 -->
|
||||||
<div v-if="radio === 'template'">
|
<div v-if="radio === 'template'">
|
||||||
<!-- 樣板管理內容 -->
|
<TemplateManager />
|
||||||
<p>這裡是樣板管理</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 任務管理 -->
|
||||||
<div v-if="radio === 'task'">
|
<div v-if="radio === 'task'">
|
||||||
<!-- 任務管理內容 -->
|
<TaskManager />
|
||||||
<p>這裡是任務管理</p>
|
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
@ -23,6 +22,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import TemplateManager from "../components/PatrolSetting/TemplateManager.vue";
|
||||||
|
import TaskManager from "../components/PatrolSetting/TaskManager.vue";
|
||||||
|
|
||||||
const radio = ref("template");
|
const radio = ref("template");
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -11,6 +11,9 @@ export default defineConfig({
|
|||||||
outDir: "../dist",
|
outDir: "../dist",
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
},
|
},
|
||||||
|
server: {
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user