Explorar o código

feat(TableCard): 添加TableCard

xieyonghong %!s(int64=3) %!d(string=hai) anos
pai
achega
d01647cd01

+ 1 - 1
src/app.tsx

@@ -238,7 +238,7 @@ export function patchRoutes(routes: any) {
 export function render(oldRender: any) {
   if (history.location.pathname !== loginPath) {
     MenuService.queryOwnThree({ paging: false }).then((res) => {
-      if (res.status === 200) {
+      if (res && res.status === 200) {
         extraRoutes = handleRoutes(res.result);
         saveMenusCache(extraRoutes);
       }

+ 3 - 1
src/components/BadgeStatus/index.tsx

@@ -14,6 +14,8 @@ export enum StatusColorEnum {
 
 export type StatusColorType = keyof typeof StatusColorEnum;
 
+export type StatusType = string | number;
+
 export interface BadgeStatusProps {
   text: string | React.ReactNode;
   status: string | number;
@@ -32,7 +34,7 @@ export default (props: BadgeStatusProps) => {
     if ('statusNames' in props) {
       return props.statusNames![props.status];
     }
-    return StatusColorEnum['default'];
+    return StatusColorEnum.default;
   };
 
   return <Badge status={handleStatusColor()} text={props.text} />;

+ 46 - 16
src/components/ProTableCard/CardItems/device.tsx

@@ -1,45 +1,75 @@
-import { Avatar, Card } from 'antd';
 import React from 'react';
 import type { DeviceInstance } from '@/pages/device/Instance/typings';
-import { BadgeStatus } from '@/components';
 import { StatusColorEnum } from '@/components/BadgeStatus';
 import '../index.less';
+import { TableCard } from '@/components';
 
 export interface DeviceCardProps extends DeviceInstance {
   actions?: React.ReactNode[];
   avatarSize?: number;
 }
 
+const defaultImage = require('/public/images/device-type-3-big.png');
+
 export default (props: DeviceCardProps) => {
   return (
-    <Card style={{ width: '100%' }} cover={null} actions={props.actions}>
+    <TableCard
+      actions={props.actions}
+      status={props.state.value}
+      statusText={props.state.text}
+      statusNames={{
+        online: StatusColorEnum.success,
+        offline: StatusColorEnum.error,
+        notActive: StatusColorEnum.processing,
+      }}
+    >
       <div className={'pro-table-card-item'}>
         <div className={'card-item-avatar'}>
-          <Avatar size={props.avatarSize || 64} src={props.photoUrl} />
+          <img width={88} height={88} src={props.photoUrl || defaultImage} alt={''} />
         </div>
         <div className={'card-item-body'}>
           <div className={'card-item-header'}>
             <span className={'card-item-header-name ellipsis'}>{props.name}</span>
-            <BadgeStatus
-              status={props.state.value}
-              text={props.state.text}
-              statusNames={{
-                online: StatusColorEnum.success,
-                offline: StatusColorEnum.error,
-                notActive: StatusColorEnum.processing,
-              }}
-            />
           </div>
           <div className={'card-item-content'}>
-            <label>设备类型:</label>
+            <label>设备类型:</label> <br />
             <span className={'ellipsis'}>{props.deviceType ? props.deviceType.text : '--'}</span>
           </div>
           <div className={'card-item-content'}>
-            <label>产品名称:</label>
+            <label>产品名称:</label> <br />
             <span className={'ellipsis'}>{props.productName || '--'}</span>
           </div>
         </div>
       </div>
-    </Card>
+    </TableCard>
+    // <Card style={{ width: '100%' }} cover={null} actions={props.actions}>
+    //   <div className={'pro-table-card-item'}>
+    //     <div className={'card-item-avatar'}>
+    //       <Avatar size={props.avatarSize || 64} src={props.photoUrl} />
+    //     </div>
+    //     <div className={'card-item-body'}>
+    //       <div className={'card-item-header'}>
+    //         <span className={'card-item-header-name ellipsis'}>{props.name}</span>
+    //         <BadgeStatus
+    //           status={props.state.value}
+    //           text={props.state.text}
+    //           statusNames={{
+    //             online: StatusColorEnum.success,
+    //             offline: StatusColorEnum.error,
+    //             notActive: StatusColorEnum.processing,
+    //           }}
+    //         />
+    //       </div>
+    //       <div className={'card-item-content'}>
+    //         <label>设备类型:</label>
+    //         <span className={'ellipsis'}>{props.deviceType ? props.deviceType.text : '--'}</span>
+    //       </div>
+    //       <div className={'card-item-content'}>
+    //         <label>产品名称:</label>
+    //         <span className={'ellipsis'}>{props.productName || '--'}</span>
+    //       </div>
+    //     </div>
+    //   </div>
+    // </Card>
   );
 };

+ 48 - 22
src/components/ProTableCard/TableCard.tsx

@@ -1,39 +1,67 @@
 import React, { useState } from 'react';
 import classNames from 'classnames';
-
-/**
- * 状态色
- */
-export enum StatusColorEnum {
-  'success' = 'success',
-  'error' = 'error',
-  'processing' = 'processing',
-  'warning' = 'warning',
-  'default' = 'default',
-}
-
-export type StatusColorType = keyof typeof StatusColorEnum;
+import { BadgeStatus } from '@/components';
+import { StatusColorEnum, StatusColorType } from '@/components/BadgeStatus';
+import './index.less';
 
 export interface TableCardProps {
+  className?: string;
+  showStatus?: boolean;
+  showTool?: boolean;
+  showMask?: boolean;
+  detail?: React.ReactNode;
   status?: string | number;
+  statusText?: React.ReactNode;
   statusNames?: Record<string | number, StatusColorType>;
   children?: React.ReactNode;
+  actions?: React.ReactNode[];
+}
+
+function getAction(actions: React.ReactNode[]) {
+  return actions.map((item: any) => {
+    return (
+      <div className={classNames('card-button', { delete: item.key === 'delete' })}>{item}</div>
+    );
+  });
 }
 
 export default (props: TableCardProps) => {
   const [maskShow, setMaskShow] = useState(false);
 
-  const maskClassName = classNames('card-mask', { show: maskShow });
-
   const handleStatusColor = (): StatusColorType | undefined => {
     if ('statusNames' in props && props.status) {
       return props.statusNames![props.status];
     }
-    return StatusColorEnum['default'];
+    return StatusColorEnum.default;
   };
 
+  const statusNode =
+    props.showStatus === false ? null : (
+      <div className={classNames('card-state', handleStatusColor())}>
+        <div className={'card-state-content'}>
+          <BadgeStatus
+            status={props.status !== undefined ? props.status : ''}
+            text={props.statusText}
+            statusNames={props.statusNames}
+          />
+        </div>
+      </div>
+    );
+
+  const maskClassName = classNames('card-mask', { show: maskShow });
+
+  const maskNode =
+    props.showMask === false ? null : <div className={maskClassName}>{props.detail}</div>;
+
+  const toolNode =
+    props.showTool === false ? null : (
+      <div className={'card-tools'}>
+        {props.actions && props.actions.length ? getAction(props.actions) : null}
+      </div>
+    );
+
   return (
-    <div className={'iot-card'}>
+    <div className={classNames('iot-card', { hover: maskShow }, props.className)}>
       <div className={'card-warp'}>
         <div
           className={'card-content'}
@@ -45,13 +73,11 @@ export default (props: TableCardProps) => {
           }}
         >
           {props.children}
-          <div className={classNames('card-state', handleStatusColor())}>
-            <div className={'card-state-content'}></div>
-          </div>
+          {statusNode}
+          {maskNode}
         </div>
-        <div className={maskClassName}></div>
       </div>
-      <div className={'card-tools'}></div>
+      {toolNode}
     </div>
   );
 };

+ 94 - 10
src/components/ProTableCard/index.less

@@ -1,4 +1,8 @@
-@import '../../../node_modules/antd/lib/style/themes/variable';
+@import '~antd/es/style/themes/default.less';
+
+@border-color: #e6e6e6;
+@card-content-padding-top: 30px;
+@card-content-padding-left: 30px;
 
 .pro-table-card {
   position: relative;
@@ -72,10 +76,13 @@
   }
 }
 
-@border-color: #e6e6e6;
-
 .iot-card {
   width: 100%;
+  background-color: #fff;
+
+  &.hover {
+    box-shadow: 0 0 24px rgba(#000, 0.1);
+  }
 
   .card-warp {
     position: relative;
@@ -83,17 +90,29 @@
 
     .card-content {
       position: relative;
-      padding: 30px 12px 12px 30px;
+      padding: @card-content-padding-top 12px 16px @card-content-padding-left;
       overflow: hidden;
 
-      .card-state {
+      &::before {
         position: absolute;
         top: 0;
-        right: 0;
+        left: @card-content-padding-left + 10px;
+        display: block;
+        width: 120px;
+        height: 4px;
+        background-image: url('/images/rectangle.png');
+        background-repeat: no-repeat;
+        content: ' ';
+      }
+
+      .card-state {
+        position: absolute;
+        top: @card-content-padding-top;
+        right: -12px;
         display: flex;
         justify-content: center;
         width: 100px;
-        padding: 8px 0;
+        padding: 2px 0;
         background-color: @info-color-deprecated-bg;
         transform: skewX(45deg);
 
@@ -119,17 +138,82 @@
       position: absolute;
       top: 0;
       left: 0;
-      display: none;
+      z-index: 2;
+      display: flex;
       align-items: center;
       justify-content: center;
       width: 100%;
       height: 100%;
       color: #fff;
-      background-color: rgba(#000, 0.5);
+      background-color: rgba(#000, 0);
       cursor: pointer;
+      transition: all 0.3s;
 
       &.show {
-        display: flex;
+        background-color: rgba(#000, 0.5);
+      }
+    }
+  }
+
+  .card-tools {
+    display: flex;
+    margin-top: 8px;
+
+    .card-button {
+      display: flex;
+      flex-grow: 1;
+      justify-content: center;
+      background: #f6f6f6;
+      border: 1px solid #e6e6e6;
+      cursor: pointer;
+
+      button {
+        width: 100%;
+      }
+
+      &:not(:last-child) {
+        margin-right: 8px;
+      }
+
+      &:hover {
+        background-color: @primary-color-hover;
+        border-color: @primary-color-hover;
+        span {
+          color: #fff !important;
+        }
+      }
+
+      &:active {
+        background-color: @primary-color-active;
+        border-color: @primary-color-active;
+        span {
+          color: #fff !important;
+        }
+      }
+
+      &.delete {
+        flex-basis: 60px;
+        flex-grow: 0;
+        background: @error-color-deprecated-bg;
+        border: 1px solid @error-color-outline;
+
+        span {
+          color: @error-color !important;
+        }
+
+        &:hover {
+          background-color: @error-color-hover;
+          span {
+            color: #fff !important;
+          }
+        }
+
+        &:active {
+          background-color: @error-color-active;
+          span {
+            color: #fff !important;
+          }
+        }
       }
     }
   }

+ 1 - 0
src/components/index.ts

@@ -1,6 +1,7 @@
 export { default as RadioCard } from './RadioCard';
 export { default as UploadImage } from './Upload/Image';
 export { default as ProTableCard } from './ProTableCard';
+export { default as TableCard } from './ProTableCard/TableCard';
 export { default as BadgeStatus } from './BadgeStatus';
 export { default as Player } from './Player';
 export { default as ScreenPlayer } from './Player/ScreenPlayer';