吉安感知网项目-前端
张含笑
2026-02-04 4ee55a85df6b5ef0e14832a02df4c76753717c50
Merge remote-tracking branch 'origin/master'
5 files modified
45 files deleted
3861 ■■■■■ changed files
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue 125 ●●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/index.vue 2 ●●● patch | view | raw | blame | history
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue 4 ●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/ComprehensiveAnalysis.js 76 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/airspace/airspace.js 19 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/base/region.js 52 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/common.js 39 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/dataCenter.js 175 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/desk/notice.js 59 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/drc.js 125 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/flow/flow.js 122 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/aggregation.js 34 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/clusterScheduling.js 45 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/common.js 61 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/event.js 75 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/index.js 240 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/machineNest.js 130 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/home/territory.js 262 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/job/jobinfo.js 78 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/job/jobserver.js 57 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/logs.js 62 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/model/index.js 35 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/panorama/index.js 38 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/planar/index.js 39 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/report/report.js 22 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/resource/attach.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/resource/oss.js 59 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/resource/sms.js 71 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/routePlan.js 132 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/client.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/dept.js 80 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/dictbiz.js 95 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/helpCenter.js 8 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/param.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/post.js 59 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/role.js 96 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/scope.js 97 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/tenantdatasource.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/tenantpackage.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/topmenu.js 77 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/system/user.js 150 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/tool/code.js 78 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/tool/codesetting.js 88 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/tool/datasource.js 49 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/tool/model.js 110 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/work/index.js 124 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/work/process.js 33 ●●●●● patch | view | raw | blame | history
applications/mobile-web-view/src/api/work/work.js 79 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/page/login/index.vue 8 ●●●●● patch | view | raw | blame | history
applications/task-work-order/src/styles/login.scss 147 ●●●● patch | view | raw | blame | history
applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -60,7 +60,11 @@
                                row-key="id"
                            >
                                <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                                <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型" />
                                <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
                                    <template v-slot="{ row }">
                                        {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                    </div>
@@ -153,7 +157,11 @@
                                row-key="id"
                            >
                                <el-table-column prop="deviceName" show-overflow-tooltip label="设备名称" />
                                <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型" />
                                <el-table-column prop="deviceType" show-overflow-tooltip label="设备类型">
                                    <template v-slot="{ row }">
                                        {{ getDictLabel(row.deviceType, dictObj.deviceType) }}
                                    </template>
                                </el-table-column>
                            </el-table>
                        </div>
                    </div>
@@ -195,9 +203,15 @@
    buildEllipsePositions,
} from '@/utils/cesium/shapeTools'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import {
    createDeviceRangePrimitive,
    getDeviceLngLat,
    normalizeDeviceRange,
} from '@/utils/cesium/deviceRange'
import { saveOperationLog } from '@ztzf/apis'
import { useRoute } from 'vue-router'
import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
const initForm = () => ({
    sceneName: '', // 配置名称
@@ -219,6 +233,7 @@
const searchName = ref('')
const areaList = ref([]) // 关联区域列表
const selectedAreaRows = ref([]) //选中列表
let lastSelectedAreaIds = []
const deviceList = ref([]) // 设备列表
const deviceLoading = ref(false)
const dictObj = inject('dictObj')
@@ -226,6 +241,9 @@
const route = useRoute()
let viewer
let areaDisplaySource
const deviceRangePrimitiveMap = new Map()
const deviceIconEntityMap = new Map()
let deviceRangeRenderToken = 0
const rules = {
    sceneName: fieldRules(true, 50),
@@ -279,12 +297,16 @@
async function loadDeviceListByAreaIds(areaIdsKey) {
    if (!areaIdsKey) {
        deviceList.value = []
        clearDeviceRangePrimitives()
        clearDeviceIconEntities()
        return
    }
    deviceLoading.value = true
    try {
        const res = await fwDeviceListApi({ areaIds: areaIdsKey })
        deviceList.value = res?.data?.data ?? []
        await nextTick()
        renderDeviceRanges(deviceList.value)
    } finally {
        deviceLoading.value = false
    }
@@ -292,11 +314,17 @@
// 关联区域变更
async function handleAreaSelectionChange(rows) {
    const prevIds = new Set(lastSelectedAreaIds)
    const selectedRows = await ensureAreaExtList(rows)
    selectedAreaRows.value = selectedRows
    const ids = selectedRows.map(item => item.id)
    lastSelectedAreaIds = [...ids]
    formData.value.areaDivideIds = ids.join(',')
    formData.value.areaCount = ids.length
    if (ids.some(id => !prevIds.has(id))) {
        await nextTick()
        fitViewToSelectedAreas()
    }
}
// 渲染面
@@ -312,6 +340,22 @@
function clearAreaDisplay() {
    if (!areaDisplaySource) return
    areaDisplaySource.entities.removeAll()
}
function clearDeviceRangePrimitives() {
    if (!viewer || !deviceRangePrimitiveMap.size) return
    for (const primitive of deviceRangePrimitiveMap.values()) {
        viewer.scene.primitives.remove(primitive)
    }
    deviceRangePrimitiveMap.clear()
}
function clearDeviceIconEntities() {
    if (!viewer || !deviceIconEntityMap.size) return
    for (const entity of deviceIconEntityMap.values()) {
        viewer.entities.remove(entity)
    }
    deviceIconEntityMap.clear()
}
function normalizeShapePoint(point) {
@@ -559,6 +603,70 @@
    })
}
function addDeviceIconEntity(device, position) {
    if (!viewer || !position) return
    const id = String(device?.id ?? '')
    if (!id || deviceIconEntityMap.has(id)) return
    const entity = viewer.entities.add({
        position: Cesium.Cartesian3.fromDegrees(position.lng, position.lat, position.height || 0),
        billboard: {
            image: equipmentIcon,
            width: 28,
            height: 38,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
        },
    })
    deviceIconEntityMap.set(id, entity)
}
async function renderDeviceRanges(rows) {
    if (!visible.value) return
    if (!viewer) initMap()
    if (!viewer) return
    deviceRangeRenderToken += 1
    const renderToken = deviceRangeRenderToken
    const selectedRows = Array.isArray(rows) ? rows : []
    const nextIds = new Set(selectedRows.map(row => String(row?.id)))
    for (const [id, primitive] of deviceRangePrimitiveMap.entries()) {
        if (!nextIds.has(id)) {
            viewer.scene.primitives.remove(primitive)
            deviceRangePrimitiveMap.delete(id)
        }
    }
    for (const [id, entity] of deviceIconEntityMap.entries()) {
        if (!nextIds.has(id)) {
            viewer.entities.remove(entity)
            deviceIconEntityMap.delete(id)
        }
    }
    const pendingRows = selectedRows.filter(row => {
        const id = String(row?.id)
        return id && !deviceRangePrimitiveMap.has(id)
    })
    if (!pendingRows.length) return
    const primitives = await Promise.all(
        pendingRows.map(async row => {
            const position = getDeviceLngLat(row)
            if (!position) return { row, primitive: null, position: null }
            const rangeMeters = normalizeDeviceRange(row.effectiveRangeKm)
            const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters)
            return { row, primitive, position }
        })
    )
    if (renderToken !== deviceRangeRenderToken) return
    primitives.forEach(({ row, primitive, position }) => {
        if (position) {
            addDeviceIconEntity(row, position)
        }
        if (!primitive) return
        const id = String(row?.id)
        if (!nextIds.has(id)) return
        deviceRangePrimitiveMap.set(id, primitive)
        viewer.scene.primitives.add(primitive)
    })
}
watch(() => selectedAreaRows.value, renderSelectedAreas)
const deviceAreaIdsKey = computed(() =>
    selectedAreaRows.value.map(item => item?.id).filter(Boolean).join(',')
@@ -570,9 +678,19 @@
    }
)
watch(
    () => deviceList.value,
    val => {
        renderDeviceRanges(val)
    }
)
watch(
    () => visible.value,
    val => {
        if (!val) clearAreaDisplay()
        if (!val) {
            clearAreaDisplay()
            clearDeviceRangePrimitives()
            clearDeviceIconEntities()
        }
    }
)
@@ -619,6 +737,7 @@
    selectedAreaRows.value = ensuredRows
    if (!ensuredRows.length) return
    formData.value.areaCount = ensuredRows.length
    lastSelectedAreaIds = ensuredRows.map(row => row.id)
}
// 初始化地图实例
applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -174,7 +174,7 @@
// 获取字典
function getDictList() {
    getDictionaryByCode('CommandSceneType,deviceMode,areaType').then(res => {
    getDictionaryByCode('CommandSceneType,deviceMode,areaType,deviceType').then(res => {
        dictObj.value = res.data.data
    })
}
applications/drone-command/src/views/detectionCountermeasure/countermeasureEvaluation/index.vue
@@ -74,8 +74,8 @@
                        </template>
                    </el-table-column>
                    <el-table-column prop="deviceSn" show-overflow-tooltip width="140" label="反制设备编码" />
                    <el-table-column prop="areaCode" show-overflow-tooltip width="120" label="场景" />
                    <el-table-column prop="areaCode" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column prop="sceneName" show-overflow-tooltip width="120" label="场景" />
                    <el-table-column prop="areaName" show-overflow-tooltip width="120" label="区域" />
                    <el-table-column show-overflow-tooltip width="172" label="部署位置">
                        <template v-slot="{ row }">
                            {{ row.deployLongitude && row.deployLatitude ? `${row.deployLongitude}, ${row.deployLatitude}` : '' }}
applications/mobile-web-view/src/api/ComprehensiveAnalysis.js
File was deleted
applications/mobile-web-view/src/api/airspace/airspace.js
File was deleted
applications/mobile-web-view/src/api/base/region.js
File was deleted
applications/mobile-web-view/src/api/common.js
File was deleted
applications/mobile-web-view/src/api/dataCenter.js
File was deleted
applications/mobile-web-view/src/api/desk/notice.js
File was deleted
applications/mobile-web-view/src/api/drc.js
File was deleted
applications/mobile-web-view/src/api/flow/flow.js
File was deleted
applications/mobile-web-view/src/api/home/aggregation.js
File was deleted
applications/mobile-web-view/src/api/home/clusterScheduling.js
File was deleted
applications/mobile-web-view/src/api/home/common.js
File was deleted
applications/mobile-web-view/src/api/home/event.js
File was deleted
applications/mobile-web-view/src/api/home/index.js
File was deleted
applications/mobile-web-view/src/api/home/machineNest.js
File was deleted
applications/mobile-web-view/src/api/home/territory.js
File was deleted
applications/mobile-web-view/src/api/job/jobinfo.js
File was deleted
applications/mobile-web-view/src/api/job/jobserver.js
File was deleted
applications/mobile-web-view/src/api/logs.js
File was deleted
applications/mobile-web-view/src/api/model/index.js
File was deleted
applications/mobile-web-view/src/api/panorama/index.js
File was deleted
applications/mobile-web-view/src/api/planar/index.js
File was deleted
applications/mobile-web-view/src/api/report/report.js
File was deleted
applications/mobile-web-view/src/api/resource/attach.js
File was deleted
applications/mobile-web-view/src/api/resource/oss.js
File was deleted
applications/mobile-web-view/src/api/resource/sms.js
File was deleted
applications/mobile-web-view/src/api/routePlan.js
File was deleted
applications/mobile-web-view/src/api/system/client.js
File was deleted
applications/mobile-web-view/src/api/system/dept.js
File was deleted
applications/mobile-web-view/src/api/system/dictbiz.js
File was deleted
applications/mobile-web-view/src/api/system/helpCenter.js
File was deleted
applications/mobile-web-view/src/api/system/param.js
File was deleted
applications/mobile-web-view/src/api/system/post.js
File was deleted
applications/mobile-web-view/src/api/system/role.js
File was deleted
applications/mobile-web-view/src/api/system/scope.js
File was deleted
applications/mobile-web-view/src/api/system/tenantdatasource.js
File was deleted
applications/mobile-web-view/src/api/system/tenantpackage.js
File was deleted
applications/mobile-web-view/src/api/system/topmenu.js
File was deleted
applications/mobile-web-view/src/api/system/user.js
File was deleted
applications/mobile-web-view/src/api/tool/code.js
File was deleted
applications/mobile-web-view/src/api/tool/codesetting.js
File was deleted
applications/mobile-web-view/src/api/tool/datasource.js
File was deleted
applications/mobile-web-view/src/api/tool/model.js
File was deleted
applications/mobile-web-view/src/api/work/index.js
File was deleted
applications/mobile-web-view/src/api/work/process.js
File was deleted
applications/mobile-web-view/src/api/work/work.js
File was deleted
applications/task-work-order/src/page/login/index.vue
@@ -5,7 +5,9 @@
        <div class="login-time">
          {{ time }}
        </div>
        <p class="title">{{ login.info }}</p>
        <div class="login-brand">
          <p class="title">{{ login.info }}</p>
        </div>
      </div>
      <div class="login-border">
        <div class="login-main">
@@ -73,10 +75,10 @@
  created() {
    this.handleLogin();
    this.getTime();
   if(!loginTitle) {
   if(!loginTitle) {
    this.getSysConfigInfo();
  }
  },
  mounted() {},
  computed: {
applications/task-work-order/src/styles/login.scss
@@ -5,15 +5,41 @@
  width: 100%;
  height: 100%;
  margin: 0 auto;
  background-color: #469BC0;
  // background-image: url("/img/bg/bg.jpg");
  background-color: #0b1220;
  overflow: hidden;
  background-size: 100% 100%;
}
.login-container::before,
.login-container::after {
  content: "";
  position: absolute;
  inset: -20%;
  z-index: 0;
  background: radial-gradient(60% 60% at 20% 30%, rgba(88, 176, 255, 0.35), transparent 60%),
    radial-gradient(50% 50% at 80% 20%, rgba(124, 92, 255, 0.35), transparent 65%),
    radial-gradient(50% 50% at 70% 80%, rgba(0, 212, 255, 0.35), transparent 65%),
    linear-gradient(135deg, rgba(10, 21, 38, 0.95), rgba(13, 24, 43, 0.92));
  filter: blur(10px);
  animation: float-sheen 16s ease-in-out infinite;
}
.login-container::after {
  inset: -30%;
  opacity: 0.5;
  background: conic-gradient(from 120deg, rgba(95, 228, 255, 0.25), rgba(128, 120, 255, 0.15), transparent 50%);
  animation: float-sheen 24s ease-in-out infinite reverse;
}
.login-weaper {
  margin: 0 auto;
  width: 1000px;
  box-shadow: -4px 5px 10px rgba(0, 0, 0, 0.4);
  position: relative;
  z-index: 1;
  border-radius: 16px;
  box-shadow: 0 30px 60px rgba(6, 12, 24, 0.55);
  backdrop-filter: blur(10px);
  animation: pop-in 0.7s ease-out;
  .el-input-group__append {
    border: none;
@@ -29,11 +55,12 @@
}
.login-left {
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
  border-top-left-radius: 16px;
  border-bottom-left-radius: 16px;
  justify-content: center;
  flex-direction: column;
  background-color: #5A9CF8 !important;
  background: linear-gradient(160deg, rgba(80, 160, 255, 0.95), rgba(73, 96, 255, 0.92)) !important;
  box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.12);
  color: #fff;
  float: left;
  width: 50%;
@@ -42,6 +69,7 @@
.login-left .img {
  width: 140px;
  filter: drop-shadow(0 8px 18px rgba(10, 22, 38, 0.35));
}
.login-time {
@@ -52,33 +80,37 @@
  color: #fff;
  font-weight: 200;
  opacity: 0.9;
  font-size: 18px;
  font-size: 16px;
  overflow: hidden;
  letter-spacing: 1px;
}
.login-left .title {
  text-align: center;
  color: #fff;
  font-weight: bold;
  font-size: 30px;
  letter-spacing: 2px;
  font-size: 32px;
  letter-spacing: 3px;
  text-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
}
.login-border {
  border-left: none;
  border-top-right-radius: 5px;
  border-bottom-right-radius: 5px;
  border-top-right-radius: 16px;
  border-bottom-right-radius: 16px;
  color: #fff;
  background-color: #fff;
  background: rgba(255, 255, 255, 0.92);
  width: 50%;
  float: left;
  box-sizing: border-box;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.4);
}
.login-main {
  margin: 0 auto;
  width: 65%;
  box-sizing: border-box;
  padding: 40px 0;
}
.login-main > h3 {
@@ -90,12 +122,13 @@
}
.login-title {
  color: #333;
  margin-bottom: 40px;
  font-weight: 500;
  font-size: 22px;
  color: #1a1f2b;
  margin-bottom: 36px;
  font-weight: 600;
  font-size: 24px;
  text-align: center;
  letter-spacing: 4px;
  letter-spacing: 6px;
  text-transform: uppercase;
}
.login-menu {
@@ -113,46 +146,49 @@
.btn-submit {
  width: 100%;
  height: 45px;
  border: 1px solid var(--el-color-primary);
  background: none;
  border: 1px solid rgba(78, 155, 255, 0.7);
  background: linear-gradient(120deg, rgba(78, 155, 255, 0.15), rgba(95, 228, 255, 0.25));
  font-size: 18px;
  letter-spacing: 2px;
  font-weight: 300;
  color: var(--el-color-primary);
  color: #1a3f6e;
  cursor: pointer;
  margin-top: 30px;
  font-family: "neo";
  transition: 0.25s;
  box-shadow: 0 12px 24px rgba(35, 93, 204, 0.2);
}
.login-submit {
  width: 60%;
  height: 45px;
  border: 1px solid var(--el-color-primary);
  background: none;
  border: 1px solid rgba(78, 155, 255, 0.7);
  background: linear-gradient(120deg, rgba(78, 155, 255, 0.15), rgba(95, 228, 255, 0.25));
  font-size: 18px;
  letter-spacing: 2px;
  font-weight: 300;
  color: var(--el-color-primary);
  color: #1a3f6e;
  cursor: pointer;
  margin-top: 30px;
  font-family: "neo";
  transition: 0.25s;
  box-shadow: 0 12px 24px rgba(35, 93, 204, 0.2);
}
.register-submit {
  width: 36%;
  height: 45px;
  border: 1px solid #EF2F63FF;
  background: none;
  border: 1px solid rgba(239, 47, 99, 0.7);
  background: linear-gradient(120deg, rgba(239, 47, 99, 0.15), rgba(255, 120, 160, 0.25));
  font-size: 18px;
  letter-spacing: 2px;
  font-weight: 300;
  color: #EF2F63FF;
  color: #b31e4b;
  cursor: pointer;
  margin-top: 30px;
  font-family: "neo";
  transition: 0.25s;
  box-shadow: 0 12px 24px rgba(239, 47, 99, 0.2);
}
.login-form {
@@ -177,8 +213,9 @@
      background: transparent;
      box-shadow: none;
      border-radius: 0;
      color: #333;
      border-bottom: 1px solid rgb(235, 237, 242);
      color: #1a1f2b;
      border-bottom: 1px solid rgba(78, 155, 255, 0.4);
      transition: border-color 0.2s ease;
    }
    .el-input__prefix {
@@ -201,9 +238,9 @@
  margin-top: 2px;
  width: 100px;
  height: 38px;
  background-color: #fdfdfd;
  border: 1px solid #f0f0f0;
  color: #333;
  background-color: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(78, 155, 255, 0.3);
  color: #1a1f2b;
  font-size: 14px;
  font-weight: bold;
  letter-spacing: 5px;
@@ -217,9 +254,9 @@
  margin-top: 2px;
  width: 100px;
  height: 38px;
  background-color: #fdfdfd;
  border: 1px solid #f0f0f0;
  color: #7c7979;
  background-color: rgba(255, 255, 255, 0.85);
  border: 1px solid rgba(78, 155, 255, 0.3);
  color: #4b5563;
  font-size: 12px;
  letter-spacing: 1px;
  line-height: 38px;
@@ -227,3 +264,45 @@
  text-align: center;
  cursor: pointer !important;
}
.btn-submit:hover,
.login-submit:hover {
  border-color: rgba(78, 155, 255, 0.9);
  background: linear-gradient(120deg, rgba(78, 155, 255, 0.35), rgba(95, 228, 255, 0.45));
  color: #0e2d56;
  transform: translateY(-1px);
}
.register-submit:hover {
  border-color: rgba(239, 47, 99, 0.9);
  background: linear-gradient(120deg, rgba(239, 47, 99, 0.3), rgba(255, 120, 160, 0.45));
  color: #8f173a;
  transform: translateY(-1px);
}
.el-input__wrapper.is-focus {
  border-bottom-color: rgba(78, 155, 255, 0.9);
}
@keyframes float-sheen {
  0% {
    transform: translate3d(0, 0, 0) scale(1);
  }
  50% {
    transform: translate3d(2%, -2%, 0) scale(1.02);
  }
  100% {
    transform: translate3d(0, 0, 0) scale(1);
  }
}
@keyframes pop-in {
  0% {
    opacity: 0;
    transform: translateY(20px) scale(0.98);
  }
  100% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}