|
|
@@ -0,0 +1,174 @@
|
|
|
+<template>
|
|
|
+ <BasicModal
|
|
|
+ v-bind="$attrs"
|
|
|
+ title="基站包设置"
|
|
|
+ :width="620"
|
|
|
+ :centered="true"
|
|
|
+ :confirmLoading="saving"
|
|
|
+ okText="保存"
|
|
|
+ @register="registerModal"
|
|
|
+ @ok="handleSave"
|
|
|
+ >
|
|
|
+ <div :class="prefixCls">
|
|
|
+ <p class="cell-local__tip">
|
|
|
+ 基站数据库文件(服务端路径,形如 <code>cell.bin</code>):文件必须已存在于服务端,保存后写入服务端配置;
|
|
|
+ 基站库在本次运行中已经加载过时,需重启服务端才会改用新文件。
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <div class="cell-local__row">
|
|
|
+ <Input
|
|
|
+ v-model:value="path"
|
|
|
+ :disabled="loading || saving"
|
|
|
+ placeholder="例如 E:\\data\\cell\\cell.bin"
|
|
|
+ allowClear
|
|
|
+ />
|
|
|
+ <Button :disabled="loading || saving || !desktopPicker" @click="handlePick">
|
|
|
+ <FolderOpenOutlined class="mr-1" />
|
|
|
+ 选择文件
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <p v-if="!desktopPicker" class="cell-local__hint">当前不在桌面客户端内,只能手动输入路径。</p>
|
|
|
+ <p v-else-if="loading" class="cell-local__hint">正在读取当前配置…</p>
|
|
|
+ </div>
|
|
|
+ </BasicModal>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+ /**
|
|
|
+ * 基站包设置:读写服务端基站数据库文件路径(/sys/getCellLocal、/sys/cellLocal)。
|
|
|
+ *
|
|
|
+ * 后端只认 .bin 文件名(TowerUtils.init 会校验后缀),这里保存前先在本地拦一道,
|
|
|
+ * 免得存进去一个用不起来的路径;保存成功后调用 initCellLocalPathFromServer() 同步本地缓存。
|
|
|
+ */
|
|
|
+ import { ref, unref, watch } from 'vue';
|
|
|
+ import { Button, Input } from 'ant-design-vue';
|
|
|
+ import { FolderOpenOutlined } from '@ant-design/icons-vue';
|
|
|
+ import { BasicModal, useModalInner } from '@/core/components/Modal';
|
|
|
+ import { useDesign } from '@/core/hooks/web/useDesign';
|
|
|
+ import { useMessage } from '@/core/hooks/web/useMessage';
|
|
|
+ import { getCellLocalApi, setCellLocalApi } from '@/core/api/sys/cellLocal';
|
|
|
+ import { initCellLocalPathFromServer } from '@/core/utils/cell/cellLocalInit';
|
|
|
+ import { isValidCellLocalPath } from '@/core/utils/cell/cellLocal';
|
|
|
+ import { isDesktopDialogAvailable, pickFile } from '@/core/utils/desktop/pathPicker';
|
|
|
+
|
|
|
+ const { prefixCls } = useDesign('cell-local-setting');
|
|
|
+ const { createMessage } = useMessage();
|
|
|
+
|
|
|
+ const desktopPicker = isDesktopDialogAvailable();
|
|
|
+ const loading = ref(false);
|
|
|
+ const saving = ref(false);
|
|
|
+ const path = ref('');
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析后端返回:/sys/getCellLocal 是 Result<String>。
|
|
|
+ *
|
|
|
+ * 该接口在 defHttp 层关掉了错误提示,所以这里必须自己看 code —— 否则「读取失败(如未登录)」
|
|
|
+ * 会和「没配置过」一样显示成空值,用户对着空框保存就把已有配置清掉了。
|
|
|
+ */
|
|
|
+ const readSetting = (payload: unknown): { ok: boolean; path: string; message: string } => {
|
|
|
+ const res = (payload ?? {}) as { code?: number; message?: string; data?: unknown };
|
|
|
+ return {
|
|
|
+ ok: Number(res.code ?? 0) === 200,
|
|
|
+ path: typeof res.data === 'string' ? res.data.trim() : '',
|
|
|
+ message: res.message || '读取基站包配置失败',
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 每次打开都重新读一次服务端配置。
|
|
|
+ *
|
|
|
+ * <p>用 `getOpen` 而不是 `useModalInner(callback)`:抽屉每次都传同一个 data(openModal(true)),
|
|
|
+ * 内部 dataTransfer 值没变化就不会再触发回调,第二次往后打开会读到上一次的残值。
|
|
|
+ */
|
|
|
+ const [registerModal, { closeModal, getOpen }] = useModalInner();
|
|
|
+
|
|
|
+ async function loadSetting() {
|
|
|
+ loading.value = true;
|
|
|
+ path.value = '';
|
|
|
+ try {
|
|
|
+ const parsed = readSetting(await getCellLocalApi());
|
|
|
+ if (!parsed.ok) {
|
|
|
+ createMessage.error(parsed.message);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ path.value = parsed.path;
|
|
|
+ } catch (error) {
|
|
|
+ createMessage.error(error instanceof Error ? error.message : '读取基站包配置失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => unref(getOpen),
|
|
|
+ (open) => {
|
|
|
+ if (open) void loadSetting();
|
|
|
+ },
|
|
|
+ { immediate: false },
|
|
|
+ );
|
|
|
+
|
|
|
+ async function handlePick() {
|
|
|
+ const picked = await pickFile({ title: '选择基站数据库文件', extensions: ['bin'], filterName: '基站数据库文件' });
|
|
|
+ if (picked) path.value = picked;
|
|
|
+ }
|
|
|
+
|
|
|
+ async function handleSave() {
|
|
|
+ const next = path.value.trim();
|
|
|
+ // 后端对空路径会走 Path.of("") → 命中「当前不支持文件夹!」这句误报,所以这里先拦住;
|
|
|
+ // 非 .bin / 非绝对路径后端也用不起来(TowerUtils 会拒绝),本地先校验一次。
|
|
|
+ if (!next) {
|
|
|
+ createMessage.warning('请填写基站包路径,或点「选择文件」挑一个 .bin 文件');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!isValidCellLocalPath(next)) {
|
|
|
+ createMessage.warning('基站包必须是绝对路径的 .bin 文件,或点「选择文件」挑一个');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ saving.value = true;
|
|
|
+ try {
|
|
|
+ const res = await setCellLocalApi(next);
|
|
|
+ if (Number((res as { code?: number } | null)?.code ?? 0) !== 200) {
|
|
|
+ createMessage.error((res as { message?: string } | null)?.message || '保存基站包配置失败');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ await initCellLocalPathFromServer();
|
|
|
+ createMessage.success(next ? '基站包已保存' : '已清空基站包配置');
|
|
|
+ closeModal();
|
|
|
+ } catch (error) {
|
|
|
+ createMessage.error(error instanceof Error ? error.message : '保存基站包配置失败');
|
|
|
+ } finally {
|
|
|
+ saving.value = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+ @prefix-cls: ~'jeesite-cell-local-setting';
|
|
|
+
|
|
|
+ .@{prefix-cls} {
|
|
|
+ .cell-local__tip {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ color: #4b5563;
|
|
|
+ line-height: 1.7;
|
|
|
+
|
|
|
+ code {
|
|
|
+ padding: 0 4px;
|
|
|
+ background: #f3f4f6;
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .cell-local__row {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .cell-local__hint {
|
|
|
+ margin: 10px 0 0;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #9ca3af;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|