page.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. "use client";
  2. import { useState, type FormEvent } from "react";
  3. import { useRouter } from "next/navigation";
  4. import { apiUrl } from "@/lib/config";
  5. import { Button } from "@/components/ui/button";
  6. import { Input } from "@/components/ui/input";
  7. import { setAgentWSToken } from "@/utils/storage";
  8. import { useI18n } from "@/lib/i18n/provider";
  9. export default function AgentLoginPage() {
  10. const { t } = useI18n();
  11. const [username, setUsername] = useState("");
  12. const [password, setPassword] = useState("");
  13. const [loading, setLoading] = useState(false);
  14. const [error, setError] = useState("");
  15. const router = useRouter();
  16. // 客服登录
  17. async function handleLogin(e: FormEvent<HTMLFormElement>) {
  18. e.preventDefault(); // 阻止默认行为
  19. if (!username || !password) {
  20. setError(t("agent.login.error.empty"));
  21. return;
  22. }
  23. setLoading(true);
  24. setError("");
  25. try {
  26. const res = await fetch(apiUrl("/login"), {
  27. method: "POST",
  28. headers: { "Content-Type": "application/json" },
  29. body: JSON.stringify({ username, password }),
  30. });
  31. const data = await res.json();
  32. if (res.ok) {
  33. // 登录成功,保存用户信息到 localStorage
  34. localStorage.setItem("agent_user_id", String(data.user_id));
  35. localStorage.setItem("agent_username", data.username);
  36. localStorage.setItem("agent_role", data.role);
  37. localStorage.setItem(
  38. "agent_permissions",
  39. JSON.stringify(Array.isArray(data.permissions) ? data.permissions : [])
  40. );
  41. if (typeof data.ws_token === "string" && typeof data.ws_token_exp === "number") {
  42. setAgentWSToken(data.ws_token, data.ws_token_exp);
  43. }
  44. // 跳转到客服工作台(三栏布局)
  45. router.push("/agent/dashboard");
  46. } else {
  47. // 登录失败,显示错误信息
  48. setError(data.error || data.message || t("agent.login.error.failed"));
  49. }
  50. } catch (error) {
  51. console.error("登录失败:", error);
  52. setError(t("agent.login.error.network"));
  53. } finally {
  54. setLoading(false);
  55. }
  56. }
  57. return (
  58. <div className="flex justify-center items-center min-h-screen bg-background">
  59. <div className="bg-card p-8 rounded-lg border shadow-lg w-full sm:w-96">
  60. <h1 className="text-center text-2xl font-bold mb-2 text-gray-800">
  61. {t("agent.login.title")}
  62. </h1>
  63. <p className="text-center text-sm text-gray-500 mb-6">
  64. {t("agent.login.subtitle")}
  65. </p>
  66. <form onSubmit={handleLogin}>
  67. <Input
  68. type="text"
  69. placeholder={t("agent.login.username")}
  70. value={username}
  71. onChange={(e) => setUsername(e.target.value)}
  72. className="w-full mb-4"
  73. disabled={loading}
  74. />
  75. <Input
  76. type="password"
  77. placeholder={t("agent.login.password")}
  78. value={password}
  79. onChange={(e) => setPassword(e.target.value)}
  80. className="w-full mb-4"
  81. disabled={loading}
  82. />
  83. {error && (
  84. <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-md text-red-600 text-sm">
  85. {error}
  86. </div>
  87. )}
  88. <Button
  89. type="submit"
  90. disabled={loading}
  91. variant="default"
  92. size="default"
  93. className="w-full"
  94. >
  95. {loading ? t("agent.login.submitting") : t("agent.login.submit")}
  96. </Button>
  97. </form>
  98. <div className="mt-4 text-center text-xs text-gray-400">
  99. <p>{t("agent.login.demoHint")}</p>
  100. </div>
  101. </div>
  102. </div>
  103. );
  104. }