Просмотр исходного кода

[0806] 1.系统中:设备管理-场站管理/光伏组件/限电配置。。。等,目前显示404。经肖工排查,可能缺少.vue的文件。请对应检查。

taosj 3 месяцев назад
Родитель
Сommit
a5ad303beb

+ 177 - 0
forecast-backend-vue/src/views/ufo/config/station/StationConfigCrud.vue

@@ -0,0 +1,177 @@
+<template>
+  <div class="station-config-page">
+    <a-card :bordered="false">
+      <a-form :model="queryParams" layout="inline">
+        <a-form-item label="场站">
+          <a-select v-model:value="queryParams.stationId" allow-clear show-search class="query-select" :options="stationOptions" :filter-option="filterOption" />
+        </a-form-item>
+        <a-form-item v-for="item in searchFields" :key="item.name" :label="item.label">
+          <a-select v-if="item.type === 'select'" v-model:value="queryParams[item.name]" allow-clear class="query-select" :options="item.options" />
+          <a-input v-else v-model:value="queryParams[item.name]" allow-clear :placeholder="'请输入' + item.label" />
+        </a-form-item>
+        <a-form-item>
+          <a-space>
+            <a-button type="primary" :loading="loading" @click="handleQuery">查询</a-button>
+            <a-button @click="resetQuery">重置</a-button>
+            <a-button v-hasPermi="[moduleConfig.perm + ':add']" type="primary" @click="openForm()">新增</a-button>
+            <a-button v-hasPermi="[moduleConfig.perm + ':export']" @click="handleExport">导出</a-button>
+          </a-space>
+        </a-form-item>
+      </a-form>
+    </a-card>
+
+    <a-card :bordered="false" class="section">
+      <a-table :columns="columns" :data-source="rows" :loading="loading" :pagination="pagination" row-key="id" @change="handleTableChange">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'operation'">
+            <a-space>
+              <a v-hasPermi="[moduleConfig.perm + ':edit']" @click="openForm(record)">编辑</a>
+              <a-popconfirm :title="'确认删除该' + moduleConfig.title + '?'" @confirm="removeRecord(record)">
+                <a v-hasPermi="[moduleConfig.perm + ':remove']" class="danger-link">删除</a>
+              </a-popconfirm>
+            </a-space>
+          </template>
+        </template>
+      </a-table>
+    </a-card>
+
+    <a-modal v-model:open="formOpen" :title="form.id ? '编辑' + moduleConfig.title : '新增' + moduleConfig.title" width="860px" @ok="submitForm">
+      <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
+        <a-row :gutter="16">
+          <a-col :span="12">
+            <a-form-item label="所属场站" name="stationId">
+              <a-select v-model:value="form.stationId" show-search :options="stationOptions" :filter-option="filterOption" @change="handleStationChange" />
+            </a-form-item>
+          </a-col>
+          <a-col v-for="item in moduleConfig.fields" :key="item[0]" :span="12">
+            <a-form-item :label="item[1]" :name="item[0]">
+              <a-select v-if="item[2] === 'select'" v-model:value="form[item[0]]" allow-clear :options="toOptions(item[4])" />
+              <a-select v-else-if="item[2] === 'device'" v-model:value="form.deviceId" allow-clear show-search :options="deviceOptions" :filter-option="filterOption" @change="handleDeviceChange" />
+              <a-input-number v-else-if="item[2] === 'number'" v-model:value="form[item[0]]" class="full-input" :min="0" />
+              <a-date-picker v-else-if="item[2] === 'date'" v-model:value="form[item[0]]" value-format="YYYY-MM-DD" class="full-input" />
+              <a-input v-else v-model:value="form[item[0]]" allow-clear />
+            </a-form-item>
+          </a-col>
+        </a-row>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup>
+import { computed, reactive, ref } from 'vue';
+import { message } from 'ant-design-vue';
+import BearJiaUtil from '@/utils/BearJiaUtil';
+import { listStation } from '@/api/config/station';
+import { listStationDevice } from '@/api/config/stationDevice';
+
+const props = defineProps({ moduleConfig: { type: Object, required: true }, api: { type: Object, required: true } });
+
+const loading = ref(false);
+const rows = ref([]);
+const stationOptions = ref([]);
+const allDevices = ref([]);
+const formOpen = ref(false);
+const formRef = ref();
+const queryParams = reactive({ pageNum: 1, pageSize: 10, stationId: undefined });
+const pagination = reactive({ current: 1, pageSize: 10, total: 0, showSizeChanger: true });
+const form = reactive({});
+
+const fieldMap = computed(() => new Map(props.moduleConfig.fields.map((item) => [item[0], item])));
+const searchFields = computed(() => props.moduleConfig.search.filter((name) => name !== 'stationId').map((name) => ({ name, label: fieldMap.value.get(name)?.[1] || name, type: fieldMap.value.get(name)?.[2], options: toOptions(fieldMap.value.get(name)?.[4]) })));
+const columns = computed(() => [
+  { title: 'ID', dataIndex: 'id', width: 80 },
+  ...props.moduleConfig.columns.map((name) => ({ title: name === 'stationName' ? '场站' : fieldMap.value.get(name)?.[1] || name, dataIndex: name, width: 140, ellipsis: true })),
+  { title: '操作', key: 'operation', width: 130, fixed: 'right' }
+]);
+const rules = computed(() => ({
+  stationId: [{ required: true, message: '请选择所属场站', trigger: 'change' }],
+  ...Object.fromEntries(props.moduleConfig.fields.filter((item) => item[3]).map((item) => [item[0], [{ required: true, message: '请输入' + item[1], trigger: item[2] === 'select' ? 'change' : 'blur' }]]))
+}));
+const deviceOptions = computed(() => allDevices.value
+  .filter((item) => !form.stationId || String(item.stationId) === String(form.stationId))
+  .map((item) => ({ label: item.deviceName || item.name || item.id, value: item.id, raw: item })));
+
+const toOptions = (items = []) => items.map((item) => ({ label: item[0], value: item[1] }));
+const filterOption = (input, option) => String(option?.label || '').toLowerCase().includes(input.toLowerCase());
+const resetObject = (target, source = {}) => { Object.keys(target).forEach((key) => delete target[key]); Object.assign(target, source); };
+
+const loadStations = async () => {
+  const res = await listStation({ pageNum: 1, pageSize: 500 });
+  const list = res?.rows || res?.data || [];
+  stationOptions.value = list.map((item) => ({ label: item.stationName || item.name || item.id, value: item.id }));
+};
+const loadDevices = async () => {
+  if (props.moduleConfig.key !== 'point') return;
+  const res = await listStationDevice({ pageNum: 1, pageSize: 500 });
+  allDevices.value = res?.rows || res?.data || [];
+};
+const handleQuery = async () => {
+  loading.value = true;
+  try {
+    const res = await props.api.list(queryParams);
+    rows.value = res?.rows || res?.data || [];
+    pagination.total = res?.total || rows.value.length;
+    pagination.current = queryParams.pageNum;
+    pagination.pageSize = queryParams.pageSize;
+  } finally {
+    loading.value = false;
+  }
+};
+const resetQuery = () => {
+  Object.keys(queryParams).forEach((key) => { if (!['pageNum', 'pageSize'].includes(key)) queryParams[key] = undefined; });
+  queryParams.pageNum = 1;
+  handleQuery();
+};
+const handleTableChange = (page) => {
+  queryParams.pageNum = page.current;
+  queryParams.pageSize = page.pageSize;
+  handleQuery();
+};
+const openForm = async (record) => {
+  await loadDevices();
+  if (record?.id) {
+    const res = await props.api.get(record.id);
+    resetObject(form, { ...res.data });
+  } else {
+    resetObject(form, { stationId: queryParams.stationId });
+  }
+  formOpen.value = true;
+};
+const handleStationChange = () => {
+  if (props.moduleConfig.key === 'point') {
+    form.deviceId = undefined;
+    form.deviceName = undefined;
+  }
+};
+const handleDeviceChange = (value) => {
+  const device = allDevices.value.find((item) => String(item.id) === String(value));
+  form.deviceName = device?.deviceName;
+};
+const submitForm = async () => {
+  await formRef.value?.validate();
+  if (form.id) await props.api.update(form); else await props.api.add(form);
+  message.success('保存成功');
+  formOpen.value = false;
+  handleQuery();
+};
+const removeRecord = async (record) => {
+  await props.api.del(record.id);
+  message.success('删除成功');
+  handleQuery();
+};
+const handleExport = () => BearJiaUtil.download('config/' + props.moduleConfig.api + '/export', queryParams, props.moduleConfig.exportName);
+
+loadStations();
+loadDevices();
+handleQuery();
+</script>
+
+<style lang="less" scoped>
+.station-config-page {
+  .section { margin-top: 12px; }
+  .query-select { width: 180px; }
+  .full-input { width: 100%; }
+  .danger-link { color: #ff4d4f; }
+}
+</style>

+ 12 - 0
forecast-backend-vue/src/views/ufo/config/station/channel/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <StationConfigCrud :module-config="moduleConfig" :api="api" />
+</template>
+
+<script setup name="StationChannel">
+import StationConfigCrud from '../StationConfigCrud.vue';
+import { stationConfigModules } from '../moduleConfig';
+import { listStationChannel, getStationChannel, addStationChannel, updateStationChannel, delStationChannel } from '@/api/config/stationChannel';
+
+const moduleConfig = stationConfigModules.find((item) => item.key === 'channel');
+const api = { list: listStationChannel, get: getStationChannel, add: addStationChannel, update: updateStationChannel, del: delStationChannel };
+</script>

+ 12 - 0
forecast-backend-vue/src/views/ufo/config/station/device/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <StationConfigCrud :module-config="moduleConfig" :api="api" />
+</template>
+
+<script setup name="StationDevice">
+import StationConfigCrud from '../StationConfigCrud.vue';
+import { stationConfigModules } from '../moduleConfig';
+import { listStationDevice, getStationDevice, addStationDevice, updateStationDevice, delStationDevice } from '@/api/config/stationDevice';
+
+const moduleConfig = stationConfigModules.find((item) => item.key === 'device');
+const api = { list: listStationDevice, get: getStationDevice, add: addStationDevice, update: updateStationDevice, del: delStationDevice };
+</script>

+ 275 - 0
forecast-backend-vue/src/views/ufo/config/station/index.vue

@@ -0,0 +1,275 @@
+<template>
+  <div class="station-page">
+    <a-card :bordered="false">
+      <a-form :model="queryParams" layout="inline">
+        <a-form-item label="场站名称">
+          <a-input v-model:value="queryParams.stationName" allow-clear placeholder="请输入场站名称" />
+        </a-form-item>
+        <a-form-item label="场站编码">
+          <a-input v-model:value="queryParams.stationCode" allow-clear placeholder="请输入场站编码" />
+        </a-form-item>
+        <a-form-item label="场站类型">
+          <a-select v-model:value="queryParams.stationType" allow-clear class="query-select" :options="stationTypeOptions" />
+        </a-form-item>
+        <a-form-item label="是否启用">
+          <a-select v-model:value="queryParams.isEnable" allow-clear class="query-select" :options="enableOptions" />
+        </a-form-item>
+        <a-form-item>
+          <a-space>
+            <a-button type="primary" :loading="loading" @click="handleQuery">查询</a-button>
+            <a-button @click="resetQuery">重置</a-button>
+            <a-button v-hasPermi="['config:station:add']" type="primary" @click="openForm()">新增</a-button>
+            <a-button v-hasPermi="['config:station:export']" @click="handleExport">导出</a-button>
+          </a-space>
+        </a-form-item>
+      </a-form>
+    </a-card>
+
+    <a-card :bordered="false" class="section">
+      <a-table
+        :columns="columns"
+        :data-source="rows"
+        :loading="loading"
+        :pagination="pagination"
+        row-key="id"
+        @change="handleTableChange"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'stationType'">
+            {{ stationTypeText(record.stationType) }}
+          </template>
+          <template v-else-if="column.key === 'isEnable'">
+            <a-tag :color="record.isEnable === '1' ? 'success' : 'default'">{{ enableText(record.isEnable) }}</a-tag>
+          </template>
+          <template v-else-if="column.key === 'operation'">
+            <a-space>
+              <a v-hasPermi="['config:station:edit']" @click="openForm(record)">编辑</a>
+              <a-popconfirm title="确认删除该场站?" @confirm="removeRecord(record)">
+                <a v-hasPermi="['config:station:remove']" class="danger-link">删除</a>
+              </a-popconfirm>
+            </a-space>
+          </template>
+        </template>
+      </a-table>
+    </a-card>
+
+    <a-modal v-model:open="formOpen" :title="form.id ? '编辑场站' : '新增场站'" width="820px" @ok="submitForm">
+      <a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 7 }" :wrapper-col="{ span: 15 }">
+        <a-row :gutter="16">
+          <a-col :span="12">
+            <a-form-item label="场站名称" name="stationName">
+              <a-input v-model:value="form.stationName" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="场站编码" name="stationCode">
+              <a-input v-model:value="form.stationCode" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="场站类型" name="stationType">
+              <a-select v-model:value="form.stationType" allow-clear :options="stationTypeOptions" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="是否启用" name="isEnable">
+              <a-select v-model:value="form.isEnable" allow-clear :options="enableOptions" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="装机容量(kW)" name="installedCapacity">
+              <a-input-number v-model:value="form.installedCapacity" class="full-input" :min="0" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="并网容量(kW)" name="gridConnectedCapacity">
+              <a-input-number v-model:value="form.gridConnectedCapacity" class="full-input" :min="0" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="经度" name="longitude">
+              <a-input v-model:value="form.longitude" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="纬度" name="latitude">
+              <a-input v-model:value="form.latitude" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="位置" name="position">
+              <a-input v-model:value="form.position" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="投运时间" name="operationTime">
+              <a-input v-model:value="form.operationTime" allow-clear placeholder="如 2026-01-01" />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="负责人" name="responsiblePersonName">
+              <a-input v-model:value="form.responsiblePersonName" allow-clear />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="负责人电话" name="responsiblePersonPhone">
+              <a-input v-model:value="form.responsiblePersonPhone" allow-clear />
+            </a-form-item>
+          </a-col>
+        </a-row>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script setup name="ConfigStation">
+import { reactive, ref } from 'vue';
+import { message } from 'ant-design-vue';
+import BearJiaUtil from '@/utils/BearJiaUtil';
+import { addStation, delStation, getStation, listStation, updateStation } from '@/api/config/station';
+
+const loading = ref(false);
+const rows = ref([]);
+const formOpen = ref(false);
+const formRef = ref();
+
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  stationName: undefined,
+  stationCode: undefined,
+  stationType: undefined,
+  isEnable: undefined
+});
+
+const pagination = reactive({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true
+});
+
+const form = reactive({});
+
+const stationTypeOptions = [
+  { label: '风电', value: 0 },
+  { label: '光伏', value: 1 }
+];
+
+const enableOptions = [
+  { label: '停用', value: '0' },
+  { label: '启用', value: '1' }
+];
+
+const rules = {
+  stationName: [{ required: true, message: '请输入场站名称', trigger: 'blur' }]
+};
+
+const columns = [
+  { title: 'ID', dataIndex: 'id', width: 90 },
+  { title: '场站名称', dataIndex: 'stationName', width: 180 },
+  { title: '场站编码', dataIndex: 'stationCode', width: 180 },
+  { title: '类型', dataIndex: 'stationType', key: 'stationType', width: 100 },
+  { title: '装机容量(kW)', dataIndex: 'installedCapacity', width: 140 },
+  { title: '并网容量(kW)', dataIndex: 'gridConnectedCapacity', width: 140 },
+  { title: '位置', dataIndex: 'position', width: 180 },
+  { title: '负责人', dataIndex: 'responsiblePersonName', width: 120 },
+  { title: '电话', dataIndex: 'responsiblePersonPhone', width: 140 },
+  { title: '启用', dataIndex: 'isEnable', key: 'isEnable', width: 90 },
+  { title: '操作', key: 'operation', width: 130, fixed: 'right' }
+];
+
+const stationTypeText = (value) => stationTypeOptions.find((item) => item.value === Number(value))?.label || '-';
+const enableText = (value) => enableOptions.find((item) => item.value === String(value))?.label || '-';
+
+const resetObject = (target, source = {}) => {
+  Object.keys(target).forEach((key) => delete target[key]);
+  Object.assign(target, source);
+};
+
+const handleQuery = async () => {
+  loading.value = true;
+  try {
+    const res = await listStation(queryParams);
+    rows.value = res?.rows || res?.data || [];
+    pagination.total = res?.total || rows.value.length;
+    pagination.current = queryParams.pageNum;
+    pagination.pageSize = queryParams.pageSize;
+  } finally {
+    loading.value = false;
+  }
+};
+
+const resetQuery = () => {
+  queryParams.pageNum = 1;
+  queryParams.stationName = undefined;
+  queryParams.stationCode = undefined;
+  queryParams.stationType = undefined;
+  queryParams.isEnable = undefined;
+  handleQuery();
+};
+
+const handleTableChange = (page) => {
+  queryParams.pageNum = page.current;
+  queryParams.pageSize = page.pageSize;
+  handleQuery();
+};
+
+const openForm = async (record) => {
+  if (record?.id) {
+    const res = await getStation(record.id);
+    const data = { ...res.data };
+    if (data.stationType !== undefined && data.stationType !== null) {
+      data.stationType = Number(data.stationType);
+    }
+    resetObject(form, data);
+  } else {
+    resetObject(form, { isEnable: '1', stationType: 0 });
+  }
+  formOpen.value = true;
+};
+
+const submitForm = async () => {
+  await formRef.value?.validate();
+  if (form.id) {
+    await updateStation(form);
+  } else {
+    await addStation(form);
+  }
+  message.success('保存成功');
+  formOpen.value = false;
+  handleQuery();
+};
+
+const removeRecord = async (record) => {
+  await delStation(record.id);
+  message.success('删除成功');
+  handleQuery();
+};
+
+const handleExport = () => {
+  BearJiaUtil.download('config/station/export', queryParams, '场站信息.xlsx');
+};
+
+handleQuery();
+</script>
+
+<style lang="less" scoped>
+.station-page {
+  .section {
+    margin-top: 12px;
+  }
+
+  .query-select {
+    width: 140px;
+  }
+
+  .full-input {
+    width: 100%;
+  }
+
+  .danger-link {
+    color: #ff4d4f;
+  }
+}
+</style>

+ 12 - 0
forecast-backend-vue/src/views/ufo/config/station/module/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <StationConfigCrud :module-config="moduleConfig" :api="api" />
+</template>
+
+<script setup name="StationModule">
+import StationConfigCrud from '../StationConfigCrud.vue';
+import { stationConfigModules } from '../moduleConfig';
+import { listStationModule, getStationModule, addStationModule, updateStationModule, delStationModule } from '@/api/config/stationModule';
+
+const moduleConfig = stationConfigModules.find((item) => item.key === 'module');
+const api = { list: listStationModule, get: getStationModule, add: addStationModule, update: updateStationModule, del: delStationModule };
+</script>

+ 453 - 0
forecast-backend-vue/src/views/ufo/config/station/moduleConfig.js

@@ -0,0 +1,453 @@
+export const stationConfigModules = [
+  {
+    "key": "module",
+    "title": "光伏组件",
+    "api": "stationModule",
+    "perm": "config:stationModule",
+    "exportName": "光伏组件配置.xlsx",
+    "fields": [
+      [
+        "model",
+        "组件型号",
+        "input",
+        true
+      ],
+      [
+        "brand",
+        "品牌",
+        "input"
+      ],
+      [
+        "ratedPower",
+        "额定功率(W)",
+        "number"
+      ],
+      [
+        "moduleType",
+        "组件类型",
+        "select",
+        false,
+        [
+          [
+            "单面",
+            "单面"
+          ],
+          [
+            "双面",
+            "双面"
+          ]
+        ]
+      ],
+      [
+        "moduleCount",
+        "数量",
+        "number"
+      ],
+      [
+        "conversionEfficiency",
+        "转换效率",
+        "input"
+      ],
+      [
+        "temperatureCoefficient",
+        "温度系数",
+        "input"
+      ],
+      [
+        "installDate",
+        "安装日期",
+        "date"
+      ]
+    ],
+    "columns": [
+      "stationName",
+      "model",
+      "brand",
+      "ratedPower",
+      "moduleType",
+      "moduleCount",
+      "conversionEfficiency",
+      "temperatureCoefficient",
+      "installDate"
+    ],
+    "search": [
+      "stationId",
+      "model",
+      "brand",
+      "moduleType"
+    ]
+  },
+  {
+    "key": "powerLimit",
+    "title": "限电配置",
+    "api": "stationPowerLimit",
+    "perm": "config:stationPowerLimit",
+    "exportName": "限电配置.xlsx",
+    "fields": [
+      [
+        "limitType",
+        "限电类型",
+        "select",
+        true,
+        [
+          [
+            "调度限电",
+            "调度限电"
+          ],
+          [
+            "检修限电",
+            "检修限电"
+          ],
+          [
+            "故障限电",
+            "故障限电"
+          ]
+        ]
+      ],
+      [
+        "startDate",
+        "开始日期",
+        "date"
+      ],
+      [
+        "endDate",
+        "结束日期",
+        "date"
+      ],
+      [
+        "limitPercent",
+        "限电比例",
+        "input"
+      ],
+      [
+        "reason",
+        "原因",
+        "input"
+      ],
+      [
+        "approver",
+        "审批方",
+        "input"
+      ],
+      [
+        "status",
+        "状态",
+        "select",
+        false,
+        [
+          [
+            "生效中",
+            "生效中"
+          ],
+          [
+            "待执行",
+            "待执行"
+          ],
+          [
+            "已结束",
+            "已结束"
+          ]
+        ]
+      ]
+    ],
+    "columns": [
+      "stationName",
+      "limitType",
+      "startDate",
+      "endDate",
+      "limitPercent",
+      "reason",
+      "approver",
+      "status"
+    ],
+    "search": [
+      "stationId",
+      "limitType",
+      "status"
+    ]
+  },
+  {
+    "key": "device",
+    "title": "设备配置",
+    "api": "stationDevice",
+    "perm": "config:stationDevice",
+    "exportName": "设备配置.xlsx",
+    "fields": [
+      [
+        "deviceName",
+        "设备名称",
+        "input",
+        true
+      ],
+      [
+        "deviceType",
+        "设备类型",
+        "input"
+      ],
+      [
+        "brand",
+        "品牌",
+        "input"
+      ],
+      [
+        "model",
+        "型号",
+        "input"
+      ],
+      [
+        "serialNumber",
+        "序列号",
+        "input"
+      ],
+      [
+        "status",
+        "状态",
+        "select",
+        false,
+        [
+          [
+            "在线",
+            "在线"
+          ],
+          [
+            "故障",
+            "故障"
+          ],
+          [
+            "离线",
+            "离线"
+          ],
+          [
+            "维护",
+            "维护"
+          ]
+        ]
+      ],
+      [
+        "installDate",
+        "安装日期",
+        "date"
+      ]
+    ],
+    "columns": [
+      "stationName",
+      "deviceName",
+      "deviceType",
+      "brand",
+      "model",
+      "serialNumber",
+      "status",
+      "installDate"
+    ],
+    "search": [
+      "stationId",
+      "deviceName",
+      "deviceType",
+      "status"
+    ]
+  },
+  {
+    "key": "channel",
+    "title": "通道配置",
+    "api": "stationChannel",
+    "perm": "config:stationChannel",
+    "exportName": "通道配置.xlsx",
+    "fields": [
+      [
+        "channelName",
+        "通道名称",
+        "input",
+        true
+      ],
+      [
+        "protocol",
+        "协议",
+        "select",
+        false,
+        [
+          [
+            "FTP",
+            "FTP"
+          ],
+          [
+            "SFTP",
+            "SFTP"
+          ],
+          [
+            "Modbus TCP",
+            "Modbus TCP"
+          ],
+          [
+            "MQTT",
+            "MQTT"
+          ],
+          [
+            "HTTP",
+            "HTTP"
+          ],
+          [
+            "OPC UA",
+            "OPC UA"
+          ],
+          [
+            "IEC 104",
+            "IEC 104"
+          ]
+        ]
+      ],
+      [
+        "host",
+        "主机地址",
+        "input"
+      ],
+      [
+        "port",
+        "端口",
+        "number"
+      ],
+      [
+        "collectInterval",
+        "采集间隔",
+        "input"
+      ],
+      [
+        "dataType",
+        "数据类型",
+        "input"
+      ],
+      [
+        "status",
+        "状态",
+        "select",
+        false,
+        [
+          [
+            "正常",
+            "正常"
+          ],
+          [
+            "延迟",
+            "延迟"
+          ],
+          [
+            "断开",
+            "断开"
+          ]
+        ]
+      ],
+      [
+        "lastSyncTime",
+        "最后同步时间",
+        "input"
+      ]
+    ],
+    "columns": [
+      "stationName",
+      "channelName",
+      "protocol",
+      "host",
+      "port",
+      "collectInterval",
+      "dataType",
+      "status",
+      "lastSyncTime"
+    ],
+    "search": [
+      "stationId",
+      "channelName",
+      "protocol",
+      "status"
+    ]
+  },
+  {
+    "key": "point",
+    "title": "测点配置",
+    "api": "stationPoint",
+    "perm": "config:stationPoint",
+    "exportName": "测点配置.xlsx",
+    "fields": [
+      [
+        "deviceId",
+        "设备",
+        "device"
+      ],
+      [
+        "deviceName",
+        "设备名称",
+        "input"
+      ],
+      [
+        "pointName",
+        "测点名称",
+        "input",
+        true
+      ],
+      [
+        "unit",
+        "单位",
+        "input"
+      ],
+      [
+        "precisionValue",
+        "精度",
+        "number"
+      ],
+      [
+        "measureRange",
+        "量程",
+        "input"
+      ],
+      [
+        "protocol",
+        "协议",
+        "select",
+        false,
+        [
+          [
+            "Modbus",
+            "Modbus"
+          ],
+          [
+            "OPC UA",
+            "OPC UA"
+          ],
+          [
+            "IEC 104",
+            "IEC 104"
+          ],
+          [
+            "MQTT",
+            "MQTT"
+          ]
+        ]
+      ],
+      [
+        "address",
+        "地址",
+        "input"
+      ],
+      [
+        "collectInterval",
+        "采集间隔",
+        "input"
+      ]
+    ],
+    "columns": [
+      "stationName",
+      "deviceName",
+      "pointName",
+      "unit",
+      "precisionValue",
+      "measureRange",
+      "protocol",
+      "address",
+      "collectInterval"
+    ],
+    "search": [
+      "stationId",
+      "deviceName",
+      "pointName",
+      "protocol"
+    ]
+  }
+];

+ 12 - 0
forecast-backend-vue/src/views/ufo/config/station/point/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <StationConfigCrud :module-config="moduleConfig" :api="api" />
+</template>
+
+<script setup name="StationPoint">
+import StationConfigCrud from '../StationConfigCrud.vue';
+import { stationConfigModules } from '../moduleConfig';
+import { listStationPoint, getStationPoint, addStationPoint, updateStationPoint, delStationPoint } from '@/api/config/stationPoint';
+
+const moduleConfig = stationConfigModules.find((item) => item.key === 'point');
+const api = { list: listStationPoint, get: getStationPoint, add: addStationPoint, update: updateStationPoint, del: delStationPoint };
+</script>

+ 12 - 0
forecast-backend-vue/src/views/ufo/config/station/powerLimit/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <StationConfigCrud :module-config="moduleConfig" :api="api" />
+</template>
+
+<script setup name="StationPowerLimit">
+import StationConfigCrud from '../StationConfigCrud.vue';
+import { stationConfigModules } from '../moduleConfig';
+import { listStationPowerLimit, getStationPowerLimit, addStationPowerLimit, updateStationPowerLimit, delStationPowerLimit } from '@/api/config/stationPowerLimit';
+
+const moduleConfig = stationConfigModules.find((item) => item.key === 'powerLimit');
+const api = { list: listStationPowerLimit, get: getStationPowerLimit, add: addStationPowerLimit, update: updateStationPowerLimit, del: delStationPowerLimit };
+</script>