|
|
@@ -1,11 +1,16 @@
|
|
|
<script setup lang="ts">
|
|
|
import { useTableComponents } from '@/components/ZmTable/useTableComponents'
|
|
|
import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
|
|
|
+import { IotOperationPlanApi } from '@/api/pms/iotopeationfill/plan'
|
|
|
+import * as DeptApi from '@/api/system/dept'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
|
|
import download from '@/utils/download'
|
|
|
import { rangeShortcuts } from '@/utils/formatTime'
|
|
|
+import { checkPermi } from '@/utils/permission'
|
|
|
+import { defaultProps, handleTree } from '@/utils/tree'
|
|
|
import DeviceStatusLogDrawer from '@/views/pms/device/statuslog/DeviceStatusLogDrawer.vue'
|
|
|
+import type { FormInstance, FormRules } from 'element-plus'
|
|
|
|
|
|
defineOptions({ name: 'IotDeviceStatus' })
|
|
|
|
|
|
@@ -20,7 +25,21 @@ interface QueryParams extends PageParam {
|
|
|
createTime?: string[]
|
|
|
}
|
|
|
|
|
|
+interface GenerateDeptOption {
|
|
|
+ id: number
|
|
|
+ name: string
|
|
|
+ parentId?: number
|
|
|
+ type?: string
|
|
|
+ disabled?: boolean
|
|
|
+ children?: GenerateDeptOption[]
|
|
|
+}
|
|
|
+
|
|
|
+interface GenerateRecordForm {
|
|
|
+ deptId?: number
|
|
|
+}
|
|
|
+
|
|
|
const { t } = useI18n()
|
|
|
+const message = useMessage()
|
|
|
const { push } = useRouter()
|
|
|
const { ZmTable, ZmTableColumn } = useTableComponents<IotDeviceVO>()
|
|
|
|
|
|
@@ -50,6 +69,18 @@ const total = ref(0)
|
|
|
const currentDeviceId = ref<number>()
|
|
|
const drawerVisible = ref(false)
|
|
|
const showDrawer = ref()
|
|
|
+const generateDialogVisible = ref(false)
|
|
|
+const generateLoading = ref(false)
|
|
|
+const generateDeptOptionsLoading = ref(false)
|
|
|
+const generateDeptOptions = ref<GenerateDeptOption[]>([])
|
|
|
+const generateFormRef = ref<FormInstance>()
|
|
|
+const generateForm = reactive<GenerateRecordForm>({
|
|
|
+ deptId: undefined
|
|
|
+})
|
|
|
+const generateFormRules: FormRules = {
|
|
|
+ deptId: [{ required: true, message: '请选择部门', trigger: 'change' }]
|
|
|
+}
|
|
|
+const generateDeptTreeProps = { ...defaultProps, disabled: 'disabled' }
|
|
|
|
|
|
const resultOptions = computed(() => [
|
|
|
{
|
|
|
@@ -129,6 +160,60 @@ const handleView = async (deviceId: number) => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+const loadGenerateDeptOptions = async () => {
|
|
|
+ generateDeptOptionsLoading.value = true
|
|
|
+ try {
|
|
|
+ const depts = await DeptApi.specifiedSimpleDepts(deptId)
|
|
|
+ const selectableDepts = depts.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ type: String(item.type || ''),
|
|
|
+ disabled: String(item.type || '') !== '3'
|
|
|
+ }))
|
|
|
+ generateDeptOptions.value = handleTree(selectableDepts)
|
|
|
+ } finally {
|
|
|
+ generateDeptOptionsLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const openGenerateDialog = async () => {
|
|
|
+ generateForm.deptId = undefined
|
|
|
+ generateDialogVisible.value = true
|
|
|
+ await nextTick()
|
|
|
+ generateFormRef.value?.clearValidate()
|
|
|
+ if (!generateDeptOptions.value.length) {
|
|
|
+ await loadGenerateDeptOptions()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const submitGenerateRecord = async () => {
|
|
|
+ if (!generateFormRef.value) return
|
|
|
+ const valid = await generateFormRef.value.validate().catch(() => false)
|
|
|
+ if (!valid || !generateForm.deptId) return
|
|
|
+
|
|
|
+ generateLoading.value = true
|
|
|
+ try {
|
|
|
+ const result = await IotOperationPlanApi.manualGenerateByDeptId(generateForm.deptId)
|
|
|
+ message.success(result)
|
|
|
+ generateDialogVisible.value = false
|
|
|
+ } finally {
|
|
|
+ generateLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleBusinessCommand = (command: string) => {
|
|
|
+ if (command === 'adjust-status') {
|
|
|
+ openForm()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (command === 'generate-record') {
|
|
|
+ openGenerateDialog()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (command === 'export') {
|
|
|
+ handleExport()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const handleExport = async () => {
|
|
|
exportLoading.value = true
|
|
|
try {
|
|
|
@@ -256,16 +341,29 @@ onMounted(() => {
|
|
|
<el-button @click="resetQuery">
|
|
|
<Icon icon="ep:refresh" class="mr-5px" />{{ t('devicePerson.reset') }}
|
|
|
</el-button>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- plain
|
|
|
- @click="openForm"
|
|
|
- v-hasPermi="['pms:iot-device-status-log:create']">
|
|
|
- <Icon icon="ep:plus" class="mr-5px" />{{ t('deviceStatus.setUp') }}
|
|
|
- </el-button>
|
|
|
- <el-button type="success" plain :loading="exportLoading" @click="handleExport">
|
|
|
- <Icon icon="ep:download" class="mr-5px" />导出
|
|
|
- </el-button>
|
|
|
+ <el-dropdown trigger="click" placement="bottom-end" @command="handleBusinessCommand">
|
|
|
+ <el-button plain :loading="exportLoading">
|
|
|
+ <Icon icon="ep:operation" class="mr-5px" />业务操作
|
|
|
+ <Icon icon="ep:arrow-down" class="ml-5px" />
|
|
|
+ </el-button>
|
|
|
+ <template #dropdown>
|
|
|
+ <el-dropdown-menu>
|
|
|
+ <el-dropdown-item
|
|
|
+ v-if="checkPermi(['pms:iot-device-status-log:create'])"
|
|
|
+ command="adjust-status">
|
|
|
+ <Icon icon="ep:refresh" class="mr-8px" />调整状态
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item
|
|
|
+ v-if="checkPermi(['rq:iot-operation-plan:create'])"
|
|
|
+ command="generate-record">
|
|
|
+ <Icon icon="ep:document-add" class="mr-8px" />生成记录
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item command="export" divided :disabled="exportLoading">
|
|
|
+ <Icon icon="ep:download" class="mr-8px" />导出数据
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ </template>
|
|
|
+ </el-dropdown>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
|
|
|
@@ -332,6 +430,75 @@ onMounted(() => {
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
+ <Dialog
|
|
|
+ v-model="generateDialogVisible"
|
|
|
+ :fullscreen="false"
|
|
|
+ width="560px"
|
|
|
+ class="generate-record-dialog">
|
|
|
+ <template #title>
|
|
|
+ <div class="generate-dialog-title">
|
|
|
+ <div class="generate-dialog-title__icon">
|
|
|
+ <Icon icon="ep:document-add" :size="22" />
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <div class="generate-dialog-title__text">生成运行记录</div>
|
|
|
+ <div class="generate-dialog-title__desc">选择作业部门并生成今日运行填报记录</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <div class="generate-dialog-content">
|
|
|
+ <section class="generate-panel">
|
|
|
+ <div class="generate-panel__header">
|
|
|
+ <div class="generate-panel__title">
|
|
|
+ <span class="generate-panel__marker"></span>
|
|
|
+ <span>生成范围</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="generate-panel__body">
|
|
|
+ <el-form
|
|
|
+ ref="generateFormRef"
|
|
|
+ :model="generateForm"
|
|
|
+ :rules="generateFormRules"
|
|
|
+ label-position="top"
|
|
|
+ size="default">
|
|
|
+ <el-form-item label="作业部门" prop="deptId">
|
|
|
+ <el-tree-select
|
|
|
+ v-model="generateForm.deptId"
|
|
|
+ :data="generateDeptOptions"
|
|
|
+ :props="generateDeptTreeProps"
|
|
|
+ :loading="generateDeptOptionsLoading"
|
|
|
+ node-key="id"
|
|
|
+ check-strictly
|
|
|
+ default-expand-all
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="请选择三级部门"
|
|
|
+ class="w-full" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <div class="generate-warning">
|
|
|
+ <div class="generate-warning__icon">
|
|
|
+ <Icon icon="ep:warning-filled" :size="18" />
|
|
|
+ </div>
|
|
|
+ <div class="generate-warning__text">
|
|
|
+ 所选部门今日如已有运行记录,系统将删除原记录并按当前配置重新生成。
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button :disabled="generateLoading" @click="generateDialogVisible = false">
|
|
|
+ 取消
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" :loading="generateLoading" @click="submitGenerateRecord">
|
|
|
+ <Icon v-if="!generateLoading" icon="ep:check" class="mr-5px" />确认生成
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </Dialog>
|
|
|
+
|
|
|
<DeviceStatusLogDrawer
|
|
|
ref="showDrawer"
|
|
|
:model-value="drawerVisible"
|
|
|
@@ -370,6 +537,108 @@ onMounted(() => {
|
|
|
margin-left: 0;
|
|
|
}
|
|
|
|
|
|
+.generate-dialog-title {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ padding-right: 44px;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-dialog-title__icon {
|
|
|
+ display: flex;
|
|
|
+ width: 38px;
|
|
|
+ height: 38px;
|
|
|
+ color: var(--el-color-primary);
|
|
|
+ background: var(--el-color-primary-light-9);
|
|
|
+ border-radius: 8px;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-dialog-title__text {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 22px;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+}
|
|
|
+
|
|
|
+.generate-dialog-title__desc {
|
|
|
+ margin-top: 2px;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 18px;
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+.generate-dialog-content {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 14px;
|
|
|
+ padding: 3px;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel {
|
|
|
+ overflow: hidden;
|
|
|
+ background: var(--el-bg-color);
|
|
|
+ border: 1px solid var(--el-border-color-lighter);
|
|
|
+ border-radius: 6px;
|
|
|
+ box-shadow: 0 8px 24px rgb(15 35 70 / 5%);
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel__header {
|
|
|
+ display: flex;
|
|
|
+ min-height: 52px;
|
|
|
+ padding: 0 16px;
|
|
|
+ background: #f4f9ff;
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel__title {
|
|
|
+ display: flex;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel__marker {
|
|
|
+ width: 4px;
|
|
|
+ height: 18px;
|
|
|
+ margin-right: 10px;
|
|
|
+ background: var(--el-color-primary);
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel__body {
|
|
|
+ padding: 20px 18px 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-panel__body :deep(.el-form-item) {
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-warning {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ padding: 13px 16px;
|
|
|
+ color: var(--el-color-warning-dark-2);
|
|
|
+ background: var(--el-color-warning-light-9);
|
|
|
+ border: 1px solid var(--el-color-warning-light-7);
|
|
|
+ border-radius: 6px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-warning__icon {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+}
|
|
|
+
|
|
|
+.generate-warning__text {
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+}
|
|
|
+
|
|
|
.query-control {
|
|
|
width: 180px;
|
|
|
}
|