Prechádzať zdrojové kódy

查询条件布局调整:时间窗口移到次行、测试点位加宽、新增时间段下拉、日期选择器中文化

18922397810 1 týždeň pred
rodič
commit
2684469b09
3 zmenil súbory, kde vykonal 95 pridanie a 36 odobranie
  1. 70 27
      frontend/src/App.vue
  2. 2 1
      frontend/src/main.ts
  3. 23 8
      frontend/src/styles.css

+ 70 - 27
frontend/src/App.vue

@@ -15,6 +15,7 @@ const selectedDevicePoints = ref<DevicePoint[]>([PRIMARY_DEVICE_POINT])
 const fileListPoint = ref<DevicePoint>(PRIMARY_DEVICE_POINT)
 const minTime = ref('')
 const maxTime = ref('')
+const selectedTimeRangePoint = ref<DevicePoint | ''>('')
 const windowSize = ref(4)
 const maxPoints = ref(200000)
 const noSampling = ref(false)
@@ -78,6 +79,9 @@ const selectedOptionRows = computed<QueryOption[]>(() => (
     row.devicePart === selectedDevicePart.value && selectedDevicePoints.value.includes(row.devicePoint)
   )) ?? []
 ))
+const timeRangeOptions = computed<QueryOption[]>(() => (
+  queryMeta.value?.options.filter((row) => row.devicePart === selectedDevicePart.value) ?? []
+))
 const selectedWindowPoints = computed(() => {
   const slice = timePoints.value.slice(startIndex.value, startIndex.value + stripWindowSize.value)
   return hasReference.value ? [...referencePoints.value, ...slice] : slice
@@ -161,6 +165,7 @@ async function loadOptions() {
     const result = await fetchQueryOptions()
     queryMeta.value = result
     selectedDevicePart.value = result.deviceParts[0] ?? ''
+    selectedTimeRangePoint.value = ''
     syncTimeBounds(true)
     initialized.value = true
     await loadTimePoints()
@@ -258,7 +263,22 @@ function scheduleTimePoints() {
   queryDebounce = setTimeout(() => void loadTimePoints(), 180)
 }
 
+function formatRange(option: QueryOption) {
+  return `${formatDateTime(option.minTime)} ~ ${formatDateTime(option.maxTime)}`
+}
+
+function onTimeRangeChange(value: DevicePoint | '') {
+  if (!value) return
+  const row = queryMeta.value?.options.find((item) => (
+    item.devicePart === selectedDevicePart.value && item.devicePoint === value
+  ))
+  if (!row) return
+  minTime.value = toInputTime(row.minTime)
+  maxTime.value = toInputTime(row.maxTime)
+}
+
 function onDevicePartChange() {
+  selectedTimeRangePoint.value = ''
   syncTimeBounds(true)
   scheduleTimePoints()
 }
@@ -752,18 +772,6 @@ onBeforeUnmount(() => {
               placeholder="选择结束时间"
             />
           </label>
-          <label class="field window-field">
-            <span class="field-label">时间窗口 <em>文件数量</em></span>
-            <el-input-number
-              v-model="windowSize"
-              class="query-control window-number"
-              size="large"
-              :min="1"
-              :max="200"
-              controls-position="right"
-              :disabled="queryLoading"
-            />
-          </label>
           <el-button class="query-action query-button" type="primary" size="large" :loading="timePointsLoading" :disabled="!selectedDevicePart" @click="loadTimePoints">
             查询时间点
           </el-button>
@@ -774,26 +782,61 @@ onBeforeUnmount(() => {
               <el-checkbox v-model="abnormalOnly" class="query-checkbox" size="large">异常</el-checkbox>
               <el-checkbox v-model="noCycleOnly" class="query-checkbox" size="large">无周期</el-checkbox>
             </label>
-            <label class="field field-status">
-              <span class="field-label">质心距离</span>
+            <div class="field-second-group">
+              <label class="field field-status">
+                <span class="field-label">质心距离</span>
+                <el-input-number
+                  v-model="minStatus"
+                  class="query-control"
+                  size="large"
+                  :min="0"
+                  :max="4"
+                  :step="1"
+                  :step-strictly="true"
+                  controls-position="right"
+                  :disabled="queryLoading || noCycleOnly"
+                  clearable
+                  placeholder="可空"
+                  :title="noCycleOnly ? '勾选无周期时忽略质心距离' : undefined"
+                />
+              </label>
+              <label class="field field-first-cycle">
+                <span class="field-label">首个周期</span>
+                <el-checkbox v-model="firstCycleOnly" class="query-checkbox" size="large" :disabled="queryLoading">连续曲线</el-checkbox>
+              </label>
+            </div>
+            <label class="field window-field">
+              <span class="field-label">时间窗口 <em>文件数量</em></span>
               <el-input-number
-                v-model="minStatus"
-                class="query-control"
+                v-model="windowSize"
+                class="query-control window-number"
                 size="large"
-                :min="0"
-                :max="4"
-                :step="1"
-                :step-strictly="true"
+                :min="1"
+                :max="200"
                 controls-position="right"
-                :disabled="queryLoading || noCycleOnly"
-                clearable
-                placeholder="可空"
-                :title="noCycleOnly ? '勾选无周期时忽略质心距离' : undefined"
+                :disabled="queryLoading"
               />
             </label>
-            <label class="field field-first-cycle">
-              <span class="field-label">首个周期</span>
-              <el-checkbox v-model="firstCycleOnly" class="query-checkbox" size="large" :disabled="queryLoading">连续曲线</el-checkbox>
+            <label class="field field-time-range">
+              <span class="field-label">时间段</span>
+              <el-select
+                v-model="selectedTimeRangePoint"
+                class="query-control"
+                size="large"
+                clearable
+                filterable
+                :disabled="queryLoading"
+                placeholder="选择测试点位"
+                filter-placeholder="搜索测试点位"
+                @change="onTimeRangeChange"
+              >
+                <el-option
+                  v-for="option in timeRangeOptions"
+                  :key="option.devicePoint"
+                  :value="option.devicePoint"
+                  :label="`${option.devicePoint} ${formatRange(option)}`"
+                />
+              </el-select>
             </label>
           </div>
         </div>

+ 2 - 1
frontend/src/main.ts

@@ -1,7 +1,8 @@
 import { createApp } from 'vue'
 import ElementPlus from 'element-plus'
+import zhCn from 'element-plus/es/locale/lang/zh-cn'
 import 'element-plus/dist/index.css'
 import App from './App.vue'
 import './styles.css'
 
-createApp(App).use(ElementPlus).mount('#app')
+createApp(App).use(ElementPlus, { locale: zhCn }).mount('#app')

+ 23 - 8
frontend/src/styles.css

@@ -100,7 +100,7 @@ h2 { font-size: 18px; line-height: 1.35; font-weight: 600; }
 
 .query-grid {
   display: grid;
-  grid-template-columns: minmax(220px, 1.7fr) minmax(260px, 2fr) minmax(180px, 1.25fr) minmax(180px, 1.25fr) 170px 126px;
+  grid-template-columns: minmax(150px, 1.7fr) minmax(240px, 2fr) minmax(170px, 1.25fr) minmax(170px, 1.25fr) 120px;
   align-items: end;
   gap: 16px 12px;
 }
@@ -119,13 +119,23 @@ h2 { font-size: 18px; line-height: 1.35; font-weight: 600; }
 .point-counts .abnormal-count { color: #e05252; font-weight: 600; }
 .window-number .el-input__inner { text-align: center; }
 .query-button.el-button { width: 100%; height: 40px; margin: 0; border-radius: 4px; font-size: 14px; }
-.query-row-2 { display: flex; align-items: flex-start; gap: 16px; grid-column: 1 / -1; }
+.field-types .el-select__wrapper { flex-wrap: nowrap; }
+.field-types .el-select__tags { flex-wrap: nowrap; overflow: hidden; }
+.field-types .el-select__tags .el-tag { flex-shrink: 0; }
+.query-row-2 { display: contents; }
 .query-row-2 .field-check,
-.query-row-2 .field-status,
-.query-row-2 .field-first-cycle { min-height: 68px; }
-.query-row-2 .field-check .field-label,
-.query-row-2 .field-status .field-label,
-.query-row-2 .field-first-cycle .field-label { white-space: nowrap; }
+.query-row-2 .field-second-group,
+.query-row-2 .window-field,
+.query-row-2 .field-time-range { min-height: 68px; }
+.query-row-2 .field-time-range { grid-column: span 2; }
+.field-check .field-label,
+.field-status .field-label,
+.field-first-cycle .field-label,
+.window-field .field-label,
+.field-time-range .field-label { white-space: nowrap; }
+.field-second-group { display: flex; align-items: flex-start; gap: 16px; min-width: 0; }
+.field-second-group .field-status { width: 120px; flex: 0 0 120px; }
+.field-second-group .field-first-cycle { flex: 1 1 auto; min-width: 0; }
 .query-checkbox.el-checkbox { height: 40px; margin-right: 20px; display: inline-flex; align-items: center; }
 .query-checkbox.el-checkbox:last-child { margin-right: 0; }
 .query-checkbox .el-checkbox__label { font-size: 14px; color: #303133; }
@@ -272,12 +282,13 @@ h2 { font-size: 18px; line-height: 1.35; font-weight: 600; }
 .period-chart { width: 100%; height: 440px; }
 .modal-loading { position: absolute; z-index: 2; top: 100px; bottom: 0; background: rgba(255, 255, 255, .82); }
 
-@media (max-width: 1250px) {
+@media (max-width: 1300px) {
   .workspace { grid-template-columns: minmax(0, 1fr) 250px; }
   .query-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
   .field-point { grid-column: span 2; }
   .field-types { grid-column: span 1; }
   .query-action { grid-column: span 1; }
+  .query-row-2 .field-time-range { grid-column: 1 / -1; }
 }
 
 @media (max-width: 930px) {
@@ -285,6 +296,8 @@ h2 { font-size: 18px; line-height: 1.35; font-weight: 600; }
   .insight-panel { order: 4; }
   .query-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
   .field-point, .field-types, .query-action { grid-column: span 2; }
+  .window-field,
+  .query-row-2 .field-time-range { grid-column: 1 / -1; }
   .insight-panel { min-height: auto; }
   .readout-grid { grid-template-columns: repeat(2, 1fr); margin: 14px -16px 0; }
   .chart-heading { align-items: flex-start; flex-direction: column; }
@@ -298,6 +311,8 @@ h2 { font-size: 18px; line-height: 1.35; font-weight: 600; }
   .query-panel, .selection-panel, .chart-panel { padding: 17px 14px 14px; }
   .query-grid { grid-template-columns: 1fr; gap: 12px; }
   .field-point, .field-types, .query-action { grid-column: span 1; }
+  .query-row-2 .window-field,
+  .query-row-2 .field-time-range { grid-column: 1; }
   .time-summary { font-size: 12px; text-align: right; }
   .selection-controls { align-items: flex-start; flex-direction: column; }
   .chart-actions { width: 100%; align-items: flex-start; flex-direction: column; gap: 10px; }