mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-08 14:14:19 +00:00
fix: settings save button and multiple UI bugs
- Fix duplicate :min attributes on a-input-number (webPort, subPort, tgCpu) - Fix mismatched closing tags (a-input/a-switch instead of a-input-number) - Fix twoFactorEnable toggle receiving MouseEvent instead of boolean - Fix noise input handlers referencing undefined global `event` - Add error handling to settings change detection polling loop - Bump version to v1.5.4-beta
This commit is contained in:
parent
5ec81ea3d0
commit
582037ae70
8 changed files with 62 additions and 12 deletions
|
|
@ -1 +1 @@
|
||||||
v1.5.3-beta
|
v1.5.4-beta
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
# 2026-04-24 Fix Settings Save Button and UI Bugs
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
Settings page save button would not enable when user changed settings.
|
||||||
|
|
||||||
|
## Root Cause Analysis
|
||||||
|
Systematic debugging found multiple UI bugs affecting the settings page:
|
||||||
|
|
||||||
|
1. **Duplicate `:min` attributes on `a-input-number`** (3 locations)
|
||||||
|
- `general.html:42` — webPort had `:min="1" :min="65535"` (second should be `:max`)
|
||||||
|
- `subscription/general.html:43` — subPort had same issue
|
||||||
|
- `telegram.html:64` — tgCpu had `:min="0" :min="100"` (second should be `:max`)
|
||||||
|
|
||||||
|
2. **Mismatched closing tags**
|
||||||
|
- `general.html:42` — `<a-input-number>` closed with `</a-input>`
|
||||||
|
- `telegram.html:64` — `<a-input-number>` closed with `</a-switch>`
|
||||||
|
|
||||||
|
3. **twoFactorEnable toggle broken** (`security.html:39`)
|
||||||
|
- Used `@click="toggleTwoFactor" :checked="..."` instead of proper event handling
|
||||||
|
- `@click` passes MouseEvent as first arg, not the boolean toggle value
|
||||||
|
- Method expected boolean but received Event → always truthy → always triggered enable flow
|
||||||
|
|
||||||
|
4. **Noise input handlers referenced undefined `event`** (`json.html:91,99`)
|
||||||
|
- `(value) => updateNoisePacket(index, event.target.value)` — `event` is not defined
|
||||||
|
- Arrow function parameter named `value` but code accessed global `event`
|
||||||
|
|
||||||
|
5. **Polling loop had no error handling** (`settings.html:653-656`)
|
||||||
|
- Any error in the `while(true)` loop would silently stop change detection
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
- Fixed `:min`/`:max` attributes on all `a-input-number` components
|
||||||
|
- Fixed closing tags to match opening tags
|
||||||
|
- Changed twoFactorEnable to use `@click.prevent="toggleTwoFactor(!allSetting.twoFactorEnable)"`
|
||||||
|
- Updated `toggleTwoFactor` method to only set `twoFactorEnable` on success
|
||||||
|
- Fixed noise input handlers to use `(e) => ... e.target.value`
|
||||||
|
- Added try/catch around polling loop comparison
|
||||||
|
|
||||||
|
## Files Modified
|
||||||
|
- `web/html/settings/panel/general.html` — webPort input fix
|
||||||
|
- `web/html/settings/panel/subscription/general.html` — subPort input fix
|
||||||
|
- `web/html/settings/panel/telegram.html` — tgCpu input fix
|
||||||
|
- `web/html/settings/panel/security.html` — twoFactorEnable toggle fix
|
||||||
|
- `web/html/settings/panel/subscription/json.html` — noise input handler fix
|
||||||
|
- `web/html/settings.html` — toggleTwoFactor method + polling error handling
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- Visual inspection of all modified templates
|
||||||
|
- Confirmed `ObjectUtil.equals()` shallow comparison works correctly with Vue 2 reactivity
|
||||||
|
- Confirmed `AllSetting` class properties match Go struct fields
|
||||||
|
|
@ -396,11 +396,9 @@
|
||||||
confirm: (success) => {
|
confirm: (success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
|
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
|
||||||
|
|
||||||
this.allSetting.twoFactorToken = newTwoFactorToken
|
this.allSetting.twoFactorToken = newTwoFactorToken
|
||||||
|
this.allSetting.twoFactorEnable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.allSetting.twoFactorEnable = success
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -412,7 +410,6 @@
|
||||||
confirm: (success) => {
|
confirm: (success) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
|
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
|
||||||
|
|
||||||
this.allSetting.twoFactorEnable = false
|
this.allSetting.twoFactorEnable = false
|
||||||
this.allSetting.twoFactorToken = ""
|
this.allSetting.twoFactorToken = ""
|
||||||
}
|
}
|
||||||
|
|
@ -655,7 +652,11 @@
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
await PromiseUtil.sleep(1000);
|
await PromiseUtil.sleep(1000);
|
||||||
this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
|
try {
|
||||||
|
this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Settings change detection error:', e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
<template #title>{{ i18n "pages.settings.panelPort"}}</template>
|
<template #title>{{ i18n "pages.settings.panelPort"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.panelPortDesc"}}</template>
|
<template #description>{{ i18n "pages.settings.panelPortDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input-number :min="1" :min="65535" v-model="allSetting.webPort" :style="{ width: '100%' }"></a-input>
|
<a-input-number :min="1" :max="65535" v-model="allSetting.webPort" :style="{ width: '100%' }"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@
|
||||||
<template #title>{{ i18n "pages.settings.security.twoFactorEnable" }}</template>
|
<template #title>{{ i18n "pages.settings.security.twoFactorEnable" }}</template>
|
||||||
<template #description>{{ i18n "pages.settings.security.twoFactorEnableDesc" }}</template>
|
<template #description>{{ i18n "pages.settings.security.twoFactorEnableDesc" }}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-switch @click="toggleTwoFactor" :checked="allSetting.twoFactorEnable"></a-switch>
|
<a-switch :checked="allSetting.twoFactorEnable" @click.prevent="toggleTwoFactor(!allSetting.twoFactorEnable)"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
<template #title>{{ i18n "pages.settings.subPort"}}</template>
|
<template #title>{{ i18n "pages.settings.subPort"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subPortDesc"}}</template>
|
<template #description>{{ i18n "pages.settings.subPortDesc"}}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input-number v-model="allSetting.subPort" :min="1" :min="65535"
|
<a-input-number v-model="allSetting.subPort" :min="1" :max="65535"
|
||||||
:style="{ width: '100%' }"></a-input-number>
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
<template #title>Packet</template>
|
<template #title>Packet</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" :value="noise.packet"
|
<a-input type="text" :value="noise.packet"
|
||||||
@input="(value) => updateNoisePacket(index, event.target.value)"
|
@input="(e) => updateNoisePacket(index, e.target.value)"
|
||||||
placeholder="5-10"></a-input>
|
placeholder="5-10"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
|
@ -96,7 +96,7 @@
|
||||||
<template #title>Delay (ms)</template>
|
<template #title>Delay (ms)</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input type="text" :value="noise.delay"
|
<a-input type="text" :value="noise.delay"
|
||||||
@input="(value) => updateNoiseDelay(index, event.target.value)"
|
@input="(e) => updateNoiseDelay(index, e.target.value)"
|
||||||
placeholder="10-20"></a-input>
|
placeholder="10-20"></a-input>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
<template #title>{{ i18n "pages.settings.tgNotifyCpu" }}</template>
|
<template #title>{{ i18n "pages.settings.tgNotifyCpu" }}</template>
|
||||||
<template #description>{{ i18n "pages.settings.tgNotifyCpuDesc" }}</template>
|
<template #description>{{ i18n "pages.settings.tgNotifyCpuDesc" }}</template>
|
||||||
<template #control>
|
<template #control>
|
||||||
<a-input-number :min="0" :min="100" v-model="allSetting.tgCpu" :style="{ width: '100%' }"></a-switch>
|
<a-input-number :min="0" :max="100" v-model="allSetting.tgCpu" :style="{ width: '100%' }"></a-input-number>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue