Переглянути джерело

Merge branch 'zzf-account'

zhangzf 1 тиждень тому
батько
коміт
b5f36fd596

+ 1 - 0
package.json

@@ -29,6 +29,7 @@
     "@heroui/tooltip": "^2.2.29",
     "@heroui/use-disclosure": "^2.2.19",
     "@paypal/react-paypal-js": "^9.2.0",
+    "@internationalized/date": "3.12.0",
     "@react-aria/visually-hidden": "^3.8.31",
     "@react-types/shared": "^3.33.1",
     "@reduxjs/toolkit": "^2.10.1",

+ 10 - 7
pnpm-lock.yaml

@@ -66,6 +66,9 @@ importers:
       '@paypal/react-paypal-js':
         specifier: ^9.2.0
         version: 9.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+      '@internationalized/date':
+        specifier: 3.12.0
+        version: 3.12.0
       '@react-aria/visually-hidden':
         specifier: ^3.8.31
         version: 3.8.31(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@@ -4250,7 +4253,7 @@ snapshots:
 
   '@react-aria/calendar@3.9.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@react-aria/i18n': 3.12.16(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
       '@react-aria/interactions': 3.27.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
       '@react-aria/live-announcer': 3.5.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@@ -4265,7 +4268,7 @@ snapshots:
 
   '@react-aria/datepicker@3.16.1(@react-spectrum/provider@3.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@internationalized/number': 3.6.5
       '@internationalized/string': 3.2.7
       '@react-aria/focus': 3.21.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@@ -4321,7 +4324,7 @@ snapshots:
 
   '@react-aria/i18n@3.12.16(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@internationalized/message': 3.1.8
       '@internationalized/number': 3.6.5
       '@internationalized/string': 3.2.7
@@ -4545,7 +4548,7 @@ snapshots:
 
   '@react-stately/calendar@3.9.3(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@react-stately/utils': 3.11.0(react@19.2.5)
       '@react-types/calendar': 3.8.3(react@19.2.5)
       '@react-types/shared': 3.33.1(react@19.2.5)
@@ -4560,7 +4563,7 @@ snapshots:
 
   '@react-stately/datepicker@3.16.1(@react-spectrum/provider@3.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@internationalized/number': 3.6.5
       '@internationalized/string': 3.2.7
       '@react-stately/form': 3.2.4(react@19.2.5)
@@ -4666,7 +4669,7 @@ snapshots:
 
   '@react-types/calendar@3.8.3(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@react-types/shared': 3.33.1(react@19.2.5)
       react: 19.2.5
 
@@ -4686,7 +4689,7 @@ snapshots:
 
   '@react-types/datepicker@3.13.5(@react-spectrum/provider@3.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
     dependencies:
-      '@internationalized/date': 3.12.0
+      '@internationalized/date': 3.12.1
       '@react-types/calendar': 3.9.0(@react-spectrum/provider@3.11.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
       '@react-types/overlays': 3.9.4(react@19.2.5)
       '@react-types/shared': 3.33.1(react@19.2.5)

+ 109 - 13
src/app/(public)/customer/account/edit/page.tsx

@@ -1,12 +1,21 @@
 "use client";
 import React from "react";
 import { useState } from "react";
+import { useEffect } from "react";
+import {
+  parseDate,
+  getLocalTimeZone,
+  CalendarDate,
+} from "@internationalized/date";
+import { DatePicker } from "@heroui/date-picker";
 const AccountEditPage = () => {
   // 统一表单对象
   const [form, setForm] = useState({
     firstname: "",
     lastname: "",
+    date: parseDate("2026-05-12"),
     email: "",
+    current_password: "",
     password: "",
   });
 
@@ -18,16 +27,66 @@ const AccountEditPage = () => {
       [name]: value,
     }));
   };
+  // 1. 定义状态:控制修改密码显示隐藏
+  const [showContent, setShowContent] = useState(false);
+  //   日期选择方法
+  const handleDateChange = (dateValue: CalendarDate | null) => {
+    if (!dateValue) return;
 
+    // 把 CalendarDate 转成 标准字符串:YYYY-MM-DD
+    const formattedDate = `${dateValue.year}-${String(dateValue.month).padStart(2, "0")}-${String(dateValue.day).padStart(2, "0")}`;
+
+    setForm((prev) => ({
+      ...prev,
+      date: formattedDate, // 现在是正常字符串了
+    }));
+  };
+  // 勾选框改变时:隐藏时 → 清空密码
+  const handleCheckChange = (e) => {
+    const isChecked = e.target.checked;
+    setShowContent(isChecked);
+
+    // ✅ 关键:不勾选时清空密码字段
+    if (!isChecked) {
+      setForm((prev) => ({
+        ...prev,
+        current_password: "",
+        password: "",
+      }));
+    }
+  };
   // 统一提交
   const handleSubmit = (e) => {
     e.preventDefault();
     // 直接拿form所有数据
     console.log("表单数据:", form);
   };
+  useEffect(() => {
+    // console.log("form 已更新 →", form);
+  }, [form]);
   return (
     <div className="w-full h-full">
       <form className="overflow-hidden" onSubmit={handleSubmit}>
+        <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
+          <a href="/customer/account" className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2">
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="24"
+              height="24"
+              viewBox="0 0 24 24"
+              fill="none"
+            >
+              <path
+                stroke="rgba(0, 0, 0, 1)"
+                stroke-width="1.2"
+                stroke-linejoin="round"
+                stroke-linecap="round"
+                d="M15 18L9 12L15 6"
+              ></path>
+            </svg>
+          </a>
+          <span className="vipReturnTitles">Account Information</span>
+        </div>
         <input name="form_key" type="hidden" value="kW7wucXHATeeem75"></input>
         <div className="bg-[#f8f8f8] p-[20px_10px]">
           <div className="text-start">*Name</div>
@@ -47,27 +106,64 @@ const AccountEditPage = () => {
               className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
             />
           </div>
-          <div>
+          <div className="mt-7.5">
+            <DatePicker
+              size="base"
+              onChange={handleDateChange}
+              radius="sm"
+              inputClassName="w-full h-[44px] bg-[#fff]"
+              className="bg-[#fff]"
+              labelPlacement={"outside"}
+              showMonthAndYearPickers
+              aria-label="date picker"
+            />
+            {/* label={"Date of Birth"} */}
+          </div>
+          <div className="mt-7.5">
+            <label htmlFor="email">Email Address*</label>
             <input
               name="email" // 必须和state字段对应
               value={form.email}
               onChange={handleChange}
-              placeholder="用户名"
+              placeholder="email"
+              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
             />
           </div>
-
-          <div>
+          {/* 选择框(开关)*/}
+          <label className="flex items-center gap-2 cursor-pointer mt-7.5">
             <input
-              type="password"
-              name="password" // name对应key
-              value={form.password}
-              onChange={handleChange}
-              placeholder="密码"
+              type="checkbox"
+              checked={showContent}
+              onChange={handleCheckChange}
+              className="w-5 h-5"
             />
-          </div>
-
-          <button type="submit" style={{ marginTop: 15 }}>
-            登录提交
+            <span className="text-base">Change Password</span>
+          </label>
+          {showContent && (
+            <div>
+              <input
+                type="password"
+                name="current_password" // name对应key
+                value={form.current_password}
+                onChange={handleChange}
+                placeholder="Old password"
+                className="mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+              />
+              <input
+                type="password"
+                name="password" // name对应key
+                value={form.password}
+                onChange={handleChange}
+                placeholder="* New Password"
+                className=" mt-7.5 w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+              />
+            </div>
+          )}
+          <button
+            className="w-full h-11 leading-11 bg-[#1bbc9b] text-sm font-normal text-center mt-7.5"
+            type="submit"
+          >
+            Save Changes
           </button>
         </div>
       </form>

+ 172 - 0
src/app/(public)/customer/address/new/page.tsx

@@ -0,0 +1,172 @@
+"use client";
+import React from "react";
+import { useState } from "react";
+import { useEffect } from "react";
+const NewAddressPage = () => {
+  // 统一表单对象
+  const [form, setForm] = useState({
+    firstname: "",
+    lastname: "",
+    email: "",
+    postcode: "",
+    street: "",
+    city: "",
+    default_billing: 0, // 账单地址默认
+    default_shipping: 0, // 收货地址默认
+  });
+  // 统一处理所有input变化
+  const handleChange = (e) => {
+    const { name, value } = e.target;
+    setForm((prev) => ({
+      ...prev,
+      [name]: value,
+    }));
+  };
+  // 统一提交
+  const handleSubmit = (e) => {
+    e.preventDefault();
+    const submitData = {
+      ...form,
+      street: [form.street], // 👈 自动变成数组,后端就能收到 street[]
+    };
+
+    // 直接拿form所有数据
+    console.log("表单数据:", form);
+  };
+  // ========== 账单地址勾选框 ==========
+  const handleBillingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setForm((prev) => ({
+      ...prev,
+      default_billing: e.target.checked ? 1 : 0,
+    }));
+  };
+
+  // ========== 收货地址勾选框 ==========
+  const handleShippingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    setForm((prev) => ({
+      ...prev,
+      default_shipping: e.target.checked ? 1 : 0,
+    }));
+  };
+  return (
+    <div className="w-full h-full">
+      <form className="overflow-hidden" onSubmit={handleSubmit}>
+        <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
+          <div
+            onClick={() => window.history.back()}
+            className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
+          >
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="24"
+              height="24"
+              viewBox="0 0 24 24"
+              fill="none"
+            >
+              <path
+                stroke="rgba(0, 0, 0, 1)"
+                stroke-width="1.2"
+                stroke-linejoin="round"
+                stroke-linecap="round"
+                d="M15 18L9 12L15 6"
+              ></path>
+            </svg>
+          </div>
+          <span className="vipReturnTitles">Add New Address</span>
+        </div>
+        <input name="form_key" type="hidden" value="kW7wucXHATeeem75"></input>
+        <div className="bg-[#f8f8f8] p-[20px_10px]">
+          <div className="text-start">*Name</div>
+          <div className="flex justify-between items-center">
+            <input
+              name="firstname" // 必须和state字段对应
+              value={form.firstname}
+              onChange={handleChange}
+              placeholder="firstname"
+              className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
+            />
+            <input
+              name="lastname" // 必须和state字段对应
+              value={form.lastname}
+              onChange={handleChange}
+              placeholder="lastname"
+              className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+
+          <div className="mt-7.5">
+            <label htmlFor="email">Email Address*</label>
+            <input
+              name="email" // 必须和state字段对应
+              value={form.email}
+              onChange={handleChange}
+              placeholder="email"
+              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+          <div className="mt-7.5">
+            <label htmlFor="postcode">Zip/Postal Code</label>
+            <input
+              name="postcode" // 必须和state字段对应
+              value={form.postcode}
+              onChange={handleChange}
+              placeholder="Zip/Postal Code"
+              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+          <div className="mt-7.5">
+            <label htmlFor="street">Street Address</label>
+            <input
+              name="street" // 必须和state字段对应
+              value={form.street}
+              onChange={handleChange}
+              placeholder="Street Address"
+              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+          <div className="mt-7.5">
+            <label htmlFor="city">City</label>
+            <input
+              name="city" // 必须和state字段对应
+              value={form.city}
+              onChange={handleChange}
+              placeholder="City"
+              className="w-full h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+          {/* 选择框(开关)*/}
+          <label className="flex items-center gap-2 cursor-pointer mt-7.5">
+            <input
+              type="checkbox"
+              checked={form.default_billing === 1}
+              onChange={handleBillingChange}
+              name="default_billing"
+              className="w-5 h-5"
+            />
+            <span className="text-base">Use as my default billing address</span>
+          </label>
+          {/* 选择框(开关)*/}
+          <label className="flex items-center gap-2 cursor-pointer mt-7.5">
+            <input
+              type="checkbox"
+              checked={form.default_shipping === 1}
+              onChange={handleShippingChange}
+              name="default_shipping"
+              className="w-5 h-5"
+            />
+            <span className="text-base">
+              Use as my default shipping address
+            </span>
+          </label>
+          <button
+            className="w-full h-11 leading-11 bg-[#1bbc9b] text-sm font-normal text-center mt-7.5"
+            type="submit"
+          >
+            Save Address
+          </button>
+        </div>
+      </form>
+    </div>
+  );
+};
+export default NewAddressPage;

+ 100 - 0
src/app/(public)/customer/address/page.tsx

@@ -0,0 +1,100 @@
+"use client";
+import React from "react";
+import { useState } from "react";
+import { useEffect } from "react";
+const CustomerAddressPage = () => {
+  // 模拟地址数据(可根据实际需求替换)
+  const addressData = {
+    billing: {
+      name: "zhang xxxx",
+      line1: "ddddd",
+      line2: "aaaaaaaa",
+      cityStateZip: "cscsc, Alabama, 90605",
+      country: "United States",
+      phone: "2123165641",
+    },
+    shipping: {
+      name: "zhang xxxx",
+      line1: "ddddd",
+      line2: "aaaaaaaa",
+      cityStateZip: "cscsc, Alabama, 90605",
+      country: "United States",
+      phone: "2123165641",
+    },
+  };
+  return (
+    <div className="w-full h-full">
+      <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
+        <div
+          onClick={() => window.history.back()}
+          className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
+        >
+          <svg
+            xmlns="http://www.w3.org/2000/svg"
+            width="24"
+            height="24"
+            viewBox="0 0 24 24"
+            fill="none"
+          >
+            <path
+              stroke="rgba(0, 0, 0, 1)"
+              stroke-width="1.2"
+              stroke-linejoin="round"
+              stroke-linecap="round"
+              d="M15 18L9 12L15 6"
+            ></path>
+          </svg>
+        </div>
+        <span className="vipReturnTitles">SHIPPING ADDRESS</span>
+      </div>
+      <div className="max-w-md mx-auto p-4 bg-[#f8f8f8]">
+        {/* 账单地址模块 */}
+        <div className="mb-6">
+          <h2 className="text-xl font-bold uppercase mb-3">
+            Default Billing Address
+          </h2>
+          <div className="border border-gray-200 p-4">
+            <p className="mb-1">{addressData.billing.name}</p>
+            <p className="mb-1">{addressData.billing.line1}</p>
+            <p className="mb-1">{addressData.billing.line2}</p>
+            <p className="mb-1">{addressData.billing.cityStateZip}</p>
+            <p className="mb-1">{addressData.billing.country}</p>
+            <p className="mb-4">T: {addressData.billing.phone}</p>
+            <div className="text-right">
+              <a
+                href="#"
+                className="text-blue-600 underline hover:text-blue-800"
+              >
+                Change Billing Address
+              </a>
+            </div>
+          </div>
+        </div>
+
+        {/* 收货地址模块 */}
+        <div>
+          <h2 className="text-xl font-bold uppercase mb-3">
+            Default Shipping Address
+          </h2>
+          <div className="border border-gray-200 p-4">
+            <p className="mb-1">{addressData.shipping.name}</p>
+            <p className="mb-1">{addressData.shipping.line1}</p>
+            <p className="mb-1">{addressData.shipping.line2}</p>
+            <p className="mb-1">{addressData.shipping.cityStateZip}</p>
+            <p className="mb-1">{addressData.shipping.country}</p>
+            <p className="mb-4">T: {addressData.shipping.phone}</p>
+            <div className="text-right">
+              <a
+                href="#"
+                className="text-blue-600 underline hover:text-blue-800"
+              >
+                Change Shipping Address
+              </a>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default CustomerAddressPage;