|
|
@@ -0,0 +1,398 @@
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <a-card class="general-card">
|
|
|
+ <a-row>
|
|
|
+ <a-col :flex="1">
|
|
|
+ <a-form
|
|
|
+ :model="formModel"
|
|
|
+ :label-col-props="{ span: 4 }"
|
|
|
+ :wrapper-col-props="{ span: 18 }"
|
|
|
+ label-align="left"
|
|
|
+ >
|
|
|
+ <a-row :gutter="16">
|
|
|
+ <a-col :span="6">
|
|
|
+ <a-form-item
|
|
|
+ field="address"
|
|
|
+ :label="t('dashboard.form.address')"
|
|
|
+ label-col-flex="50px"
|
|
|
+ >
|
|
|
+ <a-input
|
|
|
+ v-model="formModel.address"
|
|
|
+ :placeholder="t('dashboard.form.address')"
|
|
|
+ allow-clear
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="6">
|
|
|
+ <a-form-item
|
|
|
+ field="name"
|
|
|
+ :label="t('dashboard.form.name')"
|
|
|
+ label-col-flex="50px"
|
|
|
+ >
|
|
|
+ <a-input
|
|
|
+ v-model="formModel.name"
|
|
|
+ :placeholder="t('dashboard.form.name')"
|
|
|
+ allow-clear
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="6">
|
|
|
+ <a-form-item
|
|
|
+ field="entityType"
|
|
|
+ :label="t('dashboard.form.entityType')"
|
|
|
+ label-col-flex="60px"
|
|
|
+ >
|
|
|
+ <a-select
|
|
|
+ v-model="formModel.entityType"
|
|
|
+ :placeholder="t('dashboard.form.entityType')"
|
|
|
+ allow-clear
|
|
|
+ @clear="formModel.entityType = null"
|
|
|
+ >
|
|
|
+ <a-option
|
|
|
+ v-for="item of entityTypeList"
|
|
|
+ :value="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ />
|
|
|
+ </a-select>
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="6">
|
|
|
+ <a-form-item
|
|
|
+ field="statusTypeList"
|
|
|
+ :label="t('dashboard.form.status')"
|
|
|
+ label-col-flex="60px"
|
|
|
+ >
|
|
|
+ <a-select
|
|
|
+ v-model="formModel.status"
|
|
|
+ :placeholder="t('dashboard.form.status')"
|
|
|
+ allow-clear
|
|
|
+ @clear="formModel.status = null"
|
|
|
+ >
|
|
|
+ <a-option
|
|
|
+ v-for="item of statusTypeList"
|
|
|
+ :value="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ />
|
|
|
+ </a-select>
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="6">
|
|
|
+ <a-form-item
|
|
|
+ :label="t('dashboard.form.timeRange')"
|
|
|
+ label-col-flex="60px"
|
|
|
+ >
|
|
|
+ <a-range-picker v-model="formModel.time" />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ </a-form>
|
|
|
+ </a-col>
|
|
|
+ <a-divider :style="{ height: '84px' }" direction="vertical" />
|
|
|
+ <a-col :flex="'86px'">
|
|
|
+ <a-space :size="10">
|
|
|
+ <a-button type="primary" @click="search">
|
|
|
+ <template #icon>
|
|
|
+ <icon-search />
|
|
|
+ </template>
|
|
|
+ {{ t('searchTable.form.search') }}
|
|
|
+ </a-button>
|
|
|
+ <a-button @click="reset">
|
|
|
+ <template #icon>
|
|
|
+ <icon-refresh />
|
|
|
+ </template>
|
|
|
+ {{ t('searchTable.form.reset') }}
|
|
|
+ </a-button>
|
|
|
+ </a-space>
|
|
|
+ <a-button
|
|
|
+ type="primary"
|
|
|
+ class="right-side"
|
|
|
+ @click="showEditDialog = true"
|
|
|
+ >
|
|
|
+ <template #icon>
|
|
|
+ <icon-plus />
|
|
|
+ </template>
|
|
|
+ {{ t('searchTable.form.add') }}
|
|
|
+ </a-button>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+ <a-table
|
|
|
+ class="table-list"
|
|
|
+ row-key="name"
|
|
|
+ :loading="loading"
|
|
|
+ :pagination="pagination"
|
|
|
+ :columns="(cloneColumns as TableColumnData[])"
|
|
|
+ :data="renderData"
|
|
|
+ :bordered="false"
|
|
|
+ :size="size"
|
|
|
+ :scrollbar="true"
|
|
|
+ @page-change="onPageChange"
|
|
|
+ @row-dblclick="handleClick"
|
|
|
+ >
|
|
|
+ <template #index="{ rowIndex }">
|
|
|
+ {{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template #entityType="{ record }">
|
|
|
+ <span>
|
|
|
+ {{
|
|
|
+ entityTypeList.find(item => item.value === record.entityType)
|
|
|
+ ?.label
|
|
|
+ }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ <template #status="{ record }">
|
|
|
+ <BTag :status="record.status">
|
|
|
+ {{
|
|
|
+ statusTypeList.find(item => item.value === record.status)?.label
|
|
|
+ }}
|
|
|
+ </BTag>
|
|
|
+ </template>
|
|
|
+ <template #time="{ record }">
|
|
|
+ <span>{{
|
|
|
+ record.time && dayjs(record.time).format('YYYY-MM-DD HH:mm:ss')
|
|
|
+ }}</span>
|
|
|
+ </template>
|
|
|
+ <template #optional="{ record }">
|
|
|
+ <a-button
|
|
|
+ type="primary"
|
|
|
+ size="mini"
|
|
|
+ class="action-button"
|
|
|
+ @click="
|
|
|
+ () => {
|
|
|
+ (deviceId = record.id), (showEditDialog = true);
|
|
|
+ }
|
|
|
+ "
|
|
|
+ >
|
|
|
+ {{ t('searchTable.form.edit') }}
|
|
|
+ </a-button>
|
|
|
+ <a-button
|
|
|
+ status="danger"
|
|
|
+ size="mini"
|
|
|
+ @click="handleDeleteFun(record.id)"
|
|
|
+ >
|
|
|
+ {{ t('searchTable.form.delete') }}
|
|
|
+ </a-button>
|
|
|
+ </template>
|
|
|
+ </a-table>
|
|
|
+ </a-card>
|
|
|
+ <a-modal
|
|
|
+ v-model:visible="visible"
|
|
|
+ title-align="start"
|
|
|
+ @cancel="handleCancel"
|
|
|
+ >
|
|
|
+ <template #title>{{ t('dashboard.dialog.title') }}</template>
|
|
|
+ <div>
|
|
|
+ <a-descriptions :data="deviceInfo" bordered :column="2" />
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <a-button @click="handleCancel">取消</a-button>
|
|
|
+ <!-- 只保留取消按钮,确认按钮被隐藏 -->
|
|
|
+ </template>
|
|
|
+ </a-modal>
|
|
|
+ <EditDialog
|
|
|
+ v-model="showEditDialog"
|
|
|
+ :id="deviceId"
|
|
|
+ @updateList="updateListFun"
|
|
|
+ ></EditDialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup name="Dashboard">
|
|
|
+import {
|
|
|
+ ref,
|
|
|
+ reactive,
|
|
|
+ shallowRef,
|
|
|
+ h,
|
|
|
+ getCurrentInstance,
|
|
|
+ computed,
|
|
|
+} from 'vue';
|
|
|
+import {
|
|
|
+ queryDashboardList,
|
|
|
+ exportDashboardList,
|
|
|
+ deleteDeviceDetails,
|
|
|
+} from '@/api/dashboard';
|
|
|
+import type { DashboardParams, DataList } from '@/api/dashboard';
|
|
|
+import { SizeProps, Pagination } from '@/types/global';
|
|
|
+import BTag from '@/components/business/b-tag/index.vue';
|
|
|
+import EditDialog from './edit.vue';
|
|
|
+import type { TableColumnData } from '@arco-design/web-vue';
|
|
|
+import useLoading from '@/hooks/loading';
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+import { statusTypeList } from '../workplace/conf';
|
|
|
+import dayjs from 'dayjs';
|
|
|
+import { downLoadFun, DeviceInfo, entityTypeList } from '@/utils/const';
|
|
|
+import { useIntervalFn } from '@vueuse/core';
|
|
|
+import { Modal } from '@arco-design/web-vue';
|
|
|
+
|
|
|
+const { t } = useI18n();
|
|
|
+
|
|
|
+const { loading, setLoading } = useLoading(true);
|
|
|
+const cloneColumns = computed(() => [
|
|
|
+ {
|
|
|
+ title: t('searchTable.table.number'),
|
|
|
+ dataIndex: 'index',
|
|
|
+ slotName: 'index',
|
|
|
+ ellipsis: true,
|
|
|
+ tooltip: true,
|
|
|
+ width: 70,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: t('dashboard.table.time'),
|
|
|
+ dataIndex: 'time',
|
|
|
+ slotName: 'time',
|
|
|
+ ellipsis: true,
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // title: t('dashboard.form.status'),
|
|
|
+ // dataIndex: 'status',
|
|
|
+ // slotName: 'status',
|
|
|
+ // width: 120,
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ title: t('dashboard.form.name'),
|
|
|
+ dataIndex: 'name',
|
|
|
+ slotName: 'name',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: t('dashboard.form.entityType'),
|
|
|
+ dataIndex: 'entityType',
|
|
|
+ slotName: 'entityType',
|
|
|
+ },
|
|
|
+
|
|
|
+ {
|
|
|
+ title: t('dashboard.form.address'),
|
|
|
+ dataIndex: 'address',
|
|
|
+ ellipsis: true,
|
|
|
+ tooltip: true,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: t('searchTable.table.optional'),
|
|
|
+ align: 'center',
|
|
|
+ slotName: 'optional',
|
|
|
+ },
|
|
|
+]);
|
|
|
+
|
|
|
+const basePagination: Pagination = {
|
|
|
+ current: 1,
|
|
|
+ pageSize: 20,
|
|
|
+};
|
|
|
+const pagination = reactive({
|
|
|
+ ...basePagination,
|
|
|
+});
|
|
|
+const generateFormModel = () => {
|
|
|
+ return {
|
|
|
+ pageIndex: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ name: null,
|
|
|
+ address: null,
|
|
|
+ status: null,
|
|
|
+ startTime: null,
|
|
|
+ endTime: null,
|
|
|
+ time: ['', ''],
|
|
|
+ entityType: null,
|
|
|
+ } as DashboardParams;
|
|
|
+};
|
|
|
+const renderData = ref<DataList[]>([] as DataList[]);
|
|
|
+const size = ref<SizeProps>('medium');
|
|
|
+const formModel = ref<DashboardParams>(generateFormModel());
|
|
|
+const visible = shallowRef<boolean>(false);
|
|
|
+const showEditDialog = shallowRef<boolean>(false);
|
|
|
+const deviceId = ref<number | null>(null);
|
|
|
+const this_ = getCurrentInstance()?.appContext.config.globalProperties;
|
|
|
+const deviceInfo = ref<DeviceInfo[]>([] as DeviceInfo[]);
|
|
|
+function searchTable() {
|
|
|
+ // setLoading(true);
|
|
|
+ const [startTime, endTime] = formModel.value.time
|
|
|
+ ? formModel.value.time
|
|
|
+ : ['', ''];
|
|
|
+ formModel.value.startTime = startTime ? startTime : null;
|
|
|
+ formModel.value.endTime = endTime ? endTime : null;
|
|
|
+ queryDashboardList(formModel.value)
|
|
|
+ .then(res => {
|
|
|
+ pagination.current = formModel.value.pageIndex;
|
|
|
+ pagination.pageSize = pagination.pageSize;
|
|
|
+ pagination.total = res.totalCount;
|
|
|
+ renderData.value = res.data;
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ setLoading(false);
|
|
|
+ });
|
|
|
+}
|
|
|
+searchTable();
|
|
|
+
|
|
|
+const search = () => {
|
|
|
+ searchTable();
|
|
|
+};
|
|
|
+const reset = () => {
|
|
|
+ formModel.value = generateFormModel();
|
|
|
+};
|
|
|
+const onPageChange = (current: number) => {
|
|
|
+ formModel.value.pageIndex = current;
|
|
|
+ searchTable();
|
|
|
+};
|
|
|
+
|
|
|
+const handleClick = (value: DataList) => {
|
|
|
+ if (value.data) {
|
|
|
+ deviceInfo.value.length = 0;
|
|
|
+ const obj = JSON.parse(value.data);
|
|
|
+ for (const key in obj) {
|
|
|
+ deviceInfo.value.push({
|
|
|
+ label: key,
|
|
|
+ value: obj[key],
|
|
|
+ });
|
|
|
+ }
|
|
|
+ visible.value = true;
|
|
|
+ } else {
|
|
|
+ this_ &&
|
|
|
+ this_.$message.warning('No device information available at the moment');
|
|
|
+ }
|
|
|
+};
|
|
|
+const handleCancel = () => {
|
|
|
+ visible.value = false;
|
|
|
+};
|
|
|
+const updateListFun = () => {
|
|
|
+ deviceId.value = null;
|
|
|
+ searchTable();
|
|
|
+};
|
|
|
+const handleDeleteFun = (id: number) => {
|
|
|
+ Modal.warning({
|
|
|
+ title: t('modal.warning.title'),
|
|
|
+ content: t('modal.warning.content'),
|
|
|
+ okText: t('searchTable.form.confirm'),
|
|
|
+ onBeforeOk: (done: (closed: boolean) => void) => {
|
|
|
+ deleteDeviceDetails({ id })
|
|
|
+ .then(res => {
|
|
|
+ if (res.success) {
|
|
|
+ this_?.$message.success('操作成功');
|
|
|
+ searchTable();
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ done(true); // 确定关闭模态框
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less" scoped>
|
|
|
+.container {
|
|
|
+ padding: 10px 10px 20px;
|
|
|
+
|
|
|
+ .general-card {
|
|
|
+ padding-top: 20px;
|
|
|
+
|
|
|
+ .right-side {
|
|
|
+ margin-top: 20px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-list {
|
|
|
+ margin-top: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-button {
|
|
|
+ margin-right: 10px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|