Преглед на файлове

修复页面按钮布局和页面内容调整

pm преди 2 месеца
родител
ревизия
10a25b5f27

+ 4 - 1
src/api/mytask/index.ts

@@ -31,7 +31,10 @@ export interface MyTaskVO {
 export interface MyTaskPageParam {
   pageNo?: number;
   pageSize?: number;
-  key?: string; // 搜索关键字
+  key?: string; // 搜索关键字(兼容)
+  orderNo?: string; // 作业编号
+  name?: string; // 作业名称
+  status?: string;
   [key: string]: any;
 }
 

+ 21 - 17
src/components/AppMessage.tsx

@@ -409,7 +409,8 @@ export default function AppMessage() {
       <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
-          <div className="flex flex-wrap items-center justify-between gap-3">
+          <div className="flex flex-wrap items-center gap-3">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
             <div className="flex flex-wrap items-center gap-3">
               {/* 消息标题 */}
               <div className="flex items-center gap-2">
@@ -439,7 +440,7 @@ export default function AppMessage() {
                 </Select>
               </div>
 
-              {/* 发送日期 */}
+              {/* 发送日期 + 搜索/重置(放在同一组,按钮紧跟最后一个条件后面) */}
               <div className="flex items-center gap-2">
                 <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.time')}:</label>
                 <DatePicker
@@ -457,24 +458,27 @@ export default function AppMessage() {
                   format="YYYY-MM-DD"
                   style={{ width: 140 }}
                 />
+                <Space size="small">
+                  <AntButton
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleSearch}
+                    disabled={loading}
+                  >
+                    {t('common.search')}
+                  </AntButton>
+                  <AntButton
+                    icon={<RotateCcw className="w-4 h-4" />}
+                    onClick={handleReset}
+                  >
+                    {t('common.reset')}
+                  </AntButton>
+                </Space>
               </div>
             </div>
 
-            <Space size="small">
-              <AntButton
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleSearch}
-                disabled={loading}
-              >
-                {t('common.search')}
-              </AntButton>
-              <AntButton
-                icon={<RotateCcw className="w-4 h-4" />}
-                onClick={handleReset}
-              >
-                {t('common.reset')}
-              </AntButton>
+            {/* 其他操作按钮保持在最右侧(全部已读) */}
+            <Space size="small" className="ml-auto">
               <AntButton
                 icon={<BookOpen className="w-4 h-4" />}
                 onClick={handleMarkAllAsRead}

+ 24 - 23
src/components/DepartmentManagement.tsx

@@ -405,8 +405,8 @@ export default function DepartmentManagement() {
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索输入框 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.departmentName')}:</label>
               <Input
@@ -418,29 +418,30 @@ export default function DepartmentManagement() {
                 allowClear
               />
             </div>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="system:dept:query">
+                <AntButton
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </AntButton>
+              </PermissionWrapper>
+              
+              <PermissionWrapper permission="system:dept:query">
+                <AntButton
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </AntButton>
+              </PermissionWrapper>
+            </Space>
           </div>
 
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="system:dept:query">
-              <AntButton
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </AntButton>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="system:dept:query">
-              <AntButton
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </AntButton>
-            </PermissionWrapper>
-            
+          {/* 新增、展开等按钮组保持在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="system:dept:create">
               <AntButton
                 type="primary"

+ 24 - 23
src/components/DictTypeManagement.tsx

@@ -509,8 +509,8 @@ export default function DictTypeManagement() {
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索输入框 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.dictName')}:</label>
               <Input
@@ -533,29 +533,30 @@ export default function DictTypeManagement() {
                 allowClear
               />
             </div>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="system:dict:query">
+                <Button
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </Button>
+              </PermissionWrapper>
+              
+              <PermissionWrapper permission="system:dict:query">
+                <Button
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
           </div>
 
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="system:dict:query">
-              <Button
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </Button>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="system:dict:query">
-              <Button
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </Button>
-            </PermissionWrapper>
-            
+          {/* 新增等按钮组保持在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="system:dict:create">
               <Button
                 type="primary"

+ 23 - 17
src/components/FormManagement.tsx

@@ -783,23 +783,29 @@ export default function FormManagement() {
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
           <div className="flex flex-wrap items-center justify-between gap-3">
-            <div className="flex items-center gap-3">
-              <span className="text-sm text-gray-700">{t('form.formName')}:</span>
-              <Input
-                value={searchName}
-                onChange={(e) => setSearchName(e.target.value)}
-                placeholder={t('form.formNamePlaceholder')}
-                allowClear
-                style={{ width: 240 }}
-              />
+            {/* 左侧:搜索条件 + 紧跟其后的 搜索 / 重置 按钮 */}
+            <div className="flex flex-wrap items-center gap-3">
+              <div className="flex items-center gap-3">
+                <span className="text-sm text-gray-700">{t('form.formName')}:</span>
+                <Input
+                  value={searchName}
+                  onChange={(e) => setSearchName(e.target.value)}
+                  placeholder={t('form.formNamePlaceholder')}
+                  allowClear
+                  style={{ width: 240 }}
+                />
+              </div>
+              <Space size="small">
+                <Button type="primary" icon={<Search className="w-4 h-4" />} onClick={handleSearch}>
+                  {t('common.search')}
+                </Button>
+                <Button icon={<RotateCcw className="w-4 h-4" />} onClick={handleReset}>
+                  {t('common.reset')}
+                </Button>
+              </Space>
             </div>
-            <Space size="small">
-              <Button type="primary" icon={<Search className="w-4 h-4" />} onClick={handleSearch}>
-                {t('common.search')}
-              </Button>
-              <Button icon={<RotateCcw className="w-4 h-4" />} onClick={handleReset}>
-                {t('common.reset')}
-              </Button>
+            {/* 右侧:新增按钮保持在最右侧 */}
+            <div>
               <Button
                 type="primary"
                 icon={<Plus className="w-4 h-4" />}
@@ -807,7 +813,7 @@ export default function FormManagement() {
               >
                 {t('form.addForm')}
               </Button>
-            </Space>
+            </div>
           </div>
         </div>
 

+ 27 - 9
src/components/HardwareManagement.tsx

@@ -676,17 +676,35 @@ export default function HardwareManagement({ subMenu }: HardwareManagementProps)
         {/* 工具栏 */}
         <div className="p-4 border-b border-gray-200/50">
           <div className="flex items-center justify-between">
-            <div className="relative w-80">
-              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
-              <input
-                type="text"
-                placeholder={`搜索${subMenu}...`}
-                value={searchTerm}
-                onChange={(e) => setSearchTerm(e.target.value)}
-                className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-              />
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
+            <div className="flex items-center gap-3">
+              <div className="relative w-80">
+                <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
+                <input
+                  type="text"
+                  placeholder={`搜索${subMenu}...`}
+                  value={searchTerm}
+                  onChange={(e) => setSearchTerm(e.target.value)}
+                  className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+                />
+              </div>
+              <button
+                type="button"
+                onClick={() => setSearchTerm(searchTerm.trim())}
+                className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+              >
+                搜索
+              </button>
+              <button
+                type="button"
+                onClick={() => setSearchTerm('')}
+                className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+              >
+                重置
+              </button>
             </div>
 
+            {/* 新增按钮保持在最右侧 */}
             <button
               onClick={() => {
                 setEditingItem(null);

+ 49 - 19
src/components/InboxMessage.tsx

@@ -1,5 +1,6 @@
 import React, { useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
+import { useNavigate, useLocation } from 'react-router-dom';
 import { Search, RotateCcw, BookOpen, ChevronLeft, ChevronRight, Plus } from 'lucide-react';
 import { Checkbox } from './ui/checkbox';
 import { Button as AntButton, Modal, Form, Input, Select, TreeSelect, DatePicker, Tooltip, Descriptions, Table as AntdTable, Space } from 'antd';
@@ -35,6 +36,8 @@ interface MessageItem {
 
 export default function InboxMessage() {
   const { t } = useTranslation();
+  const navigate = useNavigate();
+  const location = useLocation();
   const [messageTitle, setMessageTitle] = useState<string>('');
   const [messageStatus, setMessageStatus] = useState<string | undefined>(undefined);
   const [startDate, setStartDate] = useState<Dayjs | null>(null);
@@ -444,10 +447,33 @@ export default function InboxMessage() {
         const maxLength = 20;
         const shouldTruncate = contentText.length > maxLength;
         const displayText = shouldTruncate ? contentText.slice(0, maxLength) + '...' : contentText;
-        
+
+        const goToMyTask = () => {
+          sessionStorage.setItem('navigateToMenu', JSON.stringify({
+            menu: 'isolationWork',
+            subMenu: 'myTask',
+          }));
+          if (location.pathname === '/dashboard') {
+            window.dispatchEvent(new CustomEvent('switchToMenu', {
+              detail: { menu: 'isolationWork', subMenu: 'myTask' },
+            }));
+          } else {
+            navigate('/dashboard');
+          }
+        };
+
         return (
           <Tooltip placement="topLeft" title={contentText}>
-            <span className="text-sm text-gray-900">{displayText}</span>
+            <span
+              role="button"
+              tabIndex={0}
+              className="cursor-pointer hover:underline"
+              style={{ color: '#1677ff', fontSize: 14 }}
+              onClick={(e) => { e.stopPropagation(); goToMyTask(); }}
+              onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); goToMyTask(); } }}
+            >
+              {displayText}
+            </span>
           </Tooltip>
         );
       },
@@ -519,7 +545,8 @@ export default function InboxMessage() {
       <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
-          <div className="flex flex-wrap items-center justify-between gap-3">
+          <div className="flex flex-wrap items-center gap-3">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
             <div className="flex flex-wrap items-center gap-3">
               {/* 消息标题 */}
               <div className="flex items-center gap-2">
@@ -549,7 +576,7 @@ export default function InboxMessage() {
                 </Select>
               </div>
 
-              {/* 发送日期 */}
+              {/* 发送日期 + 搜索/重置同组 */}
               <div className="flex items-center gap-2">
                 <label className="text-sm text-gray-700 whitespace-nowrap">{t('notificationManagement.time')}:</label>
                 <DatePicker
@@ -567,24 +594,27 @@ export default function InboxMessage() {
                   format="YYYY-MM-DD"
                   style={{ width: 140 }}
                 />
+                <Space size="small">
+                  <AntButton
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleSearch}
+                    disabled={loading}
+                  >
+                    {t('common.search')}
+                  </AntButton>
+                  <AntButton
+                    icon={<RotateCcw className="w-4 h-4" />}
+                    onClick={handleReset}
+                  >
+                    {t('common.reset')}
+                  </AntButton>
+                </Space>
               </div>
             </div>
 
-            <Space size="small">
-              <AntButton
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleSearch}
-                disabled={loading}
-              >
-                {t('common.search')}
-              </AntButton>
-              <AntButton
-                icon={<RotateCcw className="w-4 h-4" />}
-                onClick={handleReset}
-              >
-                {t('common.reset')}
-              </AntButton>
+            {/* 其他操作按钮保持在最右侧(全部已读) */}
+            <Space size="small" className="ml-auto">
               <AntButton
                 icon={<BookOpen className="w-4 h-4" />}
                 onClick={handleMarkAllAsRead}

+ 87 - 88
src/components/IsolationWork.tsx

@@ -3612,8 +3612,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           {/* 搜索栏 */}
           <div className="p-4 lg:p-5 border-b border-gray-200/50">
             <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-              {/* 搜索输入框 */}
-              <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+              {/* 搜索条件 + 搜索、重置紧跟其后 */}
+              <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
                 <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.formName')}:</label>
                   <Input
@@ -3638,28 +3638,27 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                     <Select.Option value="停用">{t('common.disabled')}</Select.Option>
                   </Select>
                 </div>
+                <Space className="flex-shrink-0">
+                  <Button
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleFormManagementQuery}
+                  >
+                    {t('common.search')}
+                  </Button>
+                  <Button
+                    icon={<RefreshCw className="w-4 h-4" />}
+                    onClick={() => {
+                      resetFormManagementQuery();
+                      setFormManagementPagination({ pageNo: 1, pageSize: 10 });
+                    }}
+                  >
+                    {t('common.reset')}
+                  </Button>
+                </Space>
               </div>
-
-              {/* 操作按钮组 */}
+              {/* 新增等按钮留在最右侧 */}
               <Space className="flex-shrink-0">
-                <Button
-                  type="primary"
-                  icon={<Search className="w-4 h-4" />}
-                  onClick={handleFormManagementQuery}
-                >
-                  {t('common.search')}
-                </Button>
-                
-                <Button
-                  icon={<RefreshCw className="w-4 h-4" />}
-                  onClick={() => {
-                    resetFormManagementQuery();
-                    setFormManagementPagination({ pageNo: 1, pageSize: 10 });
-                  }}
-                >
-                  {t('common.reset')}
-                </Button>
-                
                 <Button
                   type="primary"
                   icon={<Plus className="w-4 h-4" />}
@@ -3816,8 +3815,8 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           {/* 搜索栏 */}
           <div className="p-4 lg:p-5 border-b border-gray-200/50">
             <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-              {/* 搜索输入框 */}
-              <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+              {/* 搜索条件 + 搜索、重置紧跟其后 */}
+              <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
                 <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.processDesignName')}:</label>
                   <Input
@@ -3829,40 +3828,37 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                     allowClear
                   />
                 </div>
-                <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
-                </div>
+                <Space className="flex-shrink-0">
+                  <Button
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleProcessDesignQuery}
+                  >
+                    {t('common.search')}
+                  </Button>
+                  <Button
+                    icon={<RefreshCw className="w-4 h-4" />}
+                    onClick={() => {
+                      resetProcessDesignQuery();
+                    }}
+                  >
+                    {t('common.reset')}
+                  </Button>
+                </Space>
               </div>
-
-              {/* 操作按钮组 */}
+              {/* 新增等按钮留在最右侧 */}
               <Space className="flex-shrink-0">
                 <Button
                   type="primary"
-                  icon={<Search className="w-4 h-4" />}
-                  onClick={handleProcessDesignQuery}
-                >
-                  {t('common.search')}
-                </Button>
-                
-                <Button
-                  icon={<RefreshCw className="w-4 h-4" />}
+                  icon={<Plus className="w-4 h-4" />}
                   onClick={() => {
-                    resetProcessDesignQuery();
+                    setEditingItem(null);
+                    form.resetFields();
+                    setShowAddModal(true);
                   }}
                 >
-                  {t('common.reset')}
+                  {t('form.addProcess')}
                 </Button>
-                
-              <Button
-                type="primary"
-                icon={<Plus className="w-4 h-4" />}
-                onClick={() => {
-                  setEditingItem(null);
-                  form.resetFields();
-                  setShowAddModal(true);
-                }}
-              >
-                {t('form.addProcess')}
-              </Button>
               </Space>
             </div>
           </div>
@@ -4108,52 +4104,56 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
           {/* 搜索栏 */}
           <div className="p-4 lg:p-5 border-b border-gray-200/50">
             <div className="flex flex-wrap items-center justify-between gap-3 lg:gap-4">
-            <div className="flex flex-wrap items-center gap-3 lg:gap-4">
+              {/* 搜索条件 + 搜索、重置紧跟其后 */}
+              <div className="flex flex-wrap items-center gap-3 lg:gap-4 min-w-0">
                 <div className="flex items-center gap-2">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.workName')}:</label>
-              <Input
-                value={workJobQuery.name}
-                onChange={(e) => setWorkJobQuery({ ...workJobQuery, name: e.target.value })}
-                onPressEnter={handleWorkJobQuery}
+                  <Input
+                    value={workJobQuery.name}
+                    onChange={(e) => setWorkJobQuery({ ...workJobQuery, name: e.target.value })}
+                    onPressEnter={handleWorkJobQuery}
                     placeholder={t('form.workNamePlaceholder')}
-                allowClear
+                    allowClear
                     className="min-w-[200px] max-w-[300px]"
-              />
+                  />
                 </div>
                 <div className="flex items-center gap-2">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.workStatus')}:</label>
-              <Select
-                value={workJobQuery.status ?? undefined}
-                onChange={(value) => setWorkJobQuery({ ...workJobQuery, status: value || undefined })}
-                placeholder={t('form.workStatusPlaceholder')}
-                allowClear
-                className="min-w-[180px] max-w-[250px]"
-              >
-                {jobStatusDictList.map(item => (
-                  <Select.Option key={item.value} value={item.value}>
-                    {item.label}
-                  </Select.Option>
-                ))}
-              </Select>
+                  <Select
+                    value={workJobQuery.status ?? undefined}
+                    onChange={(value) => setWorkJobQuery({ ...workJobQuery, status: value || undefined })}
+                    placeholder={t('form.workStatusPlaceholder')}
+                    allowClear
+                    className="min-w-[180px] max-w-[250px]"
+                  >
+                    {jobStatusDictList.map(item => (
+                      <Select.Option key={item.value} value={item.value}>
+                        {item.label}
+                      </Select.Option>
+                    ))}
+                  </Select>
                 </div>
+                <Space className="flex-shrink-0">
+                  <Button
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleWorkJobQuery}
+                  >
+                    {t('common.search')}
+                  </Button>
+                  <Button
+                    icon={<RefreshCw className="w-4 h-4" />}
+                    onClick={() => {
+                      resetWorkJobQuery();
+                      getWorkJobList();
+                    }}
+                  >
+                    {t('common.reset')}
+                  </Button>
+                </Space>
               </div>
-              <Space>
-                <Button
-                  type="primary"
-                  icon={<Search className="w-4 h-4" />}
-                  onClick={handleWorkJobQuery}
-                >
-                  {t('common.search')}
-                </Button>
-                <Button
-                  icon={<RefreshCw className="w-4 h-4" />}
-                  onClick={() => {
-                    resetWorkJobQuery();
-                    getWorkJobList();
-                  }}
-                >
-                  {t('common.reset')}
-                </Button>
+              {/* 新增/发起作业等按钮留在最右侧 */}
+              <Space className="flex-shrink-0">
                 <Button
                   type="primary"
                   icon={<Plus className="w-4 h-4" />}
@@ -4161,7 +4161,6 @@ export default function IsolationWork({ subMenu }: IsolationWorkProps) {
                     setEditingItem(null);
                     setShowAddModal(true);
                     setOriginalBasicFormData(null); // 清空原始表单数据(新增模式)
-                    // 打开弹框时加载数据
                     getWorkTypeDictList();
                     getUrgencyLevelDictList();
                     getIsolationMethodDictList();

+ 24 - 23
src/components/KeyManagement.tsx

@@ -263,8 +263,8 @@ export default function KeyManagement({ subMenu }: KeyManagementProps) {
         {/* 搜索栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200">
           <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-            {/* 搜索输入框 */}
-            <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
+            <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
               <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                 <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.keyName')}:</label>
                 <Input
@@ -276,29 +276,30 @@ export default function KeyManagement({ subMenu }: KeyManagementProps) {
                   allowClear
                 />
               </div>
+              <Space className="flex-shrink-0" size="small">
+                <PermissionWrapper permission="iscs:key:query">
+                  <Button
+                    type="primary"
+                    icon={<Search className="w-4 h-4" />}
+                    onClick={handleQuery}
+                  >
+                    {t('common.search')}
+                  </Button>
+                </PermissionWrapper>
+                
+                <PermissionWrapper permission="iscs:key:query">
+                  <Button
+                    icon={<RefreshCw className="w-4 h-4" />}
+                    onClick={resetQuery}
+                  >
+                    {t('common.reset')}
+                  </Button>
+                </PermissionWrapper>
+              </Space>
             </div>
 
-            {/* 操作按钮组 */}
-            <Space className="flex-shrink-0">
-              <PermissionWrapper permission="iscs:key:query">
-                <Button
-                  type="primary"
-                  icon={<Search className="w-4 h-4" />}
-                  onClick={handleQuery}
-                >
-                  {t('common.search')}
-                </Button>
-              </PermissionWrapper>
-              
-              <PermissionWrapper permission="iscs:key:query">
-                <Button
-                  icon={<RefreshCw className="w-4 h-4" />}
-                  onClick={resetQuery}
-                >
-                  {t('common.reset')}
-                </Button>
-              </PermissionWrapper>
-              
+            {/* 新增 / 批量删除等按钮组保持在最右侧 */}
+            <Space className="flex-shrink-0" size="small">
               <PermissionWrapper permission="iscs:key:create">
                 <Button
                   type="primary"

+ 27 - 9
src/components/LocationManagement.tsx

@@ -301,17 +301,35 @@ export default function LocationManagement() {
         {/* 工具栏 */}
         <div className="p-4 border-b border-gray-200/50">
           <div className="flex items-center justify-between">
-            <div className="relative w-80">
-              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
-              <input
-                type="text"
-                placeholder="搜索点位..."
-                value={searchTerm}
-                onChange={(e) => setSearchTerm(e.target.value)}
-                className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-              />
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
+            <div className="flex items-center gap-3">
+              <div className="relative w-80">
+                <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
+                <input
+                  type="text"
+                  placeholder="搜索点位..."
+                  value={searchTerm}
+                  onChange={(e) => setSearchTerm(e.target.value)}
+                  className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+                />
+              </div>
+              <button
+                type="button"
+                onClick={() => setSearchTerm(searchTerm.trim())}
+                className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+              >
+                搜索
+              </button>
+              <button
+                type="button"
+                onClick={() => setSearchTerm('')}
+                className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+              >
+                重置
+              </button>
             </div>
 
+            {/* 新增按钮保持在最右侧 */}
             <button
               onClick={() => {
                 setEditingItem(null);

+ 23 - 26
src/components/MenuManagement.tsx

@@ -514,8 +514,8 @@ export default function MenuManagement() {
         {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索输入框 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.menuName')}:</label>
               <Input
@@ -527,29 +527,28 @@ export default function MenuManagement() {
                 allowClear
               />
             </div>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="system:menu:query">
+                <Button
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </Button>
+              </PermissionWrapper>
+              <PermissionWrapper permission="system:menu:query">
+                <Button
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
           </div>
-
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="system:menu:query">
-              <Button
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </Button>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="system:menu:query">
-              <Button
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </Button>
-            </PermissionWrapper>
-            
+          {/* 新增、展开/收起、刷新缓存等按钮留在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="system:menu:create">
               <Button
                 type="primary"
@@ -559,13 +558,11 @@ export default function MenuManagement() {
                 {t('common.addNew')}
               </Button>
             </PermissionWrapper>
-            
             <Button
               onClick={toggleExpandAll}
             >
               {isExpandAll ? t('common.collapseAll') : t('common.expandAll')}
             </Button>
-            
             <Button
               icon={<RefreshCw className="w-4 h-4" />}
               onClick={refreshMenu}

+ 27 - 17
src/components/MyTask.tsx

@@ -203,9 +203,10 @@ export default function MyTask() {
   const [queryParams, setQueryParams] = useState<MyTaskPageParam>({
     pageNo: 1,
     pageSize: 10,
-    key: '',
+    key: undefined,
   });
-  const [searchKey, setSearchKey] = useState('');
+  const [searchOrderNo, setSearchOrderNo] = useState('');
+  const [searchName, setSearchName] = useState('');
   const [approvalStatusDictList, setApprovalStatusDictList] = useState<any[]>([]);
   const [urgencyLevelDictList, setUrgencyLevelDictList] = useState<any[]>([]);
   
@@ -325,31 +326,30 @@ export default function MyTask() {
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [queryParams.pageNo, queryParams.pageSize, queryParams.key]);
 
-  // 搜索
+  // 搜索:两个条件都通过 key 传参,值为具体输入(多个用空格拼接)
   const handleSearch = () => {
-    setQueryParams({
+    const parts = [searchOrderNo.trim(), searchName.trim()].filter(Boolean);
+    const resetParams: MyTaskPageParam = {
       ...queryParams,
       pageNo: 1,
-      key: searchKey.trim() || undefined,
-    });
+      key: parts.length ? parts.join(' ') : undefined,
+    };
+    setQueryParams(resetParams);
+    getList(resetParams);
   };
 
   // 重置
   const handleReset = () => {
-    // 重置本地搜索关键字
-    setSearchKey('');
+    setSearchOrderNo('');
+    setSearchName('');
 
-    // 构造重置后的查询参数
     const resetParams: MyTaskPageParam = {
       ...queryParams,
       pageNo: 1,
       key: undefined,
     };
 
-    // 更新查询参数(触发 useEffect 中的列表刷新逻辑)
     setQueryParams(resetParams);
-
-    // 立即调用一次列表接口,确保点击重置时立刻刷新列表
     getList(resetParams);
   };
 
@@ -1892,13 +1892,23 @@ export default function MyTask() {
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
           <div className="flex flex-wrap items-center gap-3">
+            {/* 搜索条件:作业编号、作业名称,搜索/重置紧跟其后 */}
+            <span className="text-sm text-gray-700 whitespace-nowrap">作业编号:</span>
+            <Input
+              value={searchOrderNo}
+              onChange={(e) => setSearchOrderNo(e.target.value)}
+              placeholder="请输入作业编号"
+              allowClear
+              style={{ width: 160 }}
+              onPressEnter={handleSearch}
+            />
+            <span className="text-sm text-gray-700 whitespace-nowrap">作业名称:</span>
             <Input
-              value={searchKey}
-              onChange={(e) => setSearchKey(e.target.value)}
-              placeholder={t('form.taskSearchPlaceholder')}
+              value={searchName}
+              onChange={(e) => setSearchName(e.target.value)}
+              placeholder="请输入作业名称"
               allowClear
-              className="flex-1"
-              style={{ minWidth: 200 }}
+              style={{ width: 160 }}
               onPressEnter={handleSearch}
             />
             <Space size="small">

+ 32 - 30
src/components/PadLockManagement.tsx

@@ -596,8 +596,8 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
             {/* 搜索栏 */}
             <div className="p-4 lg:p-5 border-b border-gray-200">
               <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-              {/* 搜索输入框 */}
-              <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+              {/* 搜索条件 + 搜索、重置紧跟其后 */}
+              <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
                 <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.padLockName')}:</label>
                   <Input
@@ -609,10 +609,7 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
                     allowClear
                   />
                 </div>
-              </div>
-
-                {/* 操作按钮组 */}
-                <Space className="flex-shrink-0">
+                <Space className="flex-shrink-0" size="small">
                   <PermissionWrapper permission="iscs:lock:query">
                     <Button
                       type="primary"
@@ -631,7 +628,11 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
                       {t('common.reset')}
                     </Button>
                   </PermissionWrapper>
-                  
+                </Space>
+              </div>
+
+                {/* 新增 / 批量删除 / 设置类型 等按钮组保持在最右侧 */}
+                <Space className="flex-shrink-0" size="small">
                   <PermissionWrapper permission="iscs:lock:create">
                     <Button
                       type="primary"
@@ -726,8 +727,8 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
             {/* 搜索栏 */}
             <div className="p-5 border-b border-gray-200">
               <div className="flex items-center justify-between gap-4 flex-wrap">
-                {/* 搜索输入框 */}
-                <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+                {/* 搜索条件 + 搜索、重置紧跟其后 */}
+                <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
                   <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                     <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.padLockTypeName')}:</label>
                     <Input
@@ -739,29 +740,30 @@ export default function PadLockManagement({ subMenu }: PadLockManagementProps) {
                       allowClear
                     />
                   </div>
+                  <Space className="flex-shrink-0" size="small">
+                    <PermissionWrapper permission="iscs:lock:query">
+                      <Button
+                        type="primary"
+                        icon={<Search className="w-4 h-4" />}
+                        onClick={handleTypeQuery}
+                      >
+                        {t('common.search')}
+                      </Button>
+                    </PermissionWrapper>
+                    
+                    <PermissionWrapper permission="iscs:lock:query">
+                      <Button
+                        icon={<RefreshCw className="w-4 h-4" />}
+                        onClick={resetTypeQuery}
+                      >
+                        {t('common.reset')}
+                      </Button>
+                    </PermissionWrapper>
+                  </Space>
                 </div>
 
-                {/* 操作按钮组 */}
-                <Space className="flex-shrink-0">
-                  <PermissionWrapper permission="iscs:lock:query">
-                    <Button
-                      type="primary"
-                      icon={<Search className="w-4 h-4" />}
-                      onClick={handleTypeQuery}
-                    >
-                      {t('common.search')}
-                    </Button>
-                  </PermissionWrapper>
-                  
-                  <PermissionWrapper permission="iscs:lock:query">
-                    <Button
-                      icon={<RefreshCw className="w-4 h-4" />}
-                      onClick={resetTypeQuery}
-                    >
-                      {t('common.reset')}
-                    </Button>
-                  </PermissionWrapper>
-                  
+                {/* 新增 / 展开收起 / 返回列表 等按钮组保持在最右侧 */}
+                <Space className="flex-shrink-0" size="small">
                   <PermissionWrapper permission="iscs:lock:create">
                     <Button
                       type="primary"

+ 24 - 23
src/components/PostManagement.tsx

@@ -256,8 +256,8 @@ export default function PostManagement() {
         {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索输入框 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.postName')}:</label>
               <Input
@@ -280,29 +280,30 @@ export default function PostManagement() {
                 allowClear
               />
             </div>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="system:post:query">
+                <Button
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </Button>
+              </PermissionWrapper>
+              
+              <PermissionWrapper permission="system:post:query">
+                <Button
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
           </div>
 
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="system:post:query">
-              <Button
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </Button>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="system:post:query">
-              <Button
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </Button>
-            </PermissionWrapper>
-            
+          {/* 新增、导出等按钮组保持在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="system:post:create">
               <Button
                 type="primary"

+ 24 - 23
src/components/RoleManagement.tsx

@@ -252,8 +252,8 @@ export default function RoleManagement() {
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索输入框 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.roleName')}:</label>
               <Input
@@ -276,29 +276,30 @@ export default function RoleManagement() {
                 allowClear
               />
             </div>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="system:role:query">
+                <Button
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </Button>
+              </PermissionWrapper>
+              
+              <PermissionWrapper permission="system:role:query">
+                <Button
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
           </div>
 
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="system:role:query">
-              <Button
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </Button>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="system:role:query">
-              <Button
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </Button>
-            </PermissionWrapper>
-            
+          {/* 新增、导出等按钮组保持在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="system:role:create">
               <Button
                 type="primary"

+ 47 - 42
src/components/SegregationPointManagement.tsx

@@ -392,8 +392,9 @@ export default function SegregationPointManagement({ subMenu }: SegregationPoint
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-5">
         <div className="flex flex-col gap-4">
-          {/* 搜索字段 + 操作按钮:同一行,按钮靠右 */}
+          {/* 搜索字段 + 操作按钮 */}
           <div className="flex items-center justify-between gap-3 flex-wrap">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
             <div className="flex items-center gap-3 flex-wrap">
               <div className="flex items-center gap-2">
                 <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.segregationPointName')}:</label>
@@ -454,48 +455,52 @@ export default function SegregationPointManagement({ subMenu }: SegregationPoint
                 </div>
               </>
             )}
-            </div>
+            {/* 搜索、重置紧跟最后一个搜索条件 */}
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="iscs:point:query">
+                <Button
+                  type="primary"
+                  icon={<Search />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </Button>
+              </PermissionWrapper>
+              <PermissionWrapper permission="iscs:point:query">
+                <Button
+                  icon={<RefreshCw />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
+          </div>
 
-            <div className="flex justify-end">
-              <Space>
-                <PermissionWrapper permission="iscs:point:query">
-                  <Button
-                    type="primary"
-                    icon={<Search />}
-                    onClick={handleQuery}
-                  >
-                    {t('common.search')}
-                  </Button>
-                </PermissionWrapper>
-                <PermissionWrapper permission="iscs:point:query">
-                  <Button
-                    icon={<RefreshCw />}
-                    onClick={resetQuery}
-                  >
-                    {t('common.reset')}
-                  </Button>
-                </PermissionWrapper>
-                <PermissionWrapper permission="iscs:point:create">
-                  <Button
-                    type="primary"
-                    icon={<Plus />}
-                    onClick={() => openForm('create')}
-                  >
-                    {t('common.addNew')}
-                  </Button>
-                </PermissionWrapper>
-                <PermissionWrapper permission="iscs:point:delete">
-                  <Button
-                    danger
-                    icon={<Trash2 className="w-4 h-4" />}
-                    disabled={selectedRowKeys.length === 0}
-                    onClick={() => handleDelete()}
-                  >
-                    {t('common.batchDelete')}
-                  </Button>
-                </PermissionWrapper>
-              </Space>
-            </div>
+          {/* 新增、批量删除等按钮保持在最右侧 */}
+          <div className="flex justify-end">
+            <Space size="small">
+              <PermissionWrapper permission="iscs:point:create">
+                <Button
+                  type="primary"
+                  icon={<Plus />}
+                  onClick={() => openForm('create')}
+                >
+                  {t('common.addNew')}
+                </Button>
+              </PermissionWrapper>
+              <PermissionWrapper permission="iscs:point:delete">
+                <Button
+                  danger
+                  icon={<Trash2 className="w-4 h-4" />}
+                  disabled={selectedRowKeys.length === 0}
+                  onClick={() => handleDelete()}
+                >
+                  {t('common.batchDelete')}
+                </Button>
+              </PermissionWrapper>
+            </Space>
+          </div>
           </div>
         </div>
       </div>

+ 17 - 17
src/components/SmsMessage.tsx

@@ -305,6 +305,7 @@ export default function SmsMessage() {
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
           <div className="flex flex-wrap items-center justify-between gap-3">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
             <div className="flex flex-wrap items-center gap-3">
               {/* 接收手机号 */}
               <div className="flex items-center gap-2">
@@ -369,24 +370,23 @@ export default function SmsMessage() {
                   style={{ width: 140 }}
                 />
               </div>
+              <Space size="small">
+                <AntButton
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleSearch}
+                  disabled={loading}
+                >
+                  {t('common.search')}
+                </AntButton>
+                <AntButton
+                  icon={<RotateCcw className="w-4 h-4" />}
+                  onClick={handleReset}
+                >
+                  {t('common.reset')}
+                </AntButton>
+              </Space>
             </div>
-
-            <Space size="small">
-              <AntButton
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleSearch}
-                disabled={loading}
-              >
-                {t('common.search')}
-              </AntButton>
-              <AntButton
-                icon={<RotateCcw className="w-4 h-4" />}
-                onClick={handleReset}
-              >
-                {t('common.reset')}
-              </AntButton>
-            </Space>
           </div>
         </div>
 

+ 64 - 18
src/components/SystemConfig.tsx

@@ -554,16 +554,39 @@ export default function SystemConfig({ subMenu }: SystemConfigProps) {
               <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between flex-shrink-0">
                 {dictionaryFormMode === 'list' ? (
                   <>
-                    <div className="relative w-80">
-                      <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
-                      <input
-                        type="text"
-                        placeholder="搜索字典项..."
-                        value={dictionarySearchTerm}
-                        onChange={(e) => setDictionarySearchTerm(e.target.value)}
-                        className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-                      />
+                    {/* 左侧:搜索条件 + 紧跟其后的 搜索 / 重置 */}
+                    <div className="flex items-center gap-3">
+                      <div className="relative w-80">
+                        <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
+                        <input
+                          type="text"
+                          placeholder="搜索字典项..."
+                          value={dictionarySearchTerm}
+                          onChange={(e) => setDictionarySearchTerm(e.target.value)}
+                          className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+                          onKeyDown={(e) => {
+                            if (e.key === 'Enter') {
+                              setDictionarySearchTerm(dictionarySearchTerm.trim());
+                            }
+                          }}
+                        />
+                      </div>
+                      <button
+                        type="button"
+                        onClick={() => setDictionarySearchTerm(dictionarySearchTerm.trim())}
+                        className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+                      >
+                        搜索
+                      </button>
+                      <button
+                        type="button"
+                        onClick={() => setDictionarySearchTerm('')}
+                        className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+                      >
+                        重置
+                      </button>
                     </div>
+                    {/* 右侧:新增按钮保持在最右侧 */}
                     <button
                       onClick={() => {
                         setEditingDictionaryItem(null);
@@ -775,17 +798,40 @@ export default function SystemConfig({ subMenu }: SystemConfigProps) {
       {/* 工具栏 */}
       <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-4">
         <div className="flex items-center justify-between">
-          <div className="relative w-80">
-            <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
-            <input
-              type="text"
-              placeholder="搜索..."
-              value={searchTerm}
-              onChange={(e) => setSearchTerm(e.target.value)}
-              className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
-            />
+          {/* 左侧:搜索条件 + 紧跟其后的 搜索 / 重置 */}
+          <div className="flex items-center gap-3">
+            <div className="relative w-80">
+              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400" />
+              <input
+                type="text"
+                placeholder="搜索..."
+                value={searchTerm}
+                onChange={(e) => setSearchTerm(e.target.value)}
+                className="w-full h-10 pl-10 pr-4 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-400 focus:ring-4 focus:ring-blue-100 transition-all text-sm"
+                onKeyDown={(e) => {
+                  if (e.key === 'Enter') {
+                    setSearchTerm(searchTerm.trim());
+                  }
+                }}
+              />
+            </div>
+            <button
+              type="button"
+              onClick={() => setSearchTerm(searchTerm.trim())}
+              className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+            >
+              搜索
+            </button>
+            <button
+              type="button"
+              onClick={() => setSearchTerm('')}
+              className="px-3 py-2 text-sm border border-gray-300 rounded-lg bg-white hover:bg-gray-50 text-gray-700"
+            >
+              重置
+            </button>
           </div>
 
+          {/* 右侧:新增按钮保持在最右侧 */}
           <div className="flex gap-3">
             <button
               onClick={() => {

+ 95 - 50
src/components/TaskManagement.tsx

@@ -209,10 +209,11 @@ export default function TaskManagement() {
   const [queryParams, setQueryParams] = useState<MyTaskPageParam>({
     pageNo: 1,
     pageSize: 10,
-    key: '',
+    key: undefined,
     status: getInitialStatus(),
   });
-  const [searchKey, setSearchKey] = useState('');
+  const [searchOrderNo, setSearchOrderNo] = useState('');
+  const [searchName, setSearchName] = useState('');
   const [approvalStatusDictList, setApprovalStatusDictList] = useState<any[]>([]);
   const [urgencyLevelDictList, setUrgencyLevelDictList] = useState<any[]>([]);
   const [jobCategoryDictList, setJobCategoryDictList] = useState<any[]>([]); // 作业分类 work_type
@@ -366,18 +367,20 @@ export default function TaskManagement() {
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [queryParams.pageNo, queryParams.pageSize, queryParams.key, queryParams.status]);
 
-  // 搜索
+  // 搜索:两个条件都通过 key 传参,值为具体输入(多个用空格拼接)
   const handleSearch = () => {
+    const parts = [searchOrderNo.trim(), searchName.trim()].filter(Boolean);
     setQueryParams({
       ...queryParams,
       pageNo: 1,
-      key: searchKey.trim() || undefined,
+      key: parts.length ? parts.join(' ') : undefined,
     });
   };
 
   // 重置
   const handleReset = () => {
-    setSearchKey('');
+    setSearchOrderNo('');
+    setSearchName('');
     setQueryParams({
       ...queryParams,
       pageNo: 1,
@@ -1692,6 +1695,10 @@ export default function TaskManagement() {
   const getWorkStatusText = (status: string | number | undefined): string => {
     if (status == null || String(status).trim() === '') return t('workJobDetail.statusUnknown');
     const s = String(status).toLowerCase();
+    // 后端可能直接返回「执行完成」「已完成」等中文文案,这里统一映射为「已完成」
+    if (s.includes('执行完成') || s.includes('已完成') || s === 'completed') {
+      return '已完成';
+    }
     const map: Record<string, string> = {
       pending: t('workJobDetail.statusPending'),
       running: t('workJobDetail.statusRunning'),
@@ -1813,8 +1820,17 @@ export default function TaskManagement() {
       width: 220,
       align: 'center',
       ellipsis: true,
+      render: (text: string) => text || '-',
+    },
+    {
+      title: t('form.currentTask'),
+      dataIndex: 'currentNodeName',
+      width: 180,
+      align: 'center',
+      ellipsis: true,
       render: (text: string, record: MyTaskVO) => {
-        if (!text) return '-';
+        const displayText = text || record.currentNode || '-';
+        if (!displayText || displayText === '-') return displayText;
         return (
           <span
             className="cursor-pointer hover:underline"
@@ -1830,23 +1846,13 @@ export default function TaskManagement() {
               e.preventDefault();
               handleViewDetail(record);
             }}
-            title={text}
+            title={displayText}
           >
-            {text}
+            {displayText}
           </span>
         );
       },
     },
-    {
-      title: t('form.currentTask'),
-      dataIndex: 'currentNodeName',
-      width: 180,
-      align: 'center',
-      render: (text: string, record: MyTaskVO) => {
-        // 优先使用 currentNodeName,如果没有则使用 currentNode
-        return text || record.currentNode || '-';
-      },
-    },
     {
       title: t('form.taskStatus'),
       dataIndex: 'approvalStatus',
@@ -1977,36 +1983,48 @@ export default function TaskManagement() {
       <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm overflow-hidden">
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
-          <div className="flex flex-wrap items-center gap-3">
+          <div className="flex flex-wrap gap-3 items-center">
+            {/* 搜索条件:作业编号、作业名称 */}
+            <span className="text-sm text-gray-700 whitespace-nowrap">作业编号:</span>
             <Input
-              value={searchKey}
-              onChange={(e) => setSearchKey(e.target.value)}
-              placeholder={t('form.taskSearchPlaceholder')}
+              value={searchOrderNo}
+              onChange={(e) => setSearchOrderNo(e.target.value)}
+              placeholder="请输入作业编号"
               allowClear
-              className="flex-1"
-              style={{ minWidth: 200 }}
+              style={{ width: 160 }}
               onPressEnter={handleSearch}
             />
-            <Space size="small">
+            <span className="text-sm text-gray-700 whitespace-nowrap">作业名称:</span>
+            <Input
+              value={searchName}
+              onChange={(e) => setSearchName(e.target.value)}
+              placeholder="请输入作业名称"
+              allowClear
+              style={{ width: 160 }}
+              onPressEnter={handleSearch}
+            />
+
+            {/* 按钮组:小屏整行,大屏右对齐 */}
+            <div className="flex gap-2 w-full sm:w-auto sm:ml-auto justify-start sm:justify-end">
               <Button type="primary" icon={<Search className="w-4 h-4" />} onClick={handleSearch}>
                 {t('common.search')}
               </Button>
               <Button icon={<RotateCcw className="w-4 h-4" />} onClick={handleReset}>
                 {t('common.reset')}
               </Button>
-            </Space>
+            </div>
           </div>
         </div>
 
         {/* 表格容器 */}
-        <div className="overflow-hidden min-w-0">
+        <div className="overflow-x-auto min-w-0">
           <AntdTable
             loading={loading}
             columns={columns}
             dataSource={list}
             rowKey={(record) => record.id || Math.random()}
             pagination={false}
-            scroll={{ x: 'max-content' }}
+            scroll={{ x: 1200 }}
             locale={{
               emptyText: t('form.noData'),
             }}
@@ -2123,11 +2141,15 @@ export default function TaskManagement() {
                   );
                 })()}
               </div>
-              <div className="text-sm flex flex-wrap gap-x-6 gap-y-2" style={{ color: '#64748b', marginTop: 14 }}>
+              <div className="text-sm flex flex-wrap gap-y-2" style={{ color: '#64748b', marginTop: 14 }}>
                 <span>{t('form.workOrderNo')}:{detailData.orderNo || '-'}</span>
-                <span>{t('form.workResponsible')}:{detailData.initiatorName || '-'}</span>
-                <span>{t('form.taskResponsible')}:{detailData.workerUserName || '-'}</span>
-                <span>{t('form.initiationTime')}:{(detailData as any).workDetail?.initiationTime != null ? dateFormatter((detailData as any).workDetail.initiationTime) : (detailData.initiationTime != null ? dateFormatter(detailData.initiationTime) : (detailData.workTime ? dateFormatter(detailData.workTime) : '-'))}</span>
+                <span style={{ paddingLeft: 16, marginLeft: 12, borderLeft: '1px solid #e2e8f0' }}>
+                  {t('form.workResponsible')}:{detailData.initiatorName || '-'}</span>
+                <span style={{ paddingLeft: 16, marginLeft: 12, borderLeft: '1px solid #e2e8f0' }}>
+                  {t('form.taskResponsible')}:{detailData.workerUserName || '-'}</span>
+                <span style={{ paddingLeft: 16, marginLeft: 12, borderLeft: '1px solid #e2e8f0' }}>
+                  {t('form.initiationTime')}:{(detailData as any).workDetail?.initiationTime != null ? dateFormatter((detailData as any).workDetail.initiationTime) : (detailData.initiationTime != null ? dateFormatter(detailData.initiationTime) : (detailData.workTime ? dateFormatter(detailData.workTime) : '-'))}
+                </span>
               </div>
             </div>
 
@@ -2438,8 +2460,10 @@ export default function TaskManagement() {
                         : { backgroundColor: '#e2e8f0', color: '#475569' };
                     const getKeyStatus = (k: any) => {
                       const status = String(k?.keyStatus ?? '').trim();
-                      if (status === '1') return 'returned';
-                      if (status === '0') return 'pending';
+                      // 钥匙状态(0-待取出 1-已取出 2-已归还)
+                      if (status === '0') return 'to_take';
+                      if (status === '1') return 'taken';
+                      if (status === '2') return 'returned';
                       return 'unknown';
                     };
                     const getUserAvatarSrc = (u: any) =>
@@ -2453,9 +2477,10 @@ export default function TaskManagement() {
                       undefined;
                     const getUserName = (u: any) => u?.nickname ?? u?.nickName ?? u?.name ?? u?.username ?? '-';
                     const getLockerStatus = (u: any) => {
-                      // 上锁人状态:0=未上锁,1=已上
+                      // 上锁人状态:0初始、1已上锁、2已解
                       const s = String(u?.status ?? '').trim();
                       if (s === '1') return 'locked';
+                      if (s === '2') return 'unlocked_done';
                       if (s === '0') return 'unlocked';
                       return 'unknown';
                     };
@@ -2518,7 +2543,6 @@ export default function TaskManagement() {
                                       const name = getUserName(u);
                                       const avatarSrc = getUserAvatarSrc(u);
                                       const st = getLockerStatus(u);
-                                      const isLockedByPerson = st === 'locked';
                                       return (
                                         <div
                                           key={u.id ?? u.userId ?? u.uid ?? idx}
@@ -2543,21 +2567,26 @@ export default function TaskManagement() {
                                               </div>
                                             </div>
                                           </div>
-                                          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, color: isLockedByPerson ? '#16a34a' : '#dc2626' }}>
+                                          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12 }}>
                                             {st === 'unknown' ? (
                                               <>
                                                 <WarningOutlined style={{ fontSize: 14, color: '#f97316' }} />
-                                                <span>未知</span>
+                                                <span style={{ color: '#f97316' }}>未知</span>
+                                              </>
+                                            ) : st === 'locked' ? (
+                                              <>
+                                                <CheckCircleOutlined style={{ fontSize: 14, color: '#16a34a' }} />
+                                                <span style={{ color: '#16a34a' }}>已上锁</span>
                                               </>
-                                            ) : isLockedByPerson ? (
+                                            ) : st === 'unlocked_done' ? (
                                               <>
                                                 <CheckCircleOutlined style={{ fontSize: 14, color: '#16a34a' }} />
-                                                <span>已上锁</span>
+                                                <span style={{ color: '#16a34a' }}>已解锁</span>
                                               </>
                                             ) : (
                                               <>
                                                 <CloseCircleOutlined style={{ fontSize: 14, color: '#dc2626' }} />
-                                                <span>未上锁</span>
+                                                <span style={{ color: '#dc2626' }}>未上锁</span>
                                               </>
                                             )}
                                           </span>
@@ -2664,8 +2693,18 @@ export default function TaskManagement() {
                                         }
                                         const status = getKeyStatus(k);
                                         const isReturned = status === 'returned';
-                                        const statusText = status === 'returned' ? '已归还' : status === 'pending' ? '未归还' : '未知';
-                                        const statusColor = isReturned ? '#16a34a' : status === 'pending' ? '#dc2626' : '#6b7280';
+                                        let statusText = '未知';
+                                        let statusColor = '#6b7280';
+                                        if (status === 'to_take') {
+                                          statusText = '待取出';
+                                          statusColor = '#f97316';
+                                        } else if (status === 'taken') {
+                                          statusText = '已取出';
+                                          statusColor = '#dc2626';
+                                        } else if (isReturned) {
+                                          statusText = '已归还';
+                                          statusColor = '#16a34a';
+                                        }
                                         const returnedAt =
                                           k?.giveBackTime ??
                                           k?.returnTime ??
@@ -2716,12 +2755,14 @@ export default function TaskManagement() {
                                                     color: statusColor,
                                                   }}
                                                 >
-                                                  {isReturned ? (
+                                                  {status === 'returned' ? (
                                                     <CheckCircleOutlined style={{ fontSize: 14, color: '#16a34a' }} />
-                                                  ) : status === 'pending' ? (
+                                                  ) : status === 'taken' ? (
                                                     <CloseCircleOutlined style={{ fontSize: 14, color: '#dc2626' }} />
-                                                  ) : (
+                                                  ) : status === 'to_take' ? (
                                                     <WarningOutlined style={{ fontSize: 14, color: '#f97316' }} />
+                                                  ) : (
+                                                    <WarningOutlined style={{ fontSize: 14, color: '#9ca3af' }} />
                                                   )}
                                                   <span>{statusText}</span>
                                                 </span>
@@ -2828,8 +2869,10 @@ export default function TaskManagement() {
                       : [];
                     const getReleaseKeyStatus = (k: any) => {
                       const s = String(k?.keyStatus ?? '').trim();
-                      if (s === '1') return 'returned';
-                      if (s === '0') return 'pending';
+                      // 钥匙状态(0-待取出 1-已取出 2-已归还)
+                      if (s === '0') return 'to_take';
+                      if (s === '1') return 'taken';
+                      if (s === '2') return 'returned';
                       return 'unknown';
                     };
                     const getReleaseUserAvatarSrc = (u: any) =>
@@ -2864,7 +2907,8 @@ export default function TaskManagement() {
                                       }
                                       const name = getReleaseUserName(u);
                                       const avatarSrc = getReleaseUserAvatarSrc(u);
-                                      const released = String(u?.status ?? '').trim() === '1';
+                                      // 共锁人状态:0初始、1已上锁/已共锁、2已解锁/已解共锁
+                                      const released = String(u?.status ?? '').trim() === '2';
                                       return (
                                         <div key={u.id ?? u.userId ?? idx} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '10px 10px', borderRadius: 10, border: '1px solid #e6e8f0', background: '#ffffff' }}>
                                           <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
@@ -2933,7 +2977,8 @@ export default function TaskManagement() {
                                       const name = getReleaseUserName(u);
                                       const avatarSrc = getReleaseUserAvatarSrc(u);
                                       const unlockStatus = String(u?.status ?? '').trim();
-                                      const isUnlocked = unlockStatus === '1';
+                                      // 解锁状态:0初始、1已上锁、2已解锁
+                                      const isUnlocked = unlockStatus === '2';
                                       return (
                                         <div key={u.id ?? u.userId ?? idx} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '10px 10px', borderRadius: 10, border: '1px solid #e6e8f0', background: '#ffffff' }}>
                                           <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0 }}>

+ 24 - 23
src/components/UserManagement.tsx

@@ -529,8 +529,8 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
           {/* 搜索栏 */}
           <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
             <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-              {/* 搜索输入框 */}
-              <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+              {/* 搜索条件 + 搜索、重置紧跟其后 */}
+              <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
                 <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                   <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.nickname')}:</label>
                   <Input
@@ -564,29 +564,30 @@ export default function UserManagement({ subMenu }: UserManagementProps) {
                     allowClear
                   />
                 </div>
+                <Space className="flex-shrink-0" size="small">
+                  <PermissionWrapper permission="system:user:query">
+                    <Button
+                      type="primary"
+                      icon={<Search className="w-4 h-4" />}
+                      onClick={handleQuery}
+                    >
+                      {t('common.search')}
+                    </Button>
+                  </PermissionWrapper>
+                  
+                  <PermissionWrapper permission="system:user:query">
+                    <Button
+                      icon={<RefreshCw className="w-4 h-4" />}
+                      onClick={resetQuery}
+                    >
+                      {t('common.reset')}
+                    </Button>
+                  </PermissionWrapper>
+                </Space>
               </div>
 
-              {/* 操作按钮组 */}
-              <Space className="flex-shrink-0">
-                <PermissionWrapper permission="system:user:query">
-                  <Button
-                    type="primary"
-                    icon={<Search className="w-4 h-4" />}
-                    onClick={handleQuery}
-                  >
-                    {t('common.search')}
-                  </Button>
-                </PermissionWrapper>
-                
-                <PermissionWrapper permission="system:user:query">
-                  <Button
-                    icon={<RefreshCw className="w-4 h-4" />}
-                    onClick={resetQuery}
-                  >
-                    {t('common.reset')}
-                  </Button>
-                </PermissionWrapper>
-                
+              {/* 新增 / 导入 / 导出等按钮组保持在最右侧 */}
+              <Space className="flex-shrink-0" size="small">
                 <PermissionWrapper permission="system:user:create">
                   <Button
                     type="primary"

+ 53 - 7
src/components/WorkJobArchiveReport.css

@@ -82,6 +82,19 @@
   border-color: white;
 }
 
+/* 纸张容器:宽度 210mm(A4),大屏下缩放填满视口,减小左右留白、1:1 A4 观感 */
+.work-job-archive-report .archive-pages-wrap {
+  width: 210mm;
+  margin-left: auto;
+  margin-right: auto;
+  transform-origin: top center;
+}
+@media (min-width: 850px) {
+  .work-job-archive-report .archive-pages-wrap {
+    transform: scale(calc((100vw - 2rem) / 794px));
+  }
+}
+
 /* 通用纸张样式 - 与 test1 .page-sheet 完全一致 */
 .work-job-archive-report .page-sheet {
   background: white;
@@ -94,7 +107,7 @@
   box-sizing: border-box;
 }
 
-/* 打印时分页控制 - 仅打印两页,避免多出空白页 */
+/* 打印时分页控制 - 仅打印两页,避免多出空白页;不缩放,保持 A4 */
 @media print {
   html, body, #root, #root > * {
     height: auto !important;
@@ -111,6 +124,9 @@
   .work-job-archive-report .no-print {
     display: none !important;
   }
+  .work-job-archive-report .archive-pages-wrap {
+    transform: none !important;
+  }
   .work-job-archive-report .page-sheet {
     margin: 0 !important;
     box-shadow: none !important;
@@ -417,12 +433,17 @@
 .work-job-archive-report .page-1 .record-table-wrap {
   background: #fff;
   border-radius: 8px;
-  overflow: hidden;
+  overflow-x: auto;
+  overflow-y: visible;
   border: 1px solid #e2e8f0;
   box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
 }
+.work-job-archive-report .page-1 .record-table-wrap .record-table.record-table-log {
+  min-width: 420px;
+}
 .work-job-archive-report .page-1 .record-table {
   width: 100%;
+  table-layout: fixed;
   text-align: left;
   font-size: 0.875rem;
   border-collapse: collapse;
@@ -439,11 +460,14 @@
   vertical-align: middle;
 }
 .work-job-archive-report .page-1 .record-table.record-table-log th.col-no { width: 2.5em; }
-.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(2) { width: 8em; }
-.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(3) { width: 9em; }
+.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(2) { width: 12em; }
+.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(3) { width: 10em; min-width: 7em; }
 .work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(4) { width: 11em; }
-.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(5) { width: 6em; }
+.work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(5) { width: 5em; min-width: 5em; white-space: nowrap; }
 .work-job-archive-report .page-1 .record-table.record-table-log th:nth-child(6) { min-width: 0; }
+.work-job-archive-report .page-1 .record-table.record-table-log td:nth-child(3) {
+  min-width: 7em;
+}
 .work-job-archive-report .page-1 .record-table.record-table-log tbody tr {
   border-bottom: 1px solid #f1f5f9;
   background: #fff;
@@ -472,16 +496,37 @@
   gap: 0.5rem;
   color: #334155;
 }
+.work-job-archive-report .page-1 .record-table.record-table-log .cell-operator .archive-log-avatar {
+  flex-shrink: 0;
+  width: 24px !important;
+  min-width: 24px !important;
+  height: 24px !important;
+  min-height: 24px !important;
+}
 .work-job-archive-report .page-1 .record-table.record-table-log .archive-log-avatar {
-  width: 24px;
-  height: 24px;
+  width: 24px !important;
+  min-width: 24px !important;
+  height: 24px !important;
+  min-height: 24px !important;
   flex-shrink: 0;
+  overflow: hidden;
+  border-radius: 50%;
+}
+.work-job-archive-report .page-1 .record-table.record-table-log .archive-log-avatar [data-slot="avatar-image"] {
+  width: 100% !important;
+  height: 100% !important;
+  object-fit: cover;
 }
 .work-job-archive-report .page-1 .record-table.record-table-log .archive-log-avatar [data-slot="avatar-fallback"] {
+  width: 100% !important;
+  height: 100% !important;
   background-color: #e2e8f0;
   color: #475569;
   font-size: 0.75rem;
   font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
 }
 .work-job-archive-report .page-1 .record-table.record-table-log .archive-log-avatar [data-slot="avatar-fallback"].avatar-active {
   background-color: #dbeafe;
@@ -491,6 +536,7 @@
   display: inline-flex;
   align-items: center;
   gap: 0.35rem;
+  white-space: nowrap;
 }
 .work-job-archive-report .page-1 .record-table.record-table-log .cell-status .status-dot {
   width: 6px;

+ 294 - 59
src/components/WorkJobArchiveReport.tsx

@@ -2,8 +2,8 @@ import React, { useState, useEffect, useRef } from 'react';
 import { useSearchParams, useNavigate } from 'react-router-dom';
 import { toast } from 'sonner';
 import { ArrowLeft, Check, CheckCircle2, Clock, FileDown, FileText, Flag, Lock, Loader2, Printer, Settings, Shield } from 'lucide-react';
-import ReactFlow, { Node, Edge, useNodesState, useEdgesState, Background, BackgroundVariant, Handle, Position, ReactFlowProvider } from 'reactflow';
-import type { NodeTypes } from 'reactflow';
+import ReactFlow, { useNodesState, useEdgesState, Background, BackgroundVariant, Handle, Position, ReactFlowProvider } from 'reactflow';
+import type { Node as FlowNode, Edge, NodeTypes } from 'reactflow';
 import 'reactflow/dist/style.css';
 import { workJobApi } from '../api/WorkJob';
 import { workHandleApi } from '../api/WorkHandle';
@@ -201,6 +201,107 @@ const getNodeStepState = (approvalStatus?: string | number): 'done' | 'active' |
   return 'pending';
 };
 
+/** 流程节点(含 parentUuid,用于主路径计算) */
+type WorkflowNodeWithParent = {
+  id?: number;
+  uuid?: string;
+  nodeName?: string;
+  parentUuid?: string;
+  createTime?: number;
+  type?: string;
+  [key: string]: any;
+};
+
+/**
+ * 从完整节点列表中提取「主线」节点(仅一条从开始到结束的路径,并行/分支节点不包含)。
+ * 与 WorkJobDetail 主路径逻辑一致:找头(createJob/无父)、尾(complete/结束),沿路径遇多子节点时选分支数最少的。
+ */
+function getMainPathNodes(
+  nodeList: WorkflowNodeWithParent[]
+): WorkflowNodeWithParent[] {
+  if (!nodeList?.length) return [];
+  const nodeMap = new Map<string, WorkflowNodeWithParent>();
+  nodeList.forEach((n) => { if (n.uuid) nodeMap.set(String(n.uuid), n); });
+
+  const childrenMap = new Map<string, WorkflowNodeWithParent[]>();
+  nodeList.forEach((node) => {
+    if (node.parentUuid) {
+      const parentUuids = String(node.parentUuid).split(',').map((u) => u.trim()).filter(Boolean);
+      parentUuids.forEach((parentUuid) => {
+        if (!childrenMap.has(parentUuid)) childrenMap.set(parentUuid, []);
+        childrenMap.get(parentUuid)!.push(node);
+      });
+    }
+  });
+  childrenMap.forEach((children) => {
+    children.sort((a, b) => (a.createTime ?? 0) - (b.createTime ?? 0));
+  });
+
+  const countBranchNodes = (node: WorkflowNodeWithParent, visited: Set<string> = new Set()): number => {
+    const uid = node.uuid || '';
+    if (visited.has(uid)) return 0;
+    visited.add(uid);
+    const children = childrenMap.get(uid) || [];
+    let count = children.length;
+    children.forEach((child) => { count += countBranchNodes(child, visited); });
+    return count;
+  };
+
+  const findHead = (): WorkflowNodeWithParent | null => {
+    const byCreateJob = nodeList.find((n) => n.type === 'createJob' || (n.nodeName && (n.nodeName.includes('创建') || n.nodeName.includes('开始'))));
+    if (byCreateJob) return byCreateJob;
+    const roots = nodeList.filter((n) => !n.parentUuid || String(n.parentUuid).trim() === '');
+    if (roots.length > 0) {
+      roots.sort((a, b) => (a.createTime ?? 0) - (b.createTime ?? 0));
+      return roots[0];
+    }
+    return null;
+  };
+
+  const findTail = (): WorkflowNodeWithParent | null => {
+    return nodeList.find((n) => n.type === 'complete' || (n.nodeName && (n.nodeName === '完成/结束' || n.nodeName.includes('结束作业')))) ?? null;
+  };
+
+  const buildPathFromHead = (head: WorkflowNodeWithParent, tail: WorkflowNodeWithParent): WorkflowNodeWithParent[] => {
+    const path: WorkflowNodeWithParent[] = [head];
+    const visited = new Set<string>([head.uuid || '']);
+    let current: WorkflowNodeWithParent = head;
+
+    while (current && current.uuid !== tail.uuid) {
+      const children = childrenMap.get(current.uuid || '') || [];
+      if (children.length === 0) break;
+      const available = children.filter((c) => !visited.has(c.uuid || ''));
+      if (available.length === 0) break;
+
+      let next: WorkflowNodeWithParent;
+      if (available.length === 1) {
+        next = available[0];
+      } else {
+        const withCount = available.map((child) => ({
+          node: child,
+          count: countBranchNodes(child, new Set()),
+        }));
+        withCount.sort((a, b) => a.count - b.count);
+        next = withCount[0].node;
+      }
+      visited.add(next.uuid || '');
+      path.push(next);
+      current = next;
+    }
+    return path;
+  };
+
+  const head = findHead();
+  const tail = findTail();
+  if (!head || !tail) return [];
+
+  const mainPath = buildPathFromHead(head, tail);
+  if (mainPath.length > 0 && mainPath[mainPath.length - 1].uuid !== tail.uuid) {
+    mainPath.push(tail);
+  }
+  return mainPath;
+}
+
 /** 根据 approvalStatus 得到 React Flow 节点状态 */
 const getFlowNodeStatus = (approvalStatus?: string | number): 'completed' | 'in_progress' | 'pending' => {
   if (approvalStatus == null || approvalStatus === '') return 'pending';
@@ -214,7 +315,7 @@ const getFlowNodeStatus = (approvalStatus?: string | number): 'completed' | 'in_
 function parseDesignContentLikeJobDetail(
   designContent: string | object,
   workflowWorkNodeDOList: Array<{ uuid?: string; nodeName?: string; approvalStatus?: string | number; [key: string]: any }>
-): { nodes: Node[]; edges: Edge[] } | null {
+): { nodes: FlowNode[]; edges: Edge[] } | null {
   try {
     const jsonData = typeof designContent === 'string' ? JSON.parse(designContent) : designContent;
     if (!jsonData || typeof jsonData !== 'object') return null;
@@ -232,7 +333,7 @@ function parseDesignContentLikeJobDetail(
     const nodeMap = new Map<string, { nodeName?: string; approvalStatus?: string | number; type?: string; nodeIcon?: string; id?: number; [key: string]: any }>();
     workflowWorkNodeDOList.forEach((n) => { if (n.uuid) nodeMap.set(String(n.uuid), n); });
     const validTypes = new Set(['createJob', 'confirm', 'review', 'inputInfo', 'isolation', 'releaseIsolation', 'returnLock', 'complete']);
-    const convertedNodes: Node[] = rawNodes
+    const convertedNodes: FlowNode[] = rawNodes
       .map((node: any) => {
         const nodeId = node.uuid ?? node.id;
         if (nodeId == null || nodeId === '') return null;
@@ -263,7 +364,7 @@ function parseDesignContentLikeJobDetail(
           },
         };
       })
-      .filter((n): n is Node => n != null);
+      .filter((n): n is FlowNode => n != null);
     if (convertedNodes.length === 0) return null;
 
     const nodeIdSet = new Set(convertedNodes.map((n) => n.id));
@@ -288,13 +389,13 @@ function parseDesignContentLikeJobDetail(
 /** 仅根据 workflowWorkNodeDOList 构建简易拓扑(无 designContent 时的兜底) */
 function buildFlowFromNodeList(
   workflowWorkNodeDOList?: Array<{ uuid?: string; nodeName?: string; approvalStatus?: string | number; parentUuid?: string; childrenUuid?: string; position?: string; createTime?: number; [key: string]: any }>
-): { nodes: Node[]; edges: Edge[] } {
+): { nodes: FlowNode[]; edges: Edge[] } {
   const list = workflowWorkNodeDOList ?? [];
   if (list.length === 0) return { nodes: [], edges: [] };
   const sorted = [...list].sort((a, b) => (a.createTime ?? 0) - (b.createTime ?? 0));
   const gap = 180;
   const validTypes = new Set(['createJob', 'confirm', 'review', 'inputInfo', 'isolation', 'releaseIsolation', 'returnLock', 'complete']);
-  const nodes: Node[] = sorted
+  const nodes: FlowNode[] = sorted
     .filter((n) => n.uuid)
     .map((n, i) => {
       const id = String(n.uuid);
@@ -468,7 +569,7 @@ export default function WorkJobArchiveReport() {
       ]);
       const A4_W = 210;
       const A4_H = 297;
-      const scale = 2;
+      const scale = 2.5;
       /** 将 data URL 转为 Canvas,便于统一用 fitToA4 与 addImage */
       const dataUrlToCanvas = (dataUrl: string): Promise<HTMLCanvasElement> =>
         new Promise((resolve, reject) => {
@@ -510,11 +611,52 @@ export default function WorkJobArchiveReport() {
         }
         return s;
       };
-      /** 将拓扑图内图片转为 data URL(用 canvas 绘制,兼容 blob/同源),确保导出 PDF 时图标能正确渲染;返回恢复函数 */
+      /** 通过 fetch 将图片转为 data URL;同源带 cookie,跨域用 CORS */
+      const fetchImageAsDataUrl = (url: string): Promise<string | null> => {
+        try {
+          const isSameOrigin = typeof window !== 'undefined' && new URL(url, window.location.href).origin === window.location.origin;
+          return fetch(url, { mode: isSameOrigin ? 'same-origin' : 'cors', credentials: isSameOrigin ? 'include' : 'omit' })
+            .then((r) => (r.ok ? r.blob() : Promise.reject(new Error('not ok'))))
+            .then(
+              (blob) =>
+                new Promise<string | null>((resolve, reject) => {
+                  const r = new FileReader();
+                  r.onload = () => resolve(typeof r.result === 'string' ? r.result : null);
+                  r.onerror = () => reject(r.error);
+                  r.readAsDataURL(blob);
+                })
+            )
+            .catch(() => null);
+        } catch {
+          return Promise.resolve(null);
+        }
+      };
+
+      /** 将容器内图片转为 data URL(导出 PDF 时用);归档流水中的头像不转换,PDF 中只保留文字 */
       const preloadFlowImagesToDataUrls = async (container: HTMLElement): Promise<() => void> => {
         const imgs = container.querySelectorAll<HTMLImageElement>('img[src]');
         const restores: Array<{ el: HTMLImageElement; original: string }> = [];
-        const toDataUrl = (img: HTMLImageElement): boolean => {
+        const setDataUrl = (img: HTMLImageElement, dataUrl: string, original: string) => {
+          restores.push({ el: img, original });
+          img.src = dataUrl;
+        };
+        const waitForImgLoad = (img: HTMLImageElement, timeoutMs = 3000): Promise<void> =>
+          new Promise((resolve) => {
+            if (img.complete && img.naturalWidth) {
+              resolve();
+              return;
+            }
+            const t = setTimeout(resolve, timeoutMs);
+            img.onload = () => {
+              clearTimeout(t);
+              resolve();
+            };
+            img.onerror = () => {
+              clearTimeout(t);
+              resolve();
+            };
+          });
+        const toDataUrlFromCanvas = (img: HTMLImageElement): boolean => {
           try {
             if (!img.naturalWidth || !img.naturalHeight) return false;
             const c = document.createElement('canvas');
@@ -524,8 +666,7 @@ export default function WorkJobArchiveReport() {
             if (!ctx) return false;
             ctx.drawImage(img, 0, 0);
             const dataUrl = c.toDataURL('image/png');
-            restores.push({ el: img, original: img.src });
-            img.src = dataUrl;
+            setDataUrl(img, dataUrl, img.src);
             return true;
           } catch {
             return false;
@@ -533,16 +674,29 @@ export default function WorkJobArchiveReport() {
         };
         await Promise.all(
           Array.from(imgs).map((img) => {
-            const src = img.getAttribute('src') || img.src;
+            if (img.closest('.archive-log-avatar')) return Promise.resolve();
+            const src = (img.getAttribute('src') || img.src || '').trim();
             if (!src || src.startsWith('data:')) return Promise.resolve();
+            const resolvedUrl = img.src || src;
             return new Promise<void>((resolve) => {
-              if (img.complete && img.naturalWidth) {
-                toDataUrl(img);
-                resolve();
-                return;
-              }
-              img.onload = () => { toDataUrl(img); resolve(); };
-              img.onerror = () => resolve();
+              const tryConvert = () => {
+                if (!img.complete || !img.naturalWidth) {
+                  img.onload = tryConvert;
+                  img.onerror = () => resolve();
+                  return;
+                }
+                if (toDataUrlFromCanvas(img)) {
+                  waitForImgLoad(img).then(resolve);
+                  return;
+                }
+                fetchImageAsDataUrl(resolvedUrl).then((dataUrl) => {
+                  if (dataUrl) {
+                    setDataUrl(img, dataUrl, img.src);
+                    waitForImgLoad(img).then(resolve);
+                  } else resolve();
+                }).catch(() => resolve());
+              };
+              tryConvert();
             });
           })
         );
@@ -592,6 +746,15 @@ export default function WorkJobArchiveReport() {
         logging: false,
         onclone: (clonedDoc: Document) => {
           stripOklch(clonedDoc);
+          // 第一页只固定宽度 210mm,高度随内容(含完整流水日志),导出时再按 A4 高度切片为多页,避免截断
+          const page1 = clonedDoc.querySelector<HTMLElement>('.work-job-archive-report .page-sheet.page-1');
+          if (page1) {
+            page1.style.width = '210mm';
+            page1.style.minWidth = '210mm';
+            page1.style.minHeight = '0';
+            page1.style.overflow = 'visible';
+            page1.style.boxSizing = 'border-box';
+          }
           // 导出时隐藏连接点与控制按钮,避免拓扑图在 PDF 中渲染混乱
           const exportStyle = clonedDoc.createElement('style');
           exportStyle.textContent = [
@@ -601,8 +764,21 @@ export default function WorkJobArchiveReport() {
             /* 避免连接线被裁切,确保 SVG 边线完整导出 */
             '.archive-flow-print-wrapper, .archive-flow-inner, .react-flow__viewport, .react-flow__edges, .react-flow__renderer { overflow: visible !important; }',
             '.react-flow__edges svg { overflow: visible !important; }',
+            /* PDF 导出:归档流水操作人列不显示头像,只显示名字文本 */
+            '.archive-log-avatar { display: none !important; }',
           ].join('\n');
           clonedDoc.head.appendChild(exportStyle);
+          /* 有头像时 Radix 可能不渲染 Fallback 内容,克隆里补上首字,避免 PDF 中为空白 */
+          clonedDoc.querySelectorAll('.archive-log-avatar').forEach((avatar) => {
+            const img = avatar.querySelector('[data-slot="avatar-image"]');
+            if (!img?.getAttribute('src')) return;
+            const fallback = avatar.querySelector('[data-slot="avatar-fallback"]');
+            if (!fallback) return;
+            const nameNode = avatar.nextSibling;
+            const nameText = (nameNode?.nodeType === Node.TEXT_NODE ? nameNode.textContent : '')?.trim() ?? '';
+            const initial = (nameText && nameText.charAt(0)) || (fallback.textContent?.trim()?.charAt(0)) || '?';
+            fallback.textContent = initial;
+          });
           /* 为边线 SVG 设置宽高,改善 html2canvas 对 SVG 的渲染 */
           clonedDoc.querySelectorAll('.react-flow__edges svg').forEach((svg) => {
             const el = svg as SVGSVGElement;
@@ -614,7 +790,15 @@ export default function WorkJobArchiveReport() {
           });
         },
       };
-      const canvas1 = await html2canvas.default(el1, opts);
+      // 第一页内头像等图片转为 data URL,避免 PDF 中只渲染出 Fallback 文字(如只显示「李」)
+      const restorePage1Images = await preloadFlowImagesToDataUrls(el1);
+      await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));
+      let canvas1: HTMLCanvasElement;
+      try {
+        canvas1 = await html2canvas.default(el1, opts);
+      } finally {
+        restorePage1Images();
+      }
 
       // 截取第二页前:统一把拓扑图内图片转为 data URL(两种导出方式都生效),并隐藏手柄/控制栏(不改 overflow,避免 PDF 中位置偏移)
       const restoreFlowImages = await preloadFlowImagesToDataUrls(el2);
@@ -637,20 +821,55 @@ export default function WorkJobArchiveReport() {
         document.getElementById('pdf-export-flow-style')?.remove();
       }
       const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' });
+      // 第一页内容可能超过一屏:按 A4 高度切片为多页,保证流水日志等全部展示且每页 1:1 A4
+      const sliceHeightPx = canvas1.width * (A4_H / A4_W);
+      const needSlice = canvas1.height > sliceHeightPx;
+      if (!needSlice) {
+        const r1 = canvas1.width / canvas1.height;
+        const isA4Ratio = Math.abs(r1 - A4_W / A4_H) < 0.05;
+        if (isA4Ratio) {
+          pdf.addImage(canvas1.toDataURL('image/jpeg', 0.92), 'JPEG', 0, 0, A4_W, A4_H);
+        } else {
+          const fitToA4 = (canvas: HTMLCanvasElement) => {
+            const w = canvas.width;
+            const h = canvas.height;
+            const r = w / h;
+            if (A4_W / A4_H >= r) return { w: A4_H * r, h: A4_H };
+            return { w: A4_W, h: A4_W / r };
+          };
+          const size1 = fitToA4(canvas1);
+          const x1 = (A4_W - size1.w) / 2;
+          const y1 = (A4_H - size1.h) / 2;
+          pdf.addImage(canvas1.toDataURL('image/jpeg', 0.92), 'JPEG', x1, y1, size1.w, size1.h);
+        }
+      } else {
+        for (let y = 0; y < canvas1.height; y += sliceHeightPx) {
+          const sh = Math.min(sliceHeightPx, canvas1.height - y);
+          const temp = document.createElement('canvas');
+          temp.width = canvas1.width;
+          temp.height = sliceHeightPx;
+          const ctx = temp.getContext('2d');
+          if (ctx) {
+            ctx.fillStyle = '#ffffff';
+            ctx.fillRect(0, 0, temp.width, temp.height);
+            ctx.drawImage(canvas1, 0, y, canvas1.width, sh, 0, 0, canvas1.width, sh);
+          }
+          pdf.addImage(temp.toDataURL('image/jpeg', 0.92), 'JPEG', 0, 0, A4_W, A4_H);
+          if (y + sliceHeightPx < canvas1.height) pdf.addPage();
+        }
+      }
       const fitToA4 = (canvas: HTMLCanvasElement) => {
         const w = canvas.width;
         const h = canvas.height;
         const r = w / h;
-        if (A4_W / A4_H >= r) {
-          return { w: A4_H * r, h: A4_H };
-        }
+        if (A4_W / A4_H >= r) return { w: A4_H * r, h: A4_H };
         return { w: A4_W, h: A4_W / r };
       };
-      const size1 = fitToA4(canvas1);
-      pdf.addImage(canvas1.toDataURL('image/jpeg', 0.92), 'JPEG', 0, 0, size1.w, size1.h);
       const size2 = fitToA4(canvas2);
+      const x2 = (A4_W - size2.w) / 2;
+      const y2 = (A4_H - size2.h) / 2;
       pdf.addPage();
-      pdf.addImage(canvas2.toDataURL('image/jpeg', 0.92), 'JPEG', 0, 0, size2.w, size2.h);
+      pdf.addImage(canvas2.toDataURL('image/jpeg', 0.92), 'JPEG', x2, y2, size2.w, size2.h);
       const fileName = `作业归档报告_${jobDetail?.orderNo ?? jobDetail?.code ?? jobId ?? 'export'}.pdf`;
       pdf.save(fileName);
       toast.success('PDF 导出成功', { id: loadingId });
@@ -670,7 +889,9 @@ export default function WorkJobArchiveReport() {
 
   const conclusion = jobDetail ? statusToConclusion(jobDetail.status) : statusToConclusion('');
   const nodeList = jobDetail?.workflowWorkNodeDOList ?? [];
-  const sortedNodes = [...nodeList].sort((a, b) => (a.createTime ?? 0) - (b.createTime ?? 0));
+  // 流程执行摘要只渲染主线节点,并行/分支节点不展示(与 WorkJobDetail 主路径逻辑一致)
+  const mainPathNodes = getMainPathNodes(nodeList as WorkflowNodeWithParent[]);
+  const sortedNodes = mainPathNodes.length > 0 ? mainPathNodes : [...nodeList].sort((a, b) => (a.createTime ?? 0) - (b.createTime ?? 0));
   const currentNodeId = jobDetail?.currentNodeId ?? null;
 
   if (loading) {
@@ -727,6 +948,8 @@ export default function WorkJobArchiveReport() {
         </div>
       </div>
 
+      {/* 纸张容器:大屏下按视口缩放,1:1 还原 A4 观感、减小左右留白 */}
+      <div className="archive-pages-wrap">
       {/* ================= 第 1 页:基本信息与结果(与 test1 一致) ================= */}
       <div ref={page1Ref} className="page-sheet watermark-bg page-1" style={watermarkStyle}>
         <div className="top-bar" />
@@ -736,7 +959,7 @@ export default function WorkJobArchiveReport() {
             <p className="subtitle">Archiving Report for Complex Workflow</p>
           </div>
           <div className="doc-no-wrap">
-            <div className="doc-no-label">单据编号</div>
+            <div className="doc-no-label">作业单据编号</div>
             <div className="doc-no">{jobDetail?.orderNo ?? jobDetail?.code ?? '-'}</div>
           </div>
         </header>
@@ -763,35 +986,39 @@ export default function WorkJobArchiveReport() {
             <h3 className="section-title">流程执行摘要</h3>
           </div>
           <div className="summary-bar summary-steps">
-            <div className="summary-grid">
-              {(() => {
-                const maxSteps = 7;
-                const nodes = sortedNodes.slice(0, maxSteps);
-                const getState = (i: number) => (nodes[i] ? getNodeStepState(nodes[i].approvalStatus) : 'pending' as const);
-                const connDone = (i: number) => getState(i) !== 'pending' || getState(i + 1) !== 'pending';
-                const el: React.ReactNode[] = [];
-                for (let i = 0; i < maxSteps; i++) {
-                  const state = getState(i);
-                  const iconClass = state === 'done' ? 'summary-step-done' : state === 'active' ? 'summary-step-active summary-step-icon-active' : 'summary-step-pending';
-                  el.push(
-                    <div key={`icon-${i}`} className={`summary-step-icon ${iconClass}`}>
-                      {state === 'done' && <Check size={18} strokeWidth={2.5} />}
-                      {state === 'active' && <Clock size={18} strokeWidth={2.5} />}
-                      {state === 'pending' && <span className="summary-step-dot" />}
-                    </div>
-                  );
-                  if (i < maxSteps - 1) {
-                    el.push(<div key={`conn-${i}`} className={`summary-connector ${connDone(i) ? 'summary-connector-done' : 'summary-connector-pending'}`} />);
-                  }
+            {(() => {
+              const nodes = sortedNodes;
+              const stepCount = nodes.length;
+              if (stepCount === 0) return null;
+              const gridCols = Array.from({ length: stepCount * 2 - 1 }, (_, j) => (j % 2 === 0 ? 'minmax(36px, 1fr)' : '1fr')).join(' ');
+              const getState = (i: number) => (nodes[i] ? getNodeStepState(nodes[i].approvalStatus) : 'pending' as const);
+              const connDone = (i: number) => getState(i) !== 'pending' || getState(i + 1) !== 'pending';
+              const el: React.ReactNode[] = [];
+              for (let i = 0; i < stepCount; i++) {
+                const state = getState(i);
+                const iconClass = state === 'done' ? 'summary-step-done' : state === 'active' ? 'summary-step-active summary-step-icon-active' : 'summary-step-pending';
+                el.push(
+                  <div key={`icon-${i}`} className={`summary-step-icon ${iconClass}`}>
+                    {state === 'done' && <Check size={18} strokeWidth={2.5} />}
+                    {state === 'active' && <Clock size={18} strokeWidth={2.5} />}
+                    {state === 'pending' && <span className="summary-step-dot" />}
+                  </div>
+                );
+                if (i < stepCount - 1) {
+                  el.push(<div key={`conn-${i}`} className={`summary-connector ${connDone(i) ? 'summary-connector-done' : 'summary-connector-pending'}`} />);
                 }
-                for (let i = 0; i < maxSteps; i++) {
-                  const col = 1 + i * 2;
-                  const label = nodes[i]?.nodeName ?? '';
-                  el.push(<div key={`label-${i}`} className="summary-label-cell" style={{ gridColumn: col }}><span className="summary-step-label">{label || '\u00A0'}</span></div>);
-                }
-                return el;
-              })()}
-            </div>
+              }
+              for (let i = 0; i < stepCount; i++) {
+                const col = 1 + i * 2;
+                const label = nodes[i]?.nodeName ?? '';
+                el.push(<div key={`label-${i}`} className="summary-label-cell" style={{ gridColumn: col }}><span className="summary-step-label">{label || '\u00A0'}</span></div>);
+              }
+              return (
+                <div className="summary-grid" style={{ gridTemplateColumns: gridCols }}>
+                  {el}
+                </div>
+              );
+            })()}
           </div>
           <p className="summary-footnote">*流程分支详情请翻阅附录页完整拓扑图</p>
         </section>
@@ -834,8 +1061,15 @@ export default function WorkJobArchiveReport() {
                         <td>
                           <span className="cell-operator">
                             <Avatar className="archive-log-avatar">
-                              {(log as any).avatar && <AvatarImage src={(log as any).avatar} alt={operator} />}
-                              <AvatarFallback className={isProcessing ? 'avatar-active' : ''}>{initial}</AvatarFallback>
+                              {(log as any).avatar ? (
+                                <AvatarImage
+                                  src={(log as any).avatar}
+                                  alt={operator}
+                                />
+                              ) : null}
+                              <AvatarFallback className={isProcessing ? 'avatar-active' : ''}>
+                                {initial}
+                              </AvatarFallback>
                             </Avatar>
                             {operator}
                           </span>
@@ -906,6 +1140,7 @@ export default function WorkJobArchiveReport() {
 
         <div className="page-num">{t('common.pageOfTotal', { current: 2, total: 2 })}</div>
       </div>
+      </div>
     </div>
   );
 }

+ 21 - 19
src/components/lockCabinet/HardwareLockCabinetManagement.tsx

@@ -159,8 +159,8 @@ export default function HardwareLockCabinetManagement() {
         {/* 搜索栏 */}
         <div className="p-5 border-b border-gray-200">
           <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-            {/* 搜索条件 */}
-            <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
+            <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
               <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
                 <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.cabinetName')}:</label>
                 <Input
@@ -196,25 +196,27 @@ export default function HardwareLockCabinetManagement() {
                   )}
                 </Select>
               </div>
+
+              <Space className="flex-shrink-0" size="small">
+                <AntButton
+                  type="primary"
+                  icon={<SearchOutlined />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </AntButton>
+                
+                <AntButton
+                  icon={<ReloadOutlined />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </AntButton>
+              </Space>
             </div>
 
-            {/* 操作按钮组 */}
-            <Space className="flex-shrink-0">
-              <AntButton
-                type="primary"
-                icon={<SearchOutlined />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </AntButton>
-              
-              <AntButton
-                icon={<ReloadOutlined />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </AntButton>
-            </Space>
+            {/* 右侧可以继续扩展新增等按钮(当前机柜子菜单无新增按钮,因此保持为空) */}
+            <div />
           </div>
         </div>
 

+ 25 - 23
src/components/lockCabinet/SystemLockCabinetManagement.tsx

@@ -206,8 +206,8 @@ export default function SystemLockCabinetManagement() {
       {/* 搜索栏 */}
       <div className="bg-white rounded-xl border border-gray-200/50 shadow-sm p-4 lg:p-5">
         <div className="flex items-center justify-between gap-3 lg:gap-4 flex-wrap min-w-0">
-          {/* 搜索条件 */}
-          <div className="flex items-center gap-2 lg:gap-3 flex-wrap flex-1 min-w-0">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
+          <div className="flex items-center gap-2 lg:gap-3 flex-wrap min-w-0">
             <div className="flex items-center gap-2 lg:gap-3 flex-shrink-0">
               <label className="text-sm font-medium text-gray-700 whitespace-nowrap">{t('form.cabinetName')}:</label>
               <Input
@@ -243,29 +243,31 @@ export default function SystemLockCabinetManagement() {
                 )}
               </Select>
             </div>
-          </div>
 
-          {/* 操作按钮组 */}
-          <Space className="flex-shrink-0">
-            <PermissionWrapper permission="iscs:lock-cabinet:query">
-              <AntButton
-                type="primary"
-                icon={<SearchOutlined />}
-                onClick={handleQuery}
-              >
-                {t('common.search')}
-              </AntButton>
-            </PermissionWrapper>
-            
-            <PermissionWrapper permission="iscs:lock-cabinet:query">
-              <AntButton
-                icon={<ReloadOutlined />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </AntButton>
-            </PermissionWrapper>
+            <Space className="flex-shrink-0" size="small">
+              <PermissionWrapper permission="iscs:lock-cabinet:query">
+                <AntButton
+                  type="primary"
+                  icon={<SearchOutlined />}
+                  onClick={handleQuery}
+                >
+                  {t('common.search')}
+                </AntButton>
+              </PermissionWrapper>
+              
+              <PermissionWrapper permission="iscs:lock-cabinet:query">
+                <AntButton
+                  icon={<ReloadOutlined />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </AntButton>
+              </PermissionWrapper>
+            </Space>
+          </div>
 
+          {/* 新增等按钮组保持在最右侧 */}
+          <Space className="flex-shrink-0" size="small">
             <PermissionWrapper permission="iscs:lock-cabinet:create">
               <AntButton
                 type="primary"

+ 14 - 10
src/components/mailTemplate/MailTemplateManagement.tsx

@@ -209,6 +209,7 @@ export default function MailTemplateManagement(props: MailTemplateManagementProp
       {/* 搜索栏 */}
       <div className="bg-white rounded-2xl border border-gray-200/50 shadow-sm p-6">
         <div className="flex items-center justify-between gap-4 flex-wrap">
+          {/* 搜索条件 + 搜索、重置紧跟其后 */}
           <div className="flex items-center gap-4 flex-wrap">
             <div className="flex items-center gap-2">
               <label className="text-sm text-gray-700 whitespace-nowrap">{t('mailTemplate.name')}:</label>
@@ -232,18 +233,21 @@ export default function MailTemplateManagement(props: MailTemplateManagementProp
                 allowClear
               />
             </div>
+            <Space size="small">
+              <Button
+                type="primary"
+                icon={<Search className="w-4 h-4" />}
+                onClick={handleQuery}
+              >
+                {t('common.search')}
+              </Button>
+              <Button icon={<RefreshCw className="w-4 h-4" />} onClick={resetQuery}>
+                {t('common.reset')}
+              </Button>
+            </Space>
           </div>
+          {/* 右侧按钮:新增、返回等保持不变 */}
           <div className="flex items-center gap-2 flex-shrink-0">
-            <Button
-              type="primary"
-              icon={<Search className="w-4 h-4" />}
-              onClick={handleQuery}
-            >
-              {t('common.search')}
-            </Button>
-            <Button icon={<RefreshCw className="w-4 h-4" />} onClick={resetQuery}>
-              {t('common.reset')}
-            </Button>
             <Button
               type="primary"
               icon={<Plus className="w-4 h-4" />}

+ 18 - 14
src/components/notification/EmailNotifyManagement.tsx

@@ -227,6 +227,7 @@ export default function EmailNotifyManagement() {
         {/* 查询与操作栏 */}
         <div className="p-4 lg:p-5 border-b border-gray-200/50">
           <div className="flex flex-wrap items-center justify-between gap-3">
+            {/* 搜索条件 + 搜索、重置紧跟其后 */}
             <div className="flex flex-wrap items-center gap-3">
               {/* 提醒事项 */}
               <div className="flex items-center gap-2">
@@ -253,23 +254,26 @@ export default function EmailNotifyManagement() {
                   style={{ width: 200 }}
                 />
               </div> */}
+              <Space size="small">
+                <Button
+                  type="primary"
+                  icon={<Search className="w-4 h-4" />}
+                  onClick={handleQuery}
+                  disabled={loading}
+                >
+                  {t('common.search')}
+                </Button>
+                <Button
+                  icon={<RefreshCw className="w-4 h-4" />}
+                  onClick={resetQuery}
+                >
+                  {t('common.reset')}
+                </Button>
+              </Space>
             </div>
 
+            {/* 其他操作按钮保持在右侧(新增、设置模板) */}
             <Space size="small">
-              <Button
-                type="primary"
-                icon={<Search className="w-4 h-4" />}
-                onClick={handleQuery}
-                disabled={loading}
-              >
-                {t('common.search')}
-              </Button>
-              <Button
-                icon={<RefreshCw className="w-4 h-4" />}
-                onClick={resetQuery}
-              >
-                {t('common.reset')}
-              </Button>
               <Button
                 type="primary"
                 icon={<Plus className="w-4 h-4" />}

+ 2 - 0
src/components/user/UserForm.tsx

@@ -178,6 +178,8 @@ const UserForm = forwardRef<UserFormRef, UserFormProps>(({ onSuccess }, ref) =>
       form.setFieldsValue({
         status: 0,
         workstationIds: [],
+        // 默认过期时间:2099-12-31 00:00:00
+        expireTime: dayjs(LONG_TERM_EXPIRE_TIME),
       });
     }
   };