Explorar el Código

Code refactoring (#2865)

* refactor: use vue inline styles in entire application

* refactor: setting row in dashboard page

* refactor: use blob for download file in text modal

* refactor: move all html templates in `web/html` folder

* refactor: `DeviceUtils` -> `MediaQueryMixin`
The transition to mixins has been made, as they can update themselves.

* chore: pretty right buttons in `outbounds` tab in xray settings

* refactor: add translations for system status

* refactor: adjust gutter spacing in setting list item

* refactor: use native `a-input-password` for password field

* chore: return old system status
with new translations

* chore: add missing translation
Shishkevich D. hace 1 día
padre
commit
bea19a263d
Se han modificado 76 ficheros con 570 adiciones y 473 borrados
  1. 38 25
      web/assets/js/util/index.js
  2. 26 26
      web/html/component/aClientTable.html
  3. 0 0
      web/html/component/aCustomStatistic.html
  4. 1 1
      web/html/component/aPersianDatepicker.html
  5. 1 1
      web/html/component/aSettingListItem.html
  6. 0 0
      web/html/component/aSidebar.html
  7. 1 1
      web/html/component/aTableSortable.html
  8. 2 2
      web/html/component/aThemeSwitch.html
  9. 0 0
      web/html/form/allocate.html
  10. 2 2
      web/html/form/client.html
  11. 1 1
      web/html/form/inbound.html
  12. 9 9
      web/html/form/outbound.html
  13. 0 0
      web/html/form/protocol/dokodemo.html
  14. 5 5
      web/html/form/protocol/http.html
  15. 1 1
      web/html/form/protocol/shadowsocks.html
  16. 5 5
      web/html/form/protocol/socks.html
  17. 1 1
      web/html/form/protocol/trojan.html
  18. 2 2
      web/html/form/protocol/vless.html
  19. 0 0
      web/html/form/protocol/vmess.html
  20. 2 2
      web/html/form/protocol/wireguard.html
  21. 1 1
      web/html/form/reality_settings.html
  22. 0 0
      web/html/form/sniffing.html
  23. 7 7
      web/html/form/stream/external_proxy.html
  24. 0 0
      web/html/form/stream/stream_grpc.html
  25. 3 3
      web/html/form/stream/stream_httpupgrade.html
  26. 1 1
      web/html/form/stream/stream_kcp.html
  27. 1 1
      web/html/form/stream/stream_settings.html
  28. 4 4
      web/html/form/stream/stream_sockopt.html
  29. 8 8
      web/html/form/stream/stream_tcp.html
  30. 3 3
      web/html/form/stream/stream_ws.html
  31. 4 4
      web/html/form/stream/stream_xhttp.html
  32. 7 7
      web/html/form/tls_settings.html
  33. 36 41
      web/html/inbounds.html
  34. 67 58
      web/html/index.html
  35. 11 10
      web/html/login.html
  36. 1 1
      web/html/modals/client_bulk_modal.html
  37. 1 1
      web/html/modals/client_modal.html
  38. 1 1
      web/html/modals/dns_modal.html
  39. 0 0
      web/html/modals/fakedns_modal.html
  40. 8 8
      web/html/modals/inbound_info_modal.html
  41. 0 0
      web/html/modals/inbound_modal.html
  42. 0 0
      web/html/modals/prompt_modal.html
  43. 2 1
      web/html/modals/qrcode_modal.html
  44. 21 11
      web/html/modals/text_modal.html
  45. 9 9
      web/html/modals/warp_modal.html
  46. 0 0
      web/html/modals/xray_balancer_modal.html
  47. 1 1
      web/html/modals/xray_outbound_modal.html
  48. 0 0
      web/html/modals/xray_reverse_modal.html
  49. 4 4
      web/html/modals/xray_rule_modal.html
  50. 13 11
      web/html/settings.html
  51. 10 10
      web/html/settings/panel/general.html
  52. 5 5
      web/html/settings/panel/security.html
  53. 2 2
      web/html/settings/panel/subscription/general.html
  54. 12 12
      web/html/settings/panel/subscription/json.html
  55. 2 2
      web/html/settings/panel/telegram.html
  56. 2 2
      web/html/settings/xray/advanced.html
  57. 10 10
      web/html/settings/xray/balancers.html
  58. 27 27
      web/html/settings/xray/basics.html
  59. 9 9
      web/html/settings/xray/dns.html
  60. 18 16
      web/html/settings/xray/outbounds.html
  61. 4 4
      web/html/settings/xray/reverse.html
  62. 4 4
      web/html/settings/xray/routing.html
  63. 14 14
      web/html/xray.html
  64. 0 57
      web/html/xui/component/aPasswordInput.html
  65. 11 1
      web/translation/translate.en_US.toml
  66. 12 2
      web/translation/translate.es_ES.toml
  67. 12 2
      web/translation/translate.fa_IR.toml
  68. 12 1
      web/translation/translate.id_ID.toml
  69. 12 2
      web/translation/translate.ja_JP.toml
  70. 12 2
      web/translation/translate.pt_BR.toml
  71. 12 2
      web/translation/translate.ru_RU.toml
  72. 11 1
      web/translation/translate.tr_TR.toml
  73. 12 2
      web/translation/translate.uk_UA.toml
  74. 12 2
      web/translation/translate.vi_VN.toml
  75. 11 1
      web/translation/translate.zh_CN.toml
  76. 11 1
      web/translation/translate.zh_TW.toml

+ 38 - 25
web/assets/js/util/index.js

@@ -83,7 +83,7 @@ class PromiseUtil {
 class RandomUtil {
     static getSeq({ type = "default", hasNumbers = true, hasLowercase = true, hasUppercase = true } = {}) {
         let seq = '';
-        
+
         switch (type) {
             case "hex":
                 seq += "0123456789abcdef";
@@ -488,7 +488,7 @@ class ClipboardManager {
         return new Promise((resolve) => {
             try {
                 const textarea = window.document.createElement('textarea');
-    
+
                 textarea.style.fontSize = '12pt';
                 textarea.style.border = '0';
                 textarea.style.padding = '0';
@@ -498,14 +498,14 @@ class ClipboardManager {
                 textarea.style.top = `${window.pageYOffset || document.documentElement.scrollTop}px`;
                 textarea.setAttribute('readonly', '');
                 textarea.value = content;
-    
+
                 window.document.body.appendChild(textarea);
-    
+
                 textarea.select();
                 window.document.execCommand("copy");
-    
+
                 window.document.body.removeChild(textarea);
-    
+
                 resolve(true)
             } catch {
                 resolve(false)
@@ -558,7 +558,7 @@ class CPUFormatter {
     static cpuSpeedFormat(speed) {
         return speed > 1000 ? (speed / 1000).toFixed(2) + " GHz" : speed.toFixed(2) + " MHz";
     }
-    
+
     static cpuCoreFormat(cores) {
         return cores === 1 ? "1 Core" : cores + " Cores";
     }
@@ -579,7 +579,7 @@ class NumberFormatter {
     static addZero(num) {
         return num < 10 ? "0" + num : num;
     }
-    
+
     static toFixed(num, n) {
         n = Math.pow(10, n);
         return Math.floor(num * n) / n;
@@ -610,7 +610,7 @@ class CookieManager {
         }
         return '';
     }
-    
+
     static setCookie(cname, cvalue, exdays) {
         const d = new Date();
         d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
@@ -630,7 +630,7 @@ class ColorUtils {
             default: return "red";
         }
     }
-    
+
     static clientUsageColor(clientStats, trafficDiff) {
         switch (true) {
             case !clientStats || clientStats.total == 0: return "#7a316f";
@@ -639,7 +639,7 @@ class ColorUtils {
             default: return "#cf3c3c";
         }
     }
-    
+
     static userExpiryColor(threshold, client, isDark = false) {
         if (!client.enable) return isDark ? '#2c3950' : '#bcbcbc';
         let now = new Date().getTime(), expiry = client.expiryTime;
@@ -665,7 +665,7 @@ class URLBuilder {
         if (!host || host.length === 0) host = window.location.hostname;
         if (!port || port.length === 0) port = window.location.port;
         if (isTLS === undefined) isTLS = window.location.protocol === "https:";
-        
+
         const protocol = isTLS ? "https:" : "http:";
         port = String(port);
         if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) {
@@ -673,7 +673,7 @@ class URLBuilder {
         } else {
             port = `:${port}`;
         }
-        
+
         return `${protocol}//${host}${port}${base}${path}`;
     }
 }
@@ -744,11 +744,11 @@ class LanguageManager {
 
     static getLanguage() {
         let lang = CookieManager.getCookie("lang");
-    
+
         if (!lang) {
             if (window.navigator) {
                 lang = window.navigator.language || window.navigator.userLanguage;
-    
+
                 if (LanguageManager.isSupportLanguage(lang)) {
                     CookieManager.setCookie("lang", lang, 150);
                 } else {
@@ -760,30 +760,43 @@ class LanguageManager {
                 window.location.reload();
             }
         }
-    
+
         return lang;
     }
-    
+
     static setLanguage(language) {
         if (!LanguageManager.isSupportLanguage(language)) {
             language = "en-US";
         }
-    
+
         CookieManager.setCookie("lang", language, 150);
         window.location.reload();
     }
-    
+
     static isSupportLanguage(language) {
         const languageFilter = LanguageManager.supportedLanguages.filter((lang) => {
             return lang.value === language
         })
-    
+
         return languageFilter.length > 0;
-    }    
+    }
 }
 
-class DeviceUtils {
-    static isMobile() {
-        return window.innerWidth <= 768;
-    }
+const MediaQueryMixin = {
+    data() {
+        return {
+            isMobile: window.innerWidth <= 768,
+        };
+    },
+    methods: {
+        updateDeviceType() {
+            this.isMobile = window.innerWidth <= 768;
+        },
+    },
+    mounted() {
+        window.addEventListener('resize', this.updateDeviceType);
+    },
+    beforeDestroy() {
+        window.removeEventListener('resize', this.updateDeviceType);
+    },
 }

+ 26 - 26
web/html/xui/component/aClientTable.html → web/html/component/aClientTable.html

@@ -2,30 +2,30 @@
 <template slot="actions" slot-scope="text, client, index">
   <a-tooltip>
     <template slot="title">{{ i18n "qrCode" }}</template>
-    <a-icon style="font-size: 24px;" class="normal-icon" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
+    <a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
   </a-tooltip>
   <a-tooltip>
     <template slot="title">{{ i18n "pages.client.edit" }}</template>
-    <a-icon style="font-size: 24px;" class="normal-icon" type="edit" @click="openEditClient(record.id,client);"></a-icon>
+    <a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="edit" @click="openEditClient(record.id,client);"></a-icon>
   </a-tooltip>
   <a-tooltip>
     <template slot="title">{{ i18n "info" }}</template>
-    <a-icon style="font-size: 24px;" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
+    <a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
   </a-tooltip>
   <a-tooltip>
     <template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
     <a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
-      <a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: var(--color-primary-100)' : 'color: var(--color-primary-100)'"></a-icon>
-      <a-icon style="font-size: 24px; cursor: pointer;" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
+      <a-icon slot="icon" type="question-circle-o" :style="{ color: 'var(--color-primary-100)'}"></a-icon>
+      <a-icon :style="{ fontSize: '24px', cursor: 'pointer' }" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
     </a-popconfirm>
   </a-tooltip>
   <a-tooltip>
     <template slot="title">
-      <span style="color: #FF4D4F"> {{ i18n "delete"}}</span>
+      <span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
     </template>
     <a-popconfirm @confirm="delClient(record.id,client,false)" title='{{ i18n "pages.inbounds.deleteClientContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "delete"}}' ok-type="danger" cancel-text='{{ i18n "cancel"}}'>
-      <a-icon slot="icon" type="question-circle-o" style="color: #e04141"></a-icon>
-      <a-icon style="font-size: 24px; cursor: pointer;" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
+      <a-icon slot="icon" type="question-circle-o" :style="{ color: '#e04141' }"></a-icon>
+      <a-icon :style="{ fontSize: '24px', cursor: 'pointer' }" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
     </a-popconfirm>
   </a-tooltip>
 </template>
@@ -124,9 +124,9 @@
           </template>
         </span>
       </template>
-      <a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
+      <a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
     </a-popover>
-    <a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" style="border: none;" class="infinite-tag">
+    <a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" :style="{ border: 'none' }" class="infinite-tag">
       <svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
         <path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
       </svg>
@@ -135,27 +135,27 @@
 </template>
 <template slot="actionMenu" slot-scope="text, client, index">
   <a-dropdown :trigger="['click']">
-    <a-icon @click="e => e.preventDefault()" type="ellipsis" style="font-size: 20px;"></a-icon>
+    <a-icon @click="e => e.preventDefault()" type="ellipsis" :style="{ fontSize: '20px' }"></a-icon>
     <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
       <a-menu-item v-if="record.hasLink()" @click="showQrcode(record.id,client);">
-        <a-icon style="font-size: 14px;" type="qrcode"></a-icon>
+        <a-icon :style="{ fontSize: '14px' }" type="qrcode"></a-icon>
         {{ i18n "qrCode" }}
       </a-menu-item>
       <a-menu-item @click="openEditClient(record.id,client);">
-        <a-icon style="font-size: 14px;" type="edit"></a-icon>
+        <a-icon :style="{ fontSize: '14px' }" type="edit"></a-icon>
         {{ i18n "pages.client.edit" }}
       </a-menu-item>
       <a-menu-item @click="showInfo(record.id,client);">
-        <a-icon style="font-size: 14px;" type="info-circle"></a-icon>
+        <a-icon :style="{ fontSize: '14px' }" type="info-circle"></a-icon>
         {{ i18n "info" }}
       </a-menu-item>
       <a-menu-item @click="resetClientTraffic(client,record.id)" v-if="client.email.length > 0">
-        <a-icon style="font-size: 14px;" type="retweet"></a-icon>
+        <a-icon :style="{ fontSize: '14px' }" type="retweet"></a-icon>
         {{ i18n "pages.inbounds.resetTraffic" }}
       </a-menu-item>
       <a-menu-item v-if="isRemovable(record.id)" @click="delClient(record.id,client)">
-        <a-icon style="font-size: 14px;" type="delete"></a-icon>
-        <span style="color: #FF4D4F"> {{ i18n "delete"}}</span>
+        <a-icon :style="{ fontSize: '14px' }" type="delete"></a-icon>
+        <span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
       </a-menu-item>
       <a-menu-item>
         <a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id,client)"></a-switch>
@@ -169,10 +169,10 @@
     <template slot="content">
       <table>
         <tr>
-          <td colspan="3" style="text-align: center;">{{ i18n "pages.inbounds.traffic" }}</td>
+          <td colspan="3" :style="{ textAlign: 'center' }">{{ i18n "pages.inbounds.traffic" }}</td>
         </tr>
         <tr>
-          <td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
+          <td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
           <td width="120px" v-if="!client.enable">
             <a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
           </td>
@@ -202,14 +202,14 @@
           </td>
         </tr>
         <tr>
-          <td colspan="3" style="text-align: center;">
-            <a-divider style="margin: 0; border-collapse: separate;"></a-divider>
+          <td colspan="3" :style="{ textAlign: 'center' }">
+            <a-divider :style="{ margin: '0', borderCollapse: 'separate' }"></a-divider>
             {{ i18n "pages.inbounds.expireDate" }}
           </td>
         </tr>
         <tr>
           <template v-if="client.expiryTime !=0 && client.reset >0">
-            <td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ remainedDays(client.expiryTime) ]] </td>
+            <td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ remainedDays(client.expiryTime) ]] </td>
             <td width="120px" class="infinite-bar">
               <a-popover :overlay-class-name="themeSwitcher.currentTheme">
                 <template slot="content">
@@ -230,7 +230,7 @@
             <td width="60px">[[ client.reset + "d" ]]</td>
           </template>
           <template v-else>
-            <td colspan="3" style="text-align: center;">
+            <td colspan="3" :style="{ textAlign: 'center' }">
               <a-popover v-if="client.expiryTime != 0" :overlay-class-name="themeSwitcher.currentTheme">
                 <template slot="content">
                   <span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
@@ -244,7 +244,7 @@
                     </template>
                   </span>
                 </template>
-                <a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
+                <a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
               </a-popover>
               <a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
                 <svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
@@ -257,8 +257,8 @@
       </table>
     </template>
     <a-badge>
-      <a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled" :style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon>
-      <a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
+      <a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled" :style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
+      <a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
         <a-icon type="solution"></a-icon>
       </a-button>
     </a-badge>

+ 0 - 0
web/html/xui/component/aCustomStatistic.html → web/html/component/aCustomStatistic.html


+ 1 - 1
web/html/xui/component/aPersianDatepicker.html → web/html/component/aPersianDatepicker.html

@@ -5,7 +5,7 @@
             @input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
             :placeholder="placeholder">
             <template #addonAfter>
-                <a-icon type="calendar" style="font-size: 14px; opacity: 0.5;" />
+                <a-icon type="calendar" :style="{ fontSize: '14px', opacity: '0.5' }" />
             </template>
         </a-input>
     </div>

+ 1 - 1
web/html/xui/component/aSettingListItem.html → web/html/component/aSettingListItem.html

@@ -1,6 +1,6 @@
 {{define "component/settingListItem"}}
 <a-list-item :style="{ padding: padding }">
-    <a-row>
+    <a-row :gutter="[8,16]">
         <a-col :lg="24" :xl="12">
             <a-list-item-meta>
                 <template #title>

+ 0 - 0
web/html/xui/component/aSidebar.html → web/html/component/aSidebar.html


+ 1 - 1
web/html/xui/component/aTableSortable.html → web/html/component/aTableSortable.html

@@ -1,5 +1,5 @@
 {{define "component/sortableTableTrigger"}}
-<a-icon type="drag" class="sortable-icon" style="cursor: move;" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
+<a-icon type="drag" class="sortable-icon" :style="{ cursor: 'move' }" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
   @click="clickHandler" />
 {{end}}
 

+ 2 - 2
web/html/xui/component/aThemeSwitch.html → web/html/component/aThemeSwitch.html

@@ -8,13 +8,13 @@
       </span>
       <a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
         <span>{{ i18n "menu.dark" }}</span>
-        <a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme"
+        <a-switch :style="{ marginLeft: '2px' }" size="small" :default-checked="themeSwitcher.isDarkTheme"
           @change="themeSwitcher.toggleTheme()"></a-switch>
       </a-menu-item>
       <a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
         @mousedown="themeSwitcher.animationsOffUltra()">
         <span>{{ i18n "menu.ultraDark" }}</span>
-        <a-checkbox style="margin-left: 2px;" :checked="themeSwitcher.isUltra"
+        <a-checkbox :style="{ marginLeft: '2px' }" :checked="themeSwitcher.isUltra"
           @click="themeSwitcher.toggleUltra()"></a-checkbox>
       </a-menu-item>
     </a-sub-menu>

+ 0 - 0
web/html/xui/form/allocate.html → web/html/form/allocate.html


+ 2 - 2
web/html/xui/form/client.html → web/html/form/client.html

@@ -66,7 +66,7 @@
                 <a-icon type="question-circle"></a-icon>
             </a-tooltip>
         </template>
-        <a-input-number style="width: 50%" v-model.number="client.tgId" min="0"></a-input-number>
+        <a-input-number :style="{ width: '50%' }" v-model.number="client.tgId" min="0"></a-input-number>
     </a-form-item>
     <a-form-item v-if="client.email" label='{{ i18n "comment" }}'>
         <a-input v-model.trim="client.comment"></a-input>
@@ -97,7 +97,7 @@
             <template slot="title">
                 <span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
             </template>
-            <span style="color: #FF4D4F">
+            <span :style="{ color: '#FF4D4F' }">
                 <a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
             </span>
         </a-tooltip>

+ 1 - 1
web/html/xui/form/inbound.html → web/html/form/inbound.html

@@ -54,7 +54,7 @@
                 <a-icon type="question-circle"></a-icon>
             </a-tooltip>
         </template>
-        <a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
+        <a-date-picker :style="{ width: '100%' }" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
             format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
             v-model="dbInbound._expiryTime"></a-date-picker>
         <a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'

+ 9 - 9
web/html/xui/form/outbound.html → web/html/form/outbound.html

@@ -1,6 +1,6 @@
 {{define "form/outbound"}}
 <!-- base -->
-<a-tabs :active-key="outModal.activeKey" style="padding: 0; background-color: transparent;" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
+<a-tabs :active-key="outModal.activeKey" :style="{ padding: '0', backgroundColor: 'transparent' }" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
   <a-tab-pane key="1" tab="Form">
     <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
       <a-form-item label='{{ i18n "protocol" }}'>
@@ -60,9 +60,9 @@
         <!-- Noise Configurations -->
           <a-form v-for="(noise, index) in outbound.settings.noises" :key="index" :colon="false" :label-col="{ md: {span:8} }"
             :wrapper-col="{ md: {span:14} }">
-            <a-divider style="margin:0;"> Noise [[ index + 1 ]]
+            <a-divider :style="{ margin: '0' }"> Noise [[ index + 1 ]]
               <a-icon v-if="outbound.settings.noises.length > 1" type="delete" @click="() => outbound.settings.delNoise(index)"
-                style="color: rgb(255, 77, 79); cursor: pointer;"></a-icon>
+                :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
             </a-divider>
             <a-form-item label='Type'>
               <a-select v-model="noise.type" :dropdown-class-name="themeSwitcher.currentTheme">
@@ -164,7 +164,7 @@
           <a-button icon="plus" type="primary" size="small" @click="outbound.settings.addPeer()"></a-button>
         </a-form-item>
         <a-form v-for="(peer, index) in outbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-          <a-divider style="margin:0;"> Peer [[ index + 1 ]] <a-icon v-if="outbound.settings.peers.length>1" type="delete" @click="() => outbound.settings.delPeer(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
+          <a-divider :style="{ margin: '0' }"> Peer [[ index + 1 ]] <a-icon v-if="outbound.settings.peers.length>1" type="delete" @click="() => outbound.settings.delPeer(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
           </a-divider>
           <a-form-item label='{{ i18n "pages.xray.wireguard.endpoint" }}'>
             <a-input v-model.trim="peer.endpoint"></a-input>
@@ -180,7 +180,7 @@
               {{ i18n "pages.xray.wireguard.allowedIPs" }}
               <a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
             </template>
-            <template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
+            <template v-for="(aip, index) in peer.allowedIPs" :style="{ marginBottom: '10px' }">
               <a-input v-model.trim="peer.allowedIPs[index]">
                 <a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
               </a-input>
@@ -444,10 +444,10 @@
             </a-select>
           </a-form-item>
           <a-form-item label="Short ID">
-            <a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
+            <a-input v-model.trim="outbound.stream.reality.shortId" :style="{ width: '250px' }"></a-input>
           </a-form-item>
           <a-form-item label="SpiderX">
-            <a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
+            <a-input v-model.trim="outbound.stream.reality.spiderX" :style="{ width: '250px' }"></a-input>
           </a-form-item>
           <a-form-item label="Public Key">
             <a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
@@ -506,11 +506,11 @@
     </a-form>
   </a-tab-pane>
   <a-tab-pane key="2" tab="JSON" force-render="true">
-    <a-space direction="vertical" :size="10" style="margin-top: 10px;">
+    <a-space direction="vertical" :size="10" :style="{ marginTop: '10px' }">
       <a-input addon-before='{{ i18n "pages.xray.outbound.link" }}' v-model.trim="outModal.link" placeholder="vmess:// vless:// trojan:// ss://">
         <a-icon slot="addonAfter" type="form" @click="convertLink"></a-icon>
       </a-input>
-      <textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
+      <textarea :style="{ position: 'absolute', left: '-800px' }" id="outboundJson"></textarea>
     </a-space>
   </a-tab-pane>
 </a-tabs>

+ 0 - 0
web/html/xui/form/protocol/dokodemo.html → web/html/form/protocol/dokodemo.html


+ 5 - 5
web/html/xui/form/protocol/http.html → web/html/form/protocol/http.html

@@ -1,6 +1,6 @@
 {{define "form/http"}}
 <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-  <table style="width: 100%; text-align: center; margin: 1rem 0;">
+  <table :style="{ width: '100%', textAlign: 'center', margin: '1rem 0' }">
     <tr>
       <td width="45%">{{ i18n "username" }}</td>
       <td width="45%">{{ i18n "password" }}</td>
@@ -9,11 +9,11 @@
       </td>
     </tr>
   </table>
-  <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
-    <a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
-      <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+  <a-input-group compact v-for="(account, index) in inbound.settings.accounts" :style="{ marginBottom: '10px' }">
+    <a-input :style="{ width: '50%' }" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
+      <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
     </a-input>
-    <a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
+    <a-input :style="{ width: '50%' }" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
       <template slot="addonAfter">
         <a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
       </template>

+ 1 - 1
web/html/xui/form/protocol/shadowsocks.html → web/html/form/protocol/shadowsocks.html

@@ -37,7 +37,7 @@
         <a-input v-model.trim="inbound.settings.password"></a-input>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.inbounds.network" }}'>
-        <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
+        <a-select v-model="inbound.settings.network" :style="{ width: '100px' }" :dropdown-class-name="themeSwitcher.currentTheme">
             <a-select-option value="tcp,udp">TCP,UDP</a-select-option>
             <a-select-option value="tcp">TCP</a-select-option>
             <a-select-option value="udp">UDP</a-select-option>

+ 5 - 5
web/html/xui/form/protocol/socks.html → web/html/form/protocol/socks.html

@@ -10,7 +10,7 @@
     <a-switch :checked="inbound.settings.auth === 'password'" @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
   </a-form-item>
   <template v-if="inbound.settings.auth === 'password'">
-    <table style="width: 100%; text-align: center; margin: 1rem 0;">
+    <table :style="{ width: '100%', textAlign: 'center', margin: '1rem 0' }">
       <tr>
         <td width="45%">{{ i18n "username" }}</td>
         <td width="45%">{{ i18n "password" }}</td>
@@ -19,11 +19,11 @@
         </td>
       </tr>
     </table>
-    <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
-      <a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
-        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+    <a-input-group compact v-for="(account, index) in inbound.settings.accounts" :style="{ marginBottom: '10px' }">
+      <a-input :style="{ width: '50%' }" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
+        <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
       </a-input>
-      <a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
+      <a-input :style="{ width: '50%' }" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
         <template slot="addonAfter">
           <a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
         </template>

+ 1 - 1
web/html/xui/form/protocol/trojan.html → web/html/form/protocol/trojan.html

@@ -27,7 +27,7 @@
 
   <!-- trojan fallbacks -->
   <a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-    <a-divider style="margin:0;"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
+    <a-divider :style="{ margin: '0' }"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
     </a-divider>
     <a-form-item label='SNI'>
       <a-input v-model="fallback.name"></a-input>

+ 2 - 2
web/html/xui/form/protocol/vless.html → web/html/form/protocol/vless.html

@@ -27,7 +27,7 @@
 
   <!-- vless fallbacks -->
   <a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-    <a-divider style="margin:0;"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
+    <a-divider :style="{ margin: '0' }"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
     </a-divider>
     <a-form-item label='SNI'>
       <a-input v-model="fallback.name"></a-input>
@@ -45,6 +45,6 @@
       <a-input-number v-model.number="fallback.xver" :min="0" :max="2"></a-input-number>
     </a-form-item>
   </a-form>
-  <a-divider style="margin:5px 0;"></a-divider>
+  <a-divider :style="{ margin: '5px 0' }"></a-divider>
 </template>
 {{end}}

+ 0 - 0
web/html/xui/form/protocol/vmess.html → web/html/form/protocol/vmess.html


+ 2 - 2
web/html/xui/form/protocol/wireguard.html → web/html/form/protocol/wireguard.html

@@ -25,7 +25,7 @@
     <a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>
   </a-form-item>
   <a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-    <a-divider style="margin:0;"> Peer [[ index + 1 ]] <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
+    <a-divider :style="{ margin: '0' }"> Peer [[ index + 1 ]] <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
     </a-divider>
     <a-form-item>
       <template slot="label">
@@ -62,7 +62,7 @@
         {{ i18n "pages.xray.wireguard.allowedIPs" }}
         <a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
       </template>
-      <template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
+      <template v-for="(aip, index) in peer.allowedIPs" :style="{ marginBottom: '10px' }">
         <a-input v-model.trim="peer.allowedIPs[index]">
           <a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
         </a-input>

+ 1 - 1
web/html/xui/form/reality_settings.html → web/html/form/reality_settings.html

@@ -7,7 +7,7 @@
         <a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
     </a-form-item>
     <a-form-item label='uTLS'>
-        <a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 100%"
+        <a-select v-model="inbound.stream.reality.settings.fingerprint" :style="{ width: '100%' }"
             :dropdown-class-name="themeSwitcher.currentTheme">
             <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
         </a-select>

+ 0 - 0
web/html/xui/form/sniffing.html → web/html/form/sniffing.html


+ 7 - 7
web/html/xui/form/stream/external_proxy.html → web/html/form/stream/external_proxy.html

@@ -1,25 +1,25 @@
 {{define "form/externalProxy"}}
 <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-  <a-divider style="margin:5px 0 0;"></a-divider>
+  <a-divider :style="{ margin: '5px 0 0' }"></a-divider>
   <a-form-item label="External Proxy">
     <a-switch v-model="externalProxy"></a-switch>
-    <a-button icon="plus" v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"></a-button>
+    <a-button icon="plus" v-if="externalProxy" type="primary" :style="{ marginLeft: '10px' }" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"></a-button>
   </a-form-item>
-  <a-input-group style="margin: 8px 0;" compact v-for="(row, index) in inbound.stream.externalProxy">
+  <a-input-group :style="{ margin: '8px 0' }" compact v-for="(row, index) in inbound.stream.externalProxy">
     <template>
       <a-tooltip title="Force TLS">
-        <a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
+        <a-select v-model="row.forceTls" :style="{ width: '20%', margin: '0px' }" :dropdown-class-name="themeSwitcher.currentTheme">
           <a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
           <a-select-option value="none">{{ i18n "none" }}</a-select-option>
           <a-select-option value="tls">TLS</a-select-option>
         </a-select>
       </a-tooltip>
     </template>
-    <a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
+    <a-input :style="{ width: '35%' }" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
     <a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
-      <a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
+      <a-input-number :style="{ width: '15%' }" v-model.number="row.port" min="1" max="65531"></a-input-number>
     </a-tooltip>
-    <a-input style="width: 30%; top: 0;" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
+    <a-input :style="{ width: '50%', top: '0' }" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
       <template slot="addonAfter">
         <a-button icon="minus" size="small" @click="inbound.stream.externalProxy.splice(index, 1)"></a-button>
       </template>

+ 0 - 0
web/html/xui/form/stream/stream_grpc.html → web/html/form/stream/stream_grpc.html


+ 3 - 3
web/html/xui/form/stream/stream_httpupgrade.html → web/html/form/stream/stream_httpupgrade.html

@@ -14,10 +14,10 @@
   </a-form-item>
   <a-form-item :wrapper-col="{span:24}">
     <a-input-group compact v-for="(header, index) in inbound.stream.httpupgrade.headers">
-      <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
-        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
+        <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
       </a-input>
-      <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
         <a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.httpupgrade.removeHeader(index)"></a-button>
       </a-input>
     </a-input-group>

+ 1 - 1
web/html/xui/form/stream/stream_kcp.html → web/html/form/stream/stream_kcp.html

@@ -1,7 +1,7 @@
 {{define "form/streamKCP"}}
 <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
     <a-form-item label='{{ i18n "camouflage" }}'>
-        <a-select v-model="inbound.stream.kcp.type" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
+        <a-select v-model="inbound.stream.kcp.type" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
             <a-select-option value="none">None</a-select-option>
             <a-select-option value="srtp">SRTP</a-select-option>
             <a-select-option value="utp">uTP</a-select-option>

+ 1 - 1
web/html/xui/form/stream/stream_settings.html → web/html/form/stream/stream_settings.html

@@ -2,7 +2,7 @@
 <!-- select stream network -->
 <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
     <a-form-item label='{{ i18n "transmission" }}'>
-        <a-select v-model="inbound.stream.network" style="width: 75%" @change="streamNetworkChange"
+        <a-select v-model="inbound.stream.network" :style="{ width: '75%' }" @change="streamNetworkChange"
             :dropdown-class-name="themeSwitcher.currentTheme">
             <a-select-option value="tcp">TCP (RAW)</a-select-option>
             <a-select-option value="kcp">mKCP</a-select-option>

+ 4 - 4
web/html/xui/form/stream/stream_sockopt.html → web/html/form/stream/stream_sockopt.html

@@ -1,5 +1,5 @@
 {{define "form/streamSockopt"}}
-<a-divider style="margin:5px 0 0;"></a-divider>
+<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
 <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
     <a-form-item label="Sockopt">
         <a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
@@ -39,17 +39,17 @@
             <a-switch v-model.trim="inbound.stream.sockopt.V6Only"></a-switch>
         </a-form-item>
         <a-form-item label='Domain Strategy'>
-            <a-select v-model="inbound.stream.sockopt.domainStrategy" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
+            <a-select v-model="inbound.stream.sockopt.domainStrategy" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
               <a-select-option v-for="key in DOMAIN_STRATEGY_OPTION" :value="key">[[ key ]]</a-select-option>
             </a-select>
         </a-form-item>
         <a-form-item label='TCP Congestion'>
-            <a-select v-model="inbound.stream.sockopt.tcpcongestion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
+            <a-select v-model="inbound.stream.sockopt.tcpcongestion" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
               <a-select-option v-for="key in TCP_CONGESTION_OPTION" :value="key">[[ key ]]</a-select-option>
             </a-select>
         </a-form-item>
         <a-form-item label="TProxy">
-            <a-select v-model="inbound.stream.sockopt.tproxy" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
+            <a-select v-model="inbound.stream.sockopt.tproxy" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
                 <a-select-option value="off">Off</a-select-option>
                 <a-select-option value="redirect">Redirect</a-select-option>
                 <a-select-option value="tproxy">TProxy</a-select-option>

+ 8 - 8
web/html/xui/form/stream/stream_tcp.html → web/html/form/stream/stream_tcp.html

@@ -11,7 +11,7 @@
 
 <a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
   <!-- tcp request -->
-  <a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
+  <a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
   <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
     <a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
   </a-form-item>
@@ -33,17 +33,17 @@
   </a-form-item>
   <a-form-item :wrapper-col="{span:24}">
     <a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
-      <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
-        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
+        <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
       </a-input>
-      <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
         <a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)"></a-button>
       </a-input>
     </a-input-group>
   </a-form-item>
 
   <!-- tcp response -->
-  <a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
+  <a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
   <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
     <a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
   </a-form-item>
@@ -58,10 +58,10 @@
   </a-form-item>
   <a-form-item :wrapper-col="{span:24}">
     <a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
-      <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
-        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
+        <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
       </a-input>
-      <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
         <template slot="addonAfter">
           <a-button icon="minus" size="small" @click="inbound.stream.tcp.response.removeHeader(index)"></a-button>
         </template>

+ 3 - 3
web/html/xui/form/stream/stream_ws.html → web/html/form/stream/stream_ws.html

@@ -17,10 +17,10 @@
   </a-form-item>
   <a-form-item :wrapper-col="{span:24}">
     <a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
-      <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
-        <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
+        <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
       </a-input>
-      <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+      <a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
         <a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.ws.removeHeader(index)"></a-button>
       </a-input>
     </a-input-group>

+ 4 - 4
web/html/xui/form/stream/stream_xhttp.html → web/html/form/stream/stream_xhttp.html

@@ -11,18 +11,18 @@
     </a-form-item>
     <a-form-item :wrapper-col="{span:24}">
         <a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
-            <a-input style="width: 50%" v-model.trim="header.name"
+            <a-input :style="{ width: '50%' }" v-model.trim="header.name"
                 placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
-                <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+                <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
             </a-input>
-            <a-input style="width: 50%" v-model.trim="header.value"
+            <a-input :style="{ width: '50%' }" v-model.trim="header.value"
                 placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
                 <a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.xhttp.removeHeader(index)"></a-button>
             </a-input>
         </a-input-group>
     </a-form-item>
     <a-form-item label='Mode'>
-        <a-select v-model="inbound.stream.xhttp.mode" style="width: 50%"
+        <a-select v-model="inbound.stream.xhttp.mode" :style="{ width: '50%' }"
             :dropdown-class-name="themeSwitcher.currentTheme">
             <a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>

+ 7 - 7
web/html/xui/form/tls_settings.html → web/html/form/tls_settings.html

@@ -1,7 +1,7 @@
 {{define "form/tlsSettings"}}
 <!-- tls enable -->
 <a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
-  <a-divider style="margin:3px 0;"></a-divider>
+  <a-divider :style="{ margin: '3px 0' }"></a-divider>
   <a-form-item label='{{ i18n "security" }}'>
     <a-radio-group v-model="inbound.stream.security" button-style="solid">
       <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
@@ -23,18 +23,18 @@
     </a-form-item>
     <a-form-item label="Min/Max Version">
       <a-input-group compact>
-        <a-select v-model="inbound.stream.tls.minVersion" style="width: 50%"
+        <a-select v-model="inbound.stream.tls.minVersion" :style="{ width: '50%' }"
           :dropdown-class-name="themeSwitcher.currentTheme">
           <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
-        <a-select v-model="inbound.stream.tls.maxVersion" style="width: 50%"
+        <a-select v-model="inbound.stream.tls.maxVersion" :style="{ width: '50%' }"
           :dropdown-class-name="themeSwitcher.currentTheme">
           <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
       </a-input-group>
     </a-form-item>
     <a-form-item label="uTLS">
-      <a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 100%"
+      <a-select v-model="inbound.stream.tls.settings.fingerprint" :style="{ width: '100%' }"
         :dropdown-class-name="themeSwitcher.currentTheme">
         <a-select-option value=''>None</a-select-option>
         <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
@@ -67,9 +67,9 @@
           <a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
         </a-radio-group>
         <a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
-          style="margin-left: 10px"></a-button>
+          :style="{ marginLeft: '10px' }"></a-button>
         <a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
-          @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px"></a-button>
+          @click="inbound.stream.tls.removeCert(index)" :style="{ marginLeft: '10px' }"></a-button>
       </a-form-item>
       <template v-if="cert.useFile">
         <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
@@ -98,7 +98,7 @@
         <a-switch v-model="cert.oneTimeLoading"></a-switch>
       </a-form-item>
       <a-form-item label='Usage Option'>
-        <a-select v-model="cert.usage" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
+        <a-select v-model="cert.usage" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
           <a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
         </a-select>
       </a-form-item>

+ 36 - 41
web/html/xui/inbounds.html → web/html/inbounds.html

@@ -138,7 +138,7 @@
     <a-layout-content>
       <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
         <transition name="list" appear>
-          <a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
+          <a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
             message='{{ i18n "secAlertTitle" }}'
             color="red"
             description='{{ i18n "secAlertSsl" }}'
@@ -146,7 +146,7 @@
           </a-alert>
         </transition>
         <transition name="list" appear>
-          <a-card size="small" style="padding: 16px;" hoverable>
+          <a-card size="small" :style="{ padding: '16px' }" hoverable>
             <a-row>
               <a-col :sm="12" :md="6">
                 <a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`">
@@ -241,7 +241,7 @@
                       <a-icon type="file-done"></a-icon>
                       {{ i18n "pages.inbounds.resetAllClientTraffics" }}
                     </a-menu-item>
-                    <a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
+                    <a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
                       <a-icon type="rest"></a-icon>
                       {{ i18n "pages.inbounds.delDepletedClients" }}
                     </a-menu-item>
@@ -264,7 +264,7 @@
                       <span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span>
                       <a-select v-model="refreshInterval"
                           :disabled="!isRefreshEnabled"
-                          style="width: 100%;"
+                          :style="{ width: '100%' }"
                           @change="changeRefreshInterval"
                           :dropdown-class-name="themeSwitcher.currentTheme">
                         <a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
@@ -276,14 +276,14 @@
               </a-button-group>
             </template>
             <a-space direction="vertical">
-              <div :style="isMobile ? '' : 'display: flex; align-items: center; justify-content: flex-start;'">
+              <div :style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }">
                 <a-switch v-model="enableFilter"
-                    :style="isMobile ? 'margin-bottom: .5rem; display: flex;' : 'margin-right: .5rem;'"
+                    :style="isMobile ? { marginBottom: '.5rem', display: 'flex' } : { marginRight: '.5rem' }"
                     @change="toggleFilter">
                   <a-icon slot="checkedChildren" type="search"></a-icon>
                   <a-icon slot="unCheckedChildren" type="filter"></a-icon>
                 </a-switch>
-                <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px" :size="isMobile ? 'small' : ''"></a-input>
+                <a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus :style="{ maxWidth: '300px' }" :size="isMobile ? 'small' : ''"></a-input>
                 <a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''">
                   <a-radio-button value="">{{ i18n "none" }}</a-radio-button>
                   <a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
@@ -301,10 +301,10 @@
                   :expand-icon-column-index="0"
                   :indent-size="0"
                   :row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')"
-                  style="margin-top: 10px">
+                  :style="{ marginTop: '10px' }">
                 <template slot="action" slot-scope="text, dbInbound">
                   <a-dropdown :trigger="['click']">
-                    <a-icon @click="e => e.preventDefault()" type="more" style="font-size: 20px; text-decoration: solid;"></a-icon>
+                    <a-icon @click="e => e.preventDefault()" type="more" :style="{ fontSize: '20px', textDecoration: 'solid' }"></a-icon>
                     <a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme">
                       <a-menu-item key="edit">
                         <a-icon type="edit"></a-icon>
@@ -335,7 +335,7 @@
                           <a-icon type="export"></a-icon>
                           {{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
                         </a-menu-item>
-                        <a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
+                        <a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
                           <a-icon type="rest"></a-icon>
                           {{ i18n "pages.inbounds.delDepletedClients" }}
                         </a-menu-item>
@@ -357,7 +357,7 @@
                         <a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
                       </a-menu-item>
                       <a-menu-item key="delete">
-                        <span style="color: #FF4D4F">
+                        <span :style="{ color: '#FF4D4F' }">
                           <a-icon type="delete"></a-icon> {{ i18n "delete"}}
                         </span>
                       </a-menu-item>
@@ -369,39 +369,39 @@
                   </a-dropdown>
                 </template>
                 <template slot="protocol" slot-scope="text, dbInbound">
-                  <a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag>
+                  <a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
                   <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
-                    <a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
-                    <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
-                    <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
+                    <a-tag :style="{ margin: '0' }" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
+                    <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
+                    <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
                   </template>
                 </template>
                 <template slot="clients" slot-scope="text, dbInbound">
                   <template v-if="clientCount[dbInbound.id]">
-                    <a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
+                    <a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
                     <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
                       <template slot="content">
                         <div v-for="clientEmail in clientCount[dbInbound.id].deactive"><span>[[ clientEmail ]]</span></div>
                       </template>
-                      <a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
+                      <a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
                     </a-popover>
                     <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
                       <template slot="content">
                         <div v-for="clientEmail in clientCount[dbInbound.id].depleted"><span>[[ clientEmail ]]</span></div>
                       </template>
-                      <a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
+                      <a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
                     </a-popover>
                     <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
                       <template slot="content">
                         <div v-for="clientEmail in clientCount[dbInbound.id].expiring"><span>[[ clientEmail ]]</span></div>
                       </template>
-                      <a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
+                      <a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
                     </a-popover>
                     <a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
                       <template slot="content">
                         <div v-for="clientEmail in clientCount[dbInbound.id].online"><span>[[ clientEmail ]]</span></div>
                       </template>
-                      <a-tag style="margin:0; padding: 0 2px;" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
+                      <a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
                     </a-popover>
                   </template>
                 </template>
@@ -443,7 +443,7 @@
                     <template v-else slot="content">
                       [[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
                     </template>
-                    <a-tag style="min-width: 50px;" :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
+                    <a-tag :style="{ minWidth: '50px' }" :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
                       [[ remainedDays(dbInbound._expiryTime) ]]
                     </a-tag>
                   </a-popover>
@@ -460,11 +460,11 @@
                         <tr>
                           <td>{{ i18n "pages.inbounds.protocol" }}</td>
                           <td>
-                            <a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag>
+                            <a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
                             <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
-                              <a-tag style="margin:0;" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag>
-                              <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag>
-                              <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag>
+                              <a-tag :style="{ margin: '0' }" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag>
+                              <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag>
+                              <a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag>
                             </template>
                           </td>
                         </tr>
@@ -475,30 +475,30 @@
                         <tr v-if="clientCount[dbInbound.id]">
                           <td>{{ i18n "clients" }}</td>
                           <td>
-                            <a-tag style="margin:0;" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
+                            <a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
                             <a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
                               <template slot="content">
                                 <p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
                               </template>
-                              <a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
+                              <a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
                             </a-popover>
                             <a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
                               <template slot="content">
                                 <p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
                               </template>
-                              <a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
+                              <a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
                             </a-popover>
                             <a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
                               <template slot="content">
                                 <p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
                               </template>
-                              <a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
+                              <a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
                             </a-popover>
                             <a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
                               <template slot="content">
                                 <p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p>
                               </template>
-                              <a-tag style="margin:0; padding: 0 2px;" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
+                              <a-tag :style="{ margin: '0', padding: '0 2px' }" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
                             </a-popover>
                           </td>
                         </tr>
@@ -535,7 +535,7 @@
                         <tr>
                           <td>{{ i18n "pages.inbounds.expireDate" }}</td>
                           <td>
-                            <a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0"
+                            <a-tag :style="{ minWidth: '50px', textAlign: 'center' }" v-if="dbInbound.expiryTime > 0"
                               :color="dbInbound.isExpiry? 'red': 'blue'">
                               <template v-if="app.datepicker === 'gregorian'">
                                 [[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
@@ -544,7 +544,7 @@
                                 [[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
                               </template>
                             </a-tag>
-                            <a-tag v-else style="text-align: center;" color="purple" class="infinite-tag">
+                            <a-tag v-else :style="{ textAlign: 'center' }" color="purple" class="infinite-tag">
                               <svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
                                 <path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
                               </svg>
@@ -554,8 +554,8 @@
                       </table>
                     </template>
                     <a-badge>
-                      <a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle" :style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon>
-                      <a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
+                      <a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle" :style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
+                      <a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
                         <a-icon type="info"></a-icon>
                       </a-button>
                     </a-badge>
@@ -567,7 +567,7 @@
                     :columns="isMobile ? innerMobileColumns : innerColumns"
                     :data-source="getInboundClients(record)"
                     :pagination=pagination(getInboundClients(record))
-                    :style="isMobile ? 'margin: -10px 2px -11px;' : 'margin: -10px 22px -11px;'">
+                    :style="{ margin: `-10px ${isMobile ? '2px' : '22px'} -11px` }">
                     {{template "component/aClientTable"}}
                   </a-table>
                 </template>
@@ -678,6 +678,7 @@
     const app = new Vue({
         delimiters: ['[[', ']]'],
         el: '#app',
+        mixins: [MediaQueryMixin],
         data: {
             themeSwitcher,
             persianDatepicker,
@@ -709,7 +710,6 @@
             showAlert: false,
             ipLimitEnable: false,
             pageSize: 50,
-            isMobile: DeviceUtils.isMobile(),
         },
         methods: {
             loading(spinning = true) {
@@ -1471,9 +1471,6 @@
                     return p;
                 }
                 return false
-            },
-            onResize() {
-              this.isMobile = DeviceUtils.isMobile();
             }
         },
         watch: {
@@ -1485,8 +1482,6 @@
             if (window.location.protocol !== "https:") {
                 this.showAlert = true;
             }
-            window.addEventListener('resize', this.onResize);
-            this.onResize();
             this.loading();
             this.getDefaultSettings();
             if (this.isRefreshEnabled) {

+ 67 - 58
web/html/xui/index.html → web/html/index.html

@@ -84,7 +84,7 @@
       <a-layout-content>
         <a-spin :spinning="spinning" :delay="200" :tip="loadingTip">
           <transition name="list" appear>
-            <a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
+            <a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
               message='{{ i18n "secAlertTitle" }}'
               color="red"
               description='{{ i18n "secAlertSsl" }}'
@@ -94,8 +94,8 @@
           <transition name="list" appear>
             <template>
               <a-row v-if="!status.isLoaded">
-                <a-card hoverable style="text-align: center; padding: 30px 0; margin-top: 10px; background: transparent;">
-                  <a-spin tip="Loading..."></a-spin>
+                <a-card hoverable :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent' }">
+                  <a-spin tip='{{ i18n "loading" }}'></a-spin>
                 </a-card>
               </a-row>
               <a-row v-else>
@@ -104,19 +104,22 @@
                     <a-row>
                       <a-col :sm="24" :md="12">
                         <a-row>
-                          <a-col :span="12" style="text-align: center">
+                          <a-col :span="12" :style="{ textAlign: 'center' }">
                             <a-progress type="dashboard" status="normal"
                               :stroke-color="status.cpu.color"
                               :percent="status.cpu.percent"></a-progress>
-                            <div><b>CPU:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]] <a-tooltip>
-                              <a-icon type="area-chart"></a-icon> 
-                              <template slot="title">
-                                <div><b>Logical Processors:</b> [[ (status.logicalPro) ]]</div>
-                                <div><b>Speed:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
-                              </template>
-                            </a-tooltip></div>
+                            <div>
+                              <b>{{ i18n "pages.index.cpu" }}:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]] 
+                              <a-tooltip>
+                                <a-icon type="area-chart"></a-icon> 
+                                <template slot="title">
+                                  <div><b>{{ i18n "pages.index.logicalProcessors" }}:</b> [[ (status.logicalPro) ]]</div>
+                                  <div><b>{{ i18n "pages.index.frequency" }}:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
+                                </template>
+                              </a-tooltip>
+                            </div>
                           </a-col>
-                          <a-col :span="12" style="text-align: center">
+                          <a-col :span="12" :style="{ textAlign: 'center' }">
                             <a-progress type="dashboard" status="normal"
                               :stroke-color="status.mem.color"
                               :percent="status.mem.percent"></a-progress>
@@ -128,20 +131,20 @@
                       </a-col>
                       <a-col :sm="24" :md="12">
                         <a-row>
-                          <a-col :span="12" style="text-align: center">
+                          <a-col :span="12" :style="{ textAlign: 'center' }">
                             <a-progress type="dashboard" status="normal"
                               :stroke-color="status.swap.color"
                               :percent="status.swap.percent"></a-progress>
                             <div>
-                              <b>Swap:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]]
+                              <b>{{ i18n "pages.index.swap" }}:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]]
                             </div>
                           </a-col>
-                          <a-col :span="12" style="text-align: center">
+                          <a-col :span="12" :style="{ textAlign: 'center' }">
                             <a-progress type="dashboard" status="normal"
                               :stroke-color="status.disk.color"
                               :percent="status.disk.percent"></a-progress>
                             <div>
-                              <b>{{ i18n "pages.index.hard"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]]
+                              <b>{{ i18n "pages.index.storage"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]]
                             </div>
                           </a-col>
                         </a-row>
@@ -160,31 +163,38 @@
                       </a-space>
                     </template>
                     <template #extra>
-                      <template v-if="status.xray.state != State.Error">
-                        <a-badge status="processing" class="running-animation" :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
+                      <template v-if="status.xray.state != 'error'">
+                        <a-badge status="processing" class="running-animation" :text="status.xray.stateMsg" :color="status.xray.color"/>
                       </template>
                       <template v-else>
                         <a-popover :overlay-class-name="themeSwitcher.currentTheme">
-                          <span slot="title" style="font-size: 12pt">An error occurred while running Xray
-                            <a-tag color="purple" style="cursor: pointer; float: right;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
+                          <span slot="title">
+                            <a-row type="flex" align="middle" justify="space-between">
+                              <a-col>
+                                <span>{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
+                              </a-col>
+                              <a-col>
+                                <a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-tag>
+                              </a-col>
+                            </a-row>
                           </span>
                           <template slot="content">
-                            <p style="max-width: 400px" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</p>
+                            <span :style="{ maxWidth: '400px' }" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span>
                           </template>
-                          <a-badge :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
+                          <a-badge :text="status.xray.stateMsg" :color="status.xray.color"/>
                         </a-popover>
                       </template>
                     </template>
                     <template #actions>
-                      <a-space direction="horizontal" @click="stopXrayService" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="stopXrayService" :style="{ justifyContent: 'center' }">
                         <a-icon type="poweroff"></a-icon>
                         <span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
                       </a-space>
-                      <a-space direction="horizontal" @click="restartXrayService" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="restartXrayService" :style="{ justifyContent: 'center' }">
                         <a-icon type="reload"></a-icon>
                         <span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span>
                       </a-space>
-                      <a-space direction="horizontal" @click="openSelectV2rayVersion" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="openSelectV2rayVersion" :style="{ justifyContent: 'center' }">
                         <a-icon type="tool"></a-icon>
                         <span v-if="!isMobile">
                           [[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]]
@@ -196,15 +206,15 @@
                 <a-col :sm="24" :lg="12">
                   <a-card title='{{ i18n "menu.link" }}' hoverable>
                     <template #actions>
-                      <a-space direction="horizontal" @click="openLogs()" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="openLogs()" :style="{ justifyContent: 'center' }">
                         <a-icon type="bars"></a-icon>
                         <span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
                       </a-space>
-                      <a-space direction="horizontal" @click="openConfig" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="openConfig" :style="{ justifyContent: 'center' }">
                         <a-icon type="control"></a-icon>
                         <span v-if="!isMobile">{{ i18n "pages.index.config" }}</span>
                       </a-space>
-                      <a-space direction="horizontal" @click="openBackup" style="justify-content: center;">
+                      <a-space direction="horizontal" @click="openBackup" :style="{ justifyContent: 'center' }">
                         <a-icon type="cloud-server"></a-icon>
                         <span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span>
                       </a-space>
@@ -237,13 +247,13 @@
                 </a-col>
                 <a-col :sm="24" :lg="12">
                   <a-card title='{{ i18n "usage"}}' hoverable>
-                    <a-tag color="green"> RAM: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag>
-                    <a-tag color="green"> Threads: [[ status.appStats.threads ]] </a-tag>
+                    <a-tag color="green"> {{ i18n "pages.index.memory" }}: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag>
+                    <a-tag color="green"> {{ i18n "pages.index.threads" }}: [[ status.appStats.threads ]] </a-tag>
                   </a-card>
                 </a-col>
                 <a-col :sm="24" :lg="12">
                   <a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable>
-                    <a-row>
+                    <a-row :gutter="isMobile ? [8,8] : 0">
                       <a-col :span="12">
                         <a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
                           <template #prefix>
@@ -269,7 +279,7 @@
                 </a-col>
                 <a-col :sm="24" :lg="12">
                   <a-card title='{{ i18n "pages.index.totalData" }}' hoverable>
-                    <a-row>
+                    <a-row :gutter="isMobile ? [8,8] : 0">
                       <a-col :span="12">
                         <a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
                           <template #prefix>
@@ -297,15 +307,15 @@
                         <a-icon :type="showIp ? 'eye' : 'eye-invisible'" :style="{ fontSize: '1rem' }" @click="showIp = !showIp"></a-icon>
                       </a-tooltip>
                     </template>
-                    <a-row :class="showIp ? 'ip-visible' : 'ip-hidden'">
-                      <a-col :xs="24" :xxl="12" :style="{ marginTop: isMobile ? '10px' : 0 }">
+                    <a-row :class="showIp ? 'ip-visible' : 'ip-hidden'" :gutter="isMobile ? [8,8] : 0">
+                      <a-col :span="isMobile ? 24 : 12">
                         <a-custom-statistic title="IPv4" :value="status.publicIP.ipv4">
                           <template #prefix>
                             <a-icon type="global" />
                           </template>
                         </a-custom-statistic>
                       </a-col>
-                      <a-col :xs="24" :xxl="12" :style="{ marginTop: isMobile ? '10px' : 0 }">
+                      <a-col :span="isMobile ? 24 : 12">
                         <a-custom-statistic title="IPv6" :value="status.publicIP.ipv6">
                           <template #prefix>
                             <a-icon type="global" />
@@ -317,7 +327,7 @@
                 </a-col>
                 <a-col :sm="24" :lg="12">
                   <a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable>
-                    <a-row>
+                    <a-row :gutter="isMobile ? [8,8] : 0">
                       <a-col :span="12">
                         <a-custom-statistic title="TCP" :value="status.tcpCount">
                           <template #prefix>
@@ -343,9 +353,9 @@
     </a-layout>
     <a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true"
         @ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer="">
-      <a-alert type="warning" style="margin-bottom: 12px; width: 100%;"
+      <a-alert type="warning" :style="{ marginBottom: '12px', width: '100%' }"
         message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert>
-      <a-list class="ant-xray-version-list" bordered style="width: 100%;">
+      <a-list class="ant-xray-version-list" bordered :style="{ width: '100%' }">
         <a-list-item class="ant-xray-version-list-item" v-for="version, index in versionModal.versions">
           <a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ version ]]</a-tag>
           <a-radio :class="themeSwitcher.currentTheme" :checked="version === `v${status.xray.version}`" @click="switchV2rayVersion(version)"></a-radio>
@@ -360,15 +370,15 @@
         {{ i18n "pages.index.logs" }}
         <a-icon :spin="logModal.loading"
           type="sync"
-          style="vertical-align: middle; margin-left: 10px;"
+          :style="{ verticalAlign: 'middle', marginLeft: '10px' }"
           :disabled="logModal.loading"
           @click="openLogs()">
         </a-icon>
       </template>
       <a-form layout="inline">
-        <a-form-item style="margin-right: 0.5rem;">
+        <a-form-item :style="{ marginRight: '0.5rem' }">
           <a-input-group compact>
-            <a-select size="small" v-model="logModal.rows" style="width:70px;"
+            <a-select size="small" v-model="logModal.rows" :style="{ width: '70px' }"
                 @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
               <a-select-option value="10">10</a-select-option>
               <a-select-option value="20">20</a-select-option>
@@ -376,7 +386,7 @@
               <a-select-option value="100">100</a-select-option>
               <a-select-option value="500">500</a-select-option>
             </a-select>
-            <a-select size="small" v-model="logModal.level" style="width:95px;"
+            <a-select size="small" v-model="logModal.level" :style="{ width: '95px' }"
                 @change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
               <a-select-option value="debug">Debug</a-select-option>
               <a-select-option value="info">Info</a-select-option>
@@ -389,13 +399,13 @@
         <a-form-item>
           <a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox>
         </a-form-item>
-        <a-form-item style="float: right;">
+        <a-form-item :style="{ float: 'right' }">
           <a-button type="primary" icon="download"
             :href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs?.join('\n'))" download="x-ui.log">
           </a-button>
         </a-form-item>
       </a-form>
-      <div class="ant-input" style="height: auto; max-height: 500px; overflow: auto; margin-top: 0.5rem;" v-html="logModal.formattedLogs"></div>
+      <div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="logModal.formattedLogs"></div>
     </a-modal>
     <a-modal id="backup-modal" 
         v-model="backupModal.visible" 
@@ -403,7 +413,7 @@
         :closable="true"
         footer=""
         :class="themeSwitcher.currentTheme">
-      <a-list class="ant-backup-list" bordered style="width: 100%;">
+      <a-list class="ant-backup-list" bordered :style="{ width: '100%' }">
         <a-list-item class="ant-backup-list-item">
           <a-list-item-meta>
             <template #title>{{ i18n "pages.index.exportDatabase" }}</template>
@@ -427,13 +437,6 @@
 {{template "component/aCustomStatistic" .}}
 {{template "modals/textModal"}}
 <script>
-    const State = {
-      Running: "running",
-      Stop: "stop",
-      Error: "error",
-    }
-    Object.freeze(State);
-
     class CurTotal {
 
         constructor(current, total) {
@@ -478,7 +481,8 @@
             this.uptime = 0;
             this.appUptime = 0;
             this.appStats = {threads: 0, mem: 0, uptime: 0};
-            this.xray = { state: State.Stop, errorMsg: "", version: "", color: "" };
+
+            this.xray = { state: 'stop', stateMsg: "", errorMsg: "", version: "", color: "" };
 
             if (data == null) {
               return;
@@ -503,17 +507,22 @@
             this.appStats = data.appStats;
             this.xray = data.xray;
             switch (this.xray.state) {
-                case State.Running:
+                case 'running':
                     this.xray.color = "green";
+                    this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusRunning" }}';
                     break;
-                case State.Stop:
+                case 'stop':
                     this.xray.color = "orange";
+                    this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusStop" }}';
                     break;
-                case State.Error:
+                case 'error':
                     this.xray.color = "red";
+                    this.xray.stateMsg ='{{ i18n "pages.index.xrayStatusError" }}';
                     break;
                 default:
                     this.xray.color = "gray";
+                    this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusUnknown" }}';
+                    break;
             }
         }
     }
@@ -596,6 +605,7 @@
     const app = new Vue({
         delimiters: ['[[', ']]'],
         el: '#app',
+        mixins: [MediaQueryMixin],
         data: {
             themeSwitcher,
             status: new Status(),
@@ -605,8 +615,7 @@
             spinning: false,
             loadingTip: '{{ i18n "loading"}}',
             showAlert: false,
-            showIp: false,
-            isMobile: DeviceUtils.isMobile()
+            showIp: false
         },
         methods: {
             loading(spinning, tip = '{{ i18n "loading"}}') {

+ 11 - 10
web/html/login.html

@@ -451,7 +451,7 @@
 <body>
   <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
     <transition name="list" appear>
-      <a-layout-content class="under" style="min-height: 0;">
+      <a-layout-content class="under" :style="{ minHeight: '0' }">
         <div class="waves-header">
           <div class="waves-inner-header"></div>
           <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
@@ -484,10 +484,10 @@
             <a-button shape="circle" icon="setting"></a-button>
           </a-popover>
         </div>
-        <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto; overflow-x: hidden;">
-          <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
+        <a-row type="flex" justify="center" align="middle" :style="{ height: '100%', overflow: 'auto', overflowX: 'hidden' }">
+          <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" :style="{ margin: '3rem 0' }">
             <a-row type="flex" justify="center">
-              <a-col style="width: 100%;">
+              <a-col :style="{ width: '100%' }">
                 <h2 class="title headline zoom">
                   <span class="words-wrapper">
                     <b class="is-visible">{{ i18n "pages.login.hello" }}</b>
@@ -503,18 +503,20 @@
                     <a-form-item>
                       <a-input autocomplete="username" name="username" v-model.trim="user.username"
                         placeholder='{{ i18n "username" }}' @keydown.enter.native="login" autofocus>
-                        <a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon>
+                        <a-icon slot="prefix" type="user" :style="{ fontSize: '16px' }"></a-icon>
                       </a-input>
                     </a-form-item>
                     <a-form-item>
-                      <a-password-input autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
+                      <a-input-password autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
                         placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
-                      </a-password-input>
+                        <a-icon slot="prefix" type="lock" :style="{ fontSize: '16px' }"></a-icon>
+                      </a-input-password>
                     </a-form-item>
                     <a-form-item v-if="secretEnable">
-                      <a-password-input autocomplete="secret" name="secret" icon="key" v-model.trim="user.loginSecret"
+                      <a-input-password autocomplete="secret" name="secret" icon="lock" v-model.trim="user.loginSecret"
                         placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
-                      </a-password-input>
+                        <a-icon slot="prefix" type="key" :style="{ fontSize: '16px' }"></a-icon>
+                      </a-input-password>
                     </a-form-item>
                     <a-form-item>
                       <a-row justify="center" class="centered">
@@ -538,7 +540,6 @@
   </a-layout>
   {{template "js" .}}
   {{template "component/aThemeSwitch" .}}
-  {{template "component/aPasswordInput" .}}
   <script>
     const app = new Vue({
       delimiters: ['[[', ']]'],

+ 1 - 1
web/html/xui/modals/client_bulk_modal.html → web/html/modals/client_bulk_modal.html

@@ -61,7 +61,7 @@
                     <a-icon type="question-circle"></a-icon>
                 </a-tooltip>
             </template>
-            <a-input-number style="width: 50%" v-model.number="clientsBulkModal.tgId" min="0"></a-input-number>
+            <a-input-number :style="{ width: '50%' }" v-model.number="clientsBulkModal.tgId" min="0"></a-input-number>
         </a-form-item>
         <a-form-item v-if="app.ipLimitEnable">
             <template slot="label">

+ 1 - 1
web/html/xui/modals/client_modal.html → web/html/modals/client_modal.html

@@ -4,7 +4,7 @@
          :class="themeSwitcher.currentTheme"
          :ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
     <template v-if="isEdit">
-        <a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
+        <a-tag v-if="isExpiry || isTrafficExhausted" color="red" :style="{ marginBottom: '10px', display: 'block', textAlign: 'center' }">Account is (Expired|Traffic Ended) And Disabled</a-tag>
     </template>
     {{template "form/client"}}
 </a-modal>

+ 1 - 1
web/html/xui/modals/dns_modal.html → web/html/modals/dns_modal.html

@@ -16,7 +16,7 @@
       </template>
     </a-form-item>
     <a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
-      <a-select v-model="dnsModal.dnsServer.queryStrategy" style="width: 100%"
+      <a-select v-model="dnsModal.dnsServer.queryStrategy" :style="{ width: '100%' }"
         :dropdown-class-name="themeSwitcher.currentTheme">
         <a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
       </a-select>

+ 0 - 0
web/html/xui/modals/fakedns_modal.html → web/html/modals/fakedns_modal.html


+ 8 - 8
web/html/xui/modals/inbound_info_modal.html → web/html/modals/inbound_info_modal.html

@@ -107,7 +107,7 @@
         <a-tag v-else color="orange">{{ i18n "none" }}</a-tag>
       </template>
     </template>
-    <table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
+    <table v-if="dbInbound.isSS" :style="{ marginBottom: '10px', width: '100%' }">
       <tr>
         <td>{{ i18n "encryption" }}</td>
         <td>
@@ -131,7 +131,7 @@
     </table>
     <template v-if="infoModal.clientSettings">
       <a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
-      <table style="margin-bottom: 10px;">
+      <table :style="{ marginBottom: '10px' }">
         <tr>
           <td>{{ i18n "pages.inbounds.email" }}</td>
           <td v-if="infoModal.clientSettings.email">
@@ -203,7 +203,7 @@
           <td>{{ i18n "pages.inbounds.IPLimitlog" }}</td>
           <td>
             <a-tag>[[ infoModal.clientIps ]]</a-tag>
-            <a-icon type="sync" :spin="refreshing" @click="refreshIPs" style="margin: 0 5px;"></a-icon>
+            <a-icon type="sync" :spin="refreshing" @click="refreshIPs" :style="{ margin: '0 5px' }"></a-icon>
             <a-tooltip :title="[[ dbInbound.address ]]">
               <template slot="title">
                 <span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
@@ -213,7 +213,7 @@
           </td>
         </tr>
       </table>
-      <table style="display: inline-table; margin-block: 10px; width: 100%; text-align: center;">
+      <table :style="{ display: 'inline-table', marginBlock: '10px', width: '100%', textAlign: 'center' }">
         <tr>
           <th>{{ i18n "remained" }}</th>
           <th>{{ i18n "pages.inbounds.totalFlow" }}</th>
@@ -290,7 +290,7 @@
           <tr-info-title class="tr-info-title">
             <a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
             <a-tooltip title='{{ i18n "copy" }}'>
-              <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
+              <a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(link.link)"></a-button>
             </a-tooltip>
           </tr-info-title>
           <code>[[ link.link ]]</code>
@@ -304,7 +304,7 @@
           <tr-info-title class="tr-info-title">
             <a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
             <a-tooltip title='{{ i18n "copy" }}'>
-              <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
+              <a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(link.link)"></a-button>
             </a-tooltip>
           </tr-info-title>
           <code>[[ link.link ]]</code>
@@ -431,10 +431,10 @@
                 <tr-info-title class="tr-info-title">
                   <a-tag color="blue">Config</a-tag>
                   <a-tooltip title='{{ i18n "copy" }}'>
-                    <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
+                    <a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
                   </a-tooltip>
                 </tr-info-title>
-                <div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" style="border-radius: 1rem; padding: 0.5rem;" class="client-table-odd-row">
+                <div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" :style="{ borderRadius: '1rem', padding: '0.5rem' }" class="client-table-odd-row">
                 </div>
               </tr-info-row>
             </td>

+ 0 - 0
web/html/xui/modals/inbound_modal.html → web/html/modals/inbound_modal.html


+ 0 - 0
web/html/xui/modals/prompt_modal.html → web/html/modals/prompt_modal.html


+ 2 - 1
web/html/xui/modals/qrcode_modal.html → web/html/modals/qrcode_modal.html

@@ -1,6 +1,6 @@
 {{define "modals/qrcodeModal"}}
 <a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
-    :dialog-style="DeviceUtils.isMobile() ? { top: '18px' } : {}"
+    :dialog-style="isMobile ? { top: '18px' } : {}"
     :closable="true"
     :class="themeSwitcher.currentTheme"
     :footer="null" width="fit-content">
@@ -73,6 +73,7 @@
   const qrModalApp = new Vue({
     delimiters: ['[[', ']]'],
     el: '#qrcode-modal',
+    mixins: [MediaQueryMixin],
     data: {
       qrModal: qrModal,
     },

+ 21 - 11
web/html/xui/modals/text_modal.html → web/html/modals/text_modal.html

@@ -1,20 +1,20 @@
 {{define "modals/textModal"}}
-<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title"
-         :closable="true"
-         :class="themeSwitcher.currentTheme">
+<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title" :closable="true"
+    :class="themeSwitcher.currentTheme">
+    <a-input :style="{ overflowY: 'auto' }" type="textarea" v-model="txtModal.content"
+        :autosize="{ minRows: 10, maxRows: 20}"></a-input>
     <template slot="footer">
         <a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" icon="download"
-            :href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)"
-            :download="txtModal.fileName">[[ txtModal.fileName ]]
+            @click="txtModal.download(txtModal.content, txtModal.fileName)">
+            <span>[[ txtModal.fileName ]]</span>
+        </a-button>
+        <a-button type="primary" icon="copy" @click="txtModal.copy(txtModal.content)">
+            <span>{{ i18n "copy" }}</span>
         </a-button>
-        <a-button type="primary" @click="txtModal.copy(txtModal.content)">{{ i18n "copy" }}</a-button>
     </template>
-    <a-input style="overflow-y: auto;" type="textarea" v-model="txtModal.content"
-        :autosize="{ minRows: 10, maxRows: 20}"></a-input>
 </a-modal>
 
 <script>
-
     const txtModal = {
         title: '',
         content: '',
@@ -35,6 +35,17 @@
                     this.close();
                 })
         },
+        download: function (content = '', fileName = '') {
+            let link = document.createElement('a');
+
+            link.download = fileName;
+            link.href = URL.createObjectURL(new Blob([content], { type: 'text/plain' }));
+            link.click();
+
+            URL.revokeObjectURL(link.href);
+
+            link.remove();
+        },
         close: function () {
             this.visible = false;
         },
@@ -47,6 +58,5 @@
             txtModal: txtModal,
         },
     });
-
 </script>
-{{end}}
+{{end}}

+ 9 - 9
web/html/xui/modals/warp_modal.html → web/html/modals/warp_modal.html

@@ -6,7 +6,7 @@
         <a-button icon="api" @click="register" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.create" }}</a-button>
     </template>
     <template v-else>
-        <table style="margin: 5px 0; width: 100%;">
+        <table :style="{ margin: '5px 0', width: '100%' }">
             <tr class="client-table-odd-row">
                 <td>Access Token</td>
                 <td>[[ warpModal.warpData.access_token ]]</td>
@@ -25,8 +25,8 @@
             </tr>
         </table>
         <a-button @click="delConfig" :loading="warpModal.confirmLoading" type="danger">{{ i18n "delete" }}</a-button>
-        <a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
-        <a-collapse style="margin: 10px 0;">
+        <a-divider :style="{ margin: '0' }">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
+        <a-collapse :style="{ margin: '10px 0' }">
             <a-collapse-panel header='WARP/WARP+ License Key'>
                 <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
                     <a-form-item label="Key">
@@ -37,11 +37,11 @@
                 </a-form>
             </a-collapse-panel>
         </a-collapse>
-        <a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
-        <a-button icon="sync" @click="getConfig" style="margin-top: 5px; margin-bottom: 10px;"
+        <a-divider :style="{ margin: '0' }">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
+        <a-button icon="sync" @click="getConfig" :style="{ marginTop: '5px', marginBottom: '10px' }"
             :loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
         <template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
-            <table style="width: 100%">
+            <table :style="{ width: '100%' }">
                 <tr class="client-table-odd-row">
                     <td>Device Name</td>
                     <td>[[ warpModal.warpConfig.name ]]</td>
@@ -77,14 +77,14 @@
                     </tr>
                 </template>
             </table>
-            <a-divider style="margin: 10px 0;">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
+            <a-divider :style="{ margin: '10px 0' }">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
             <a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
                 <template v-if="warpOutboundIndex>=0">
-                    <a-tag color="green" style="line-height: 31px;">{{ i18n "enabled" }}</a-tag>
+                    <a-tag color="green" :style="{ lineHeight: '31px' }">{{ i18n "enabled" }}</a-tag>
                     <a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
                 </template>
                 <template v-else>
-                    <a-tag color="orange" style="line-height: 31px;">{{ i18n "disabled" }}</a-tag>
+                    <a-tag color="orange" :style="{ lineHeight: '31px' }">{{ i18n "disabled" }}</a-tag>
                     <a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
                 </template>
                 </a-form-item>

+ 0 - 0
web/html/xui/modals/xray_balancer_modal.html → web/html/modals/xray_balancer_modal.html


+ 1 - 1
web/html/xui/modals/xray_outbound_modal.html → web/html/modals/xray_outbound_modal.html

@@ -1,7 +1,7 @@
 {{define "modals/outModal"}}
 <a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
          :confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
-         :ok-button-props="{ props: { disabled: !outModal.isValid } }" style="overflow: hidden;"
+         :ok-button-props="{ props: { disabled: !outModal.isValid } }" :style="{ overflow: 'hidden' }"
          :ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
          {{template "form/outbound"}}
 </a-modal>

+ 0 - 0
web/html/xui/modals/xray_reverse_modal.html → web/html/modals/xray_reverse_modal.html


+ 4 - 4
web/html/xui/modals/xray_rule_modal.html → web/html/modals/xray_rule_modal.html

@@ -37,14 +37,14 @@
       </a-select>
     </a-form-item>
     <a-form-item label='Attributes'>
-      <a-button icon="plus" size="small" style="margin-left: 10px" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
+      <a-button icon="plus" size="small" :style="{ marginLeft: '10px' }" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
     </a-form-item>
     <a-form-item :wrapper-col="{span: 24}">
       <a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
-        <a-input style="width: 50%" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
-          <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
+        <a-input :style="{ width: '50%' }" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
+          <template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
         </a-input>
-        <a-input style="width: 50%" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
+        <a-input :style="{ width: '50%' }" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
           <a-button icon="minus" slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)"></a-button>
         </a-input>
       </a-input-group>

+ 13 - 11
web/html/xui/settings.html → web/html/settings.html

@@ -53,6 +53,9 @@
     color: inherit;
     font-size: 24px;
   }
+  .dark .ant-input-password-icon {
+    color: var(--dark-color-text-primary);
+  }
 </style>
 <body>
   <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
@@ -61,7 +64,7 @@
       <a-layout-content>
         <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
           <transition name="list" appear>
-            <a-alert type="error" v-if="confAlerts.length>0" style="margin-bottom: 10px;"
+            <a-alert type="error" v-if="confAlerts.length>0" :style="{ marginBottom: '10px' }"
                 message='{{ i18n "secAlertTitle" }}'
                 color="red"
                 show-icon closable>
@@ -72,9 +75,9 @@
             </a-alert>
           </transition>
           <a-space direction="vertical">
-            <a-card hoverable style="margin-bottom: .5rem; overflow-x: hidden;">
-              <a-row style="display: flex; flex-wrap: wrap; align-items: center;">
-                <a-col :xs="24" :sm="10" style="padding: 4px;">
+            <a-card hoverable :style="{ marginBottom: '.5rem', overflowX: 'hidden' }">
+              <a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
+                <a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
                   <a-space direction="horizontal">
                     <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
                     <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
@@ -84,7 +87,7 @@
                   <template>
                     <div>
                       <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
-                      <a-alert type="warning" style="float: right; width: fit-content"
+                      <a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }"
                         message='{{ i18n "pages.settings.infoDesc" }}'
                         show-icon>
                       </a-alert>
@@ -94,19 +97,19 @@
               </a-row>
             </a-card>
             <a-tabs default-active-key="1">
-              <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' style="padding-top: 20px;">
+              <a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/panel/general" . }}
               </a-tab-pane>
-              <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' style="padding-top: 20px;">
+              <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/panel/security" . }}
               </a-tab-pane>
-              <a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' style="padding-top: 20px;">
+              <a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/panel/telegram" . }}
               </a-tab-pane>
-              <a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' style="padding-top: 20px;">
+              <a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/panel/subscription/general" . }}
               </a-tab-pane>
-              <a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" style="padding-top: 20px;">
+              <a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
                 {{ template "settings/panel/subscription/json" . }}
               </a-tab-pane>
             </a-tabs>
@@ -119,7 +122,6 @@
 <script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
 {{template "component/aSidebar" .}}
 {{template "component/aThemeSwitch" .}}
-{{template "component/aPasswordInput" .}}
 {{template "component/aSettingListItem" .}}
 <script>
   const app = new Vue({

+ 10 - 10
web/html/xui/settings/panel/general.html → web/html/settings/panel/general.html

@@ -9,12 +9,12 @@
                 {{ i18n "pages.settings.sampleRemark"}}: <i>#[[ remarkSample ]]</i>
             </template>
             <template #control>
-                <a-input-group style="width: 100%;">
-                    <a-select style="padding-right: .5rem; min-width: 80%; width: auto;" mode="multiple"
+                <a-input-group :style="{ width: '100%' }">
+                    <a-select :style="{ paddingRight: '.5rem', minWidth: '80%', width: 'auto' }" mode="multiple"
                         v-model="remarkModel" :dropdown-class-name="themeSwitcher.currentTheme">
                         <a-select-option v-for="(value, key) in remarkModels" :value="key">[[ value ]]</a-select-option>
                     </a-select>
-                    <a-select style="width: 20%;" v-model="remarkSeparator"
+                    <a-select :style="{ width: '20%' }" v-model="remarkSeparator"
                         :dropdown-class-name="themeSwitcher.currentTheme">
                         <a-select-option v-for="key in remarkSeparators" :value="key">[[ key ]]</a-select-option>
                     </a-select>
@@ -39,7 +39,7 @@
             <template #title>{{ i18n "pages.settings.panelPort"}}</template>
             <template #description>{{ i18n "pages.settings.panelPortDesc"}}</template>
             <template #control>
-                <a-input-number :min="1" :min="65531" v-model="allSetting.webPort" style="width: 100%;"></a-input>
+                <a-input-number :min="1" :min="65531" v-model="allSetting.webPort" :style="{ width: '100%' }"></a-input>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
@@ -53,21 +53,21 @@
             <template #title>{{ i18n "pages.settings.sessionMaxAge" }}</template>
             <template #description>{{ i18n "pages.settings.sessionMaxAgeDesc" }}</template>
             <template #control>
-                <a-input-number :min="60" v-model="allSetting.sessionMaxAge" style="width: 100%;"></a-input>
+                <a-input-number :min="60" v-model="allSetting.sessionMaxAge" :style="{ width: '100%' }"></a-input>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.settings.pageSize" }}</template>
             <template #description>{{ i18n "pages.settings.pageSizeDesc" }}</template>
             <template #control>
-                <a-input-number :min="0" step="5" v-model="allSetting.pageSize" style="width: 100%;"></a-input>
+                <a-input-number :min="0" step="5" v-model="allSetting.pageSize" :style="{ width: '100%' }"></a-input>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.settings.language"}}</template>
             <template #control>
                 <a-select ref="selectLang" v-model="lang" @change="LanguageManager.setLanguage(lang)"
-                    :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
+                    :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
                     <a-select-option :value="l.value" :label="l.value" v-for="l in LanguageManager.supportedLanguages">
                         <span role="img" :aria-label="l.name" v-text="l.icon"></span> &nbsp;&nbsp; <span
                             v-text="l.name"></span>
@@ -81,14 +81,14 @@
             <template #title>{{ i18n "pages.settings.expireTimeDiff" }}</template>
             <template #description>{{ i18n "pages.settings.expireTimeDiffDesc" }}</template>
             <template #control>
-                <a-input-number :min="0" v-model="allSetting.expireDiff" style="width: 100%;"></a-input>
+                <a-input-number :min="0" v-model="allSetting.expireDiff" :style="{ width: '100%' }"></a-input>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.settings.trafficDiff" }}</template>
             <template #description>{{ i18n "pages.settings.trafficDiffDesc" }}</template>
             <template #control>
-                <a-input-number :min="0" v-model="allSetting.trafficDiff" style="width: 100%;"></a-input>
+                <a-input-number :min="0" v-model="allSetting.trafficDiff" :style="{ width: '100%' }"></a-input>
             </template>
         </a-setting-list-item>
     </a-collapse-panel>
@@ -137,7 +137,7 @@
             <template #title>{{ i18n "pages.settings.datepicker"}}</template>
             <template #description>{{ i18n "pages.settings.datepickerDescription"}}</template>
             <template #control>
-                <a-select style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme" v-model="datepicker">
+                <a-select :style="{ width: '100%' }" :dropdown-class-name="themeSwitcher.currentTheme" v-model="datepicker">
                     <a-select-option v-for="item in datepickerList" :value="item.value">
                         <span v-text="item.name"></span>
                     </a-select-option>

+ 5 - 5
web/html/xui/settings/panel/security.html → web/html/settings/panel/security.html

@@ -10,7 +10,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.settings.currentPassword"}}</template>
             <template #control>
-                <a-password-input autocomplete="current-password" v-model="user.oldPassword"></a-password-input>
+                <a-input-password autocomplete="current-password" v-model="user.oldPassword"></a-input-password>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
@@ -22,11 +22,11 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.settings.newPassword"}}</template>
             <template #control>
-                <a-password-input autocomplete="new-password" v-model="user.newPassword"></a-password-input>
+                <a-input-password autocomplete="new-password" v-model="user.newPassword"></a-input-password>
             </template>
         </a-setting-list-item>
         <a-list-item>
-            <a-space direction="horizontal" style="padding: 0 20px;">
+            <a-space direction="horizontal" :style="{ padding: '0 20px' }">
                 <a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
             </a-space>
         </a-list-item>
@@ -37,7 +37,7 @@
             <template #description>{{ i18n "pages.settings.security.loginSecurityDesc" }}</template>
             <template #control>
                 <a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
-                <a-icon style="margin-left: 1rem;" v-if="allSetting.secretEnable" :spin="this.changeSecret" type="sync"
+                <a-icon :style="{ marginLeft: '1rem' }" v-if="allSetting.secretEnable" :spin="this.changeSecret" type="sync"
                     @click="getNewSecret"></a-icon>
             </template>
         </a-setting-list-item>
@@ -49,7 +49,7 @@
             </template>
         </a-setting-list-item>
         <a-list-item>
-            <a-space direction="horizontal" style="padding: 0 20px;">
+            <a-space direction="horizontal" :style="{ padding: '0 20px' }">
                 <a-button type="primary" :loading="this.changeSecret" @click="updateSecret">
                     <span>{{ i18n "confirm"}}</span>
                 </a-button>

+ 2 - 2
web/html/xui/settings/panel/subscription/general.html → web/html/settings/panel/subscription/general.html

@@ -34,7 +34,7 @@
             <template #description>{{ i18n "pages.settings.subPortDesc"}}</template>
             <template #control>
                 <a-input-number v-model="allSetting.subPort" :min="1" :min="65531"
-                    style="width: 100%;"></a-input-number>
+                    :style="{ width: '100%' }"></a-input-number>
             </template>
         </a-setting-list-item>
         <a-setting-list-item paddings="small">
@@ -90,7 +90,7 @@
             <template #title>{{ i18n "pages.settings.subUpdates"}}</template>
             <template #description>{{ i18n "pages.settings.subUpdatesDesc"}}</template>
             <template #control>
-                <a-input-number :min="1" v-model="allSetting.subUpdates" style="width: 100%;"></a-input-number>
+                <a-input-number :min="1" v-model="allSetting.subUpdates" :style="{ width: '100%' }"></a-input-number>
             </template>
         </a-setting-list-item>
     </a-collapse-panel>

+ 12 - 12
web/html/xui/settings/panel/subscription/json.html → web/html/settings/panel/subscription/json.html

@@ -25,7 +25,7 @@
                 <a-switch v-model="fragment"></a-switch>
             </template>
         </a-setting-list-item>
-        <a-list-item v-if="fragment" style="padding: 10px 20px;">
+        <a-list-item v-if="fragment" :style="{ padding: '10px 20px' }">
             <a-collapse>
                 <a-collapse-panel header='{{ i18n "pages.settings.fragmentSett"}}' v-if="fragment">
                     <a-setting-list-item paddings="small">
@@ -59,13 +59,13 @@
                 <a-switch v-model="noises"></a-switch>
             </template>
         </a-setting-list-item>
-        <a-list-item v-if="noises" style="padding: 10px 20px;">
+        <a-list-item v-if="noises" :style="{ padding: '10px 20px' }">
             <a-collapse>
                 <a-collapse-panel v-for="(noise, index) in noisesArray" :key="index" :header="`Noise №${index + 1}`">
                     <a-setting-list-item paddings="small">
                         <template #title>Type</template>
                         <template #control>
-                            <a-select :value="noise.type" style="width: 100%"
+                            <a-select :value="noise.type" :style="{ width: '100%' }"
                                 :dropdown-class-name="themeSwitcher.currentTheme"
                                 @change="(value) => updateNoiseType(index, value)">
                                 <a-select-option :value="p" :label="p" v-for="p in ['rand', 'base64', 'str', 'hex']" :key="p">
@@ -90,13 +90,13 @@
                                 placeholder="10-20"></a-input>
                         </template>
                     </a-setting-list-item>
-                    <a-space direction="horizontal" style="padding: 10px 20px;">
+                    <a-space direction="horizontal" :style="{ padding: '10px 20px' }">
                         <a-button v-if="noisesArray.length > 1" type="danger"
                             @click="removeNoise(index)">Remove</a-button>
                     </a-space>
                 </a-collapse-panel>
             </a-collapse>
-            <a-button v-if="noises" type="primary" @click="addNoise" style="margin-top: 10px">Add Noise</a-button>
+            <a-button v-if="noises" type="primary" @click="addNoise" :style="{ marginTop: '10px' }">Add Noise</a-button>
         </a-list-item>
     </a-collapse-panel>
     <a-collapse-panel key="4" header='{{ i18n "pages.settings.mux"}}'>
@@ -107,27 +107,27 @@
                 <a-switch v-model="enableMux"></a-switch>
             </template>
         </a-setting-list-item>
-        <a-list-item v-if="enableMux" style="padding: 10px 20px;">
+        <a-list-item v-if="enableMux" :style="{ padding: '10px 20px' }">
             <a-collapse>
                 <a-collapse-panel header='{{ i18n "pages.settings.muxSett"}}'>
                     <a-setting-list-item paddings="small">
                         <template #title>Concurrency</template>
                         <template #control>
                             <a-input-number v-model="muxConcurrency" :min="-1" :max="1024"
-                                style="width: 100%;"></a-input-number>
+                                :style="{ width: '100%' }"></a-input-number>
                         </template>
                     </a-setting-list-item>
                     <a-setting-list-item paddings="small">
                         <template #title>xudp Concurrency</template>
                         <template #control>
                             <a-input-number v-model="muxXudpConcurrency" :min="-1" :max="1024"
-                                style="width: 100%;"></a-input-number>
+                                :style="{ width: '100%' }"></a-input-number>
                         </template>
                     </a-setting-list-item>
                     <a-setting-list-item paddings="small">
                         <template #title>xudp UDP 443</template>
                         <template #control>
-                            <a-select v-model="muxXudpProxyUDP443" style="width: 100%"
+                            <a-select v-model="muxXudpProxyUDP443" :style="{ width: '100%' }"
                                 :dropdown-class-name="themeSwitcher.currentTheme">
                                 <a-select-option :value="p" :label="p" v-for="p in ['reject', 'allow', 'skip']">
                                     <span>[[ p ]]</span>
@@ -147,13 +147,13 @@
                 <a-switch v-model="enableDirect"></a-switch>
             </template>
         </a-setting-list-item>
-        <a-list-item v-if="enableDirect" style="padding: 10px 20px;">
+        <a-list-item v-if="enableDirect" :style="{ padding: '10px 20px' }">
             <a-collapse>
                 <a-collapse-panel header='{{ i18n "pages.settings.direct"}}'>
                     <a-setting-list-item paddings="small">
                         <template #title>{{ i18n "pages.xray.directips" }}</template>
                         <template #control>
-                            <a-select mode="tags" style="width: 100%" v-model="directIPs"
+                            <a-select mode="tags" :style="{ width: '100%' }" v-model="directIPs"
                                 :dropdown-class-name="themeSwitcher.currentTheme">
                                 <a-select-option :value="p.value" :label="p.label" v-for="p in directIPsOptions">
                                     <span>[[ p.label ]]</span>
@@ -164,7 +164,7 @@
                     <a-setting-list-item paddings="small">
                         <template #title>{{ i18n "pages.xray.directdomains" }}</template>
                         <template #control>
-                            <a-select mode="tags" style="width: 100%" v-model="directDomains"
+                            <a-select mode="tags" :style="{ width: '100%' }" v-model="directDomains"
                                 :dropdown-class-name="themeSwitcher.currentTheme">
                                 <a-select-option :value="p.value" :label="p.label" v-for="p in diretDomainsOptions">
                                     <span>[[ p.label ]]</span>

+ 2 - 2
web/html/xui/settings/panel/telegram.html → web/html/settings/panel/telegram.html

@@ -26,7 +26,7 @@
             <template #title>{{ i18n "pages.settings.telegramBotLanguage"}}</template>
             <template #control>
                 <a-select ref="selectBotLang" v-model="allSetting.tgLang"
-                    :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
+                    :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
                     <a-select-option :value="l.value" :label="l.value" v-for="l in LanguageManager.supportedLanguages">
                         <span role="img" :aria-label="l.name" v-text="l.icon"></span> &nbsp;&nbsp; <span
                             v-text="l.name"></span>
@@ -61,7 +61,7 @@
             <template #title>{{ i18n "pages.settings.tgNotifyCpu" }}</template>
             <template #description>{{ i18n "pages.settings.tgNotifyCpuDesc" }}</template>
             <template #control>
-                <a-input-number :min="0" :min="100" v-model="allSetting.tgCpu" style="width: 100%;"></a-switch>
+                <a-input-number :min="0" :min="100" v-model="allSetting.tgCpu" :style="{ width: '100%' }"></a-switch>
             </template>
         </a-setting-list-item>
     </a-collapse-panel>

+ 2 - 2
web/html/xui/settings/xray/advanced.html → web/html/settings/xray/advanced.html

@@ -2,13 +2,13 @@
 <a-space direction="vertical" size="small">
     <a-list-item-meta title='{{ i18n "pages.xray.Template"}}'
         description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
-    <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;"
+    <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }"
         :size="isMobile ? 'small' : ''">
         <a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
         <a-radio-button value="inboundSettings">{{ i18n "pages.xray.Inbounds" }}</a-radio-button>
         <a-radio-button value="outboundSettings">{{ i18n "pages.xray.Outbounds" }}</a-radio-button>
         <a-radio-button value="routingRuleSettings">{{ i18n "pages.xray.Routings" }}</a-radio-button>
     </a-radio-group>
-    <textarea style="position:absolute; left: -800px;" id="xraySetting"></textarea>
+    <textarea :style="{ position: 'absolute', left: '-800px' }" id="xraySetting"></textarea>
 </a-space>
 {{end}}

+ 10 - 10
web/html/xui/settings/xray/balancers.html → web/html/settings/xray/balancers.html

@@ -10,14 +10,14 @@
                 <span>[[ index+1 ]]</span>
                 <a-dropdown :trigger="['click']">
                     <a-icon @click="e => e.preventDefault()" type="more"
-                        style="font-size: 16px; text-decoration: bold;"></a-icon>
+                        :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                     <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                         <a-menu-item @click="editBalancer(index)">
                             <a-icon type="edit"></a-icon>
                             <span>{{ i18n "edit" }}</span>
                         </a-menu-item>
                         <a-menu-item @click="deleteBalancer(index)">
-                            <span style="color: #FF4D4F">
+                            <span :style="{ color: '#FF4D4F' }">
                                 <a-icon type="delete"></a-icon> 
                                 <span>{{ i18n "delete"}}</span>
                             </span>
@@ -26,13 +26,13 @@
                 </a-dropdown>
             </template>
             <template slot="strategy" slot-scope="text, balancer, index">
-                <a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="purple">Random</a-tag>
-                <a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag>
-                <a-tag style="margin:0;" v-if="balancer.strategy=='leastLoad'" color="green">Least Load</a-tag>
-                <a-tag style="margin:0;" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='random'" color="purple">Random</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='leastLoad'" color="green">Least Load</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
             </template>
             <template slot="selector" slot-scope="text, balancer, index">
-                <a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
+                <a-tag class="info-large-tag" :style="{ margin: '1' }" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
             </template>
         </a-table>
         <a-radio-group v-if="observatoryEnable || burstObservatoryEnable" v-model="obsSettings" @change="changeObsCode"
@@ -40,12 +40,12 @@
             <a-radio-button value="observatory" v-if="observatoryEnable">Observatory</a-radio-button>
             <a-radio-button value="burstObservatory" v-if="burstObservatoryEnable">Burst Observatory</a-radio-button>
         </a-radio-group>
-        <textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea>
+        <textarea :style="{ position: 'absolute', left: '-800px' }" id="obsSetting"></textarea>
     </a-space>
 </template>
 <template v-else>
-    <a-empty description='{{ i18n "emptyBalancersDesc" }}' style="margin: 10px;">
-        <a-button type="primary" icon="plus" @click="addBalancer()" style="margin-top: 10px;">
+    <a-empty description='{{ i18n "emptyBalancersDesc" }}' :style="{ margin: '10px' }">
+        <a-button type="primary" icon="plus" @click="addBalancer()" :style="{ marginTop: '10px' }">
             <span>{{ i18n "pages.xray.balancer.addBalancer"}}</span>
         </a-button>
     </a-empty>

+ 27 - 27
web/html/xui/settings/xray/basics.html → web/html/settings/xray/basics.html

@@ -2,9 +2,9 @@
 <a-collapse default-active-key="1">
     <a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center;">
+            <a-alert type="warning" :style="{ textAlign: 'center' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.generalConfigsDesc" }}</span>
                 </template>
             </a-alert>
@@ -14,7 +14,7 @@
             <template #description>{{ i18n "pages.xray.FreedomStrategyDesc" }}</template>
             <template #control>
                 <a-select v-model="freedomStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
-                    style="width: 100%">
+                    :style="{ width: '100%' }">
                     <a-select-option v-for="s in OutboundDomainStrategies" :value="s">
                         <span>[[ s ]]</span>
                     </a-select-option>
@@ -26,7 +26,7 @@
             <template #description>{{ i18n "pages.xray.RoutingStrategyDesc" }}</template>
             <template #control>
                 <a-select v-model="routingStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
-                    style="width: 100%">
+                    :style="{ width: '100%' }">
                     <a-select-option v-for="s in routingDomainStrategies" :value="s">
                         <span>[[ s ]]</span>
                     </a-select-option>
@@ -66,9 +66,9 @@
     </a-collapse-panel>
     <a-collapse-panel key="3" header='{{ i18n "pages.xray.logConfigs" }}'>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center;">
+            <a-alert type="warning" :style="{ textAlign: 'center' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.logConfigsDesc" }}</span>
                 </template>
             </a-alert>
@@ -77,7 +77,7 @@
             <template #title>{{ i18n "pages.xray.logLevel" }}</template>
             <template #description>{{ i18n "pages.xray.logLevelDesc" }}</template>
             <template #control>
-                <a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
+                <a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
                     <a-select-option v-for="s in log.loglevel" :value="s">
                         <span>[[ s ]]</span>
                     </a-select-option>
@@ -88,7 +88,7 @@
             <template #title>{{ i18n "pages.xray.accessLog" }}</template>
             <template #description>{{ i18n "pages.xray.accessLogDesc" }}</template>
             <template #control>
-                <a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
+                <a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
                     <a-select-option value=''>
                         <span>Empty</span>
                     </a-select-option>
@@ -102,7 +102,7 @@
             <template #title>{{ i18n "pages.xray.errorLog" }}</template>
             <template #description>{{ i18n "pages.xray.errorLogDesc" }}</template>
             <template #control>
-                <a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
+                <a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
                     <a-select-option value=''>
                         <span>Empty</span>
                     </a-select-option>
@@ -117,7 +117,7 @@
             <template #description>{{ i18n "pages.xray.maskAddressDesc" }}</template>
             <template #control>
                 <a-select v-model="maskAddressLog" :dropdown-class-name="themeSwitcher.currentTheme"
-                    style="width: 100%">
+                    :style="{ width: '100%' }">
                     <a-select-option value=''>
                         <span>Empty</span>
                     </a-select-option>
@@ -137,9 +137,9 @@
     </a-collapse-panel>
     <a-collapse-panel key="4" header='{{ i18n "pages.xray.blockConfigs"}}'>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center;">
+            <a-alert type="warning" :style="{ textAlign: 'center' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.blockConfigsDesc" }}</span>
                 </template>
             </a-alert>
@@ -161,9 +161,9 @@
     </a-collapse-panel>
     <a-collapse-panel key="5" header='{{ i18n "pages.xray.basicRouting"}}'>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center;">
+            <a-alert type="warning" :style="{ textAlign: 'center' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.blockConnectionsConfigsDesc" }}</span>
                 </template>
             </a-alert>
@@ -171,7 +171,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.xray.blockips" }}</template>
             <template #control>
-                <a-select mode="tags" v-model="blockedIPs" style="width: 100%"
+                <a-select mode="tags" v-model="blockedIPs" :style="{ width: '100%' }"
                     :dropdown-class-name="themeSwitcher.currentTheme">
                     <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">
                         <span>[[ p.label ]]</span>
@@ -182,7 +182,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.xray.blockdomains" }}</template>
             <template #control>
-                <a-select mode="tags" v-model="blockedDomains" style="width: 100%"
+                <a-select mode="tags" v-model="blockedDomains" :style="{ width: '100%' }"
                     :dropdown-class-name="themeSwitcher.currentTheme">
                     <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.BlockDomainsOptions">
                         <span>[[ p.label ]]</span>
@@ -191,9 +191,9 @@
             </template>
         </a-setting-list-item>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center; margin-top: 20px;">
+            <a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.directConnectionsConfigsDesc" }}</span>
                 </template>
             </a-alert>
@@ -201,7 +201,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.xray.directips" }}</template>
             <template #control>
-                <a-select mode="tags" style="width: 100%" v-model="directIPs"
+                <a-select mode="tags" :style="{ width: '100%' }" v-model="directIPs"
                     :dropdown-class-name="themeSwitcher.currentTheme">
                     <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">
                         <span>[[ p.label ]]</span>
@@ -212,7 +212,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.xray.directdomains" }}</template>
             <template #control>
-                <a-select mode="tags" style="width: 100%" v-model="directDomains"
+                <a-select mode="tags" :style="{ width: '100%' }" v-model="directDomains"
                     :dropdown-class-name="themeSwitcher.currentTheme">
                     <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.DomainsOptions">
                         <span>[[ p.label ]]</span>
@@ -221,9 +221,9 @@
             </template>
         </a-setting-list-item>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center; margin-top: 20px;">
+            <a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     <span>{{ i18n "pages.xray.ipv4RoutingDesc" }}</span>
                 </template>
             </a-alert>
@@ -231,7 +231,7 @@
         <a-setting-list-item paddings="small">
             <template #title>{{ i18n "pages.xray.ipv4Routing" }}</template>
             <template #control>
-                <a-select mode="tags" style="width: 100%" v-model="ipv4Domains"
+                <a-select mode="tags" :style="{ width: '100%' }" v-model="ipv4Domains"
                     :dropdown-class-name="themeSwitcher.currentTheme">
                     <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
                         <span>[[ p.label ]]</span>
@@ -240,9 +240,9 @@
             </template>
         </a-setting-list-item>
         <a-row :xs="24" :sm="24" :lg="12">
-            <a-alert type="warning" style="text-align: center; margin-top: 20px;">
+            <a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
                 <template slot="message">
-                    <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
+                    <a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
                     {{ i18n "pages.xray.warpRoutingDesc" }}
                 </template>
             </a-alert>
@@ -251,7 +251,7 @@
             <template #title>{{ i18n "pages.xray.warpRouting" }}</template>
             <template #control>
                 <template v-if="WarpExist">
-                    <a-select mode="tags" style="width: 100%" v-model="warpDomains"
+                    <a-select mode="tags" :style="{ width: '100%' }" v-model="warpDomains"
                         :dropdown-class-name="themeSwitcher.currentTheme">
                         <a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
                             <span>[[ p.label ]]</span>
@@ -265,7 +265,7 @@
         </a-setting-list-item>
     </a-collapse-panel>
     <a-collapse-panel key="6" header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
-        <a-space direction="horizontal" style="padding: 0 20px">
+        <a-space direction="horizontal" :style="{ padding: '0 20px' }">
             <a-button type="danger" @click="resetXrayConfigToDefault">
                 <span>{{ i18n "pages.settings.resetDefaultConfig" }}</span>
             </a-button>

+ 9 - 9
web/html/xui/settings/xray/dns.html → web/html/settings/xray/dns.html

@@ -27,7 +27,7 @@
                 <template #title>{{ i18n "pages.xray.dns.strategy" }}</template>
                 <template #description>{{ i18n "pages.xray.dns.strategyDesc" }}</template>
                 <template #control>
-                    <a-select v-model="dnsStrategy" style="width: 100%"
+                    <a-select v-model="dnsStrategy" :style="{ width: '100%' }"
                         :dropdown-class-name="themeSwitcher.currentTheme">
                         <a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']">
                             <span>[[ l ]]</span>
@@ -71,14 +71,14 @@
                             <span>[[ index+1 ]]</span>
                             <a-dropdown :trigger="['click']">
                                 <a-icon @click="e => e.preventDefault()" type="more"
-                                    style="font-size: 16px; text-decoration: bold;"></a-icon>
+                                    :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                                 <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                                     <a-menu-item @click="editDNSServer(index)">
                                         <a-icon type="edit"></a-icon>
                                         <span>{{ i18n "edit" }}</span>
                                     </a-menu-item>
                                     <a-menu-item @click="deleteDNSServer(index)">
-                                        <span style="color: #FF4D4F">
+                                        <span :style="{ color: '#FF4D4F' }">
                                             <a-icon type="delete"></a-icon>
                                             <span>{{ i18n "delete"}}</span>
                                         </span>
@@ -100,8 +100,8 @@
                 </a-space>
             </template>
             <template v-else>
-                <a-empty description='{{ i18n "emptyDnsDesc" }}' style="margin: 10px;">
-                    <a-button type="primary" icon="plus" @click="addDNSServer()" style="margin-top: 10px;">
+                <a-empty description='{{ i18n "emptyDnsDesc" }}' :style="{ margin: '10px' }">
+                    <a-button type="primary" icon="plus" @click="addDNSServer()" :style="{ marginTop: '10px' }">
                         <span>{{ i18n "pages.xray.dns.add" }}</span>
                     </a-button>
                 </a-empty>
@@ -118,14 +118,14 @@
                             <span>[[ index+1 ]]</span>
                             <a-dropdown :trigger="['click']">
                                 <a-icon @click="e => e.preventDefault()" type="more"
-                                    style="font-size: 16px; text-decoration: bold;"></a-icon>
+                                    :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                                 <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                                     <a-menu-item @click="editFakedns(index)">
                                         <a-icon type="edit"></a-icon>
                                         <span>{{ i18n "edit" }}</span>
                                     </a-menu-item>
                                     <a-menu-item @click="deleteFakedns(index)">
-                                        <span style="color: #FF4D4F">
+                                        <span :style="{ color: '#FF4D4F' }">
                                             <a-icon type="delete"></a-icon>
                                             <span>{{ i18n "delete"}}</span>
                                         </span>
@@ -137,8 +137,8 @@
                 </a-space>
             </template>
             <template v-else>
-                <a-empty description='{{ i18n "emptyFakeDnsDesc" }}' style="margin: 20px;">
-                    <a-button type="primary" icon="plus" @click="addFakedns()" style="margin-top: 10px;">
+                <a-empty description='{{ i18n "emptyFakeDnsDesc" }}' :style="{ margin: '20px' }">
+                    <a-button type="primary" icon="plus" @click="addFakedns()" :style="{ marginTop: '10px' }">
                         <span>{{ i18n "pages.xray.fakedns.add" }}</span>
                     </a-button>
                 </a-empty>

+ 18 - 16
web/html/xui/settings/xray/outbounds.html → web/html/settings/xray/outbounds.html

@@ -9,15 +9,17 @@
                 <a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
             </a-space>
         </a-col>
-        <a-col :xs="12" :sm="12" :lg="12" style="text-align: right;">
-            <a-icon type="sync" :spin="refreshing" @click="refreshOutboundTraffic()" style="margin: 0 5px;"></a-icon>
-            <a-popconfirm placement="topRight" @confirm="resetOutboundTraffic(-1)"
-                title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme"
-                ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
-                <a-icon slot="icon" type="question-circle-o"
-                    :style="themeSwitcher.isDarkTheme ? 'color: #008771' : 'color: #008771'"></a-icon>
-                <a-icon type="retweet" style="cursor: pointer;"></a-icon>
-            </a-popconfirm>
+        <a-col :xs="12" :sm="12" :lg="12" :style="{ textAlign: 'right' }">
+            <a-button-group>
+                <a-button icon="sync" @click="refreshOutboundTraffic()" :loading="refreshing"></a-button>
+                <a-popconfirm placement="topRight" @confirm="resetOutboundTraffic(-1)"
+                    title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme"
+                    ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
+                    <a-icon slot="icon" type="question-circle-o"
+                        :style="{ color: themeSwitcher.isDarkTheme ? '#008771' : '#008771' }"></a-icon>
+                    <a-button icon="retweet"></a-button>
+                </a-popconfirm>
+            </a-button-group>
         </a-col>
     </a-row>
     <a-table :columns="outboundColumns" bordered :row-key="r => r.key" :data-source="outboundData"
@@ -26,7 +28,7 @@
             <span>[[ index+1 ]]</span>
             <a-dropdown :trigger="['click']">
                 <a-icon @click="e => e.preventDefault()" type="more"
-                    style="font-size: 16px; text-decoration: bold;"></a-icon>
+                    :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                 <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                     <a-menu-item v-if="index>0" @click="setFirstOutbound(index)">
                         <a-icon type="vertical-align-top"></a-icon>
@@ -43,7 +45,7 @@
                         </span>
                     </a-menu-item>
                     <a-menu-item @click="deleteOutbound(index)">
-                        <span style="color: #FF4D4F">
+                        <span :style="{ color: '#FF4D4F' }">
                             <a-icon type="delete"></a-icon> 
                             <span>{{ i18n "delete"}}</span>
                         </span>
@@ -52,15 +54,15 @@
             </a-dropdown>
         </template>
         <template slot="address" slot-scope="text, outbound, index">
-            <p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
+            <p :style="{ margin: '0 5px' }" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
         </template>
         <template slot="protocol" slot-scope="text, outbound, index">
-            <a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
+            <a-tag :style="{ margin: '0' }" color="purple">[[ outbound.protocol ]]</a-tag>
             <template
                 v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
-                <a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
-                <a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
-                <a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'"
+                <a-tag :style="{ margin: '0' }" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
+                <a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='reality'"
                     color="green">reality</a-tag>
             </template>
         </template>

+ 4 - 4
web/html/xui/settings/xray/reverse.html → web/html/settings/xray/reverse.html

@@ -10,14 +10,14 @@
                 <span>[[ index+1 ]]</span>
                 <a-dropdown :trigger="['click']">
                     <a-icon @click="e => e.preventDefault()" type="more"
-                        style="font-size: 16px; text-decoration: bold;"></a-icon>
+                        :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                     <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                         <a-menu-item @click="editReverse(index)">
                             <a-icon type="edit"></a-icon>
                             <span>{{ i18n "edit" }}</span>
                         </a-menu-item>
                         <a-menu-item @click="deleteReverse(index)">
-                            <span style="color: #FF4D4F">
+                            <span :style="{ color: '#FF4D4F' }">
                                 <a-icon type="delete"></a-icon> 
                                 <span>{{ i18n "delete"}}</span>
                             </span>
@@ -29,8 +29,8 @@
     </a-space>
 </template>
 <template v-else>
-    <a-empty description='{{ i18n "emptyReverseDesc" }}' style="margin: 10px;">
-        <a-button type="primary" icon="plus" @click="addReverse()" style="margin-top: 10px;">
+    <a-empty description='{{ i18n "emptyReverseDesc" }}' :style="{ margin: '10px' }">
+        <a-button type="primary" icon="plus" @click="addReverse()" :style="{ marginTop: '10px' }">
             {{ i18n "pages.xray.outbound.addReverse" }}
         </a-button>
     </a-empty>

+ 4 - 4
web/html/xui/settings/xray/routing.html → web/html/settings/xray/routing.html

@@ -9,7 +9,7 @@
             <span class="ant-table-row-index"> [[ index+1 ]] </span>
             <a-dropdown :trigger="['click']">
                 <a-icon @click="e => e.preventDefault()" type="more"
-                    style="font-size: 16px; text-decoration: bold;"></a-icon>
+                    :style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
                 <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
                     <a-menu-item v-if="index>0" @click="replaceRule(index,0)">
                         <a-icon type="vertical-align-top"></a-icon>
@@ -33,7 +33,7 @@
                         {{ i18n "edit" }}
                     </a-menu-item>
                     <a-menu-item @click="deleteRule(index)">
-                        <span style="color: #FF4D4F">
+                        <span :style="{ color: '#FF4D4F' }">
                             <a-icon type="delete"></a-icon> {{ i18n "delete"}}
                         </span>
                     </a-menu-item>
@@ -70,7 +70,7 @@
                 v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0"
                 :overlay-class-name="themeSwitcher.currentTheme" trigger="click">
                 <template slot="content">
-                    <table cellpadding="2" style="max-width: 300px;">
+                    <table cellpadding="2" :style="{ maxWidth: '300px' }">
                         <tr v-if="rule.source">
                             <td>Source</td>
                             <td><a-tag color="blue" v-for="r in rule.source.split(',')">[[ r ]]</a-tag></td>
@@ -109,7 +109,7 @@
                         </tr>
                     </table>
                 </template>
-                <a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
+                <a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
                     <a-icon type="info"></a-icon>
                 </a-button>
             </a-popover>

+ 14 - 14
web/html/xui/xray.html → web/html/xray.html

@@ -50,7 +50,7 @@
       <a-layout-content>
         <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
           <transition name="list" appear>
-            <a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
+            <a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
               message='{{ i18n "secAlertTitle" }}'
               color="red"
               description='{{ i18n "secAlertSsl" }}'
@@ -58,17 +58,17 @@
             </a-alert>
           </transition>
           <a-space direction="vertical">
-            <a-card hoverable style="margin-bottom: .5rem;">
-              <a-row style="display: flex; flex-wrap: wrap; align-items: center;">
-                <a-col :xs="24" :sm="10" style="padding: 4px;">
+            <a-card hoverable :style="{ marginBottom: '.5rem' }">
+              <a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
+                <a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
                   <a-space direction="horizontal">
                     <a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
                     <a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
                     <a-popover v-if="restartResult"
                         :overlay-class-name="themeSwitcher.currentTheme">
-                      <span slot="title" style="font-size: 12pt">Error in running xray-core</span>
+                      <span slot="title">{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
                       <template slot="content">
-                        <p style="max-width: 400px" v-for="line in restartResult.split('\n')">[[ line ]]</p>
+                        <span :style="{ maxWidth: '400px' }" v-for="line in restartResult.split('\n')">[[ line ]]</span>
                       </template>
                       <a-icon type="question-circle"></a-icon>
                     </a-popover>
@@ -78,7 +78,7 @@
                   <template>
                     <div>
                       <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
-                      <a-alert type="warning" style="float: right; width: fit-content" message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
+                      <a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
                       </a-alert>
                     </div>
                   </template>
@@ -88,25 +88,25 @@
             <a-tabs class="ant-card-dark-box-nohover" default-active-key="1"
                 @change="(activeKey) => { this.changePage(activeKey); }"
                 :class="themeSwitcher.currentTheme">
-              <a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
+              <a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/xray/basics" . }}
               </a-tab-pane>
-              <a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;">
+              <a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' :style="{ paddingTop: '20px' }">
                 {{ template "settings/xray/routing" . }}
               </a-tab-pane>
               <a-tab-pane key="tpl-outbound" tab='{{ i18n "pages.xray.Outbounds"}}' force-render="true">
                 {{ template "settings/xray/outbounds" . }}
               </a-tab-pane>
-              <a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true">
+              <a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' :style="{ paddingTop: '20px' }" force-render="true">
                 {{ template "settings/xray/reverse" . }}
               </a-tab-pane>
-              <a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true">
+              <a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' :style="{ paddingTop: '20px' }" force-render="true">
                 {{ template "settings/xray/balancers" . }}
               </a-tab-pane>
-              <a-tab-pane key="tpl-dns" tab='DNS' style="padding-top: 20px;" force-render="true">
+              <a-tab-pane key="tpl-dns" tab='DNS' :style="{ paddingTop: '20px' }" force-render="true">
                 {{ template "settings/xray/dns" . }}
               </a-tab-pane>
-              <a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
+              <a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' :style="{ paddingTop: '20px' }" force-render="true">
                 {{ template "settings/xray/advanced" . }}
               </a-tab-pane>
             </a-tabs>
@@ -192,6 +192,7 @@
 
     const app = new Vue({
         delimiters: ['[[', ']]'],
+        mixins: [MediaQueryMixin],
         el: '#app',
         data: {
             themeSwitcher,
@@ -205,7 +206,6 @@
             refreshing: false,
             restartResult: '',
             showAlert: false,
-            isMobile: DeviceUtils.isMobile(),
             advSettings: 'xraySetting',
             obsSettings: '',
             cm: null,

+ 0 - 57
web/html/xui/component/aPasswordInput.html

@@ -1,57 +0,0 @@
-{{define "component/passwordInput"}}
-<template>
-  <a-input :value="value" :type="showPassword ? 'text' : 'password'" :placeholder="placeholder"
-    :autocomplete="autocomplete" :name="name" @input="$emit('input', $event.target.value)">
-    <template v-if="icon" #prefix>
-      <a-icon :type="icon" style="font-size: 16px;" />
-    </template>
-    <template #addonAfter>
-      <a-icon :type="showPassword ? 'eye-invisible' : 'eye'" @click="toggleShowPassword" style="font-size: 16px;" />
-    </template>
-  </a-input>
-</template>
-{{end}}
-
-{{define "component/aPasswordInput"}}
-<script>
-  Vue.component('a-password-input', {
-    props: {
-      'title': {
-        type: String,
-        required: false,
-      },
-      'value': {
-        type: String,
-        required: false,
-      },
-      'placeholder': {
-        type: String,
-        required: false,
-      },
-      'autocomplete': {
-        type: String,
-        required: false,
-      },
-      'name': {
-        type: String,
-        required: false,
-      },
-      'icon': {
-        type: undefined,
-        required: false
-      }
-    },
-    template: `{{template "component/passwordInput"}}`,
-    data() {
-      return {
-        showPassword: false,
-      };
-    },
-    methods: {
-      toggleShowPassword() {
-        this.showPassword = !this.showPassword;
-      },
-    },
-  });
-</script>
-{{end}}

+ 11 - 1
web/translation/translate.en_US.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Overview"
+"cpu" = "CPU"
+"logicalProcessors" = "Logical Processors"
+"frequency" = "Frequency"
+"swap" = "Swap"
+"storage" = "Storage"
 "memory" = "RAM"
-"hard" = "Disk"
+"threads" = "Threads"
 "xrayStatus" = "Xray"
 "stopXray" = "Stop"
 "restartXray" = "Restart"
 "xraySwitch" = "Version"
 "xraySwitchClick" = "Choose the version you want to switch to."
 "xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with current configurations."
+"xrayStatusUnknown" = "Unknown"
+"xrayStatusRunning" = "Running"
+"xrayStatusStop" = "Stop"
+"xrayStatusError" = "Error"
+"xrayErrorPopoverTitle" = "An error occurred while running Xray"
 "operationHours" = "Uptime"
 "systemLoad" = "System Load"
 "systemLoadDesc" = "System load average for the past 1, 5, and 15 minutes"

+ 12 - 2
web/translation/translate.es_ES.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Estado del Sistema"
-"memory" = "Memoria"
-"hard" = "Disco Duro"
+"cpu" = "CPU"
+"logicalProcessors" = "Procesadores lógicos"
+"frequency" = "Frecuencia"
+"swap" = "Intercambio"
+"storage" = "Almacenamiento"
+"memory" = "RAM"
+"threads" = "Hilos"
 "xrayStatus" = "Xray"
 "stopXray" = "Detener"
 "restartXray" = "Reiniciar"
 "xraySwitch" = "Versión"
 "xraySwitchClick" = "Elige la versión a la que deseas cambiar."
 "xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales."
+"xrayStatusUnknown" = "Desconocido"
+"xrayStatusRunning" = "En ejecución"
+"xrayStatusStop" = "Detenido"
+"xrayStatusError" = "Error"
+"xrayErrorPopoverTitle" = "Se produjo un error al ejecutar Xray"
 "operationHours" = "Tiempo de Funcionamiento"
 "systemLoad" = "Carga del Sistema"
 "systemLoadDesc" = "promedio de carga del sistema en los últimos 1, 5 y 15 minutos"

+ 12 - 2
web/translation/translate.fa_IR.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "نمای کلی"
-"memory" = "RAM"
-"hard" = "Disk"
+"cpu" = "پردازنده"
+"logicalProcessors" = "پردازنده‌های منطقی"
+"frequency" = "فرکانس"
+"swap" = "سواپ"
+"storage" = "ذخیره‌سازی"
+"memory" = "حافظه رم"
+"threads" = "رشته‌ها"
 "xrayStatus" = "ایکس‌ری"
 "stopXray" = "توقف"
 "restartXray" = "شروع‌مجدد"
 "xraySwitch" = "‌نسخه"
 "xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
 "xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمی‌تر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
+"xrayStatusUnknown" = "ناشناخته"
+"xrayStatusRunning" = "در حال اجرا"
+"xrayStatusStop" = "متوقف"
+"xrayStatusError" = "خطا"
+"xrayErrorPopoverTitle" = "خطا در هنگام اجرای Xray رخ داد"
 "operationHours" = "مدت‌کارکرد"
 "systemLoad" = "بارسیستم"
 "systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته"

+ 12 - 1
web/translation/translate.id_ID.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Ikhtisar"
+"cpu" = "CPU" 
+"logicalProcessors" = "Prosesor logis"
+"frequency" = "Frekuensi"
+"swap" = "Swap"
+"storage" = "Penyimpanan"
 "memory" = "RAM"
-"hard" = "Disk"
+"threads" = "Thread"
 "xrayStatus" = "Xray"
 "stopXray" = "Stop"
 "restartXray" = "Restart"
 "xraySwitch" = "Versi"
 "xraySwitchClick" = "Pilih versi yang ingin Anda pindah."
 "xraySwitchClickDesk" = "Pilih dengan hati-hati, karena versi yang lebih lama mungkin tidak kompatibel dengan konfigurasi saat ini."
+"xrayStatusUnknown" = "Tidak diketahui"
+"xrayStatusRunning" = "Berjalan"
+"xrayStatusStop" = "Berhenti"
+"xrayStatusError" = "Kesalahan"
+"xrayErrorPopoverTitle" = "Terjadi kesalahan saat menjalankan Xray"
 "operationHours" = "Waktu Aktif"
 "systemLoad" = "Beban Sistem"
 "systemLoadDesc" = "Rata-rata beban sistem selama 1, 5, dan 15 menit terakhir"
@@ -322,6 +332,7 @@
 "subShowInfo" = "Tampilkan Info Penggunaan"
 "subShowInfoDesc" = "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien."
 "subURI" = "URI Proxy Terbalik"
+"subURIDesc" = "Path URI dari URL langganan untuk digunakan di belakang proxy."
 "externalTrafficInformEnable" = "Informasikan API eksternal pada setiap pembaruan lalu lintas."
 "externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
 "externalTrafficInformURI" = "Lalu Lintas Eksternal Menginformasikan URI"

+ 12 - 2
web/translation/translate.ja_JP.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "システムステータス"
-"memory" = "メモリ"
-"hard" = "ハードディスク"
+"cpu" = "CPU"
+"logicalProcessors" = "論理プロセッサ"
+"frequency" = "周波数"
+"swap" = "スワップ"
+"storage" = "ストレージ"
+"memory" = "RAM"
+"threads" = "スレッド"
 "xrayStatus" = "Xray"
 "stopXray" = "停止"
 "restartXray" = "再起動"
 "xraySwitch" = "バージョン"
 "xraySwitchClick" = "切り替えるバージョンを選択してください"
 "xraySwitchClickDesk" = "慎重に選択してください。古いバージョンは現在の設定と互換性がない可能性があります。"
+"xrayStatusUnknown" = "不明"
+"xrayStatusRunning" = "実行中"
+"xrayStatusStop" = "停止"
+"xrayStatusError" = "エラー"
+"xrayErrorPopoverTitle" = "Xrayの実行中にエラーが発生しました"
 "operationHours" = "システム稼働時間"
 "systemLoad" = "システム負荷"
 "systemLoadDesc" = "過去1、5、15分間のシステム平均負荷"

+ 12 - 2
web/translation/translate.pt_BR.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Visão Geral"
-"memory" = "Memória RAM"
-"hard" = "Disco"
+"cpu" = "CPU" 
+"logicalProcessors" = "Processadores lógicos"
+"frequency" = "Frequência"
+"swap" = "Swap"
+"storage" = "Armazenamento"
+"memory" = "RAM"
+"threads" = "Threads"
 "xrayStatus" = "Xray"
 "stopXray" = "Parar"
 "restartXray" = "Reiniciar"
 "xraySwitch" = "Versão"
 "xraySwitchClick" = "Escolha a versão para a qual deseja alternar."
 "xraySwitchClickDesk" = "Escolha com cuidado, pois versões mais antigas podem não ser compatíveis com as configurações atuais."
+"xrayStatusUnknown" = "Desconhecido"
+"xrayStatusRunning" = "Em execução"
+"xrayStatusStop" = "Parado"
+"xrayStatusError" = "Erro"
+"xrayErrorPopoverTitle" = "Ocorreu um erro ao executar o Xray"
 "operationHours" = "Tempo de Atividade"
 "systemLoad" = "Carga do Sistema"
 "systemLoadDesc" = "Média de carga do sistema nos últimos 1, 5 e 15 minutos"

+ 12 - 2
web/translation/translate.ru_RU.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Статус системы"
-"memory" = "Память"
-"hard" = "Жесткий диск"
+"cpu" = "ЦП"
+"logicalProcessors" = "Логические процессоры"
+"frequency" = "Частота"
+"swap" = "Файл подкачки"
+"storage" = "Хранилище"
+"memory" = "ОЗУ"
+"threads" = "Потоки"
 "xrayStatus" = "Xray"
 "stopXray" = "Остановить"
 "restartXray" = "Перезапустить"
 "xraySwitch" = "Выбор версии"
 "xraySwitchClick" = "Выберите желаемую версию"
 "xraySwitchClickDesk" = "Обратите внимание: старые версии могут не поддерживать текущие настройки"
+"xrayStatusUnknown" = "Неизвестно"
+"xrayStatusRunning" = "Запущен"
+"xrayStatusStop" = "Остановлен"
+"xrayStatusError" = "Ошибка"
+"xrayErrorPopoverTitle" = "Произошла ошибка при запуске Xray"
 "operationHours" = "Время работы системы"
 "systemLoad" = "Нагрузка на систему"
 "systemLoadDesc" = "Средняя загрузка системы за последние 1, 5 и 15 минут"

+ 11 - 1
web/translation/translate.tr_TR.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Genel Bakış"
+"cpu" = "İşlemci"
+"logicalProcessors" = "Mantıksal işlemciler"
+"frequency" = "Frekans"
+"swap" = "Takas"
+"storage" = "Depolama"
 "memory" = "RAM"
-"hard" = "Disk"
+"threads" = "İş parçacıkları"
 "xrayStatus" = "Xray"
 "stopXray" = "Durdur"
 "restartXray" = "Yeniden Başlat"
 "xraySwitch" = "Sürüm"
 "xraySwitchClick" = "Geçiş yapmak istediğiniz sürümü seçin."
 "xraySwitchClickDesk" = "Dikkatli seçin, eski sürümler mevcut yapılandırmalarla uyumlu olmayabilir."
+"xrayStatusUnknown" = "Bilinmiyor"
+"xrayStatusRunning" = "Çalışıyor"
+"xrayStatusStop" = "Durduruldu"
+"xrayStatusError" = "Hata"
+"xrayErrorPopoverTitle" = "Xray çalıştırılırken bir hata oluştu"
 "operationHours" = "Çalışma Süresi"
 "systemLoad" = "Sistem Yükü"
 "systemLoadDesc" = "Geçmiş 1, 5 ve 15 dakika için sistem yük ortalaması"

+ 12 - 2
web/translation/translate.uk_UA.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Огляд"
-"memory" = "Пам'ять"
-"hard" = "Диск"
+"cpu" = "ЦП"
+"logicalProcessors" = "Логічні процесори"
+"frequency" = "Частота"
+"swap" = "Своп"
+"storage" = "Сховище"
+"memory" = "ОЗП"
+"threads" = "Потоки"
 "xrayStatus" = "Xray"
 "stopXray" = "Зупинити"
 "restartXray" = "Перезапустити"
 "xraySwitch" = "Версія"
 "xraySwitchClick" = "Виберіть версію, на яку ви хочете перейти."
 "xraySwitchClickDesk" = "Вибирайте уважно, оскільки старіші версії можуть бути несумісними з поточними конфігураціями."
+"xrayStatusUnknown" = "Невідомо"
+"xrayStatusRunning" = "Запущено"
+"xrayStatusStop" = "Зупинено"
+"xrayStatusError" = "Помилка"
+"xrayErrorPopoverTitle" = "Під час роботи Xray сталася помилка"
 "operationHours" = "Час роботи"
 "systemLoad" = "Завантаження системи"
 "systemLoadDesc" = "Середнє завантаження системи за останні 1, 5 і 15 хвилин"

+ 12 - 2
web/translation/translate.vi_VN.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "Trạng thái hệ thống"
-"memory" = "Ram"
-"hard" = "Dung lượng"
+"cpu" = "CPU"
+"logicalProcessors" = "Bộ xử lý logic"
+"frequency" = "Tần số"
+"swap" = "Swap"
+"storage" = "Lưu trữ"
+"memory" = "RAM"
+"threads" = "Luồng"
 "xrayStatus" = "Xray"
 "stopXray" = "Dừng lại"
 "restartXray" = "Khởi động lại"
 "xraySwitch" = "Phiên bản"
 "xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
 "xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại."
+"xrayStatusUnknown" = "Không xác định"
+"xrayStatusRunning" = "Đang chạy"
+"xrayStatusStop" = "Dừng"
+"xrayStatusError" = "Lỗi"
+"xrayErrorPopoverTitle" = "Đã xảy ra lỗi khi chạy Xray"
 "operationHours" = "Thời gian hoạt động"
 "systemLoad" = "Tải hệ thống"
 "systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua"

+ 11 - 1
web/translation/translate.zh_CN.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "系统状态"
+"cpu" = "CPU"
+"logicalProcessors" = "逻辑处理器"
+"frequency" = "频率"
+"swap" = "交换分区"
+"storage" = "存储"
 "memory" = "内存"
-"hard" = "磁盘"
+"threads" = "线程"
 "xrayStatus" = "Xray"
 "stopXray" = "停止"
 "restartXray" = "重启"
 "xraySwitch" = "版本"
 "xraySwitchClick" = "选择你要切换到的版本"
 "xraySwitchClickDesk" = "请谨慎选择,因为较旧版本可能与当前配置不兼容"
+"xrayStatusUnknown" = "未知"
+"xrayStatusRunning" = "运行中"
+"xrayStatusStop" = "停止"
+"xrayStatusError" = "错误"
+"xrayErrorPopoverTitle" = "运行Xray时发生错误"
 "operationHours" = "系统正常运行时间"
 "systemLoad" = "系统负载"
 "systemLoadDesc" = "过去 1、5 和 15 分钟的系统平均负载"

+ 11 - 1
web/translation/translate.zh_TW.toml

@@ -91,14 +91,24 @@
 
 [pages.index]
 "title" = "系統狀態"
+"cpu" = "CPU"
+"logicalProcessors" = "邏輯處理器"
+"frequency" = "頻率"
+"swap" = "交換空間"
+"storage" = "儲存"
 "memory" = "記憶體"
-"hard" = "磁碟"
+"threads" = "執行緒"
 "xrayStatus" = "Xray"
 "stopXray" = "停止"
 "restartXray" = "重啟"
 "xraySwitch" = "版本"
 "xraySwitchClick" = "選擇你要切換到的版本"
 "xraySwitchClickDesk" = "請謹慎選擇,因為較舊版本可能與當前配置不相容"
+"xrayStatusUnknown" = "未知"
+"xrayStatusRunning" = "運行中"
+"xrayStatusStop" = "停止"
+"xrayStatusError" = "錯誤"
+"xrayErrorPopoverTitle" = "執行Xray時發生錯誤"
 "operationHours" = "系統正常執行時間"
 "systemLoad" = "系統負載"
 "systemLoadDesc" = "過去 1、5 和 15 分鐘的系統平均負載"