Explorar el Código

feat: add comments under client id (#3131)

Vadim Iskuchekov hace 14 horas
padre
commit
5c10035bd9

+ 31 - 8
web/html/component/aClientTable.html

@@ -41,14 +41,28 @@
   </template>
 </template>
 <template slot="client" slot-scope="text, client">
-  <a-tooltip>
-    <template slot="title">
-      <template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
-      <template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
-      <template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
-    </template>
-    <a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
-  </a-tooltip> [[ client.email ]]
+  <div class="client-cell">
+    <div class="client-info-row">
+      <a-tooltip>
+        <template slot="title">
+          <template v-if="!isClientEnabled(record, client.email)">{{ i18n "depleted" }}</template>
+          <template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
+          <template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
+        </template>
+        <a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
+      </a-tooltip>
+      <span class="client-email">[[ client.email ]]</span>
+    </div>
+    <div v-if="client.comment && client.comment.trim()" style="margin-left: 18px;">
+      <a-tooltip v-if="client.comment.length > 50" :overlay-class-name="themeSwitcher.currentTheme">
+        <template slot="title">
+          [[ client.comment ]]
+        </template>
+        <span class="client-comment">[[ client.comment.substring(0, 47) + '...' ]]</span>
+      </a-tooltip>
+      <span v-else class="client-comment">[[ client.comment ]]</span>
+    </div>
+  </div>
 </template>
 <template slot="traffic" slot-scope="text, client">
   <a-popover :overlay-class-name="themeSwitcher.currentTheme">
@@ -168,6 +182,15 @@
   <a-popover placement="bottomRight" :overlay-class-name="themeSwitcher.currentTheme" trigger="click">
     <template slot="content">
       <table>
+        <tr v-if="client.comment && client.comment.trim()">
+          <td colspan="3" :style="{ textAlign: 'left', borderBottom: '1px solid #f0f0f0', paddingBottom: '8px', marginBottom: '8px' }">
+            <div :style="{ fontSize: '0.9em', fontWeight: '500', marginBottom: '4px' }">[[ client.email ]]</div>
+            <div :style="{ fontSize: '0.85em', color: '#666', fontStyle: 'italic', wordBreak: 'break-word', maxWidth: '250px' }">[[ client.comment ]]</div>
+          </td>
+        </tr>
+        <tr v-else>
+          <td colspan="3" :style="{ textAlign: 'center', fontWeight: '500', paddingBottom: '8px' }">[[ client.email ]]</td>
+        </tr>
         <tr>
           <td colspan="3" :style="{ textAlign: 'center' }">{{ i18n "pages.inbounds.traffic" }}</td>
         </tr>

+ 6 - 1
web/html/form/protocol/shadowsocks.html

@@ -13,7 +13,12 @@
                     <th>Password</th>
                 </tr>
                 <tr v-for="(client, index) in inbound.settings.shadowsockses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
-                    <td>[[ client.email ]]</td>
+                    <td>
+                      <div class="client-cell">
+                        <div class="client-email">[[ client.email ]]</div>
+                        <div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
+                      </div>
+                    </td>
                     <td>[[ client.password ]]</td>
                 </tr>
             </table>

+ 6 - 1
web/html/form/protocol/trojan.html

@@ -12,7 +12,12 @@
         <th>Password</th>
       </tr>
       <tr v-for="(client, index) in inbound.settings.trojans" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
-        <td>[[ client.email ]]</td>
+        <td>
+          <div class="client-cell">
+            <div class="client-email">[[ client.email ]]</div>
+            <div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
+          </div>
+        </td>
         <td>[[ client.password ]]</td>
       </tr>
     </table>

+ 6 - 1
web/html/form/protocol/vless.html

@@ -12,7 +12,12 @@
         <th>ID</th>
       </tr>
       <tr v-for="(client, index) in inbound.settings.vlesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
-        <td>[[ client.email ]]</td>
+        <td>
+          <div class="client-cell">
+            <div class="client-email">[[ client.email ]]</div>
+            <div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
+          </div>
+        </td>
         <td>[[ client.id ]]</td>
       </tr>
     </table>

+ 6 - 1
web/html/form/protocol/vmess.html

@@ -13,7 +13,12 @@
                 <th>{{ i18n "security" }}</th>
             </tr>
             <tr v-for="(client, index) in inbound.settings.vmesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
-                <td>[[ client.email ]]</td>
+                <td>
+                  <div class="client-cell">
+                    <div class="client-email">[[ client.email ]]</div>
+                    <div v-if="client.comment && client.comment.trim()" class="client-comment">[[ client.comment ]]</div>
+                  </div>
+                </td>
                 <td>[[ client.id ]]</td>
                 <td>[[ client.security ]]</td>
             </tr>

+ 91 - 8
web/html/inbounds.html

@@ -79,6 +79,54 @@
     max-width: 200px;
     overflow: hidden;
   }
+  .client-comment {
+    font-size: 12px;
+    color: #888;
+    font-style: italic;
+    line-height: 1.2;
+  }
+  .dark .client-comment {
+    color: #bbb;
+  }
+  .client-email {
+    font-weight: 500;
+  }
+  .client-cell {
+    display: flex;
+    flex-direction: column;
+    gap: 2px;
+  }
+  .client-info-row {
+    display: flex;
+    align-items: center;
+    gap: 6px;
+  }
+  .client-popup-item {
+    margin-bottom: 8px;
+    padding: 4px 0;
+    border-bottom: 1px solid #f0f0f0;
+  }
+  .dark .client-popup-item {
+    border-bottom: 1px solid #333;
+  }
+  .client-popup-item:last-child {
+    border-bottom: none;
+    margin-bottom: 0;
+  }
+  .client-popup-email {
+    font-weight: 500;
+    margin-bottom: 2px;
+  }
+  .client-popup-comment {
+    font-size: 11px;
+    color: #666;
+    font-style: italic;
+    max-width: 200px;
+    word-break: break-word;
+  }
+  .dark .client-popup-comment {
+    color: #aaa;
+  }
   .online-animation .ant-badge-status-dot {
     animation: onlineAnimation 1.2s linear infinite;
   }
@@ -382,25 +430,37 @@
                     <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>
+                        <div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
+                          <div class="client-popup-email">[[ clientEmail ]]</div>
+                          <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                        </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-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>
+                        <div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
+                          <div class="client-popup-email">[[ clientEmail ]]</div>
+                          <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                        </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-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>
+                        <div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
+                          <div class="client-popup-email">[[ clientEmail ]]</div>
+                          <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                        </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-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>
+                        <div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
+                          <div class="client-popup-email">[[ clientEmail ]]</div>
+                          <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                        </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-popover>
@@ -479,25 +539,37 @@
                             <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>
+                                <div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail" class="client-popup-item">
+                                  <div class="client-popup-email">[[ clientEmail ]]</div>
+                                  <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                                </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-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>
+                                <div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail" class="client-popup-item">
+                                  <div class="client-popup-email">[[ clientEmail ]]</div>
+                                  <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                                </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-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>
+                                <div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail" class="client-popup-item">
+                                  <div class="client-popup-email">[[ clientEmail ]]</div>
+                                  <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                                </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-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>
+                                <div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail" class="client-popup-item">
+                                  <div class="client-popup-email">[[ clientEmail ]]</div>
+                                  <div v-if="getClientWithComment(clientEmail, dbInbound.id).comment" class="client-popup-comment">[[ getClientWithComment(clientEmail, dbInbound.id).comment ]]</div>
+                                </div>
                               </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-popover>
@@ -716,6 +788,17 @@
             loading(spinning = true) {
                 this.spinning = spinning;
             },
+            getClientWithComment(email, inboundId) {
+                const dbInbound = this.dbInbounds.find(inbound => inbound.id === inboundId);
+                if (!dbInbound) return { email, comment: '' };
+                
+                const inboundSettings = JSON.parse(dbInbound.settings);
+                if (inboundSettings.clients) {
+                    const client = inboundSettings.clients.find(c => c.email === email);
+                    return client ? { email: client.email, comment: client.comment || '' } : { email, comment: '' };
+                }
+                return { email, comment: '' };
+            },
             async getDBInbounds() {
                 this.refreshing = true;
                 const msg = await HttpUtil.post('/panel/inbound/list');