wanshenmean
2026-03-17 737dec3c384f394fd6f9849b4480b697d1ba35d5
Code/WCS/WIDESEAWCS_S7Simulator/WIDESEAWCS_S7Simulator.Web/src/views/CreateView.vue
@@ -1,217 +1,219 @@
<template>
  <div>
    <div class="d-flex justify-content-between align-items-center mb-4">
      <div>
        <h2 class="mb-0">
          <i class="bi bi-plus-circle me-2"></i>创建实例
    <div class="page-header">
      <div class="header-left">
        <h2>
          <el-icon :size="24"><Plus /></el-icon>
          创建实例
        </h2>
        <p class="text-muted mb-0 mt-1">创建新的 S7 PLC 仿真器实例</p>
        <p class="text-muted">创建新的 S7 PLC 仿真实例</p>
      </div>
      <router-link to="/" class="btn btn-outline-secondary">
        <i class="bi bi-arrow-left me-1"></i>返回列表
      </router-link>
      <el-button @click="$router.push('/')">
        <el-icon><Back /></el-icon>
        返回列表
      </el-button>
    </div>
    <div class="row justify-content-center">
      <div class="col-lg-8">
        <div class="card">
          <div class="card-body">
            <form @submit.prevent="handleSubmit">
              <!-- 基本信息 -->
              <h5 class="card-title mb-3">基本信息</h5>
              <div class="row mb-3">
                <div class="col-md-6">
                  <label for="id" class="form-label">实例ID *</label>
                  <input
                    type="text"
                    class="form-control"
                    id="id"
    <el-row justify="center">
      <el-col :lg="24">
        <el-card shadow="never">
          <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
            <!-- 基本信息 -->
            <el-divider content-position="left">
              <h3>基本信息</h3>
            </el-divider>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="实例ID" prop="id">
                  <el-input
                    v-model="form.id"
                    pattern="[a-zA-Z0-9_-]+"
                    required
                    placeholder="例如: PLC_001"
                  >
                  <div class="form-text">只能包含字母、数字、下划线和连字符</div>
                </div>
                <div class="col-md-6">
                  <label for="name" class="form-label">实例名称 *</label>
                  <input
                    type="text"
                    class="form-control"
                    id="name"
                    v-model="form.name"
                    required
                    placeholder="例如: 1号PLC"
                  >
                </div>
              </div>
                    <template #append>
                      <el-tooltip content="只能包含字母、数字、下划线和连字符" placement="top">
                        <el-icon><QuestionFilled /></el-icon>
                      </el-tooltip>
                    </template>
                  </el-input>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="实例名称" prop="name">
                  <el-input v-model="form.name" placeholder="例如: 1号PLC" />
                </el-form-item>
              </el-col>
            </el-row>
              <div class="row mb-3">
                <div class="col-md-6">
                  <label for="plcType" class="form-label">PLC型号 *</label>
                  <select class="form-select" id="plcType" v-model="form.plcType" required>
                    <option value="S7200Smart">S7-200 Smart</option>
                    <option value="S71200">S7-1200</option>
                    <option value="S71500">S7-1500</option>
                    <option value="S7300">S7-300</option>
                    <option value="S7400">S7-400</option>
                  </select>
                </div>
                <div class="col-md-6">
                  <label for="port" class="form-label">监听端口 *</label>
                  <input
                    type="number"
                    class="form-control"
                    id="port"
                    v-model.number="form.port"
                    min="1"
                    max="65535"
                    required
                    placeholder="102"
                  >
                </div>
              </div>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="PLC型号" prop="plcType">
                  <el-select v-model="form.plcType" style="width: 100%">
                    <el-option label="S7-200 Smart" value="S7200Smart" />
                    <el-option label="S7-1200" value="S71200" />
                    <el-option label="S7-1500" value="S71500" />
                    <el-option label="S7-300" value="S7300" />
                    <el-option label="S7-400" value="S7400" />
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="监听端口" prop="port">
                  <el-input-number
                    v-model="form.port"
                    :min="1"
                    :max="65535"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
            </el-row>
              <div class="row mb-3">
                <div class="col-md-6">
                  <label for="activationKey" class="form-label">HSL激活码</label>
                  <input
                    type="text"
                    class="form-control"
                    id="activationKey"
                    v-model="form.activationKey"
                    placeholder="可选,留空使用默认"
                  >
                </div>
                <div class="col-md-6 d-flex align-items-center">
                  <div class="form-check">
                    <input class="form-check-input" type="checkbox" id="autoStart" v-model="form.autoStart">
                    <label class="form-check-label" for="autoStart">
                      自动启动
                    </label>
                  </div>
                </div>
              </div>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="HSL激活码">
                  <el-input v-model="form.activationKey" placeholder="可选,留空使用默认" />
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="自动启动">
                  <el-switch v-model="form.autoStart" />
                </el-form-item>
              </el-col>
            </el-row>
              <!-- 内存配置 -->
              <h5 class="card-title mb-3 mt-4">内存配置</h5>
              <div class="row mb-3">
                <div class="col-md-4">
                  <label for="mRegionSize" class="form-label">M区域大小</label>
                  <input
                    type="number"
                    class="form-control"
                    id="mRegionSize"
                    v-model.number="form.mRegionSize"
                    min="0"
                    placeholder="1024"
                  >
                </div>
                <div class="col-md-4">
                  <label for="iRegionSize" class="form-label">I区域大小</label>
                  <input
                    type="number"
                    class="form-control"
                    id="iRegionSize"
                    v-model.number="form.iRegionSize"
                    min="0"
                    placeholder="256"
                  >
                </div>
                <div class="col-md-4">
                  <label for="qRegionSize" class="form-label">Q区域大小</label>
                  <input
                    type="number"
                    class="form-control"
                    id="qRegionSize"
                    v-model.number="form.qRegionSize"
                    min="0"
                    placeholder="256"
                  >
                </div>
              </div>
            <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="协议模板" prop="protocolTemplateId">
                  <el-select v-model="form.protocolTemplateId" style="width: 100%">
                    <el-option
                      v-for="tpl in protocolTemplates"
                      :key="tpl.id"
                      :label="`${tpl.name} (${tpl.id})`"
                      :value="tpl.id"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
            </el-row>
              <div class="row mb-3">
                <div class="col-md-4">
                  <label for="dbBlockCount" class="form-label">DB块数量</label>
                  <input
                    type="number"
                    class="form-control"
                    id="dbBlockCount"
                    v-model.number="form.dbBlockCount"
                    min="0"
                    placeholder="100"
                  >
                </div>
                <div class="col-md-4">
                  <label for="dbBlockSize" class="form-label">DB块大小</label>
                  <input
                    type="number"
                    class="form-control"
                    id="dbBlockSize"
                    v-model.number="form.dbBlockSize"
                    min="0"
                    placeholder="1024"
                  >
                </div>
                <div class="col-md-2">
                  <label for="tRegionCount" class="form-label">定时器数量</label>
                  <input
                    type="number"
                    class="form-control"
                    id="tRegionCount"
                    v-model.number="form.tRegionCount"
                    min="0"
                    placeholder="64"
                  >
                </div>
                <div class="col-md-2">
                  <label for="cRegionCount" class="form-label">计数器数量</label>
                  <input
                    type="number"
                    class="form-control"
                    id="cRegionCount"
                    v-model.number="form.cRegionCount"
                    min="0"
                    placeholder="64"
                  >
                </div>
              </div>
            <!-- 内存配置 -->
            <el-divider content-position="left">
              <h3>内存配置</h3>
            </el-divider>
              <!-- 提交按钮 -->
              <div class="d-flex gap-2 mt-4">
                <button type="submit" class="btn btn-primary" :disabled="submitting">
                  <span v-if="submitting" class="spinner-border spinner-border-sm me-1"></span>
                  <i v-else class="bi bi-check-lg me-1"></i>
                  {{ submitting ? '创建中...' : '创建实例' }}
                </button>
                <router-link to="/" class="btn btn-outline-secondary">取消</router-link>
              </div>
            </form>
          </div>
        </div>
      </div>
    </div>
            <el-row :gutter="20">
              <el-col :span="8">
                <el-form-item label="M区大小">
                  <el-input-number
                    v-model="form.mRegionSize"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item label="I区大小">
                  <el-input-number
                    v-model="form.iRegionSize"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item label="Q区大小">
                  <el-input-number
                    v-model="form.qRegionSize"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="20">
              <el-col :span="8">
                <el-form-item label="DB块列表">
                  <el-select
                    v-model="form.dbBlockNumbers"
                    multiple
                    filterable
                    allow-create
                    default-first-option
                    :reserve-keyword="false"
                    style="width: 100%"
                    placeholder="输入块号后回车,例如 50、900、901"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item label="DB块大小">
                  <el-input-number
                    v-model="form.dbBlockSize"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="4">
                <el-form-item label="定时器数量">
                  <el-input-number
                    v-model="form.tRegionCount"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="4">
                <el-form-item label="计数器数量">
                  <el-input-number
                    v-model="form.cRegionCount"
                    :min="0"
                    style="width: 100%"
                  />
                </el-form-item>
              </el-col>
            </el-row>
            <!-- 提交按钮 -->
            <el-form-item>
              <el-button type="primary" @click="handleSubmit" :loading="submitting">
                {{ submitting ? '创建中...' : '创建实例' }}
              </el-button>
              <el-button @click="$router.push('/')">取消</el-button>
            </el-form-item>
          </el-form>
        </el-card>
      </el-col>
    </el-row>
  </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Plus, Back, QuestionFilled } from '@element-plus/icons-vue'
import * as api from '../api'
import type { InstanceConfig, MemoryRegionConfig, SiemensPLCType } from '../types'
import type { InstanceConfig, MemoryRegionConfig, ProtocolTemplate, SiemensPLCType } from '../types'
const router = useRouter()
const formRef = ref<FormInstance>()
const form = ref({
  id: '',
  name: '',
  id: 'GWSC1',
  name: '高温1号堆垛机',
  plcType: 'S71200' as SiemensPLCType,
  port: 102,
  activationKey: '',
  activationKey: '4b86f3fc-f650-3b08-5924-b0f8278d6ed2',
  autoStart: false,
  protocolTemplateId: '',
  mRegionSize: 1024,
  dbBlockCount: 100,
  dbBlockCount: 0,
  dbBlockNumbers: [] as Array<number | string>,
  dbBlockSize: 1024,
  iRegionSize: 256,
  qRegionSize: 256,
@@ -220,56 +222,124 @@
})
const submitting = ref(false)
const protocolTemplates = ref<ProtocolTemplate[]>([])
const rules: FormRules = {
  id: [
    { required: true, message: '请输入实例ID', trigger: 'blur' },
    { pattern: /^[a-zA-Z0-9_-]+$/, message: '只能包含字母、数字、下划线和连字符', trigger: 'blur' }
  ],
  name: [
    { required: true, message: '请输入实例名称', trigger: 'blur' }
  ],
  plcType: [
    { required: true, message: '请选择PLC型号', trigger: 'change' }
  ],
  port: [
    { required: true, message: '请输入监听端口', trigger: 'blur' },
    { type: 'number', min: 1, max: 65535, message: '端口必须在 1-65535 之间', trigger: 'blur' }
  ],
  protocolTemplateId: [
    { required: true, message: '请选择协议模板', trigger: 'change' }
  ]
}
onMounted(async () => {
  protocolTemplates.value = await api.getProtocolTemplates()
  if (protocolTemplates.value.length > 0) {
    form.value.protocolTemplateId = protocolTemplates.value[0].id
  }
})
async function handleSubmit() {
  submitting.value = true
  if (!formRef.value) return
  try {
    const memoryConfig: MemoryRegionConfig = {
      mRegionSize: form.value.mRegionSize > 0 ? form.value.mRegionSize : 1024,
      dBBBlockCount: form.value.dbBlockCount > 0 ? form.value.dbBlockCount : 100,
      dBBBlockSize: form.value.dbBlockSize > 0 ? form.value.dbBlockSize : 1024,
      iRegionSize: form.value.iRegionSize > 0 ? form.value.iRegionSize : 256,
      qRegionSize: form.value.qRegionSize > 0 ? form.value.qRegionSize : 256,
      tRegionCount: form.value.tRegionCount > 0 ? form.value.tRegionCount : 64,
      cRegionCount: form.value.cRegionCount > 0 ? form.value.cRegionCount : 64
  await formRef.value.validate(async (valid) => {
    if (!valid) return
    submitting.value = true
    try {
      const dbBlockNumbers = normalizeDbBlockNumbers(form.value.dbBlockNumbers)
      if (dbBlockNumbers.length === 0) {
        ElMessage.error('请至少配置一个DB块号,例如 50,900,901')
        return
      }
      const memoryConfig: MemoryRegionConfig = {
        mRegionSize: form.value.mRegionSize > 0 ? form.value.mRegionSize : 1024,
        dbBlockCount: 0,
        dbBlockNumbers,
        dbBlockSize: form.value.dbBlockSize > 0 ? form.value.dbBlockSize : 1024,
        iRegionSize: form.value.iRegionSize > 0 ? form.value.iRegionSize : 256,
        qRegionSize: form.value.qRegionSize > 0 ? form.value.qRegionSize : 256,
        tRegionCount: form.value.tRegionCount > 0 ? form.value.tRegionCount : 64,
        cRegionCount: form.value.cRegionCount > 0 ? form.value.cRegionCount : 64
      }
      const config: InstanceConfig = {
        id: form.value.id,
        name: form.value.name,
        plcType: form.value.plcType,
        port: form.value.port,
        activationKey: form.value.activationKey || '',
        autoStart: form.value.autoStart,
        protocolTemplateId: form.value.protocolTemplateId,
        memoryConfig
      }
      const result = await api.createInstance(config)
      if (result) {
        ElMessage.success(`实例 "${form.value.id}" 创建成功!`)
        router.push('/')
      } else {
        ElMessage.error('创建实例失败,请检查输入')
      }
    } catch (err) {
      console.error('创建实例失败:', err)
      ElMessage.error('创建实例失败,请查看控制台')
    } finally {
      submitting.value = false
    }
  })
}
    const config: InstanceConfig = {
      id: form.value.id,
      name: form.value.name,
      plcType: form.value.plcType,
      port: form.value.port,
      activationKey: form.value.activationKey || '',
      autoStart: form.value.autoStart,
      memoryConfig
    }
    const result = await api.createInstance(config)
    if (result) {
      alert(`实例 "${form.value.id}" 创建成功!`)
      router.push('/')
    } else {
      alert('创建实例失败,请检查输入')
    }
  } catch (err) {
    console.error('创建实例失败:', err)
    alert('创建实例失败,请查看控制台')
  } finally {
    submitting.value = false
  }
function normalizeDbBlockNumbers(input: Array<number | string>): number[] {
  return Array.from(new Set(
    input
      .map(x => Number(String(x).trim()))
      .filter(x => Number.isInteger(x) && x > 0)
  )).sort((a, b) => a - b)
}
</script>
<style scoped>
.card-title {
  color: #495057;
  font-weight: 600;
.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 20px;
  flex-wrap: wrap;
  gap: 16px;
}
.form-label {
  font-weight: 500;
  color: #495057;
.header-left h2 {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px 0;
}
.text-muted {
  color: #909399;
  margin: 0;
}
.el-divider h3 {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: #303133;
}
</style>