Browse Source

Merge branch 'master' of http://172.14.1.63:3000/liuxf/ds-yamato-bbi-base

huning 5 months ago
parent
commit
538bfc524d
4 changed files with 671 additions and 155 deletions
  1. 2 1
      package.json
  2. 178 146
      src/pages/Base/Order/index.tsx
  3. 480 0
      src/pages/Partner/Order/ChatPartner.tsx
  4. 11 8
      src/pages/Partner/Order/index.tsx

+ 2 - 1
package.json

@@ -78,7 +78,8 @@
     "react-dev-inspector": "^2.0.1",
     "react-dom": "^18.3.0",
     "react-helmet-async": "^2.0.0",
-    "react-highlight": "^0.15.0"
+    "react-highlight": "^0.15.0",
+    "react-tabs": "^6.1.0"
   },
   "devDependencies": {
     "@ant-design/pro-cli": "^3.3.0",

+ 178 - 146
src/pages/Base/Order/index.tsx

@@ -44,53 +44,52 @@ const reasonOptions = [
     "休日調整",
     "その他"
 ];
-
-const orderInfo = {
-    deliveryPartner: {
-        id: "0311111111",
-        name: "AAA株式会社"
-    },
-    status: {
-        companyCode: "R000001",
-        status: "全承諾",
-        version: 1,
-        instructionDate: "2025/6/25",
-        approvalDate: "2025/6/25"
-    },
-    monthlyInstruction: {
-        versionOptions: ["ver-1", "ver-2", "ver-3", "ver-4", "ver-5"],
-        selectedVersion: "ver-1"
-    }
-};
-
+// TODO type
 const historyData = [
     {
         type: '履歴',
-        date: '2025/06/16 10:00',
-        action: '指示書生成'
+        actionDate: '2025/06/16 10:00',
+        actionType: '指示書生成'
     },
     {
         type: '履歴',
-        date: '2025/06/16 15:24',
-        action: '指示書編集'
+        actionDate: '2025/06/16 15:24',
+        actionType: '指示書編集'
     },
     {
         type: '履歴',
-        date: '2025/06/16 15:24',
-        action: '指示書送付'
+        actionDate: '2025/06/16 15:24',
+        actionType: '指示書送付'
     }
 ];
 
 const weekTexts = ["火", "水", "木", "金", "土", "日", "月"];
 
 const GridExample = () => {
+     const [orderInfo, setOrderInfo] = useState({
+        deliveryPartner: {
+            id: "0311111111",
+            corpName: "AAA株式会社"
+        },
+        status: {
+            corpCode: "R000001",
+            orderStatus: "全承諾",
+            version: 1,
+            instructionDeliveryDate: "2025/6/25",
+            commitDate: "2025/6/25"
+        },
+        monthlyInstruction: {
+            versionOptions: ["ver-1", "ver-2", "ver-3", "ver-4", "ver-5"],
+            selectedVersion: "ver-1"
+        }
+    });
     const containerStyle = useMemo(() => ({ width: "100%" }), []);
     const gridStyle = useMemo(() => ({ width: "100%" }), []);
     const [rowData, setRowData] = useState(initializeRowData());
     const [showDialog, setShowDialog] = useState(false);
     const [modifiedCells, setModifiedCells] = useState<Set<string>>(new Set());
     const [selectedCell, setSelectedCell] = useState<{rowIndex: number | null, colId: string, currentValue: string} | null>(null);
-    const [cellReasons, setCellReasons] = useState<Record<string, string>>({}) ;
+    const [cellReasons, setCellReasons] = useState<Record<string, string>>({});
     const [showChat, setShowChat] = useState(false); // 控制聊天侧边栏显示的状态
 
     // 初始化样式
@@ -147,17 +146,17 @@ const GridExample = () => {
         font-weight: normal;
       }
 
-      .version-select-container {
+      .base-select-container {
         display: flex;
         align-items: center;
         margin-left: 20px;
       }
-      .version-select-container label {
+      .base-select-container label {
         margin-right: 10px;
         white-space: nowrap;
         font-size: 14px;
       }
-      .version-select-container select {
+      .base-select-container select {
         padding: 5px 35px;
         font-size: 14px;
         margin-left: 20px;
@@ -286,12 +285,14 @@ const GridExample = () => {
             columns.push({
                 headerName: i.toString(),
                 headerClass: "header-center date-group-header",
+            
                 children: [
                     {
                         field: colId,
                         headerName: weekTexts[weekIndex],
                         headerClass: "header-center",
-                        width: 55,
+                        flex: 1,
+                        minWidth: 55, 
                         cellRenderer: (params: ICellRendererParams) => {
                             const cellValue = params.value || "";
                             const rowIndex = params.node.rowIndex;
@@ -342,14 +343,14 @@ const GridExample = () => {
     function initializeRowData() {
         return [
             {
-                "AM": "",
-                "": "032990",
-                "": "025990",
-                "系統": "02",
-                "線便名": "大賀10t①",
-                "入庫場所": "羽田CGB",
-                "入庫時間": "21:00",
-                "使用車両(トン)": "13",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "025990",
+                "systemCode": "02",
+                "lineName": "大賀10t①",
+                "wareHouseLocation": "羽田CGB",
+                "wareHouseTime": "21:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "東京支店",
                 "approveStatus": "approved",
@@ -358,14 +359,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "032990",
-                "": "027990",
-                "系統": "23",
-                "線便名": "大賀10t②",
-                "入庫場所": "桜丘",
-                "入庫時間": "19:00",
-                "使用車両(トン)": "13",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "027990",
+                "systemCode": "23",
+                "lineName": "大賀10t②",
+                "wareHouseLocation": "桜丘",
+                "wareHouseTime": "19:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "神奈川支店",
                 "approveStatus": "unapproved",
@@ -374,14 +375,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "*",
-                "": "032990",
-                "": "027990",
-                "系統": "G8",
-                "線便名": "大賀10t③",
-                "入庫場所": "目黒",
-                "入庫時間": "18:00",
-                "使用車両(トン)": "13",
+                "amFlag": "*",
+                "departureCode": "032990",
+                "arrivalCode": "027990",
+                "systemCode": "G8",
+                "lineName": "大賀10t③",
+                "wareHouseLocation": "目黒",
+                "wareHouseTime": "18:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "品川営業所",
                 "approveStatus": "approved",
@@ -390,14 +391,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "032990",
-                "": "027990",
-                "系統": "G9",
-                "線便名": "西大阪交表",
-                "入庫場所": "羽田CGB",
-                "入庫時間": "21:00",
-                "使用車両(トン)": "13",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "027990",
+                "systemCode": "G9",
+                "lineName": "西大阪交表",
+                "wareHouseLocation": "羽田CGB",
+                "wareHouseTime": "21:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "札幌支店",
                 "approveStatus": "unapproved",
@@ -406,14 +407,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "061990",
-                "": "032990",
-                "系統": "06",
-                "線便名": "羽田CG交裏",
-                "入庫場所": "西大阪B",
-                "入庫時間": "19:00",
-                "使用車両(トン)": "10",
+                "amFlag": "",
+                "departureCode": "061990",
+                "arrivalCode": "032990",
+                "systemCode": "06",
+                "lineName": "羽田CG交裏",
+                "wareHouseLocation": "西大阪B",
+                "wareHouseTime": "19:00",
+                "vehicleTon": "10",
                 "checkboxCol": "",
                 "tokyoBranchCol": "福岡支店",
                 "approveStatus": "approved",
@@ -422,14 +423,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "032990",
-                "": "032990",
-                "系統": "M8",
-                "線便名": "大賀10t⑥",
-                "入庫場所": "羽田CGB",
-                "入庫時間": "21:00",
-                "使用車両(トン)": "13",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "032990",
+                "systemCode": "M8",
+                "lineName": "大賀10t⑥",
+                "wareHouseLocation": "羽田CGB",
+                "wareHouseTime": "21:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "東京支店",
                 "approveStatus": "approved",
@@ -438,14 +439,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "032990",
-                "": "053990",
-                "系統": "06",
-                "線便名": "中部日祝",
-                "入庫場所": "成城",
-                "入庫時間": "19:00",
-                "使用車両(トン)": "13",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "053990",
+                "systemCode": "06",
+                "lineName": "中部日祝",
+                "wareHouseLocation": "成城",
+                "wareHouseTime": "19:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "神奈川支店",
                 "approveStatus": "unapproved",
@@ -454,14 +455,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "*",
-                "": "032990",
-                "": "027990",
-                "系統": "G8",
-                "線便名": "大賀10t③",
-                "入庫場所": "目黒",
-                "入庫時間": "18:00",
-                "使用車両(トン)": "13",
+                "amFlag": "*",
+                "departureCode": "032990",
+                "arrivalCode": "027990",
+                "systemCode": "G8",
+                "lineName": "大賀10t③",
+                "wareHouseLocation": "目黒",
+                "wareHouseTime": "18:00",
+                "vehicleTon": "13",
                 "checkboxCol": "",
                 "tokyoBranchCol": "品川営業所",
                 "approveStatus": "approved",
@@ -470,14 +471,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "032990",
-                "": "132990",
-                "系統": "02",
-                "線便名": "大賀7t",
-                "入庫場所": "八幡山",
-                "入庫時間": "19:00",
-                "使用車両(トン)": "2",
+                "amFlag": "",
+                "departureCode": "032990",
+                "arrivalCode": "132990",
+                "systemCode": "02",
+                "lineName": "大賀7t",
+                "wareHouseLocation": "八幡山",
+                "wareHouseTime": "19:00",
+                "vehicleTon": "2",
                 "checkboxCol": "",
                 "tokyoBranchCol": "札幌支店",
                 "approveStatus": "unapproved",
@@ -486,14 +487,14 @@ const GridExample = () => {
                 })).reduce((acc, curr) => ({...acc,...curr }), {})
             },
             {
-                "AM": "",
-                "": "061990",
-                "": "032990",
-                "系統": "06",
-                "線便名": "羽田CG交裏",
-                "入庫場所": "西大阪B",
-                "入庫時間": "19:00",
-                "使用車両(トン)": "10",
+                "amFlag": "",
+                "departureCode": "061990",
+                "arrivalCode": "032990",
+                "systemCode": "06",
+                "lineName": "羽田CG交裏",
+                "wareHouseLocation": "西大阪B",
+                "wareHouseTime": "19:00",
+                "vehicleTon": "10",
                 "checkboxCol": "",
                 "tokyoBranchCol": "福岡支店",
                 "approveStatus": "approved",
@@ -542,17 +543,31 @@ const GridExample = () => {
                 const status = params.data?.approveStatus;
                 const content = status === "approved"? (
                     <span>
-        承認済
-        <a
-            href="#"
-            style={{
-                marginLeft: "5px",
-                color: "#3a8aca"
-            }}
-        >
-          差戻し
-        </a>
-      </span>
+  承認済
+  <a
+    href="#"
+    style={{
+      marginLeft: "5px",
+      color: "#3a8aca",
+      cursor: "pointer",
+      textDecoration: "underline"
+    }}
+    onClick={(e) => {
+      e.preventDefault();
+      const rowIndex = params.node.rowIndex;
+      if (rowIndex!== null) {
+        const newRowData = [...rowData];
+        newRowData[rowIndex] = {
+         ...newRowData[rowIndex],
+          approveStatus: "unapproved"
+        };
+        setRowData(newRowData);
+      }
+    }}
+  >
+    差戻し
+  </a>
+</span>
                 ) : status === "unapproved"? (
                     <span>未承認</span>
                 ) : (
@@ -567,8 +582,9 @@ const GridExample = () => {
             headerClass: "header-center"
         },
         {
-            field: "AM",
+            field: "amFlag",
             headerClass: "header-center",
+            headerName: "AM",
             width: 60,
         },
         {
@@ -576,32 +592,37 @@ const GridExample = () => {
             headerClass: "header-center line-group-header",
             children: [
                 {
-                    field: "",
+                    field: "departureCode",
                     headerClass: "header-center",
+                    headerName: "発",
                     width: 85,
                 },
                 {
-                    field: "",
+                    field: "arrivalCode",
                     headerClass: "header-center",
+                    headerName: "着",
                     width: 85,
                 },
                 {
-                    field: "系統",
+                    field: "systemCode",
                     headerClass: "header-center",
+                    headerName: "系統",
                     width: 65,
                 },
                 {
-                    field: "線便名",
+                    field: "lineName",
                     headerClass: "header-center",
+                    headerName: "線便名",
                     width: 120,
                 },
             ],
         },
-        { field: "入庫場所", headerClass: "header-center", width: 110 },
-        { field: "入庫時間", headerClass: "header-center", width: 90 },
+        { field: "wareHouseLocation", headerClass: "header-center",headerName: "入庫場所", width: 110 },
+        { field: "wareHouseTime", headerClass: "header-center",headerName: "入庫時間", width: 90 },
         {
-            field: "使用車両(トン)",
+            field: "vehicleTon",
             headerClass: "header-center",
+            headerName: "使用車両(トン)",
             width: 125,
             cellRenderer: (params: ICellRendererParams) => {
                 return (
@@ -663,14 +684,14 @@ const GridExample = () => {
 
     const handleAddRow = () => {
         const newRow: any = {
-            "AM": "",
-            "": "",
-            "": "",
-            "系統": "",
-            "線便名": "",
-            "入庫場所": "",
-            "入庫時間": "",
-            "使用車両(トン)": "",
+            "amFlag": "",
+            "departureCode": "",
+            "arrivalCode": "",
+            "systemCode": "",
+            "lineName": "",
+            "wareHouseLocation": "",
+            "wareHouseTime": "",
+            "vehicleTon": "",
             "checkboxCol": "",
             "tokyoBranchCol": "",
             "approveStatus": ""
@@ -703,7 +724,7 @@ const GridExample = () => {
                         輸送パートナーID:{orderInfo.deliveryPartner.id}
                     </div>
                     <div className="order-info-row">
-                        輸送パートナー名:{orderInfo.deliveryPartner.name}
+                        輸送パートナー名:{orderInfo.deliveryPartner.corpName}
                     </div>
                 </div>
 
@@ -721,11 +742,11 @@ const GridExample = () => {
                         </thead>
                         <tbody>
                         <tr>
-                            <td>{orderInfo.status.companyCode}</td>
-                            <td>{orderInfo.status.status}</td>
+                            <td>{orderInfo.status.corpCode}</td>
+                            <td>{orderInfo.status.orderStatus}</td>
                             <td>{orderInfo.status.version}</td>
-                            <td>{orderInfo.status.instructionDate}</td>
-                            <td>{orderInfo.status.approvalDate}</td>
+                            <td>{orderInfo.status.instructionDeliveryDate}</td>
+                            <td>{orderInfo.status.commitDate}</td>
                         </tr>
                         </tbody>
                     </table>
@@ -733,9 +754,20 @@ const GridExample = () => {
 
                 <div className="order-info-section">
                     <h3>月間運行指示書</h3>
-                    <div className="version-select-container">
+                    <div className="base-select-container">
                         <label>version選択</label>
-                        <select value={orderInfo.monthlyInstruction.selectedVersion}>
+                         <select 
+                            value={orderInfo.monthlyInstruction.selectedVersion}
+                            onChange={(e) => {
+                                setOrderInfo(prev => ({
+                                   ...prev,
+                                    monthlyInstruction: {
+                                       ...prev.monthlyInstruction,
+                                        selectedVersion: e.target.value
+                                    }
+                                }));
+                            }}
+                        >
                             {orderInfo.monthlyInstruction.versionOptions.map(version => (
                                 <option key={version} value={version}>{version}</option>
                             ))}
@@ -774,8 +806,8 @@ const GridExample = () => {
                     {historyData.map((item, index) => (
                         <tr key={index}>
                             <td className="type-td">{item.type}</td>
-                            <td className="date-td">{item.date}</td>
-                            <td className="action-td">{item.action}</td>
+                            <td className="date-td">{item.actionDate}</td>
+                            <td className="action-td">{item.actionType}</td>
                         </tr>
                     ))}
                     </tbody>

+ 480 - 0
src/pages/Partner/Order/ChatPartner.tsx

@@ -0,0 +1,480 @@
+import React, { useState, useRef, useEffect } from "react";
+
+// 聊天组件 props 类型
+type ChatProps = {
+  onClose: () => void;
+};
+
+// 聊天消息类型定义
+type ChatMessage = {
+  id: string;
+  sender: "other" | "me";
+  content: string;
+  timestamp: string;
+  attachment?: {
+    name: string;
+    url: string;
+  };
+};
+
+// 聊天组件 props 类型
+type ChatComponentProps = {
+  messageList: ChatMessage[];
+  onClose: () => void;
+};
+
+const ChatComponent: React.FC<ChatComponentProps> = ({ messageList, onClose }) => {
+  const messagesEndRef = useRef<HTMLDivElement>(null);
+  // 新增:下拉选中值状态
+  const [selectedBase, setSelectedBase] = useState("羽田ベース"); 
+  // 新增:下拉假数据选项
+  const baseOptions = ["羽田ベース", "成田ベース", "大阪ベース", "福岡ベース"]; 
+
+  const baseMessageStyle = {
+    display: "flex",
+    marginBottom: "12px",
+    width: "100%",
+  } as React.CSSProperties;
+
+  const senderInfoStyle = {
+    display: "flex",
+    alignItems: "center",
+    marginBottom: "4px",
+  } as React.CSSProperties;
+
+  const iconStyle = {
+    width: "20px",
+    height: "20px",
+    marginRight: "4px",
+  } as React.CSSProperties;
+
+  const senderNameStyle = {
+    fontSize: "14px",
+    fontWeight: "bold",
+  } as React.CSSProperties;
+
+  const bubbleStyle = {
+    padding: "10px 14px",
+    borderRadius: "8px",
+    boxShadow: "0 1px 2px rgba(0,0,0,0.1)",
+    maxWidth: "70%",
+    wordWrap: "break-word",
+    position: "relative", 
+  } as React.CSSProperties;
+
+  const contentStyle = {
+    marginBottom: "0",
+    lineHeight: "1.5",
+    fontSize: "14px",
+    textAlign: "left",
+    paddingTop: "5px",
+  } as React.CSSProperties;
+
+  const attachmentContainerStyle = {
+    display: "flex",
+    alignItems: "center",
+    marginBottom: "4px",
+    gap: "8px",
+  } as React.CSSProperties;
+
+  const fileNameWrapStyle = {
+    border: "1px solid #ccc",
+    borderRadius: "4px",
+    padding: "6px 8px",
+    display: "inline-block",
+  } as React.CSSProperties;
+
+  const attachmentNameStyle = {
+    fontSize: "14px",
+    color: "#000",
+    whiteSpace: "nowrap",
+    overflow: "hidden",
+    textOverflow: "ellipsis",
+    maxWidth: "200px",
+  } as React.CSSProperties;
+
+  const downloadLinkStyle = {
+    fontSize: "14px",
+    color: "#0066cc",
+    textDecoration: "underline",
+    cursor: "pointer",
+    whiteSpace: "nowrap",
+  } as React.CSSProperties;
+
+  const timestampStyle = {
+    fontSize: "14px",
+    color: "#000",
+  } as React.CSSProperties;
+
+  useEffect(() => {
+    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
+  }, [messageList]);
+
+  // 新增:下拉选项改变时的处理函数
+  const handleBaseChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
+    setSelectedBase(e.target.value);
+    // 这里可扩展选中后要执行的逻辑,比如调用接口等,当前仅打印示例
+    console.log("当前选中的ベース:", e.target.value); 
+  };
+
+  return (
+    <div>
+      <div
+        style={{
+          marginBottom: "10px",
+          display: "flex",
+          alignItems: "center",
+          marginLeft: "-10px",
+        }}
+      >
+        <img
+          src="/return.svg"
+          alt="返回箭头"
+          style={{ width: "25px", height: "25px" }}
+        />
+        <span
+          style={{ fontSize: "14px", cursor: "pointer" }}
+          onClick={onClose}
+        > 戻る </span>
+      </div>
+
+      {/* 新增的下拉列表部分 */}
+      <div style={{ marginBottom: "10px", marginLeft: "15px" }}>
+        <label htmlFor="baseSelect" style={{ marginRight: "5px" }}>ベース選択:</label>
+        <select 
+          id="baseSelect" 
+          style={{ padding: "6px 12px", border: "1px solid #ccc" }} 
+          value={selectedBase}
+          onChange={handleBaseChange}
+        >
+          {baseOptions.map((option) => (
+            <option key={option} value={option}>{option}</option>
+          ))}
+        </select>
+      </div>
+
+      {messageList.map((msg) => (
+        <div
+          key={msg.id}
+          style={{
+           ...baseMessageStyle,
+            justifyContent: msg.sender === "other"? "flex-end" : "flex-start",
+            display: "flex",
+            flexDirection: "column",
+            alignItems: msg.sender === "other"? "flex-end" : "flex-start",
+            paddingLeft: "40px", 
+          }}
+        >
+          {msg.sender === "other" && (
+            <div style={senderInfoStyle}>
+               <span style={senderNameStyle}>AAA株式会社</span>
+              <img
+                src="/partner.png"
+                alt="AAA株式会社图标"
+                style={iconStyle}
+              />
+            </div>
+          )}
+          {msg.sender === "me" && (
+            <div style={senderInfoStyle}>
+              <img
+                src="/base.png"
+                alt="羽田ベース图标"
+                style={{...iconStyle, marginLeft: "4px", marginRight: "0" }}
+              />
+              <span style={senderNameStyle}>羽田ベース</span>
+            </div>
+          )}
+
+          {msg.content.trim()!== "" && (
+            <div
+              style={{
+               ...bubbleStyle,
+                backgroundColor: msg.sender === "other"? "#d9d9d9" : "#deebf7",
+                marginBottom: "4px",
+                marginTop: "15px", 
+              }}
+            >
+              {msg.sender === "other" && (
+                <div
+                  style={{
+                    content: '""',
+                    position: "absolute",
+                    right: "12px", 
+                    top: "-23px", 
+                    borderTop: "20px solid transparent", 
+                    borderBottom: "20px solid transparent",
+                    borderRight: "15px solid #d9d9d9", 
+                    transform: "translateY(20%)", 
+                  }}
+                />
+              )}
+              {msg.sender === "me" && (
+                <div
+                  style={{
+                    content: '""',
+                    position: "absolute",
+                    left: "12px", 
+                    top: "-23px", 
+                    borderTop: "20px solid transparent", 
+                    borderBottom: "20px solid transparent",
+                    borderLeft: "15px solid #deebf7", 
+                    transform: "translateY(20%)", 
+                  }}
+                />
+              )}
+              <div style={contentStyle}>
+                {msg.content.split("\n").map((line, index) => (
+                  <React.Fragment key={index}>
+                    {line}
+                    {index < msg.content.split("\n").length - 1 && <br />}
+                  </React.Fragment>
+                ))}
+              </div>
+            </div>
+          )}
+          {msg.attachment && (
+            <div style={attachmentContainerStyle}>
+              <div style={fileNameWrapStyle}>
+                <span style={attachmentNameStyle}>{msg.attachment.name}</span>
+              </div>
+              <a
+                href={msg.attachment.url}
+                download={msg.attachment.name}
+                style={downloadLinkStyle}
+              >
+                ダウンロード
+              </a>
+            </div>
+          )}
+          <div
+            style={{
+             ...timestampStyle,
+              marginLeft: msg.sender === "other"? "10px" : "0",
+              marginRight: msg.sender === "me"? "10px" : "0",
+              textAlign: msg.sender === "other"? "left" : "right",
+            }}
+          >
+            {msg.timestamp}
+          </div>
+        </div>
+      ))}
+      <div ref={messagesEndRef} />
+    </div>
+  );
+};
+
+// 输入区域组件
+const InputArea: React.FC<{ onSendMessage: (content: string, file: File | null) => void }> = ({ onSendMessage }) => {
+  const [inputValue, setInputValue] = useState("");
+  const [selectedFile, setSelectedFile] = useState<File | null>(null);
+
+  const handleSend = () => {
+    if (inputValue.trim() || selectedFile) {
+      onSendMessage(inputValue.trim(), selectedFile);
+      setInputValue("");
+      setSelectedFile(null);
+    }
+  };
+
+  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    if (e.target.files && e.target.files.length > 0) {
+      setSelectedFile(e.target.files[0]);
+    }
+  };
+
+  const handleRemoveFile = () => {
+    setSelectedFile(null);
+  };
+
+  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
+    if (e.key === 'Enter' &&!e.shiftKey) {
+      e.preventDefault();
+      handleSend();
+    }
+  };
+
+  return (
+    <div
+      style={{
+        width: "100%",
+        margin: "0",
+        padding: "16px",
+        display: "flex",
+        flexDirection: "column",
+        gap: "8px",
+        boxSizing: "border-box",
+      }}
+    >
+      <textarea
+        value={inputValue}
+        onChange={(e) => setInputValue(e.target.value)}
+        onKeyDown={handleKeyDown}
+        placeholder="メッセージを入力する..."
+        style={{
+          width: "100%",
+          height: "180px",
+          padding: "8px",
+          borderRadius: "4px",
+          border: "1px solid #ccc",
+          resize: "none",
+          boxSizing: "border-box",
+        }}
+      />
+      {selectedFile && (
+        <div
+          style={{
+            display: "flex",
+            alignItems: "center",
+            justifyContent: "space-between",
+            backgroundColor: "#f9f9f9",
+            padding: "8px",
+            borderRadius: "4px",
+          }}
+        >
+          <div style={{ display: "flex", alignItems: "center" }}>
+            <span style={{ marginRight: "8px", color: "#0066cc" }}>
+              {selectedFile.name.split('.').pop()?.toUpperCase() || 'FILE'}
+            </span>
+            <span>{selectedFile.name}</span>
+          </div>
+          <span
+            style={{
+              cursor: "pointer",
+              color: "#000",
+              fontWeight: "bold",
+              fontSize: "16px",
+            }}
+            onClick={handleRemoveFile}
+          >
+            ×
+          </span>
+        </div>
+      )}
+      <div
+        style={{
+          display: "flex",
+          alignItems: "center",
+          justifyContent: "space-between",
+        }}
+      >
+        <label
+          htmlFor="fileInput"
+          style={{
+            color: "#0066cc",
+            textDecoration: "underline",
+            cursor: "pointer",
+          }}
+        >
+          ファイルアップロード
+        </label>
+        <input
+          type="file"
+          id="fileInput"
+          onChange={handleFileChange}
+          style={{
+            display: "none",
+          }}
+        />
+        <button
+          style={{
+            padding: "8px 16px",
+            backgroundColor: "#000",
+            color: "#fff",
+            border: "none",
+            borderRadius: "4px",
+            cursor: "pointer",
+          }}
+          onClick={handleSend}
+        >
+          送信
+        </button>
+      </div>
+    </div>
+  );
+};
+
+// 示例数据
+const initialMessages: ChatMessage[] = [
+  {
+    id: "1",
+    sender: "other",
+    content: "実積を確認しました。\n21日は追加で稼働していませんが、実積に反映されていません。",
+    timestamp: "2025-7-1 12:21",
+  },
+  {
+    id: "2",
+    sender: "me",
+    content: "ご確認ありがとうございます。\n恐れ入りますが、運行の証跡がかかるものをアップロードいただけますでしょうか。",
+    timestamp: "2025-7-1 12:25",
+  },
+  {
+    id: "3",
+    sender: "other",
+    content: "1ファイルを送ります。\nこちらでご確認お願いします。",
+    timestamp: "2025-7-1 13:07",
+    attachment: {
+      name: "6月運行証明.xlsx",
+      url: "#",
+    },
+  },
+  {
+    id: "4",
+    sender: "me",
+    content: "ありがとうございます。\n内容確認して、再度ご連絡させていただきます。",
+    timestamp: "2025-7-1 13:31",
+  },
+];
+
+const Chat: React.FC<ChatProps> = ({ onClose }) => {
+  const [messages, setMessages] = useState<ChatMessage[]>(initialMessages);
+
+  const handleSendMessage = (content: string, file: File | null) => {
+    const newMessage: ChatMessage = {
+      id: Date.now().toString(),
+      sender: "me",
+      content,
+      timestamp: new Date()
+       .toLocaleString("ja-JP", {
+          year: "numeric",
+          month: "long",
+          day: "numeric",
+          hour: "2-digit",
+          minute: "2-digit",
+        })
+       .replace(/[年月]/g, "-")
+       .replace("日", ""),
+    };
+
+    if (file) {
+      newMessage.attachment = {
+        name: file.name,
+        url: "#", 
+      };
+    }
+
+    setMessages([...messages, newMessage]);
+  };
+
+  return (
+    <div style={{ 
+      height: '100%', 
+      display: 'flex', 
+      flexDirection: 'column',
+      margin: 0,
+      padding: 0
+    }}>
+      <div className="chat-messages-container" style={{
+        padding: '16px',
+        boxSizing: 'border-box',
+        flex: 1,
+        overflow: 'auto'
+      }}>
+        <ChatComponent messageList={messages} onClose={onClose} />
+      </div>
+      <InputArea onSendMessage={handleSendMessage} />
+    </div>
+  );
+};
+
+export default Chat;

+ 11 - 8
src/pages/Partner/Order/index.tsx

@@ -35,7 +35,7 @@ const reasonOptions = [
 ];
 
 const affiliationName = "羽田ベース"; 
-
+// TODO type
 const historyData = [
     {
         type: '履歴',
@@ -138,7 +138,7 @@ const GridExample = () => {
       }
 
        .confirm-request-btn {
-        margin-left:70%;
+        margin-left:65%;
         padding: 7px 50px;
         background-color: #000;
         color: #fff;
@@ -176,7 +176,7 @@ const GridExample = () => {
         position: relative;
         top: -17px;
       }
-      .edit-btn {
+      .save-btn {
         margin-left: auto;
         padding: 7px 50px;
         background-color: #000;
@@ -291,7 +291,8 @@ const GridExample = () => {
                         field: colId,
                         headerName: weekTexts[weekIndex],
                         headerClass: "header-center",
-                        width: 55,
+                        flex: 1,
+                        minWidth: 55, 
                         cellRenderer: (params: ICellRendererParams) => {
                             const cellValue = params.value || "";
                             const rowIndex = params.node.rowIndex;
@@ -637,7 +638,7 @@ const GridExample = () => {
                             ))}
                         </select>
                         <button  className="confirm-request-btn" > 確認依頼</button>
-                        <button className="edit-btn" onClick={handleEditClick}>
+                        <button className="save-btn" onClick={handleEditClick}>
                             保存
                         </button>
                     </div>
@@ -645,14 +646,16 @@ const GridExample = () => {
                          marginLeft: '20px', 
                          marginTop: '40px', 
                          display: 'flex',
-                         alignItems: 'flex-start'
+                         alignItems: 'flex-start',
+                         width: '100%',
+                         boxSizing: 'border-box'
                      }}> 
                          <div style={{ display: 'flex', flexDirection: 'column' }}> 
                              <span style={{ marginRight: '10px' }}>{documentInfo.corpCode}</span>
                              <span style={{ marginTop: '15px' }}>{documentInfo.corpName}</span>
                          </div>
-                         <span style={{ marginLeft: '240px' }}>月間運行指示書</span>
-                         <div style={{ marginLeft: '960px', fontSize: '14px' }}> 
+                         <span style={{ marginLeft: '140px' }}>月間運行指示書</span>
+                         <div style={{ marginLeft: 'auto', fontSize: '14px' }}> 
                              <p>【{documentInfo.reportMonth}】</p>
                              <p>署名・捺印の上、送達願います。</p>
                              <p>(御社名+社員・納品担当者印)</p>