Просмотр исходного кода

fix(产品): 修复产品保存提示缺陷;修复产品详情展示错误;

xieyonghong 3 лет назад
Родитель
Сommit
c69e8e6eea

+ 52 - 0
src/components/ProTableCard/CardItems/product.tsx

@@ -0,0 +1,52 @@
+import { Avatar, Card } from 'antd';
+import React from 'react';
+import type { ProductItem } from '@/pages/device/Product/typings';
+import { BadgeStatus } from '@/components';
+import { StatusColorEnum } from '@/components/BadgeStatus';
+import '@/style/common.less';
+import { useIntl } from '@@/plugin-locale/localeExports';
+
+export interface ProductCardProps extends ProductItem {
+  actions?: React.ReactNode[];
+  avatarSize?: number;
+}
+
+export default (props: ProductCardProps) => {
+  const intl = useIntl();
+
+  return (
+    <Card style={{ width: 340 }} 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}
+              text={intl.formatMessage({
+                id: `pages.system.tenant.assetInformation.${
+                  props.state ? 'published' : 'unpublished'
+                }`,
+                defaultMessage: '已发布',
+              })}
+              statusNames={{
+                0: StatusColorEnum.error,
+                1: 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.transportProtocol || '--'}</span>
+          </div>
+        </div>
+      </div>
+    </Card>
+  );
+};

+ 3 - 2
src/locales/zh-CN/pages.ts

@@ -190,9 +190,9 @@ export default {
   'pages.device.productDetail.id': '产品ID',
   'pages.device.productDetail.classifiedName': '所属品类',
   'pages.device.productDetail.protocolName': '消息协议',
-  'pages.device.productDetail.transportProtocol': '链接协议',
+  'pages.device.productDetail.transportProtocol': '接入方式',
   'pages.device.productDetail.createTime': '创建时间',
-  'pages.device.productDetail.updateTime': '创建时间',
+  'pages.device.productDetail.updateTime': '更新时间',
   'pages.device.productDetail.base': '配置信息',
   'pages.device.productDetail.base.save': '保存',
   'pages.device.productDetail.metadata': '物模型',
@@ -231,6 +231,7 @@ export default {
   'pages.device.productDetail.setting': '应用配置',
   'pages.device.productDetail.disable': '停用',
   'pages.device.productDetail.enabled': '启用',
+  'pages.device.productDetail.deleteTip': '已发布的产品不能进行删除操作',
 
   // 设备管理-设备分类
   'pages.device.type.device': '直连设备',

+ 11 - 10
src/pages/device/Product/Detail/BaseInfo/index.tsx

@@ -162,35 +162,36 @@ const BaseInfo = () => {
         </Descriptions.Item>
         <Descriptions.Item
           label={intl.formatMessage({
-            id: 'pages.device.productDetail.protocolName',
-            defaultMessage: '消息协议',
+            id: 'pages.device.instanceDetail.deviceType',
+            defaultMessage: '设备类型',
           })}
         >
-          {productModel.current?.protocolName}
+          {productModel.current?.deviceType ? productModel.current?.deviceType.text : '-'}
         </Descriptions.Item>
         <Descriptions.Item
           label={intl.formatMessage({
             id: 'pages.device.productDetail.transportProtocol',
-            defaultMessage: '链接协议',
+            defaultMessage: '接入方式',
           })}
         >
           {productModel.current?.transportProtocol}
         </Descriptions.Item>
+
         <Descriptions.Item
           label={intl.formatMessage({
-            id: 'pages.device.productDetail.updateTime',
-            defaultMessage: '更新时间',
+            id: 'pages.device.productDetail.createTime',
+            defaultMessage: '创建时间',
           })}
         >
-          {getDateFormat(productModel.current?.updateTime)}
+          {getDateFormat(productModel.current?.createTime)}
         </Descriptions.Item>
         <Descriptions.Item
           label={intl.formatMessage({
-            id: 'pages.device.productDetail.createTime',
-            defaultMessage: '创建时间',
+            id: 'pages.device.productDetail.updateTime',
+            defaultMessage: '更新时间',
           })}
         >
-          {getDateFormat(productModel.current?.createTime)}
+          {getDateFormat(productModel.current?.updateTime)}
         </Descriptions.Item>
         <Descriptions.Item
           span={3}

+ 12 - 22
src/pages/device/Product/Detail/index.tsx

@@ -1,17 +1,6 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { history, Link, useParams } from 'umi';
-import {
-  Badge,
-  Button,
-  Card,
-  Descriptions,
-  message,
-  Space,
-  Spin,
-  Switch,
-  Tabs,
-  Tooltip,
-} from 'antd';
+import { Badge, Card, Descriptions, message, Space, Spin, Switch, Tabs, Tooltip } from 'antd';
 import BaseInfo from '@/pages/device/Product/Detail/BaseInfo';
 import { observer } from '@formily/react';
 import { productModel, service } from '@/pages/device/Product';
@@ -151,21 +140,22 @@ const ProductDetail = observer(() => {
       subTitle={
         <Switch
           key={2}
-          checkedChildren="启用"
-          unCheckedChildren="停用"
+          checked={productModel.current?.state === 1}
+          checkedChildren="已发布"
+          unCheckedChildren="未发布"
           onChange={() => {
             changeDeploy(statusMap[productModel.current?.state || 0].action);
           }}
         />
       }
-      extra={[
-        <Button key="1" type="primary" onClick={() => changeDeploy('deploy')}>
-          {intl.formatMessage({
-            id: 'pages.device.productDetail.setting',
-            defaultMessage: '应用配置',
-          })}
-        </Button>,
-      ]}
+      // extra={[
+      //   <Button key="1" type="primary" onClick={() => changeDeploy('deploy')}>
+      //     {intl.formatMessage({
+      //       id: 'pages.device.productDetail.setting',
+      //       defaultMessage: '应用配置',
+      //     })}
+      //   </Button>,
+      // ]}
     >
       <Card>
         <Tabs defaultActiveKey="base">

+ 113 - 101
src/pages/device/Product/index.tsx

@@ -16,12 +16,13 @@ import { model } from '@formily/reactive';
 import { Link, useHistory } from 'umi';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
-import ProTable from '@jetlinks/pro-table';
 import { useEffect, useRef, useState } from 'react';
 import encodeQuery from '@/utils/encodeQuery';
 import Save from '@/pages/device/Product/Save';
 import SearchComponent from '@/components/SearchComponent';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import { ProTableCard } from '@/components';
+import ProductCard from '@/components/ProTableCard/CardItems/product';
 
 export const service = new Service('device-product');
 export const statusMap = {
@@ -105,6 +106,111 @@ const Product = observer(() => {
     });
   };
 
+  const tools = (record: ProductItem) => [
+    <Tooltip
+      title={intl.formatMessage({
+        id: 'pages.data.option.detail',
+        defaultMessage: '查看',
+      })}
+      key={'detail'}
+    >
+      <Link
+        onClick={() => {
+          productModel.current = record;
+        }}
+        to={`${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], record.id)}`}
+        key="link"
+      >
+        <EyeOutlined />
+      </Link>
+    </Tooltip>,
+    <Tooltip
+      title={intl.formatMessage({
+        id: 'pages.data.option.edit',
+        defaultMessage: '编辑',
+      })}
+      key={'edit'}
+    >
+      <a
+        key="warning"
+        onClick={() => {
+          setCurrent(record);
+          setVisible(true);
+        }}
+      >
+        <EditOutlined />
+      </a>
+    </Tooltip>,
+    <Tooltip
+      title={intl.formatMessage({
+        id: 'pages.data.option.download',
+        defaultMessage: '下载',
+      })}
+      key={'download'}
+    >
+      <a key="download">
+        <DownloadOutlined
+          onClick={async () => {
+            await message.success(
+              `${intl.formatMessage({
+                id: 'pages.data.option.download',
+                defaultMessage: '下载',
+              })}`,
+            );
+          }}
+        />
+      </a>
+    </Tooltip>,
+    <Popconfirm
+      key={'state'}
+      title={intl.formatMessage({
+        id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
+        defaultMessage: '是否删除?',
+      })}
+      onConfirm={() => {
+        changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
+      }}
+    >
+      <Tooltip
+        title={intl.formatMessage({
+          id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}`,
+          defaultMessage: record.state ? '禁用' : '启用',
+        })}
+      >
+        <a key="state">{record.state ? <StopOutlined /> : <PlayCircleOutlined />}</a>
+      </Tooltip>
+    </Popconfirm>,
+    <Popconfirm
+      key="unBindUser"
+      title={intl.formatMessage({
+        id:
+          record.state === 1
+            ? 'pages.device.productDetail.deleteTip'
+            : 'page.system.menu.table.delete',
+        defaultMessage: '是否删除?',
+      })}
+      onConfirm={async () => {
+        if (record.state === 0) {
+          await deleteItem(record.id);
+        } else {
+          message.error('已发布的产品不能进行删除操作');
+        }
+      }}
+    >
+      <Tooltip
+        title={intl.formatMessage({
+          id: 'pages.data.option.remove.tips',
+          defaultMessage: '删除',
+        })}
+        key={'remove'}
+      >
+        <a key="delete">
+          <DeleteOutlined />
+        </a>
+      </Tooltip>
+    </Popconfirm>,
+  ];
+
   const columns: ProColumns<ProductItem>[] = [
     {
       title: 'ID',
@@ -116,7 +222,8 @@ const Product = observer(() => {
     },
     {
       title: '设备类型',
-      dataIndex: 'classifiedName',
+      dataIndex: 'deviceType',
+      render: (_, row) => <>{row.deviceType ? row.deviceType.text : undefined}</>,
     },
     {
       title: '状态',
@@ -130,110 +237,14 @@ const Product = observer(() => {
       valueType: 'option',
       align: 'center',
       width: 200,
-      render: (_, record) => [
-        <Tooltip
-          title={intl.formatMessage({
-            id: 'pages.data.option.detail',
-            defaultMessage: '查看',
-          })}
-          key={'detail'}
-        >
-          <Link
-            onClick={() => {
-              productModel.current = record;
-            }}
-            to={`${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], record.id)}`}
-            key="link"
-          >
-            <EyeOutlined />
-          </Link>
-        </Tooltip>,
-        <Tooltip
-          title={intl.formatMessage({
-            id: 'pages.data.option.edit',
-            defaultMessage: '编辑',
-          })}
-          key={'edit'}
-        >
-          <a
-            key="warning"
-            onClick={() => {
-              setCurrent(record);
-              setVisible(true);
-            }}
-          >
-            <EditOutlined />
-          </a>
-        </Tooltip>,
-        <Tooltip
-          title={intl.formatMessage({
-            id: 'pages.data.option.download',
-            defaultMessage: '下载',
-          })}
-          key={'download'}
-        >
-          <a key="download">
-            <DownloadOutlined
-              onClick={async () => {
-                await message.success(
-                  `${intl.formatMessage({
-                    id: 'pages.data.option.download',
-                    defaultMessage: '下载',
-                  })}`,
-                );
-              }}
-            />
-          </a>
-        </Tooltip>,
-        <Popconfirm
-          key={'state'}
-          title={intl.formatMessage({
-            id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}.tips`,
-            defaultMessage: '是否删除?',
-          })}
-          onConfirm={() => {
-            changeDeploy(record.id, record.state ? 'undeploy' : 'deploy');
-          }}
-        >
-          <Tooltip
-            title={intl.formatMessage({
-              id: `pages.data.option.${record.state ? 'disabled' : 'enabled'}`,
-              defaultMessage: record.state ? '禁用' : '启用',
-            })}
-          >
-            <a key="state">{record.state ? <StopOutlined /> : <PlayCircleOutlined />}</a>
-          </Tooltip>
-        </Popconfirm>,
-        <Popconfirm
-          key="unBindUser"
-          title={intl.formatMessage({
-            id: 'page.system.menu.table.delete',
-            defaultMessage: '是否删除?',
-          })}
-          onConfirm={async () => {
-            await deleteItem(record.id);
-          }}
-        >
-          <Tooltip
-            title={intl.formatMessage({
-              id: 'pages.data.option.remove.tips',
-              defaultMessage: '删除',
-            })}
-            key={'remove'}
-          >
-            <a key="delete">
-              <DeleteOutlined />
-            </a>
-          </Tooltip>
-        </Popconfirm>,
-      ],
+      render: (_, record) => tools(record),
     },
   ];
 
   return (
     <PageContainer>
       <SearchComponent field={columns} onSearch={searchFn} />
-      <ProTable<ProductItem>
+      <ProTableCard<ProductItem>
         columns={columns}
         actionRef={actionRef}
         options={{ fullScreen: true }}
@@ -249,7 +260,7 @@ const Product = observer(() => {
         rowKey="id"
         search={false}
         pagination={{ pageSize: 10 }}
-        toolBarRender={() => [
+        headerTitle={[
           <Button
             onClick={() => {
               setCurrent(undefined);
@@ -265,6 +276,7 @@ const Product = observer(() => {
             })}
           </Button>,
         ]}
+        cardRender={(record) => <ProductCard {...record} actions={tools(record)} />}
       />
       <Save
         model={!current ? 'add' : 'edit'}

+ 2 - 1
src/pages/device/Product/typings.d.ts

@@ -14,7 +14,7 @@ export type ProductItem = {
   createTime: number;
   updateTime: number;
   creatorId: string;
-  deviceType: string | DeviceType;
+  deviceType: DeviceType;
   count?: number;
   messageProtocol: string;
   metadata: string;
@@ -23,6 +23,7 @@ export type ProductItem = {
   state: number;
   transportProtocol: string;
   describe?: string;
+  photoUrl?: string;
 };
 
 export type ConfigProperty = {

+ 15 - 4
src/pages/system/Menu/Detail/edit.tsx

@@ -102,7 +102,12 @@ export default (props: EditProps) => {
           <Title title={'基本信息'} />
           <Row>
             <Col span={3}>
-              <Form.Item name={'icon'} label={'菜单图标'} required={true}>
+              <Form.Item
+                name={'icon'}
+                label={'菜单图标'}
+                required={true}
+                rules={[{ required: true, message: '请上传图标' }]}
+              >
                 <UploadImage disabled={disabled} style={{ width: 140, height: 130 }} />
               </Form.Item>
             </Col>
@@ -116,7 +121,7 @@ export default (props: EditProps) => {
                       defaultMessage: '名称',
                     })}
                     required={true}
-                    rules={[{ required: true, message: '该字段是必填字段' }]}
+                    rules={[{ required: true, message: '请输入名称' }]}
                   >
                     <Input disabled={disabled} />
                   </Form.Item>
@@ -129,7 +134,7 @@ export default (props: EditProps) => {
                       defaultMessage: '编码',
                     })}
                     required={true}
-                    rules={[{ required: true, message: '该字段是必填字段' }]}
+                    rules={[{ required: true, message: '请输入编码' }]}
                   >
                     <Input disabled={disabled} />
                   </Form.Item>
@@ -145,7 +150,7 @@ export default (props: EditProps) => {
                     })}
                     required={true}
                     rules={[
-                      { required: true, message: '该字段是必填字段' },
+                      { required: true, message: '请输入页面地址' },
                       { max: 120, message: '最多可输入120字符' },
                     ]}
                   >
@@ -252,6 +257,12 @@ export default (props: EditProps) => {
                   />
                   {/*</Form.Item>*/}
                 </Form.Item>
+                <Form.Item hidden name={'id'}>
+                  <Input />
+                </Form.Item>
+                <Form.Item hidden name={'parentId'}>
+                  <Input />
+                </Form.Item>
               </Col>
             </Row>
           )}