可切換內網訪問
This commit is contained in:
1 parent
1ff69f3cb3
commit
fd67fa8f63
10 files changed
+493
-40
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import { http } from "uview-pro";
|
||||
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
|
||||
|
||||
/**
|
||||
* 访问线路(公网 / 内网直连)——App 手动切换,运行时即时生效。
|
||||
*
|
||||
* 背景:手机与服务器在同一局域网时,内网地址直连比公网域名快一个量级(飞牛 NAS 同款体验);
|
||||
* 本项目只做「手动切换」:用户在「我的 → 访问线路」里选线路并填写内网地址,不做自动探测,
|
||||
* 零探测开销、零误判。
|
||||
*
|
||||
* 生效范围:
|
||||
* - 接口请求:uview-pro 每次请求时读 http 实例的 config.baseUrl,切换后新请求立即走新线路;
|
||||
* - 资源地址:缩略图/原图/视频/头像由后端按请求 Host 动态拼(FileTypePolicy.BuildApplicationUrl),
|
||||
* 切到内网后后端返回的 URL 自动变成内网地址,前端无需改写;
|
||||
* - 进行中的上传:URL 在发起时已固定,切换不影响在途任务,后续任务走新线路。
|
||||
*/
|
||||
|
||||
const STORAGE_KEY = "api-endpoint-setting";
|
||||
|
||||
/** 线路设置:mode=wan 走公网(默认);lan 走内网直连(lanUrl 必须是合法 http(s) 地址) */
|
||||
export interface EndpointSetting {
|
||||
mode: "wan" | "lan";
|
||||
lanUrl: string;
|
||||
}
|
||||
|
||||
// 公网地址沿用原 baseUrl 口径:开发构建区分 App 真机连不上宿主机 localhost 的问题,发布构建用正式域名
|
||||
let devBaseUrl = WebSitePolicy.WebApiRootDev;
|
||||
// #ifdef APP-PLUS
|
||||
devBaseUrl = WebSitePolicy.WebApiRootDevApp;
|
||||
// #endif
|
||||
/** 公网(默认)地址 */
|
||||
const WAN_BASE_URL = import.meta.env.DEV
|
||||
? devBaseUrl
|
||||
: WebSitePolicy.WebApiRoot;
|
||||
|
||||
/** 规范化内网地址:允许省略 http://,去掉末尾斜杠;非法返回空串(调用方据此判无效) */
|
||||
export function normalizeLanUrl(raw: string): string {
|
||||
const text = String(raw ?? "")
|
||||
.trim()
|
||||
.replace(/\/+$/, "");
|
||||
if (!text) {
|
||||
return "";
|
||||
}
|
||||
const withScheme = /^https?:\/\//i.test(text) ? text : `http://${text}`;
|
||||
return /^https?:\/\/[^\s/]+$/i.test(withScheme) ? withScheme : "";
|
||||
}
|
||||
|
||||
function readSetting(): EndpointSetting {
|
||||
try {
|
||||
const raw = uni.getStorageSync(STORAGE_KEY) as
|
||||
| Partial<EndpointSetting>
|
||||
| "";
|
||||
if (raw && typeof raw === "object") {
|
||||
const lanUrl = normalizeLanUrl(String(raw.lanUrl ?? ""));
|
||||
// 存储里有脏数据(lan 模式但地址非法)时按公网兜底,避免全站请求打到空地址
|
||||
return { mode: raw.mode === "lan" && lanUrl ? "lan" : "wan", lanUrl };
|
||||
}
|
||||
} catch {
|
||||
// 存储读取失败按默认公网处理,不阻塞启动
|
||||
}
|
||||
return { mode: "wan", lanUrl: "" };
|
||||
}
|
||||
|
||||
// 模块级缓存:请求层高频读地址,不每次都走同步 storage
|
||||
let cached: EndpointSetting = readSetting();
|
||||
|
||||
/** 当前线路设置(只读快照) */
|
||||
export function currentEndpoint(): EndpointSetting {
|
||||
return { ...cached };
|
||||
}
|
||||
|
||||
/** 当前生效的后端地址:内网模式且地址合法时返回内网地址,否则公网 */
|
||||
export function apiBaseUrl(): string {
|
||||
return cached.mode === "lan" ? cached.lanUrl : WAN_BASE_URL;
|
||||
}
|
||||
|
||||
/** 把线路写入请求层并落盘,返回生效后的地址 */
|
||||
export function saveEndpoint(setting: EndpointSetting): string {
|
||||
const lanUrl = normalizeLanUrl(setting.lanUrl);
|
||||
cached = {
|
||||
mode: setting.mode === "lan" && lanUrl ? "lan" : "wan",
|
||||
lanUrl,
|
||||
};
|
||||
uni.setStorageSync(STORAGE_KEY, cached);
|
||||
// uview-pro 的 http 实例每次请求读 config.baseUrl,改这里即对新请求生效
|
||||
http.setConfig({ baseUrl: apiBaseUrl() });
|
||||
return apiBaseUrl();
|
||||
}
|
||||
|
||||
/** 测试内网地址连通性:用免鉴权的 RSA 公钥接口探活(与登录首调同源,最接近真实可用性) */
|
||||
export function testEndpoint(rawUrl: string): Promise<boolean> {
|
||||
const url = normalizeLanUrl(rawUrl);
|
||||
if (!url) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
uni.request({
|
||||
url: `${url}/Auth/GetRsaPublicKey`,
|
||||
method: "GET",
|
||||
timeout: 3000,
|
||||
success: (res) => resolve(res.statusCode >= 200 && res.statusCode < 300),
|
||||
fail: () => resolve(false),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
} from "uview-pro";
|
||||
import type { JwtInfo } from "@/data/auto/jwtInfo";
|
||||
import { http } from "uview-pro";
|
||||
import { apiBaseUrl } from "@/common/apiEndpoint";
|
||||
import { markErrorNotified } from "@/common/global-error";
|
||||
import {
|
||||
currentUiLocale,
|
||||
@@ -14,7 +15,6 @@ import {
|
||||
} from "@/common/locale-const";
|
||||
import enUS from "@/locale/lang/en-US.json";
|
||||
import zhCN from "@/locale/lang/zh-CN.json";
|
||||
import { WebSitePolicy } from "@/policy/custom/webSitePolicy";
|
||||
import {
|
||||
currentRealPath,
|
||||
errorPageUrl,
|
||||
@@ -37,18 +37,10 @@ import { copyIntoAppUploadDir } from "@/utils/appUploadFile";
|
||||
|
||||
// ==================== 全局配置 ====================
|
||||
|
||||
// 后端地址唯一来源:policy/custom/webSitePolicy(值可被项目根 .env 覆盖,缺失时回落该文件常量)
|
||||
// 与旧系统 WebVue 同逻辑:开发构建用 WebApiRootDev,发布构建用 WebApiRoot
|
||||
// App 端开发构建(真机/模拟器)连不上宿主机的 localhost,改用 WebApiRootDevApp
|
||||
// 小程序端需在平台配置 https 合法域名;本地 H5 联调如遇 CORS 需后端放行开发端口
|
||||
// 换环境只改 .env(详见 src/md/environment.md),本文件不允许再写死地址
|
||||
let devBaseUrl = WebSitePolicy.WebApiRootDev;
|
||||
// #ifdef APP-PLUS
|
||||
devBaseUrl = WebSitePolicy.WebApiRootDevApp;
|
||||
// #endif
|
||||
export const baseUrl = import.meta.env.DEV
|
||||
? devBaseUrl
|
||||
: WebSitePolicy.WebApiRoot;
|
||||
// 后端地址唯一来源:common/apiEndpoint(公网/内网线路可运行时切换,见「我的 → 访问线路」)。
|
||||
// 原 baseUrl 常量已改为 apiBaseUrl():uview-pro 每次请求读 http 实例的 config.baseUrl,
|
||||
// 切换线路后新请求立即生效;直接拼 URL 的调用方(登录页/上传队列)同步改用 apiBaseUrl()。
|
||||
// 换环境只改 .env(详见 src/md/environment.md),本文件不允许写死地址
|
||||
// 登录页真实路径:getCurrentPages() 拿到的 route 永远是 pages.json 里的 path,
|
||||
// 不含 H5 运行时补的短地址别名,故路径比较只能用真实路径,跳转 URL 才走 pageUrl()
|
||||
const loginPage = toRealPath("login");
|
||||
@@ -144,7 +136,7 @@ function doRefreshToken(): Promise<JwtInfo> {
|
||||
|
||||
return new Promise<JwtInfo>((resolve, reject) => {
|
||||
uni.request({
|
||||
url: `${baseUrl}/Auth/RefreshToken`,
|
||||
url: `${apiBaseUrl()}/Auth/RefreshToken`,
|
||||
method: "POST",
|
||||
data,
|
||||
timeout,
|
||||
@@ -293,7 +285,8 @@ export function extractBusinessError(data: any): string {
|
||||
// ==================== 全局配置(httpPlugin 注册用) ====================
|
||||
|
||||
const httpRequestConfig: RequestConfig = {
|
||||
baseUrl,
|
||||
// 启动时的当前线路;运行中切换由 apiEndpoint.saveEndpoint 调 http.setConfig 覆盖
|
||||
baseUrl: apiBaseUrl(),
|
||||
header: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
@@ -531,7 +524,7 @@ function sendByUniUpload(
|
||||
* 协议物理名再交给 uni.uploadFile(basename 即 filename,头像/身份证/网盘共用同一落盘口径)。 */
|
||||
export function uploadFile(options: UploadOptions): Promise<any> {
|
||||
const { url, filePath, name = "file", fileName, formData, header = {} } = options;
|
||||
const fullUrl = url.indexOf("http") === 0 ? url : baseUrl + url;
|
||||
const fullUrl = url.indexOf("http") === 0 ? url : apiBaseUrl() + url;
|
||||
const auth = bearerAuth();
|
||||
|
||||
// #ifdef H5
|
||||
@@ -618,7 +611,7 @@ export async function requestBuffer(options: {
|
||||
data?: any;
|
||||
}): Promise<ArrayBuffer> {
|
||||
const { url, method = "POST", data } = options;
|
||||
const fullUrl = url.indexOf("http") === 0 ? url : baseUrl + url;
|
||||
const fullUrl = url.indexOf("http") === 0 ? url : apiBaseUrl() + url;
|
||||
const auth = bearerAuth();
|
||||
|
||||
const res = await new Promise<UniApp.RequestSuccessCallbackResult>(
|
||||
|
||||
@@ -50,6 +50,21 @@
|
||||
"logoutConfirmContent": "Are you sure you want to log out?",
|
||||
"logoutSuccess": "Logged out",
|
||||
"currentTheme": "Current Theme",
|
||||
"accessLine": "Access Line",
|
||||
"lineHint": "Choose LAN Direct when your device is on the same network as the server for faster transfers",
|
||||
"lineWan": "WAN",
|
||||
"lineWanDesc": "Access via the public domain, available anywhere",
|
||||
"lineLan": "LAN Direct",
|
||||
"lineLanDesc": "Connect directly over the same Wi-Fi for faster speed",
|
||||
"lineLanPlaceholder": "LAN address, e.g. http://192.168.1.10:5235",
|
||||
"lineTest": "Test",
|
||||
"lineTesting": "Testing…",
|
||||
"lineTestOk": "Connected",
|
||||
"lineTestFail": "Connection failed. Check the address and network",
|
||||
"lineInvalid": "Invalid address format",
|
||||
"lineCancel": "Cancel",
|
||||
"lineSave": "Save",
|
||||
"lineSaved": "Line switched",
|
||||
"aboutTitle": "About",
|
||||
"appName": "App",
|
||||
"versionLabel": "Version",
|
||||
|
||||
@@ -50,6 +50,21 @@
|
||||
"logoutConfirmContent": "确定要退出当前账号吗?",
|
||||
"logoutSuccess": "已退出登录",
|
||||
"currentTheme": "当前主题",
|
||||
"accessLine": "访问线路",
|
||||
"lineHint": "与服务器在同一局域网时选内网直连,上传更快速",
|
||||
"lineWan": "公网",
|
||||
"lineWanDesc": "通过公网域名访问,随时随地可用",
|
||||
"lineLan": "内网直连",
|
||||
"lineLanDesc": "同一 Wi-Fi 下直连服务器内网地址,速度更快",
|
||||
"lineLanPlaceholder": "内网地址,如 http://192.168.1.10:5235",
|
||||
"lineTest": "测试连接",
|
||||
"lineTesting": "测试中…",
|
||||
"lineTestOk": "连接成功",
|
||||
"lineTestFail": "连接失败,请检查地址与网络",
|
||||
"lineInvalid": "地址格式不正确",
|
||||
"lineCancel": "取消",
|
||||
"lineSave": "保存",
|
||||
"lineSaved": "已切换线路",
|
||||
"aboutTitle": "关于",
|
||||
"appName": "应用名称",
|
||||
"versionLabel": "当前版本",
|
||||
|
||||
@@ -7,8 +7,9 @@ import type { LoginProfile } from "@/data/auto/loginProfile";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed, ref } from "vue";
|
||||
import { apiBaseUrl } from "@/common/apiEndpoint";
|
||||
import { brandGlyph, veinGlyph } from "@/common/brand";
|
||||
import { backendLocale, baseUrl } from "@/common/http.interceptor";
|
||||
import { backendLocale } from "@/common/http.interceptor";
|
||||
import { DEFAULT_LOCALE, setCurrentUiLocale } from "@/common/locale-const";
|
||||
import { encryptLoginPassword } from "@/common/pwd-crypto";
|
||||
import { useThemeGradient, useThemeTokens } from "@/composables";
|
||||
@@ -65,6 +66,8 @@ setCurrentUiLocale(currentLocale.value?.name ?? DEFAULT_LOCALE);
|
||||
/** 非发布构建才显示「当前环境 + 实际调用的后端地址」:发布版 import.meta.env.PROD 为 true,整行编译期即被剔除 */
|
||||
const isProd = import.meta.env.PROD;
|
||||
const envMode = import.meta.env.MODE;
|
||||
/** 当前后端地址:跟随「我的 → 访问线路」切换(内网直连时显示内网地址) */
|
||||
const currentApiUrl = apiBaseUrl();
|
||||
function toggleLang() {
|
||||
const next = isZh.value ? "en-US" : "zh-CN";
|
||||
setLocale(next);
|
||||
@@ -191,7 +194,7 @@ async function handleLogin() {
|
||||
</text>
|
||||
</view>
|
||||
<view v-if="!isProd" class="login-env">
|
||||
{{ envMode }} · {{ baseUrl }}
|
||||
{{ envMode }} · {{ currentApiUrl }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* 版本号取 common/constant 的 APP_INFO(与 manifest/package 对齐);H5 无 plus.runtime.version。
|
||||
*/
|
||||
import { useLocale } from "uview-pro";
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { currentEndpoint, normalizeLanUrl, saveEndpoint, testEndpoint } from "@/common/apiEndpoint";
|
||||
import { APP_INFO } from "@/common/constant";
|
||||
import { useAuthGuard, useThemeGradient, useThemeTokens } from "@/composables";
|
||||
import { usePermission } from "@/permission/permission-context";
|
||||
@@ -46,6 +47,63 @@ function goTheme(): void {
|
||||
uni.navigateTo({ url: pageUrl("themePage") });
|
||||
}
|
||||
|
||||
// ===== 访问线路(App 手动切换公网/内网直连;仅影响后续请求,不中断在途传输) =====
|
||||
const linePopupVisible = ref(false);
|
||||
/** 当前生效线路(保存后刷新展示) */
|
||||
const lineSetting = ref(currentEndpoint());
|
||||
/** 弹层草稿:确认前不动真实线路,取消即作废 */
|
||||
const lineModeDraft = ref<"wan" | "lan">("wan");
|
||||
const lineLanDraft = ref("");
|
||||
const lineTesting = ref(false);
|
||||
const lineTestResult = ref<"" | "ok" | "fail">("");
|
||||
|
||||
/** 当前线路展示文案:公网 / 内网直连(附地址) */
|
||||
const currentLineLabel = computed(() => {
|
||||
const setting = lineSetting.value;
|
||||
return setting.mode === "lan" ? `${t("MinePage.lineLan")} · ${setting.lanUrl}` : t("MinePage.lineWan");
|
||||
});
|
||||
|
||||
/** 打开线路弹层:草稿回填当前设置,清掉上次测活结果 */
|
||||
function openLinePopup(): void {
|
||||
const setting = currentEndpoint();
|
||||
lineModeDraft.value = setting.mode;
|
||||
lineLanDraft.value = setting.lanUrl;
|
||||
lineTestResult.value = "";
|
||||
linePopupVisible.value = true;
|
||||
}
|
||||
|
||||
/** 测试内网地址连通性:用 RSA 公钥接口探活(与登录首调同源) */
|
||||
async function handleTestLine(): Promise<void> {
|
||||
if (lineTesting.value) {
|
||||
return;
|
||||
}
|
||||
const url = normalizeLanUrl(lineLanDraft.value);
|
||||
if (!url) {
|
||||
uni.showToast({ title: t("MinePage.lineInvalid"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
lineTesting.value = true;
|
||||
lineTestResult.value = "";
|
||||
const ok = await testEndpoint(url);
|
||||
lineTesting.value = false;
|
||||
lineTestResult.value = ok ? "ok" : "fail";
|
||||
if (!ok) {
|
||||
uni.showToast({ title: t("MinePage.lineTestFail"), icon: "none" });
|
||||
}
|
||||
}
|
||||
|
||||
/** 保存线路:内网模式地址必填且合法;生效只影响后续请求,各页切回时自然按新线路拉数据 */
|
||||
function handleSaveLine(): void {
|
||||
if (lineModeDraft.value === "lan" && !normalizeLanUrl(lineLanDraft.value)) {
|
||||
uni.showToast({ title: t("MinePage.lineInvalid"), icon: "none" });
|
||||
return;
|
||||
}
|
||||
saveEndpoint({ mode: lineModeDraft.value, lanUrl: lineLanDraft.value });
|
||||
lineSetting.value = currentEndpoint();
|
||||
linePopupVisible.value = false;
|
||||
uni.showToast({ title: t("MinePage.lineSaved"), icon: "none" });
|
||||
}
|
||||
|
||||
/** 进入「安全管理」枢纽页:权限体系等基础设定的聚合入口 */
|
||||
function goSecurity(): void {
|
||||
uni.navigateTo({ url: pageUrl("securityPage") });
|
||||
@@ -126,6 +184,22 @@ async function handleLogout(): Promise<void> {
|
||||
<u-icon name="arrow-right" size="32" color="#9aa0a6" />
|
||||
</view>
|
||||
|
||||
<!-- 访问线路:App 手动切换公网/内网直连(资源地址由后端按请求 Host 自适应,仅 App 展示) -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<view class="mine-cell" @tap="openLinePopup">
|
||||
<view class="mine-cell__icon">
|
||||
<u-icon name="server-man" size="30" color="#ffffff" />
|
||||
</view>
|
||||
<text class="mine-cell__title">
|
||||
{{ t("MinePage.accessLine") }}
|
||||
</text>
|
||||
<text class="mine-cell__value">
|
||||
{{ currentLineLabel }}
|
||||
</text>
|
||||
<u-icon name="arrow-right" size="32" color="#9aa0a6" />
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- 安全管理:进入权限体系基础设定聚合页(/security) -->
|
||||
<view class="mine-cell" @tap="goSecurity">
|
||||
<view class="mine-cell__icon">
|
||||
@@ -240,6 +314,79 @@ async function handleLogout(): Promise<void> {
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 访问线路弹层:公网/内网单选 + 内网地址手填 + 测连通(仅 App 有入口) -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-popup :model-value="linePopupVisible" mode="bottom" round="24" @close="linePopupVisible = false">
|
||||
<view class="line-sheet">
|
||||
<text class="line-sheet__title">
|
||||
{{ t("MinePage.accessLine") }}
|
||||
</text>
|
||||
<text class="line-sheet__hint">
|
||||
{{ t("MinePage.lineHint") }}
|
||||
</text>
|
||||
|
||||
<!-- 公网:默认线路,随时随地可用 -->
|
||||
<view class="line-option" :class="{ 'line-option--active': lineModeDraft === 'wan' }"
|
||||
@tap="lineModeDraft = 'wan'">
|
||||
<view class="line-option__body">
|
||||
<text class="line-option__name">
|
||||
{{ t("MinePage.lineWan") }}
|
||||
</text>
|
||||
<text class="line-option__desc">
|
||||
{{ t("MinePage.lineWanDesc") }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="line-option__radio" :class="{ 'line-option__radio--on': lineModeDraft === 'wan' }" />
|
||||
</view>
|
||||
|
||||
<!-- 内网直连:需与服务器同局域网,地址手填 -->
|
||||
<view class="line-option" :class="{ 'line-option--active': lineModeDraft === 'lan' }"
|
||||
@tap="lineModeDraft = 'lan'">
|
||||
<view class="line-option__body">
|
||||
<text class="line-option__name">
|
||||
{{ t("MinePage.lineLan") }}
|
||||
</text>
|
||||
<text class="line-option__desc">
|
||||
{{ t("MinePage.lineLanDesc") }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="line-option__radio" :class="{ 'line-option__radio--on': lineModeDraft === 'lan' }" />
|
||||
</view>
|
||||
|
||||
<!-- 内网地址:选中内网才展开;测连通用免鉴权的 RSA 公钥接口 -->
|
||||
<view v-if="lineModeDraft === 'lan'" class="line-lan">
|
||||
<input v-model="lineLanDraft" class="line-lan__input" :placeholder="t('MinePage.lineLanPlaceholder')"
|
||||
placeholder-class="line-lan__ph">
|
||||
<view class="line-lan__test" @tap="handleTestLine">
|
||||
<text class="line-lan__test-text">
|
||||
{{ lineTesting ? t("MinePage.lineTesting") : t("MinePage.lineTest") }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="lineModeDraft === 'lan' && lineTestResult === 'ok'" class="line-lan__result line-lan__result--ok">
|
||||
{{ t("MinePage.lineTestOk") }}
|
||||
</text>
|
||||
<text v-else-if="lineModeDraft === 'lan' && lineTestResult === 'fail'"
|
||||
class="line-lan__result line-lan__result--fail">
|
||||
{{ t("MinePage.lineTestFail") }}
|
||||
</text>
|
||||
|
||||
<view class="line-sheet__actions">
|
||||
<view class="line-sheet__btn line-sheet__btn--ghost" @tap="linePopupVisible = false">
|
||||
<text class="line-sheet__btn-text line-sheet__btn-text--ghost">
|
||||
{{ t("MinePage.lineCancel") }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="line-sheet__btn line-sheet__btn--primary" @tap="handleSaveLine">
|
||||
<text class="line-sheet__btn-text">
|
||||
{{ t("MinePage.lineSave") }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- #endif -->
|
||||
</app-page>
|
||||
</template>
|
||||
|
||||
@@ -356,4 +503,157 @@ async function handleLogout(): Promise<void> {
|
||||
font-size: 26rpx;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
/* ===== 访问线路弹层(底部抽屉,同 file-action-sheet 风格) ===== */
|
||||
.line-sheet {
|
||||
padding: 32rpx 24rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||
background-color: #f5f6f7;
|
||||
}
|
||||
|
||||
.line-sheet__title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.line-sheet__hint {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
/* 单选行:白卡;选中态用内阴影描边(不改变布局尺寸) */
|
||||
.line-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 20rpx;
|
||||
padding: 24rpx;
|
||||
border-radius: 20rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.line-option--active {
|
||||
box-shadow: 0 0 0 2rpx var(--fg-primary, #ff7d1f) inset;
|
||||
}
|
||||
|
||||
.line-option__body {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.line-option__name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.line-option__desc {
|
||||
font-size: 24rpx;
|
||||
color: #9aa0a6;
|
||||
}
|
||||
|
||||
.line-option__radio {
|
||||
box-sizing: border-box;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-left: 16rpx;
|
||||
border: 2rpx solid #d4d7dc;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.line-option__radio--on {
|
||||
border-color: var(--fg-primary, #ff7d1f);
|
||||
background: radial-gradient(circle, var(--fg-primary, #ff7d1f) 0 42%, transparent 48%);
|
||||
}
|
||||
|
||||
/* 内网地址:输入框 + 测试连接一行 */
|
||||
.line-lan {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.line-lan__input {
|
||||
flex: 1;
|
||||
height: 76rpx;
|
||||
padding: 0 20rpx;
|
||||
border-radius: 16rpx;
|
||||
font-size: 26rpx;
|
||||
color: #1f2329;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.line-lan__ph {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.line-lan__test {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 76rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 16rpx;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.line-lan__test-text {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: var(--fg-primary, #ff7d1f);
|
||||
}
|
||||
|
||||
.line-lan__result {
|
||||
display: block;
|
||||
margin-top: 12rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.line-lan__result--ok {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.line-lan__result--fail {
|
||||
color: #fa3534;
|
||||
}
|
||||
|
||||
/* 底部双按钮:取消(描边)/ 保存(主题渐变) */
|
||||
.line-sheet__actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.line-sheet__btn {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 88rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.line-sheet__btn--ghost {
|
||||
border: 2rpx solid #d4d7dc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.line-sheet__btn--primary {
|
||||
background: var(--fg-gradient, linear-gradient(135deg, #ff9a3d, #ff7d1f));
|
||||
box-shadow: var(--fg-shadow, 0 6rpx 16rpx rgba(255, 125, 31, 0.35));
|
||||
}
|
||||
|
||||
.line-sheet__btn-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.line-sheet__btn-text--ghost {
|
||||
color: #4e5969;
|
||||
}
|
||||
</style>
|
||||
@@ -2,11 +2,8 @@ import type { PathNode } from "@/data/custom/fileBrowser";
|
||||
import type { PickedFile } from "@/utils/chooseFiles";
|
||||
import { defineStore } from "pinia";
|
||||
import { computed, ref } from "vue";
|
||||
import {
|
||||
baseUrl,
|
||||
bearerAuth,
|
||||
extractBusinessError,
|
||||
} from "@/common/http.interceptor";
|
||||
import { apiBaseUrl } from "@/common/apiEndpoint";
|
||||
import { bearerAuth, extractBusinessError } from "@/common/http.interceptor";
|
||||
import { copyIntoAppUploadDir } from "@/utils/appUploadFile";
|
||||
|
||||
/**
|
||||
@@ -87,7 +84,7 @@ function uniqueUploadTag(): string {
|
||||
|
||||
/** 分片大小:内网 3MB / 公网 1MB(旧系统 FileUpload 同口径;开发构建视为内网) */
|
||||
function resolveChunkSize(): number {
|
||||
const publicNet = /localhost|127\.|192\.168\.|10\./.test(baseUrl);
|
||||
const publicNet = /localhost|127\.|192\.168\.|10\./.test(apiBaseUrl());
|
||||
const intranet = import.meta.env.DEV || publicNet;
|
||||
return (intranet ? 3 : 1) * 1024 * 1024;
|
||||
}
|
||||
@@ -233,7 +230,7 @@ export const useUploadQueueStore = defineStore(
|
||||
chunk,
|
||||
`${uid}-${index}-${total}${extOf(item.name)}`,
|
||||
);
|
||||
xhr.open("POST", `${baseUrl}/DmsFile/UploadDmsFile`);
|
||||
xhr.open("POST", `${apiBaseUrl()}/DmsFile/UploadDmsFile`);
|
||||
xhr.setRequestHeader("Authorization", bearerAuth());
|
||||
xhr.setRequestHeader("FilePath", item.targetPath);
|
||||
xhr.upload.onprogress = (e: ProgressEvent) => {
|
||||
@@ -293,7 +290,7 @@ export const useUploadQueueStore = defineStore(
|
||||
return;
|
||||
}
|
||||
task = uni.uploadFile({
|
||||
url: `${baseUrl}/DmsFile/UploadDmsFile`,
|
||||
url: `${apiBaseUrl()}/DmsFile/UploadDmsFile`,
|
||||
filePath,
|
||||
// 后端约定:form 字段名 = 原始文件名
|
||||
name: item.name,
|
||||
|
||||
Reference in new issue
Block a user