feat: add offline environment setting (#12637)

This commit is contained in:
ssongliu
2026-04-30 11:05:55 +08:00
committed by zhengkunwang223
parent 6941014153
commit 8fb8d97dcf
41 changed files with 159 additions and 68 deletions

View File

@@ -126,7 +126,7 @@ func (a AppService) PageApp(ctx *gin.Context, req request.AppSearch) (*response.
lang := strings.ToLower(common.GetLang(ctx))
for _, ap := range apps {
if req.Type == "php" {
if !global.CONF.Base.IsOffLine && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
if !global.CONF.Base.IsOffline && (ap.RequiredPanelVersion == 0 || !common.CompareAppVersion(common.GetSystemVersion(info.SystemVersion), fmt.Sprintf("%f", ap.RequiredPanelVersion))) {
continue
}
}

View File

@@ -681,7 +681,7 @@ func GetWebsiteID() uint {
}
func pullImage(imageType string) {
if global.CONF.Base.IsOffLine {
if global.CONF.Base.IsOffline {
return
}
if imageType == "npx" {

View File

@@ -15,7 +15,7 @@ type Base struct {
Mode string `mapstructure:"mode"` // xpack [ Enable / Disable ]
IsDemo bool `mapstructure:"is_demo"`
InstallDir string `mapstructure:"install_dir"`
IsOffLine bool `mapstructure:"is_offline"`
IsOffline bool `mapstructure:"is_offline"`
}
type SystemDir struct {

View File

@@ -20,7 +20,7 @@ func Init() {
}
func syncApp() {
if global.CONF.Base.IsOffLine {
if global.CONF.Base.IsOffline {
return
}
setting, err := service.NewISettingService().GetSettingInfo()

View File

@@ -241,7 +241,7 @@ func (b *BaseApi) GetLoginSetting(c *gin.Context) {
IsDemo: global.CONF.Base.IsDemo,
IsIntl: global.CONF.Base.Edition == "intl",
IsFxplay: global.CONF.Base.IsFxplay,
IsOffLine: global.CONF.Base.IsOffLine,
IsOffline: global.CONF.Base.IsOffline || settingInfo.IsOffline == constant.StatusEnable,
IsEnterprise: global.CONF.Base.IsEnterprise,
Language: settingInfo.Language,
MenuTabs: settingInfo.MenuTabs,

View File

@@ -51,3 +51,26 @@ type SystemSetting struct {
type PasskeyID struct {
ID string `json:"id" validate:"required"`
}
type CurrentUserInfo struct {
Name string `json:"name"`
SessionTimeout int `json:"sessionTimeout"`
MFAStatus string `json:"mfaStatus"`
MFAInterval string `json:"mfaInterval"`
ExpirationDays int `json:"expirationDays"`
ExpirationTime string `json:"expirationTime"`
ComplexitySetting string `json:"complexitySetting"`
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
ApiKey string `json:"apiKey"`
IpWhiteList string `json:"ipWhiteList"`
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
}
type CurrentUserUpdate struct {
Name string `json:"name" validate:"required"`
Password string `json:"password"`
OldPassword string `json:"oldPassword"`
SessionTimeout int `json:"sessionTimeout" validate:"required,min=300,max=864000"`
ExpirationDays int `json:"expirationDays" validate:"min=0,max=60"`
ExpirationTime string `json:"expirationTime"`
}

View File

@@ -18,6 +18,7 @@ type SettingInfo struct {
MenuTabs string `json:"menuTabs"`
Language string `json:"language"`
DocSource string `json:"docSource"`
IsOffline string `json:"isOffline"`
ServerPort string `json:"serverPort"`
SSL string `json:"ssl"`
@@ -73,35 +74,12 @@ type SettingBaseInfo struct {
DashboardSimpleNodeVisible string `json:"dashboardSimpleNodeVisible"`
}
type CurrentUserInfo struct {
Name string `json:"name"`
SessionTimeout int `json:"sessionTimeout"`
MFAStatus string `json:"mfaStatus"`
MFAInterval string `json:"mfaInterval"`
ExpirationDays int `json:"expirationDays"`
ExpirationTime string `json:"expirationTime"`
ComplexitySetting string `json:"complexitySetting"`
ApiInterfaceStatus string `json:"apiInterfaceStatus"`
ApiKey string `json:"apiKey"`
IpWhiteList string `json:"ipWhiteList"`
ApiKeyValidityTime string `json:"apiKeyValidityTime"`
}
type CurrentUserUpdate struct {
Name string `json:"name" validate:"required"`
Password string `json:"password"`
OldPassword string `json:"oldPassword"`
SessionTimeout int `json:"sessionTimeout" validate:"required,min=300,max=864000"`
ExpirationDays int `json:"expirationDays" validate:"min=0,max=60"`
ExpirationTime string `json:"expirationTime"`
}
type SettingKey struct {
Key string `json:"key" validate:"required,oneof=ScriptSync"`
}
type SettingUpdate struct {
Key string `json:"key" validate:"required"`
Key string `json:"key" validate:"required,base_setting_key"`
Value string `json:"value"`
}
@@ -291,7 +269,7 @@ type AppstoreConfig struct {
type LoginSetting struct {
IsDemo bool `json:"isDemo"`
IsIntl bool `json:"isIntl"`
IsOffLine bool `json:"isOffLine"`
IsOffline bool `json:"isOffline"`
IsFxplay bool `json:"isFxplay"`
IsEnterprise bool `json:"isEnterprise"`
Language string `json:"language"`

View File

@@ -172,7 +172,7 @@ func StartSync() {
}
service := NewIScriptService()
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
if !global.CONF.Base.IsOffline && scriptSync == constant.StatusEnable {
minuteRand, err := rand.Int(rand.Reader, big.NewInt(60))
if err != nil {
global.LOG.Errorf("generate random minute failed: %v", err)
@@ -202,7 +202,7 @@ func LoadScriptInfo(id uint) (model.ScriptLibrary, error) {
}
func (u *ScriptService) Sync(req dto.OperateByTaskID) error {
if global.CONF.Base.IsOffLine {
if global.CONF.Base.IsOffline {
return nil
}
syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("RemoteScriptLibrary"), task.TaskSync, task.TaskScopeScript, req.TaskID, 0)

View File

@@ -106,6 +106,9 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
} else {
info.ProxyPasswd, _ = encrypt.StringDecrypt(info.ProxyPasswd)
}
if !global.CONF.Base.IsEnterprise {
info.IsOffline = constant.StatusDisable
}
return &info, err
}
@@ -167,6 +170,10 @@ func (u *SettingService) Update(c *gin.Context, key, value string) error {
return nil
}
switch key {
case "IsOffline":
if !global.CONF.Base.IsEnterprise {
return buserr.New("ErrNotSupportInEnterpriseEdition")
}
case "AppStoreLastModified":
exist, _ := settingRepo.Get(repo.WithByKey("AppStoreLastModified"))
if exist.ID == 0 {

View File

@@ -87,7 +87,7 @@ func NewIUpgradeService() IUpgradeService {
}
func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) {
if global.CONF.Base.IsOffLine {
if global.CONF.Base.IsOffline {
return &dto.UpgradeInfo{}, nil
}
var upgrade dto.UpgradeInfo

View File

@@ -12,7 +12,7 @@ type Base struct {
Password string `mapstructure:"password"`
Language string `mapstructure:"language"`
IsDemo bool `mapstructure:"is_demo"`
IsOffLine bool `mapstructure:"is_offline"`
IsOffline bool `mapstructure:"is_offline"`
IsFxplay bool `mapstructure:"is_fxplay"`
Edition string `mapstructure:"edition"`
IsEnterprise bool `mapstructure:"is_enterprise"`

View File

@@ -40,6 +40,7 @@ func Init() {
migrations.AddAppStoreInstallAllowPortSetting,
migrations.AddUserManagementMenu,
migrations.AddOperationLogUser,
migrations.AddIsOfflineSetting,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@@ -1048,3 +1048,13 @@ var AddOperationLogUser = &gormigrate.Migration{
return tx.AutoMigrate(&model.OperationLog{})
},
}
var AddIsOfflineSetting = &gormigrate.Migration{
ID: "20260429-add-is-offline-setting",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "IsOffline", Value: constant.StatusDisable}).Error; err != nil {
return err
}
return nil
},
}

View File

@@ -10,7 +10,7 @@ import (
func Init() {
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
if !global.CONF.Base.IsOffline && scriptSync == constant.StatusEnable {
if err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil {
global.LOG.Errorf("sync scripts from remote failed, err: %v", err)
}

View File

@@ -20,9 +20,36 @@ func Init() {
if err := validator.RegisterValidation("password", checkPasswordPattern); err != nil {
panic(err)
}
if err := validator.RegisterValidation("base_setting_key", checkBaseSettingKey); err != nil {
panic(err)
}
global.VALID = validator
}
var baseSettingKeys = map[string]struct{}{
"PanelName": {},
"Theme": {},
"MenuTabs": {},
"Language": {},
"DeveloperMode": {},
"UpgradeBackupCopies": {},
"SecurityEntrance": {},
"BindDomain": {},
"AllowIPs": {},
"PasskeyTrustedProxies": {},
"ComplexityVerification": {},
"NoAuthSetting": {},
"DashboardMemoVisible": {},
"DashboardSimpleNodeVisible": {},
"MFAStatus": {},
"Edition": {},
"DocSource": {},
"IsOffline": {},
"AppStoreLastModified": {},
"ScriptSync": {},
"HideMenu": {},
}
func checkNamePattern(fl validator.FieldLevel) bool {
value := fl.Field().String()
result, err := regexp.MatchString("^[a-zA-Z\u4e00-\u9fa5]{1}[a-zA-Z0-9_\u4e00-\u9fa5]{0,30}$", value)
@@ -63,3 +90,8 @@ func checkPasswordPattern(fl validator.FieldLevel) bool {
return false
}
func checkBaseSettingKey(fl validator.FieldLevel) bool {
_, ok := baseSettingKeys[fl.Field().String()]
return ok
}

View File

@@ -87,7 +87,7 @@ func Init() {
global.CONF.Base.InstallDir = baseDir
global.CONF.Base.IsDemo = v.GetBool("base.is_demo")
global.CONF.Base.IsFxplay = v.GetBool("base.is_fxplay")
global.CONF.Base.IsOffLine = v.GetBool("base.is_offline")
global.CONF.Base.IsOffline = v.GetBool("base.is_offline")
if edition == "intl" {
global.CONF.Base.Edition = "intl"
} else {

View File

@@ -39,7 +39,7 @@ export namespace Login {
menuTabs: string;
panelName: string;
theme: string;
isOffLine: boolean;
isOffline: boolean;
isEnterprise: boolean;
needCaptcha: boolean;
passkeySetting: boolean;

View File

@@ -39,6 +39,7 @@ export namespace Setting {
menuTabs: string;
language: string;
docSource: string;
isOffline: string;
serverPort: number;
ipv6: string;

View File

@@ -28,6 +28,7 @@
<span v-else-if="isMasterPro">
{{ $t('license.pro') }}
</span>
<span v-else-if="isOffline">
</el-link>
<el-link v-else-if="isOffLine" underline="never" type="primary" @click="to1Panel">
<span v-if="isOffLine">
@@ -44,7 +45,7 @@
</el-link>
<el-badge
is-dot
v-if="globalStore.isAdmin && !globalStore.isOffLine"
v-if="globalStore.isAdmin && !globalStore.isOffline"
class="-mt-0.5"
:hidden="version === 'Waiting' || !globalStore.hasNewVersion"
>
@@ -73,7 +74,7 @@ import { GlobalStore } from '@/store';
import { storeToRefs } from 'pinia';
const globalStore = GlobalStore();
const { docsUrl, isOffLine, isFxplay } = storeToRefs(globalStore);
const { docsUrl, isOffline, isFxplay } = storeToRefs(globalStore);
const upgradeRef = ref();
const releasesRef = ref();
const isMasterPro = computed(() => {
@@ -107,6 +108,10 @@ const getVersionLog = () => {
};
const toLxware = () => {
if (isOffline.value) {
to1Panel();
return;
}
if (!globalStore.isIntl) {
window.open('https://www.lxware.cn/1panel' + '', '_blank', 'noopener,noreferrer');
} else {

View File

@@ -2230,6 +2230,7 @@ const message = {
auto: 'Follow System',
language: 'Language',
runtimeEnv: 'Runtime environment',
offlineEnv: 'Offline environment',
docSource: 'Documentation Source',
withByRegion: 'Match Region Setting (Default)',
withByLang: 'Match System Language',

View File

@@ -2270,6 +2270,7 @@ const message = {
auto: 'Seguir sistema',
language: 'Idioma',
runtimeEnv: 'Entorno de ejecución',
offlineEnv: 'Entorno sin conexión',
docSource: 'Fuente de documentación',
withByRegion: 'Seguir región de operación (Predeterminado)',
withByLang: 'Seguir idioma del sistema',

View File

@@ -2235,6 +2235,7 @@ const message = {
auto: 'システムをフォローします',
language: '言語',
runtimeEnv: '実行環境',
offlineEnv: 'オフライン環境',
docSource: 'ドキュメントの参照先',
withByRegion: '運用リージョンに従うデフォルト',
withByLang: 'システム言語に従う',

View File

@@ -2195,6 +2195,7 @@ const message = {
auto: '시스템 따라가기',
language: '언어',
runtimeEnv: '실행 환경',
offlineEnv: '오프라인 환경',
docSource: '문서 출처',
withByRegion: '운영 지역 따름(기본값)',
withByLang: '시스템 언어 따름',

View File

@@ -2275,6 +2275,7 @@ const message = {
auto: 'Ikut Sistem',
language: 'Bahasa',
runtimeEnv: 'Persekitaran operasi',
offlineEnv: 'Persekitaran luar talian',
docSource: 'Sumber dokumentasi',
withByRegion: 'Ikut wilayah operasi (Lalai)',
withByLang: 'Ikut bahasa sistem',

View File

@@ -2387,6 +2387,7 @@ const message = {
auto: 'Seguir o sistema',
language: 'Idioma',
runtimeEnv: 'Ambiente de execução',
offlineEnv: 'Ambiente offline',
docSource: 'Fonte da documentação',
withByRegion: 'Seguir região de operação (Padrão)',
withByLang: 'Seguir idioma do sistema',

View File

@@ -2252,6 +2252,7 @@ const message = {
auto: 'Как в системе',
language: 'Язык',
runtimeEnv: 'Среда выполнения',
offlineEnv: 'Офлайн-среда',
docSource: 'Источник документации',
withByRegion: 'Следовать региону работы (по умолчанию)',
withByLang: 'Следовать языку системы',

View File

@@ -2266,6 +2266,7 @@ const message = {
auto: 'Sistemi takip et',
language: 'Dil',
runtimeEnv: 'Çalışma ortamı',
offlineEnv: 'Çevrimdışı ortam',
docSource: 'Dokümantasyon kaynağı',
withByRegion: 'Çalışma bölgesini takip et (Varsayılan)',
withByLang: 'Sistem dilini takip et',

View File

@@ -2093,6 +2093,7 @@ const message = {
auto: '跟隨系統',
language: '系統語言',
runtimeEnv: '運行環境',
offlineEnv: '離線環境',
docSource: '文件來源',
withByRegion: '跟隨運行區域預設',
withByLang: '跟隨系統語言',

View File

@@ -2093,6 +2093,7 @@ const message = {
withByRegion: '跟随运行区域默认',
withByLang: '跟随系统语言',
runtimeEnv: '运行环境',
offlineEnv: '离线环境',
region: '运行区域',
cn: '中国大陆',
intl: '全球',

View File

@@ -88,7 +88,6 @@
</span>
</template>
<el-switch
v-if="form.mfaStatus !== null"
@change="handleMFA"
v-model="form.mfaStatus"
active-value="Enable"
@@ -377,7 +376,7 @@
</template>
<script setup lang="ts">
import { computed, nextTick, reactive, ref } from 'vue';
import { computed, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessageBox, FormInstance } from 'element-plus';
import { Check, Close, QuestionFilled } from '@element-plus/icons-vue';
@@ -415,7 +414,6 @@ const loading = ref(false);
const userRef = ref<FormInstance>();
const mfaFormRef = ref<FormInstance>();
const apiRef = ref<FormInstance>();
const switchReady = ref(false);
const mfaDialogOpen = ref(false);
const passkeyPrereqDialogOpen = ref(false);
const passkeyDialogOpen = ref(false);
@@ -442,7 +440,7 @@ const form = reactive({
password: '',
oldPassword: '',
sessionTimeout: 0,
mfaStatus: null as string | null,
mfaStatus: 'Disable',
mfaInterval: 30,
expirationDays: 0,
expirationTime: '',
@@ -533,20 +531,17 @@ const openDrawer = async () => {
if (!props.currentUser) {
return;
}
switchReady.value = false;
loadComplexitySetting();
syncCurrentUser(props.currentUser);
open.value = true;
await nextTick();
switchReady.value = true;
};
const syncApiConfig = (currentUser: Login.AuthInfo) => {
form.apiInterfaceStatus = currentUser.apiInterfaceStatus;
form.apiInterfaceStatus = currentUser.apiInterfaceStatus || 'Disable';
form.apiKey = currentUser.apiKey;
form.ipWhiteList = currentUser.ipWhiteList;
form.apiKeyValidityTime = currentUser.apiKeyValidityTime;
savedApiStatus.value = currentUser.apiInterfaceStatus;
savedApiStatus.value = form.apiInterfaceStatus;
};
const syncCurrentUser = (currentUser: Login.AuthInfo) => {
@@ -557,9 +552,9 @@ const syncCurrentUser = (currentUser: Login.AuthInfo) => {
form.sessionTimeout = currentUser.sessionTimeout;
form.expirationTime = currentUser.expirationTime;
form.expirationDays = currentUser.expirationDays;
form.mfaStatus = currentUser.mfaStatus;
form.mfaStatus = currentUser.mfaStatus || 'Disable';
form.mfaInterval = currentUser.mfaInterval;
savedMfaStatus.value = currentUser.mfaStatus;
savedMfaStatus.value = form.mfaStatus;
mfaDialogOpen.value = false;
apiDialogOpen.value = false;
form.expirationDays = form.expirationDays ? form.expirationDays : 0;
@@ -929,7 +924,7 @@ function loadTimeOut() {
}
const handleMFA = async () => {
if (!switchReady.value || !form.mfaStatus) {
if (!form.mfaStatus) {
return;
}
if (form.mfaStatus === 'Enable') {
@@ -967,7 +962,7 @@ const handleMFA = async () => {
};
const handleApi = async () => {
if (!switchReady.value || !form.apiInterfaceStatus) {
if (!form.apiInterfaceStatus) {
return;
}
if (form.apiInterfaceStatus === 'Enable') {

View File

@@ -62,7 +62,7 @@ export interface GlobalState {
isIntl: boolean;
docWithRegion: boolean;
isFxplay: boolean;
isOffLine: boolean;
isOffline: boolean;
// license
isProductPro: boolean;
productProExpires: number;

View File

@@ -59,7 +59,7 @@ const GlobalStore = defineStore({
isIntl: false,
docWithRegion: true,
isFxplay: false,
isOffLine: false,
isOffline: false,
// license
isProductPro: false,
productProExpires: 0,

View File

@@ -7,7 +7,7 @@
</template>
<template #leftToolBar>
<el-button @click="sync" type="primary" plain :disabled="syncing">
<span>{{ syncCustomAppstore || isOffLine ? $t('app.syncCustomApp') : $t('app.syncAppList') }}</span>
<span>{{ syncCustomAppstore || isOffline ? $t('app.syncCustomApp') : $t('app.syncAppList') }}</span>
</el-button>
<el-button @click="syncLocal" type="primary" plain :disabled="syncing" class="ml-2">
{{ $t('app.syncLocalApp') }}
@@ -85,7 +85,7 @@ import AppCard from '@/views/app-store/apps/app/index.vue';
import MainDiv from '@/components/main-div/index.vue';
import { jumpToInstall } from '@/utils/app';
import { useGlobalStore } from '@/composables/useGlobalStore';
const { globalStore, isProductPro, isOffLine } = useGlobalStore();
const { globalStore, isProductPro, isOffline } = useGlobalStore();
const mobile = computed(() => {
return globalStore.isMobile();
@@ -180,7 +180,7 @@ const sync = async () => {
};
try {
let res;
if (isOffLine.value || (isProductPro.value && syncCustomAppstore.value)) {
if (isOffline.value || (isProductPro.value && syncCustomAppstore.value)) {
res = await syncCutomAppStore(syncReq);
} else {
res = await syncApp(syncReq);
@@ -246,7 +246,7 @@ onMounted(async () => {
syncCustomAppstore.value = res.data.status === 'Enable';
}
}
if (isOffLine.value) {
if (isOffline.value) {
syncCustomAppstore.value = true;
}
mainHeight.value = window.innerHeight - 380;

View File

@@ -322,7 +322,7 @@ const initForm = async (appKey: string) => {
formData.value.version = defaultVersion;
getVersionDetail(defaultVersion);
}
if (globalStore.isOffLine) {
if (globalStore.isOffline) {
formData.value.pullImage = false;
}
};

View File

@@ -7,7 +7,17 @@
path: '/',
},
]"
/>
>
<template #route-button>
<div class="router-button" v-if="!isOffline">
<template v-if="!isProductPro">
<el-button link type="primary" @click="toUpload">
{{ $t('license.levelUpPro') }}
</el-button>
</template>
</div>
</template>
</RouterButton>
<el-alert
v-if="!isSafety && globalStore.showEntranceWarn"
@@ -529,6 +539,7 @@ const ioOptionsFromCache = ref(false);
const hasRefreshedOptionsOnHover = ref(false);
const quickJumpRef = ref();
const { isProductPro, isOffline } = storeToRefs(globalStore);
const searchInfo = reactive({
ioOption: 'all',

View File

@@ -628,7 +628,7 @@ const getSetting = async () => {
isIntl.value = res.data.isIntl;
isFxplay.value = res.data.isFxplay;
globalStore.isFxplay = isFxplay.value;
globalStore.isOffLine = res.data.isOffLine;
globalStore.isOffline = res.data.isOffline;
globalStore.isEnterprise = res.data.isEnterprise;
globalStore.isEnterpriseLicenseLoaded = !res.data.isEnterprise;
globalStore.ignoreCaptcha = !res.data.needCaptcha;

View File

@@ -11,7 +11,7 @@
import { computed } from 'vue';
import i18n from '@/lang';
import { useGlobalStore } from '@/composables/useGlobalStore';
const { isOffLine, isFxplay, isAdmin, isEnterprise } = useGlobalStore();
const { isOffline, isFxplay, isAdmin, isEnterprise } = useGlobalStore();
const buttons = computed(() => {
const items = [
@@ -39,7 +39,7 @@ const buttons = computed(() => {
},
]
: []),
...(isOffLine.value || !isAdmin.value
...(isOffline.value || !isAdmin.value
? []
: [
{

View File

@@ -152,7 +152,7 @@ const loadLoginTheme = async () => {
globalStore.isEnterpriseLicenseLoaded = !res.data.isEnterprise;
globalStore.isIntl = res.data.isIntl;
globalStore.isFxplay = res.data.isFxplay;
globalStore.isOffLine = res.data.isOffLine;
globalStore.isOffline = res.data.isOffline;
globalStore.openMenuTabs = res.data.menuTabs === 'Enable';
globalStore.themeConfig = {
...globalStore.themeConfig,

View File

@@ -156,6 +156,19 @@
</el-button>
</el-form-item>
<el-form-item
:label="$t('setting.offlineEnv')"
v-if="globalStore.isEnterprise"
prop="isOffline"
>
<el-switch
v-model="form.isOffline"
active-value="Enable"
inactive-value="Disable"
@change="onSave('IsOffline', form.isOffline)"
/>
</el-form-item>
<el-form-item :label="$t('setting.runtimeEnv')" prop="edition">
<el-button icon="Setting" @click="onChangeRegion">
{{ runtimeEnvLabel() }}
@@ -222,6 +235,7 @@ const form = reactive({
language: '',
docSource: 'withByRegion',
edition: '',
isOffline: 'Disable',
developerMode: '',
systemIP: '',
@@ -276,6 +290,7 @@ const search = async () => {
form.language = res.data.language;
form.docSource = res.data.docSource || 'withByRegion';
form.edition = res.data.edition;
form.isOffline = res.data.isOffline || 'Disable';
form.proxyUrl = res.data.proxyUrl;
form.proxyType = res.data.proxyType;
@@ -412,6 +427,9 @@ const onSave = async (key: string, val: any) => {
await globalStore.updateLanguage(val);
location.reload();
break;
case 'IsOffline':
globalStore.isOffline = val === 'Enable';
break;
}
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();

View File

@@ -35,7 +35,7 @@ import { App } from '@/api/interface/app';
import { getAppByKey, getAppDetail, searchApp } from '@/api/modules/app';
import { useVModel } from '@vueuse/core';
import { useGlobalStore } from '@/composables/useGlobalStore';
const { isOffLine } = useGlobalStore();
const { isOffline } = useGlobalStore();
const props = defineProps({
mode: {
@@ -93,7 +93,7 @@ const getApp = async (appkey: string, mode: string) => {
const searchAppList = async (appID: number) => {
try {
if (isOffLine.value) {
if (isOffline.value) {
appReq.resource = 'custom';
}
const res = await searchApp(appReq);

View File

@@ -24,7 +24,7 @@
v-model="runtime.resource"
@change="changeResource(runtime.resource)"
>
<el-radio :value="'appstore'" v-if="!globalStore.isOffLine">
<el-radio :value="'appstore'" v-if="!globalStore.isOffline">
{{ $t('menu.apps') }}
</el-radio>
<el-radio :value="'local'">
@@ -491,7 +491,7 @@ const acceptParams = async (props: OperateRrops) => {
initParam.value = false;
if (props.mode === 'create') {
Object.assign(runtime, initData(props.type));
if (globalStore.isOffLine) {
if (globalStore.isOffline) {
runtime.resource = 'local';
} else {
searchAppList(null);