welin 3 месяцев назад
Родитель
Сommit
cfc1e13b70

+ 1 - 1
.env.local

@@ -1,4 +1,4 @@
 # .env.local
 # 如果你的后端在本地运行,通常是 http://127.0.0.1:8000
 # 如果已经部署到服务器,填写服务器地址,如 https://api.visafly.com
-NEXT_PUBLIC_API_URL=https://visafly.top
+NEXT_PUBLIC_API_URL=http://localhost:8888

+ 18 - 18
src/components/admin/remote-server/ConfigManager.tsx

@@ -150,43 +150,43 @@ export default function ConfigManager({
   };
 
   return (
-    <div className="space-y-6">
+    <div className="space-y-4 sm:space-y-6">
       <div className="flex items-center justify-between">
         <h3 className="text-lg font-semibold text-slate-800">配置文件管理</h3>
       </div>
 
       {error && (
-        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-center gap-2">
+        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-center gap-2 text-sm">
           <AlertCircle size={18} />
           {error}
         </div>
       )}
 
       {success && (
-        <div className="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-center gap-2">
+        <div className="bg-green-50 border border-green-200 text-green-700 px-4 py-3 rounded-md flex items-center gap-2 text-sm">
           <Settings size={18} />
           {success}
         </div>
       )}
 
       {/* 读取配置 */}
-      <div className="bg-white rounded-lg border border-slate-200 p-6">
+      <div className="bg-white rounded-lg border border-slate-200 p-4 sm:p-6">
         <h4 className="text-sm font-semibold text-slate-700 mb-4 flex items-center gap-2">
           <FileText size={18} />
           读取配置文件
         </h4>
-        <div className="flex gap-2">
+        <div className="flex flex-col sm:flex-row gap-2">
           <input
             type="text"
             value={configFile}
             onChange={(e) => setConfigFile(e.target.value)}
             placeholder="配置文件路径,如: config/troov_config.json"
-            className="flex-1 px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+            className="flex-1 px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
           />
           <button
             onClick={fetchConfig}
             disabled={loading}
-            className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+            className="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2 text-sm"
           >
             <RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
             读取
@@ -197,21 +197,21 @@ export default function ConfigManager({
       {/* 显示配置内容 */}
       {configData && (
         <>
-          <div className="bg-slate-900 rounded-lg border border-slate-200 p-4">
+          <div className="bg-slate-900 rounded-lg border border-slate-200 p-3 sm:p-4">
             <div className="flex items-center justify-between mb-2">
               <h4 className="text-sm font-semibold text-white">配置文件内容 (JSON)</h4>
             </div>
             <textarea
               value={configJson}
               onChange={(e) => setConfigJson(e.target.value)}
-              className="w-full h-96 px-3 py-2 bg-slate-800 text-green-400 font-mono text-sm rounded border border-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
+              className="w-full h-64 sm:h-96 px-3 py-2 bg-slate-800 text-green-400 font-mono text-xs sm:text-sm rounded border border-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500"
               spellCheck={false}
             />
-            <div className="mt-2 flex justify-end">
+            <div className="mt-3 flex justify-end">
               <button
                 onClick={updateConfigByJson}
                 disabled={loading}
-                className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+                className="w-full sm:w-auto px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2 text-sm"
               >
                 <Save size={18} />
                 保存整个配置
@@ -220,14 +220,14 @@ export default function ConfigManager({
           </div>
 
           {/* 更新单个键值 */}
-          <div className="bg-white rounded-lg border border-slate-200 p-6">
+          <div className="bg-white rounded-lg border border-slate-200 p-4 sm:p-6">
             <h4 className="text-sm font-semibold text-slate-700 mb-4 flex items-center gap-2">
               <Settings size={18} />
               更新单个配置项
             </h4>
             <div className="space-y-4">
               <div>
-                <label className="block text-sm font-medium text-slate-700 mb-1">
+                <label className="block text-xs sm:text-sm font-medium text-slate-700 mb-1">
                   键路径 (如: lockV1.sessionLimit)
                 </label>
                 <input
@@ -235,26 +235,26 @@ export default function ConfigManager({
                   value={keyPath}
                   onChange={(e) => setKeyPath(e.target.value)}
                   placeholder="lockV1.sessionLimit"
-                  className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                  className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 />
               </div>
               <div>
-                <label className="block text-sm font-medium text-slate-700 mb-1">新值</label>
+                <label className="block text-xs sm:text-sm font-medium text-slate-700 mb-1">新值</label>
                 <input
                   type="text"
                   value={newValue}
                   onChange={(e) => setNewValue(e.target.value)}
                   placeholder={'10 或 true 或 \'string\' 或 {"key": "value"}'}
-                  className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                  className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 />
-                <p className="mt-1 text-xs text-slate-500">
+                <p className="mt-1 text-[10px] sm:text-xs text-slate-500">
                   支持数字、字符串、布尔值、JSON对象或数组
                 </p>
               </div>
               <button
                 onClick={updateConfigByKey}
                 disabled={loading}
-                className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+                className="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2 text-sm"
               >
                 <Save size={18} />
                 更新配置项

+ 145 - 76
src/components/admin/remote-server/DockerControl.tsx

@@ -125,7 +125,7 @@ export default function DockerControl({
   return (
     <div className="space-y-6">
       {/* 操作栏 */}
-      <div className="flex items-center justify-between">
+      <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
         <div className="flex items-center gap-2">
           <h3 className="text-lg font-semibold text-slate-800">Docker 容器管理</h3>
           <button
@@ -137,22 +137,22 @@ export default function DockerControl({
             <RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
           </button>
         </div>
-        <div className="flex gap-2">
+        <div className="grid grid-cols-2 sm:flex gap-2">
           <button
             onClick={() => handleComposeAction('up')}
             disabled={loading}
-            className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+            className="px-3 sm:px-4 py-2 bg-green-600 text-white text-sm sm:text-base rounded-md hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
           >
             <Play size={18} />
-            启动所有服务
+            <span className="whitespace-nowrap">全部启动</span>
           </button>
           <button
             onClick={() => handleComposeAction('down')}
             disabled={loading}
-            className="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+            className="px-3 sm:px-4 py-2 bg-red-600 text-white text-sm sm:text-base rounded-md hover:bg-red-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
           >
             <Square size={18} />
-            停止所有服务
+            <span className="whitespace-nowrap">全部停止</span>
           </button>
         </div>
       </div>
@@ -165,80 +165,149 @@ export default function DockerControl({
       )}
 
       {/* 容器列表 */}
-      <div className="bg-slate-50 rounded-lg border border-slate-200 overflow-hidden">
-        <table className="w-full">
-          <thead className="bg-slate-100 border-b border-slate-200">
-            <tr>
-              <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">容器名称</th>
-              <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">状态</th>
-              <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">镜像</th>
-              <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">操作</th>
-            </tr>
-          </thead>
-          <tbody className="divide-y divide-slate-200">
-            {Object.keys(containers).length === 0 ? (
+      <div className="bg-white rounded-lg border border-slate-200 overflow-hidden">
+        {/* 桌面端表格视图 */}
+        <div className="hidden md:block overflow-x-auto">
+          <table className="w-full">
+            <thead className="bg-slate-50 border-b border-slate-200">
               <tr>
-                <td colSpan={4} className="px-4 py-8 text-center text-slate-500">
-                  {loading ? '加载中...' : '暂无容器'}
-                </td>
+                <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">容器名称</th>
+                <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">状态</th>
+                <th className="px-4 py-3 text-left text-sm font-semibold text-slate-700">镜像</th>
+                <th className="px-4 py-3 text-right text-sm font-semibold text-slate-700">操作</th>
               </tr>
-            ) : (
-              Object.entries(containers).map(([name, container]) => (
-                <tr key={name} className="hover:bg-slate-50">
-                  <td className="px-4 py-3 text-sm text-slate-800 font-medium">{name}</td>
-                  <td className="px-4 py-3 text-sm">
-                    <span
-                      className={`inline-flex px-2 py-1 rounded-full text-xs font-medium ${
-                        container.status.includes('Up')
-                          ? 'bg-green-100 text-green-800'
-                          : 'bg-red-100 text-red-800'
-                      }`}
-                    >
-                      {container.status}
-                    </span>
-                  </td>
-                  <td className="px-4 py-3 text-sm text-slate-600">{container.image}</td>
-                  <td className="px-4 py-3 text-right">
-                    <div className="flex items-center justify-end gap-2">
-                      <button
-                        onClick={() => handleDockerAction('start', name)}
-                        disabled={loading || container.status.includes('Up')}
-                        className="p-1.5 text-green-600 hover:bg-green-50 rounded transition-colors disabled:opacity-50"
-                        title="启动"
-                      >
-                        <Play size={16} />
-                      </button>
-                      <button
-                        onClick={() => handleDockerAction('stop', name)}
-                        disabled={loading || !container.status.includes('Up')}
-                        className="p-1.5 text-red-600 hover:bg-red-50 rounded transition-colors disabled:opacity-50"
-                        title="停止"
-                      >
-                        <Square size={16} />
-                      </button>
-                      <button
-                        onClick={() => handleDockerAction('restart', name)}
-                        disabled={loading}
-                        className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors disabled:opacity-50"
-                        title="重启"
-                      >
-                        <RotateCw size={16} />
-                      </button>
-                      <button
-                        onClick={() => fetchLogs(name)}
-                        disabled={loading}
-                        className="p-1.5 text-slate-600 hover:bg-slate-100 rounded transition-colors disabled:opacity-50"
-                        title="查看日志"
-                      >
-                        <FileText size={16} />
-                      </button>
-                    </div>
+            </thead>
+            <tbody className="divide-y divide-slate-200">
+              {Object.keys(containers).length === 0 ? (
+                <tr>
+                  <td colSpan={4} className="px-4 py-8 text-center text-slate-500">
+                    {loading ? '加载中...' : '暂无容器'}
                   </td>
                 </tr>
-              ))
-            )}
-          </tbody>
-        </table>
+              ) : (
+                Object.entries(containers).map(([name, container]) => (
+                  <tr key={name} className="hover:bg-slate-50">
+                    <td className="px-4 py-3 text-sm text-slate-800 font-medium">{name}</td>
+                    <td className="px-4 py-3 text-sm">
+                      <span
+                        className={`inline-flex px-2 py-1 rounded-full text-xs font-medium ${
+                          container.status.includes('Up')
+                            ? 'bg-green-100 text-green-800'
+                            : 'bg-red-100 text-red-800'
+                        }`}
+                      >
+                        {container.status}
+                      </span>
+                    </td>
+                    <td className="px-4 py-3 text-sm text-slate-600">{container.image}</td>
+                    <td className="px-4 py-3 text-right">
+                      <div className="flex items-center justify-end gap-2">
+                        <button
+                          onClick={() => handleDockerAction('start', name)}
+                          disabled={loading || container.status.includes('Up')}
+                          className="p-1.5 text-green-600 hover:bg-green-50 rounded transition-colors disabled:opacity-50"
+                          title="启动"
+                        >
+                          <Play size={16} />
+                        </button>
+                        <button
+                          onClick={() => handleDockerAction('stop', name)}
+                          disabled={loading || !container.status.includes('Up')}
+                          className="p-1.5 text-red-600 hover:bg-red-50 rounded transition-colors disabled:opacity-50"
+                          title="停止"
+                        >
+                          <Square size={16} />
+                        </button>
+                        <button
+                          onClick={() => handleDockerAction('restart', name)}
+                          disabled={loading}
+                          className="p-1.5 text-blue-600 hover:bg-blue-50 rounded transition-colors disabled:opacity-50"
+                          title="重启"
+                        >
+                          <RotateCw size={16} />
+                        </button>
+                        <button
+                          onClick={() => fetchLogs(name)}
+                          disabled={loading}
+                          className="p-1.5 text-slate-600 hover:bg-slate-100 rounded transition-colors disabled:opacity-50"
+                          title="查看日志"
+                        >
+                          <FileText size={16} />
+                        </button>
+                      </div>
+                    </td>
+                  </tr>
+                ))
+              )}
+            </tbody>
+          </table>
+        </div>
+
+        {/* 移动端卡片视图 */}
+        <div className="md:hidden divide-y divide-slate-200">
+          {Object.keys(containers).length === 0 ? (
+            <div className="px-4 py-8 text-center text-slate-500">
+              {loading ? '加载中...' : '暂无容器'}
+            </div>
+          ) : (
+            Object.entries(containers).map(([name, container]) => (
+              <div key={name} className="p-4 space-y-3">
+                <div className="flex items-center justify-between">
+                  <span className="text-sm font-semibold text-slate-800">{name}</span>
+                  <span
+                    className={`inline-flex px-2 py-0.5 rounded-full text-[10px] font-medium ${
+                      container.status.includes('Up')
+                        ? 'bg-green-100 text-green-800'
+                        : 'bg-red-100 text-red-800'
+                    }`}
+                  >
+                    {container.status}
+                  </span>
+                </div>
+                <div className="text-xs text-slate-500 truncate">
+                  <span className="font-medium text-slate-600 mr-1">镜像:</span>
+                  {container.image}
+                </div>
+                <div className="flex items-center justify-between pt-2 border-t border-slate-100">
+                  <div className="flex gap-4">
+                    <button
+                      onClick={() => handleDockerAction('start', name)}
+                      disabled={loading || container.status.includes('Up')}
+                      className="flex flex-col items-center gap-1 text-green-600 disabled:opacity-30"
+                    >
+                      <Play size={18} />
+                      <span className="text-[10px]">启动</span>
+                    </button>
+                    <button
+                      onClick={() => handleDockerAction('stop', name)}
+                      disabled={loading || !container.status.includes('Up')}
+                      className="flex flex-col items-center gap-1 text-red-600 disabled:opacity-30"
+                    >
+                      <Square size={18} />
+                      <span className="text-[10px]">停止</span>
+                    </button>
+                    <button
+                      onClick={() => handleDockerAction('restart', name)}
+                      disabled={loading}
+                      className="flex flex-col items-center gap-1 text-blue-600 disabled:opacity-30"
+                    >
+                      <RotateCw size={18} />
+                      <span className="text-[10px]">重启</span>
+                    </button>
+                  </div>
+                  <button
+                    onClick={() => fetchLogs(name)}
+                    disabled={loading}
+                    className="flex flex-col items-center gap-1 text-slate-600 disabled:opacity-30"
+                  >
+                    <FileText size={18} />
+                    <span className="text-[10px]">日志</span>
+                  </button>
+                </div>
+              </div>
+            ))
+          )}
+        </div>
       </div>
 
       {/* 日志查看 */}

+ 66 - 62
src/components/admin/remote-server/LogViewer.tsx

@@ -106,13 +106,13 @@ export default function LogViewer({
   };
 
   return (
-    <div className="space-y-6">
-      <div className="flex items-center justify-between">
+    <div className="space-y-4 sm:space-y-6">
+      <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
         <h3 className="text-lg font-semibold text-slate-800">日志文件查看</h3>
         <button
           onClick={fetchLogList}
           disabled={loading}
-          className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
+          className="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
         >
           <RefreshCw size={18} className={loading ? 'animate-spin' : ''} />
           刷新列表
@@ -120,17 +120,17 @@ export default function LogViewer({
       </div>
 
       {error && (
-        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-center gap-2">
+        <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-md flex items-center gap-2 text-sm">
           <AlertCircle size={18} />
           {error}
         </div>
       )}
 
-      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
+      <div className="flex flex-col lg:grid lg:grid-cols-3 gap-6">
         {/* 日志文件列表 */}
         <div className="bg-slate-50 rounded-lg border border-slate-200 p-4">
           <h4 className="text-sm font-semibold text-slate-700 mb-3">日志文件列表</h4>
-          <div className="space-y-1 max-h-96 overflow-y-auto">
+          <div className="space-y-1 max-h-48 sm:max-h-96 overflow-y-auto">
             {logFiles.length === 0 ? (
               <p className="text-sm text-slate-500 text-center py-4">暂无日志文件</p>
             ) : (
@@ -138,13 +138,13 @@ export default function LogViewer({
                 <button
                   key={file}
                   onClick={() => setSelectedLog(file)}
-                  className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors ${
+                  className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors truncate ${
                     selectedLog === file
                       ? 'bg-blue-600 text-white'
                       : 'bg-white text-slate-700 hover:bg-slate-100'
                   }`}
                 >
-                  <FileText size={16} className="inline mr-2" />
+                  <FileText size={16} className="inline mr-2 shrink-0" />
                   {file}
                 </button>
               ))
@@ -157,10 +157,10 @@ export default function LogViewer({
           {selectedLog ? (
             <>
               {/* 控制栏 */}
-              <div className="bg-white rounded-lg border border-slate-200 p-4">
-                <div className="flex items-center gap-4 flex-wrap">
+              <div className="bg-white rounded-lg border border-slate-200 p-3 sm:p-4">
+                <div className="flex flex-wrap items-center gap-3 sm:gap-4">
                   <div className="flex items-center gap-2">
-                    <label className="text-sm text-slate-700">行数:</label>
+                    <label className="text-xs sm:text-sm text-slate-700 whitespace-nowrap">行数:</label>
                     <input
                       type="number"
                       value={lines}
@@ -169,73 +169,77 @@ export default function LogViewer({
                         setFull(false);
                       }}
                       disabled={full || loading}
-                      className="w-20 px-2 py-1 border border-slate-300 rounded text-sm disabled:opacity-50"
+                      className="w-16 sm:w-20 px-2 py-1 border border-slate-300 rounded text-xs sm:text-sm disabled:opacity-50"
                     />
                   </div>
-                  <label className="flex items-center gap-2 text-sm text-slate-700">
-                    <input
-                      type="checkbox"
-                      checked={fromHead}
-                      onChange={(e) => {
-                        setFromHead(e.target.checked);
-                        setFull(false);
-                      }}
-                      disabled={full || loading}
-                      className="rounded"
-                    />
-                    从开头读取
-                  </label>
-                  <label className="flex items-center gap-2 text-sm text-slate-700">
-                    <input
-                      type="checkbox"
-                      checked={full}
-                      onChange={(e) => {
-                        setFull(e.target.checked);
-                        if (e.target.checked) {
-                          setFromHead(false);
-                        }
-                      }}
+                  <div className="flex items-center gap-4">
+                    <label className="flex items-center gap-1.5 text-xs sm:text-sm text-slate-700 cursor-pointer">
+                      <input
+                        type="checkbox"
+                        checked={fromHead}
+                        onChange={(e) => {
+                          setFromHead(e.target.checked);
+                          setFull(false);
+                        }}
+                        disabled={full || loading}
+                        className="rounded text-blue-600 focus:ring-blue-500"
+                      />
+                      从开头
+                    </label>
+                    <label className="flex items-center gap-1.5 text-xs sm:text-sm text-slate-700 cursor-pointer">
+                      <input
+                        type="checkbox"
+                        checked={full}
+                        onChange={(e) => {
+                          setFull(e.target.checked);
+                          if (e.target.checked) {
+                            setFromHead(false);
+                          }
+                        }}
+                        disabled={loading}
+                        className="rounded text-blue-600 focus:ring-blue-500"
+                      />
+                      全部
+                    </label>
+                  </div>
+                  <div className="flex items-center gap-2 w-full sm:w-auto">
+                    <button
+                      onClick={fetchLogContent}
                       disabled={loading}
-                      className="rounded"
-                    />
-                    读取全部
-                  </label>
-                  <button
-                    onClick={fetchLogContent}
-                    disabled={loading}
-                    className="px-4 py-1.5 bg-blue-600 text-white text-sm rounded hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
-                  >
-                    <RefreshCw size={16} className={loading ? 'animate-spin' : ''} />
-                    刷新
-                  </button>
-                  <button
-                    onClick={downloadLog}
-                    disabled={!logContent}
-                    className="px-4 py-1.5 bg-green-600 text-white text-sm rounded hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center gap-2"
-                  >
-                    <Download size={16} />
-                    下载
-                  </button>
+                      className="flex-1 sm:flex-none px-3 py-1.5 bg-blue-600 text-white text-xs sm:text-sm rounded hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-1.5"
+                    >
+                      <RefreshCw size={14} className={loading ? 'animate-spin' : ''} />
+                      刷新
+                    </button>
+                    <button
+                      onClick={downloadLog}
+                      disabled={!logContent}
+                      className="flex-1 sm:flex-none px-3 py-1.5 bg-green-600 text-white text-xs sm:text-sm rounded hover:bg-green-700 transition-colors disabled:opacity-50 flex items-center justify-center gap-1.5"
+                    >
+                      <Download size={14} />
+                      下载
+                    </button>
+                  </div>
                 </div>
               </div>
 
               {/* 日志内容显示 */}
-              <div className="bg-slate-900 rounded-lg border border-slate-200 p-4">
+              <div className="bg-slate-900 rounded-lg border border-slate-200 p-3 sm:p-4">
                 <div className="flex items-center justify-between mb-2">
-                  <h4 className="text-sm font-semibold text-white">{selectedLog}</h4>
-                  <span className="text-xs text-slate-400">
+                  <h4 className="text-xs sm:text-sm font-semibold text-white truncate mr-2">{selectedLog}</h4>
+                  <span className="text-[10px] sm:text-xs text-slate-400 whitespace-nowrap">
                     {logContent.split('\n').length} 行
                   </span>
                 </div>
-                <pre className="text-xs text-green-400 font-mono overflow-x-auto max-h-[600px] overflow-y-auto whitespace-pre-wrap">
+                <pre className="text-[10px] sm:text-xs text-green-400 font-mono overflow-x-auto max-h-[400px] sm:max-h-[600px] overflow-y-auto whitespace-pre-wrap">
                   {logContent || (loading ? '加载中...' : '暂无内容')}
                 </pre>
               </div>
             </>
           ) : (
-            <div className="bg-slate-50 rounded-lg border border-slate-200 p-8 text-center">
-              <FileText size={48} className="mx-auto text-slate-400 mb-3" />
-              <p className="text-slate-500">请选择一个日志文件查看</p>
+            <div className="bg-slate-50 rounded-lg border border-slate-200 p-6 sm:p-8 text-center">
+              <FileText className="mx-auto text-slate-400 mb-3 w-8 h-8 sm:w-12 sm:h-12" />
+              <p className="text-sm sm:text-base text-slate-500">请选择一个日志文件查看</p>
             </div>
           )}
         </div>

+ 31 - 31
src/components/admin/remote-server/RemoteServerControl.tsx

@@ -143,15 +143,15 @@ export default function RemoteServerControl() {
   };
 
   return (
-    <div className="space-y-6">
+    <div className="space-y-4 sm:space-y-6">
       {/* 服务器连接配置 */}
-      <div className="bg-white rounded-lg shadow-sm border border-slate-200 p-6">
-        <div className="flex items-center justify-between mb-4">
+      <div className="bg-white rounded-lg shadow-sm border border-slate-200 p-4 sm:p-6">
+        <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 mb-4">
           <div className="flex items-center gap-2">
             <Server className="text-blue-600" size={24} />
-            <h2 className="text-xl font-semibold text-slate-800">服务器连接配置</h2>
+            <h2 className="text-lg sm:text-xl font-semibold text-slate-800">服务器连接配置</h2>
           </div>
-          <div className="flex bg-slate-100 p-1 rounded-md">
+          <div className="flex bg-slate-100 p-1 rounded-md self-start sm:self-auto">
             <button
               onClick={() => {
                 if (!isConnected) setUsePreConfigured(true);
@@ -188,7 +188,7 @@ export default function RemoteServerControl() {
               <select
                 value={selectedServerId}
                 onChange={(e) => setSelectedServerId(e.target.value)}
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               >
                 <option value="">-- 请选择服务器 --</option>
@@ -203,73 +203,73 @@ export default function RemoteServerControl() {
         ) : (
           <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">服务器地址 *</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">服务器地址 *</label>
               <input
                 type="text"
                 value={serverConfig.host}
                 onChange={(e) => setServerConfig({ ...serverConfig, host: e.target.value })}
                 placeholder="192.168.1.100"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
             
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">SSH端口</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">SSH端口</label>
               <input
                 type="number"
                 value={serverConfig.port}
                 onChange={(e) => setServerConfig({ ...serverConfig, port: parseInt(e.target.value) || 22 })}
                 placeholder="22"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
             
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">用户名 *</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">用户名 *</label>
               <input
                 type="text"
                 value={serverConfig.username}
                 onChange={(e) => setServerConfig({ ...serverConfig, username: e.target.value })}
                 placeholder="root"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
             
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">密码</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">密码</label>
               <input
                 type="password"
                 value={serverConfig.password || ''}
                 onChange={(e) => setServerConfig({ ...serverConfig, password: e.target.value })}
                 placeholder="SSH密码"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
             
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">私钥文件路径</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">私钥文件路径</label>
               <input
                 type="text"
                 value={serverConfig.key_file || ''}
                 onChange={(e) => setServerConfig({ ...serverConfig, key_file: e.target.value })}
                 placeholder="/path/to/key.pem"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
             
             <div>
-              <label className="block text-sm font-medium text-slate-700 mb-1">项目路径</label>
+              <label className="block text-sm font-medium text-slate-700 mb-1 text-xs sm:text-sm">项目路径</label>
               <input
                 type="text"
                 value={serverConfig.project_path}
                 onChange={(e) => setServerConfig({ ...serverConfig, project_path: e.target.value })}
                 placeholder="/root/troov-asyncio"
-                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
+                className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                 disabled={isConnected}
               />
             </div>
@@ -278,23 +278,23 @@ export default function RemoteServerControl() {
         
         <div className="mt-4 space-y-2">
           {connectionError && (
-            <div className="bg-red-50 border border-red-200 text-red-700 px-4 py-2 rounded-md text-sm">
+            <div className="bg-red-50 border border-red-200 text-red-700 px-3 sm:px-4 py-2 rounded-md text-xs sm:text-sm overflow-x-auto whitespace-pre-wrap">
               {connectionError}
             </div>
           )}
           {isConnected && (
-            <div className="bg-green-50 border border-green-200 text-green-700 px-4 py-2 rounded-md text-sm flex items-center gap-2">
+            <div className="bg-green-50 border border-green-200 text-green-700 px-3 sm:px-4 py-2 rounded-md text-xs sm:text-sm flex items-center gap-2">
               <Server size={16} />
               已连接到服务器
             </div>
           )}
-          <div className="flex gap-2">
+          <div className="flex flex-wrap gap-2">
             {!isConnected ? (
               <>
                 <button
                   onClick={handleConnect}
                   disabled={connecting}
-                  className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
+                  className="px-4 py-2 bg-blue-600 text-white text-sm sm:text-base rounded-md hover:bg-blue-700 transition-colors flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
                 >
                   <Server size={18} className={connecting ? 'animate-pulse' : ''} />
                   {connecting ? '连接中...' : '连接服务器'}
@@ -308,7 +308,7 @@ export default function RemoteServerControl() {
                       alert(`API 测试失败: ${err.response?.status || '无响应'} - ${err.response?.data?.message || err.message}`);
                     }
                   }}
-                  className="px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors flex items-center gap-2"
+                  className="px-4 py-2 bg-gray-600 text-white text-sm sm:text-base rounded-md hover:bg-gray-700 transition-colors flex items-center gap-2"
                   title="测试后端 API 连接"
                 >
                   <RefreshCw size={18} />
@@ -318,7 +318,7 @@ export default function RemoteServerControl() {
             ) : (
               <button
                 onClick={handleDisconnect}
-                className="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors flex items-center gap-2"
+                className="px-4 py-2 bg-red-600 text-white text-sm sm:text-base rounded-md hover:bg-red-700 transition-colors flex items-center gap-2"
               >
                 <Server size={18} />
                 断开连接
@@ -330,12 +330,12 @@ export default function RemoteServerControl() {
 
       {/* 功能标签页 */}
       {isConnected && (
-        <div className="bg-white rounded-lg shadow-sm border border-slate-200">
-          <div className="border-b border-slate-200">
-            <nav className="flex -mb-px">
+        <div className="bg-white rounded-lg shadow-sm border border-slate-200 overflow-hidden">
+          <div className="border-b border-slate-200 overflow-x-auto">
+            <nav className="flex -mb-px min-w-max">
               <button
                 onClick={() => setActiveTab('docker')}
-                className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
+                className={`px-4 sm:px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
                   activeTab === 'docker'
                     ? 'border-blue-600 text-blue-600'
                     : 'border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300'
@@ -346,7 +346,7 @@ export default function RemoteServerControl() {
               </button>
               <button
                 onClick={() => setActiveTab('logs')}
-                className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
+                className={`px-4 sm:px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
                   activeTab === 'logs'
                     ? 'border-blue-600 text-blue-600'
                     : 'border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300'
@@ -357,7 +357,7 @@ export default function RemoteServerControl() {
               </button>
               <button
                 onClick={() => setActiveTab('config')}
-                className={`px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
+                className={`px-4 sm:px-6 py-3 text-sm font-medium border-b-2 transition-colors flex items-center gap-2 ${
                   activeTab === 'config'
                     ? 'border-blue-600 text-blue-600'
                     : 'border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300'
@@ -369,7 +369,7 @@ export default function RemoteServerControl() {
             </nav>
           </div>
           
-          <div className="p-6">
+          <div className="p-4 sm:p-6">
             {activeTab === 'docker' && (
               <DockerControl 
                 serverConfig={serverConfig}