|
|
@@ -1,67 +1,66 @@
|
|
|
<template>
|
|
|
<div :class="prefixCls" class="ai-base-station-page">
|
|
|
- <div class="filter-bar filter-bar--stacked">
|
|
|
- <div class="filter-row filter-row--carrier">
|
|
|
+ <section class="filter-bar">
|
|
|
+ <div class="qj-filter-head">
|
|
|
+ <div class="qj-card-title">查询条件</div>
|
|
|
+ <div class="qj-filter-actions">
|
|
|
+ <div class="side-actions">
|
|
|
+ <!-- Web 化:模版与转换结果都通过浏览器下载,不再依赖 Electron 打开本机目录 -->
|
|
|
+ <Button :loading="templateLoading" @click="handleFetchTemplate">下载转换基站模版</Button>
|
|
|
+ <Button :loading="convertLoading" @click="convertFileInputRef?.click()">批量转换基站</Button>
|
|
|
+ <input
|
|
|
+ ref="convertFileInputRef"
|
|
|
+ class="side-actions__file-input"
|
|
|
+ type="file"
|
|
|
+ accept=".xls,.xlsx"
|
|
|
+ @change="handleConvertFileChange"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <Button type="primary" :loading="queryLoading" @click="handleSearch">搜索</Button>
|
|
|
+ <Button @click="handleReset">重置</Button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="qj-filter-grid">
|
|
|
<div class="filter-item filter-item--carrier">
|
|
|
- <span class="filter-item__label">MNC:</span>
|
|
|
+ <span class="filter-label">运营商</span>
|
|
|
<RadioGroup v-model:value="formState.mnc">
|
|
|
<Radio v-for="item in carrierOptions" :key="item.value" :value="item.value">{{ item.label }}</Radio>
|
|
|
</RadioGroup>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="filter-row">
|
|
|
<div class="filter-item">
|
|
|
- <span class="filter-item__label">LAC:</span>
|
|
|
+ <span class="filter-label">LAC</span>
|
|
|
<Input
|
|
|
v-model:value="formState.lac"
|
|
|
- class="filter-item__input"
|
|
|
+ class="filter-input filter-input--code"
|
|
|
:maxlength="15"
|
|
|
- placeholder="请输入搜索内容"
|
|
|
+ placeholder="请输入 LAC"
|
|
|
@input="handleCodeInput('lac', $event)"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
- <span class="filter-item__label">CID:</span>
|
|
|
+ <span class="filter-label">CID</span>
|
|
|
<Input
|
|
|
v-model:value="formState.ci"
|
|
|
- class="filter-item__input"
|
|
|
+ class="filter-input filter-input--code"
|
|
|
:maxlength="15"
|
|
|
- placeholder="请输入搜索内容"
|
|
|
+ placeholder="请输入 CID"
|
|
|
@input="handleCodeInput('ci', $event)"
|
|
|
/>
|
|
|
</div>
|
|
|
<div class="filter-item">
|
|
|
- <span class="filter-item__label">DEC/HEX:</span>
|
|
|
- <Select v-model:value="formState.radixMode" class="filter-item__select" :options="radixOptions" />
|
|
|
- </div>
|
|
|
- <div class="filter-actions">
|
|
|
- <Button type="primary" :loading="queryLoading" @click="handleSearch">搜索</Button>
|
|
|
- <Button @click="handleReset">重置</Button>
|
|
|
- </div>
|
|
|
- <div class="side-actions">
|
|
|
- <!-- Web 化:模版与转换结果都通过浏览器下载,不再依赖 Electron 打开本机目录 -->
|
|
|
- <Button :loading="templateLoading" @click="handleFetchTemplate">下载转换基站模版</Button>
|
|
|
- <Button :loading="convertLoading" @click="convertFileInputRef?.click()">批量转换基站</Button>
|
|
|
- <input
|
|
|
- ref="convertFileInputRef"
|
|
|
- class="side-actions__file-input"
|
|
|
- type="file"
|
|
|
- accept=".xls,.xlsx"
|
|
|
- @change="handleConvertFileChange"
|
|
|
- />
|
|
|
+ <span class="filter-label">进制</span>
|
|
|
+ <Select v-model:value="formState.radixMode" class="filter-select" :options="radixOptions" />
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </section>
|
|
|
|
|
|
<div class="content-panel">
|
|
|
- <div class="table-card">
|
|
|
+ <section class="table-card">
|
|
|
<div class="panel-header">
|
|
|
- <div>
|
|
|
+ <div class="panel-heading">
|
|
|
<div class="panel-title">基站结果地图</div>
|
|
|
- <div class="panel-desc">
|
|
|
- {{ resultSummary }}
|
|
|
- </div>
|
|
|
+ <div class="panel-desc">{{ resultSummary }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="table-panel">
|
|
|
@@ -69,7 +68,7 @@
|
|
|
<BaseStationQueryMap :points="mapPoints" />
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </section>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -304,132 +303,391 @@
|
|
|
</script>
|
|
|
|
|
|
<style lang="less">
|
|
|
- @import '../styles/trans-analysis-table-ui.less';
|
|
|
-
|
|
|
@prefix-cls: ~'jeesite-ai-base-station';
|
|
|
|
|
|
+ /* ==========================================================================
|
|
|
+ * 基站查询:卡片语言对齐「持续联系」
|
|
|
+ * 背景 #f4f6fa · 卡片圆角 14 · 细边框 #e7ebf3 · 卡片间距 8px · 主色 #2a50ec
|
|
|
+ * ========================================================================== */
|
|
|
.@{prefix-cls}.ai-base-station-page {
|
|
|
- .analysis-single-panel-frame();
|
|
|
-
|
|
|
- // 去掉 content-panel 的底色。
|
|
|
- // 该底色来自 .analysis-page-frame() → .content-panel { .analysis-card-strip() },
|
|
|
- // 而 @analysis-card-strip-bg = @content-bg = #f0f2f5,在白色页面上会显出一圈灰边。
|
|
|
- // ★ 只覆盖 background,保留 strip 的 padding / flex / overflow,避免动到布局;
|
|
|
- // 也刻意不改 mixin 本身(.analysis-page-frame 被十几个分析页共用)。
|
|
|
- .content-panel {
|
|
|
- background: transparent;
|
|
|
- }
|
|
|
-
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
+ width: calc(100% + 24px);
|
|
|
+ height: 100%;
|
|
|
+ min-height: 0;
|
|
|
+ margin: 0 -12px;
|
|
|
+ padding: 8px 10px !important;
|
|
|
+ background: #f4f6fa;
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ /* ---------------- 查询条件卡 ---------------- */
|
|
|
.filter-bar {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
flex-direction: column;
|
|
|
+ flex-wrap: nowrap;
|
|
|
align-items: stretch;
|
|
|
- gap: 10px;
|
|
|
- padding: 6px 8px 8px;
|
|
|
+ gap: 0;
|
|
|
+ min-height: 0;
|
|
|
+ width: 100%;
|
|
|
+ padding: 0;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 14px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
- .filter-row {
|
|
|
+ .qj-filter-head {
|
|
|
display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ flex-wrap: wrap;
|
|
|
align-items: center;
|
|
|
- gap: 8px;
|
|
|
+ gap: 8px 10px;
|
|
|
+ min-height: 42px;
|
|
|
+ padding: 0 14px;
|
|
|
+ border-bottom: 1px solid #eff2f7;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .qj-card-title {
|
|
|
+ position: relative;
|
|
|
+ padding-left: 11px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 22px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 4px;
|
|
|
+ bottom: 4px;
|
|
|
+ width: 3px;
|
|
|
+ border-radius: 2px;
|
|
|
+ background: #2a50ec;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .qj-filter-actions {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ margin-left: auto;
|
|
|
}
|
|
|
|
|
|
- .filter-row--carrier {
|
|
|
- min-height: 24px;
|
|
|
+ .qj-filter-actions .ant-btn {
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 16px;
|
|
|
+ border-radius: 8px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ box-shadow: none;
|
|
|
}
|
|
|
|
|
|
- .filter-item {
|
|
|
+ .qj-filter-actions .ant-btn-primary {
|
|
|
+ border-color: #2a50ec;
|
|
|
+ background: #2a50ec;
|
|
|
+
|
|
|
+ &:hover,
|
|
|
+ &:focus {
|
|
|
+ border-color: #4b6bf0;
|
|
|
+ background: #4b6bf0;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ border-color: #1e3ed0;
|
|
|
+ background: #1e3ed0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .qj-filter-actions .ant-btn:not(.ant-btn-primary) {
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ background: #fff;
|
|
|
+ color: #5b6577;
|
|
|
+
|
|
|
+ &:hover,
|
|
|
+ &:focus {
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ background: #f8faff;
|
|
|
+ color: #2a50ec;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .qj-filter-grid {
|
|
|
display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
align-items: center;
|
|
|
- gap: 4px;
|
|
|
- min-width: 0;
|
|
|
+ gap: 8px 16px;
|
|
|
+ padding: 12px 14px 13px;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
- .filter-item--carrier {
|
|
|
- gap: 8px;
|
|
|
+ .filter-item {
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .filter-item__label {
|
|
|
- color: #6b7280;
|
|
|
+ .filter-label {
|
|
|
+ flex: 0 0 auto;
|
|
|
+ color: #5b6577;
|
|
|
font-size: 12px;
|
|
|
- line-height: 20px;
|
|
|
+ line-height: 32px;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
- .filter-item__input {
|
|
|
- width: 180px;
|
|
|
+ .filter-input {
|
|
|
+ width: 160px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .filter-item__select {
|
|
|
- width: 160px;
|
|
|
+ .filter-select {
|
|
|
+ width: 140px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .filter-actions,
|
|
|
- .side-actions {
|
|
|
- display: flex;
|
|
|
+ .filter-item--carrier {
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .filter-item--carrier .ant-radio-group {
|
|
|
+ display: inline-flex;
|
|
|
align-items: center;
|
|
|
- gap: 6px;
|
|
|
+ gap: 14px;
|
|
|
+ min-height: 32px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .filter-item--carrier .ant-radio-wrapper {
|
|
|
+ margin-inline-end: 0;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 32px;
|
|
|
}
|
|
|
|
|
|
.side-actions {
|
|
|
- margin-left: auto;
|
|
|
+ display: inline-flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ }
|
|
|
|
|
|
- // 原生 file 控件只用于触发选择,视觉上由「批量转换基站」按钮承担。
|
|
|
- // 之前这个类没有任何样式,导致原生控件直接裸露在按钮旁边。
|
|
|
- &__file-input {
|
|
|
- display: none;
|
|
|
- }
|
|
|
+ .side-actions .ant-btn {
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 12px;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ color: #5b6577;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 500;
|
|
|
+ box-shadow: none;
|
|
|
}
|
|
|
|
|
|
- .table-card {
|
|
|
- padding: 8px;
|
|
|
+ .side-actions .ant-btn:hover,
|
|
|
+ .side-actions .ant-btn:focus {
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ background: #f8faff;
|
|
|
+ color: #2a50ec;
|
|
|
}
|
|
|
|
|
|
- .table-panel {
|
|
|
- padding-top: 0;
|
|
|
+ .side-actions__file-input {
|
|
|
+ display: none;
|
|
|
}
|
|
|
|
|
|
- .map-panel__body {
|
|
|
+ /* ---------------- 表单控件 ---------------- */
|
|
|
+ .qj-filter-grid {
|
|
|
+ .ant-input,
|
|
|
+ .ant-input-affix-wrapper {
|
|
|
+ height: 32px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: none;
|
|
|
+ font-size: 12px;
|
|
|
+ transition:
|
|
|
+ border-color 0.2s ease,
|
|
|
+ box-shadow 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-input-affix-wrapper > .ant-input {
|
|
|
+ height: 100%;
|
|
|
+ padding: 0;
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ background: transparent;
|
|
|
+ box-shadow: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-input:hover,
|
|
|
+ .ant-input-affix-wrapper:hover,
|
|
|
+ .ant-select:hover .ant-select-selector {
|
|
|
+ border-color: #bfcdfb;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-input:focus,
|
|
|
+ .ant-input-affix-wrapper-focused,
|
|
|
+ .ant-select-focused .ant-select-selector {
|
|
|
+ border-color: #2a50ec !important;
|
|
|
+ box-shadow: 0 0 0 2px rgb(42 80 236 / 12%) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-select {
|
|
|
+ height: 32px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-select .ant-select-selector {
|
|
|
+ height: 32px !important;
|
|
|
+ padding: 0 10px;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-select-single .ant-select-selection-item,
|
|
|
+ .ant-select-single .ant-select-selection-placeholder {
|
|
|
+ line-height: 30px;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-select-arrow {
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ---------------- 结果卡 ---------------- */
|
|
|
+ .content-panel {
|
|
|
display: flex;
|
|
|
flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 8px;
|
|
|
min-height: 0;
|
|
|
+ padding: 0;
|
|
|
+ background: transparent;
|
|
|
+ box-sizing: border-box;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
- :deep(.ant-radio-wrapper) {
|
|
|
- color: #475569;
|
|
|
- font-size: 14px;
|
|
|
+ .table-card {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 0;
|
|
|
+ border: 1px solid #e7ebf3;
|
|
|
+ border-radius: 14px;
|
|
|
+ background: #fff;
|
|
|
+ box-shadow: 0 1px 2px rgb(16 24 40 / 4%);
|
|
|
+ box-sizing: border-box;
|
|
|
+ overflow: hidden;
|
|
|
}
|
|
|
|
|
|
- :deep(.ant-input),
|
|
|
- :deep(.ant-select-selector),
|
|
|
- :deep(.ant-btn) {
|
|
|
- border-radius: 8px;
|
|
|
+ .panel-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 42px;
|
|
|
+ padding: 6px 14px;
|
|
|
+ border-bottom: 1px solid #eff2f7;
|
|
|
+ background: #fff;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+
|
|
|
+ .panel-heading {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- @media screen and (max-width: 1280px) {
|
|
|
- .side-actions {
|
|
|
- margin-left: 0;
|
|
|
+ .panel-title {
|
|
|
+ position: relative;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ padding-left: 11px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 22px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ content: '';
|
|
|
+ position: absolute;
|
|
|
+ left: 0;
|
|
|
+ top: 4px;
|
|
|
+ bottom: 4px;
|
|
|
+ width: 3px;
|
|
|
+ border-radius: 2px;
|
|
|
+ background: #2a50ec;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .panel-desc {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ color: #98a2b3;
|
|
|
+ font-size: 11px;
|
|
|
+ line-height: 16px;
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-panel {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ flex-direction: column;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .map-panel__body {
|
|
|
+ display: flex;
|
|
|
+ flex: 1 1 auto;
|
|
|
+ min-height: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .map-panel__body .base-station-query-map__box {
|
|
|
+ border: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ background: #f4f7fb;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* ---------------- 响应式 ---------------- */
|
|
|
@media screen and (max-width: 992px) {
|
|
|
- .filter-row {
|
|
|
- gap: 12px;
|
|
|
+ .filter-item {
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
|
|
|
- .filter-item,
|
|
|
- .filter-item__input,
|
|
|
- .filter-item__select,
|
|
|
- .filter-actions,
|
|
|
- .side-actions {
|
|
|
- width: 100%;
|
|
|
+ .filter-input,
|
|
|
+ .filter-select,
|
|
|
+ .qj-filter-grid .ant-select {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ width: auto;
|
|
|
+ min-width: 0;
|
|
|
}
|
|
|
|
|
|
- .filter-actions,
|
|
|
- .side-actions {
|
|
|
- margin-left: 0;
|
|
|
- justify-content: flex-start;
|
|
|
+ .filter-item--carrier .ant-radio-group {
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px 14px;
|
|
|
}
|
|
|
}
|
|
|
}
|