Kaynağa Gözat

Merge xyh

Next xyh
XieYongHong 3 yıl önce
ebeveyn
işleme
01634006d7

+ 5 - 25
src/components/ProTableCard/CardItems/device.tsx

@@ -4,9 +4,8 @@ import { StatusColorEnum } from '@/components/BadgeStatus';
 import { TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
-import { CheckOutlined, DisconnectOutlined } from '@ant-design/icons';
-import { Popconfirm, Tooltip } from 'antd';
-import { useIntl } from '@@/plugin-locale/localeExports';
+import { CheckOutlined } from '@ant-design/icons';
+import { Tooltip } from 'antd';
 
 export interface DeviceCardProps extends Partial<DeviceInstance> {
   detail?: React.ReactNode;
@@ -19,6 +18,7 @@ export interface DeviceCardProps extends Partial<DeviceInstance> {
   onUnBind?: (e: any) => void;
   showBindBtn?: boolean;
   cardType?: 'bind' | 'unbind';
+  showTool?: boolean;
 }
 
 const defaultImage = require('/public/images/device-type-3-big.png');
@@ -36,14 +36,14 @@ export const handlePermissionsMap = (permissions?: string[]) => {
 };
 
 export const ExtraDeviceCard = (props: DeviceCardProps) => {
-  const intl = useIntl();
   const [imgUrl, setImgUrl] = useState<string>(props.photoUrl || defaultImage);
 
   return (
     <TableCard
-      showTool={false}
+      showTool={props.showTool}
       showMask={false}
       status={props.state?.value}
+      actions={props.actions}
       statusText={props.state?.text}
       statusNames={{
         online: StatusColorEnum.processing,
@@ -97,26 +97,6 @@ export const ExtraDeviceCard = (props: DeviceCardProps) => {
                 </div>
               </div>
             )}
-
-            {props.showBindBtn !== false && (
-              <Popconfirm
-                title={intl.formatMessage({
-                  id: 'pages.system.role.option.unBindUser',
-                  defaultMessage: '是否解除绑定',
-                })}
-                key="unBind"
-                onConfirm={(e) => {
-                  e?.stopPropagation();
-                  if (props.onUnBind) {
-                    props.onUnBind(e);
-                  }
-                }}
-              >
-                <div className={'flex-button'}>
-                  <DisconnectOutlined />
-                </div>
-              </Popconfirm>
-            )}
           </div>
         </div>
       </div>

+ 5 - 22
src/components/ProTableCard/CardItems/product.tsx

@@ -5,8 +5,8 @@ import { useIntl } from 'umi';
 import { TableCard } from '@/components';
 import '@/style/common.less';
 import '../index.less';
-import { Popconfirm, Tooltip } from 'antd';
-import { CheckOutlined, DisconnectOutlined } from '@ant-design/icons';
+import { Tooltip } from 'antd';
+import { CheckOutlined } from '@ant-design/icons';
 
 export interface ProductCardProps extends Partial<ProductItem> {
   detail?: React.ReactNode;
@@ -19,6 +19,7 @@ export interface ProductCardProps extends Partial<ProductItem> {
   onUnBind?: (e: any) => void;
   showBindBtn?: boolean;
   cardType?: 'bind' | 'unbind';
+  showTool?: boolean;
 }
 
 const defaultImage = require('/public/images/device-product.png');
@@ -41,9 +42,10 @@ export const ExtraProductCard = (props: ProductCardProps) => {
 
   return (
     <TableCard
-      showTool={false}
       showMask={false}
       status={props.state}
+      showTool={props.showTool}
+      actions={props.actions}
       statusText={intl.formatMessage({
         id: `pages.system.tenant.assetInformation.${props.state ? 'published' : 'unpublished'}`,
         defaultMessage: '已发布',
@@ -102,25 +104,6 @@ export const ExtraProductCard = (props: ProductCardProps) => {
                 </div>
               </div>
             )}
-            {props.showBindBtn !== false && (
-              <Popconfirm
-                title={intl.formatMessage({
-                  id: 'pages.system.role.option.unBindUser',
-                  defaultMessage: '是否解除绑定',
-                })}
-                key="unBind"
-                onConfirm={(e) => {
-                  e?.stopPropagation();
-                  if (props.onUnBind) {
-                    props.onUnBind(e);
-                  }
-                }}
-              >
-                <div className={'flex-button'}>
-                  <DisconnectOutlined />
-                </div>
-              </Popconfirm>
-            )}
           </div>
         </div>
       </div>

+ 3 - 3
src/components/ProTableCard/index.less

@@ -145,8 +145,8 @@
 
   &.item-active {
     position: relative;
-    color: @primary-color-active;
-    border: 1px solid @primary-color-active;
+    color: #2f54eb;
+    border: 1px solid #2f54eb;
 
     .checked-icon {
       display: block;
@@ -163,7 +163,7 @@
     height: 44px;
     color: #fff;
     background-color: red;
-    background-color: @primary-color-active;
+    background-color: #2f54eb;
     transform: rotate(-45deg);
 
     > div {

+ 7 - 2
src/components/ProTableCard/index.tsx

@@ -22,6 +22,7 @@ type ModelType = keyof typeof ModelEnum;
 interface ProTableCardProps<T> {
   cardRender?: (data: T) => JSX.Element | React.ReactNode;
   gridColumn?: number;
+  height?: 'none';
 }
 
 const ProTableCard = <
@@ -108,7 +109,7 @@ const ProTableCard = <
                 display: 'flex',
                 justifyContent: 'center',
                 alignItems: 'center',
-                minHeight: minHeight - 150,
+                minHeight: props.height === 'none' ? 'auto' : minHeight - 150,
               }}
             >
               <Empty />
@@ -146,7 +147,11 @@ const ProTableCard = <
   }, [props.params]);
 
   return (
-    <div className={'pro-table-card'} style={{ minHeight: minHeight }} ref={domRef}>
+    <div
+      className={'pro-table-card'}
+      style={{ minHeight: props.height === 'none' ? 'auto' : minHeight }}
+      ref={domRef}
+    >
       <ProTable<T, U, ValueType>
         {...extraProps}
         params={

+ 1 - 1
src/components/SearchComponent/index.tsx

@@ -559,7 +559,7 @@ const SearchComponent = <T extends Record<string, any>>(props: Props<T>) => {
 
   useEffect(() => {
     // 防止页面下多个TabsTabPane中的查询组件共享路由中的参数
-    if (url.q) {
+    if (url.q && props.model !== 'simple') {
       if (url.target) {
         if (props.target && url.target === props.target) {
           form.setValues(JSON.parse(url.q));

+ 27 - 22
src/pages/device/Instance/Detail/index.tsx

@@ -24,7 +24,7 @@ import SystemConst from '@/utils/const';
 import { getMenuPathByCode, getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 import { PermissionButton } from '@/components';
-import { ExclamationCircleOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons';
+import { QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons';
 import Service from '@/pages/device/Instance/service';
 import useLocation from '@/hooks/route/useLocation';
 import { onlyMessage } from '@/utils/util';
@@ -107,21 +107,21 @@ const InstanceDetail = observer(() => {
         <Card>
           <Metadata
             type="device"
-          // tabAction={
-          //   <PermissionButton
-          //     isPermission={permission.update}
-          //     popConfirm={{
-          //       title: '确认重置?',
-          //       onConfirm: resetMetadata,
-          //     }}
-          //     tooltip={{
-          //       title: '重置后将使用产品的物模型配置',
-          //     }}
-          //     key={'reload'}
-          //   >
-          //     重置操作1
-          //   </PermissionButton>
-          // }
+            // tabAction={
+            //   <PermissionButton
+            //     isPermission={permission.update}
+            //     popConfirm={{
+            //       title: '确认重置?',
+            //       onConfirm: resetMetadata,
+            //     }}
+            //     tooltip={{
+            //       title: '重置后将使用产品的物模型配置',
+            //     }}
+            //     key={'reload'}
+            //   >
+            //     重置操作1
+            //   </PermissionButton>
+            // }
           />
         </Card>
       ),
@@ -309,7 +309,7 @@ const InstanceDetail = observer(() => {
               size={'small'}
               tooltip={{
                 title: InstanceModel.detail?.productName,
-                placement: 'topLeft'
+                placement: 'topLeft',
               }}
               isPermission={!!getMenuPathByCode(MENUS_CODE['device/Product'])}
               onClick={() => {
@@ -393,12 +393,17 @@ const InstanceDetail = observer(() => {
                 断开连接
               </PermissionButton>
             )}
-            {InstanceModel.detail?.accessProvider === 'child-device' && InstanceModel.detail?.state?.value === "offline" ? (
+            {InstanceModel.detail?.accessProvider === 'child-device' &&
+            InstanceModel.detail?.state?.value === 'offline' ? (
               <div>
-                <Tooltip placement='bottom'
-                  title={InstanceModel.detail?.features?.find((item) => item.id === 'selfManageState')
-                    ? '该设备的在线状态与父设备(网关设备)保持一致'
-                    : '该设备在线状态由设备自身运行状态决定,不继承父设备(网关设备)的在线状态'}>
+                <Tooltip
+                  placement="bottom"
+                  title={
+                    InstanceModel.detail?.features?.find((item) => item.id === 'selfManageState')
+                      ? '该设备的在线状态与父设备(网关设备)保持一致'
+                      : '该设备在线状态由设备自身运行状态决定,不继承父设备(网关设备)的在线状态'
+                  }
+                >
                   <QuestionCircleOutlined style={{ fontSize: 14, marginRight: 5 }} />
                 </Tooltip>
               </div>

+ 30 - 31
src/pages/device/Product/Detail/Access/index.tsx

@@ -56,7 +56,7 @@ const Access = () => {
   const [configVisible, setConfigVisible] = useState<boolean>(false);
 
   const [metadata, setMetadata] = useState<ConfigMetadata[]>([]);
-  const ref = useRef(0)
+  const ref = useRef(0);
 
   const steps = [
     {
@@ -274,9 +274,9 @@ const Access = () => {
 
   const id = productModel.current?.id;
 
-  const guide = (data:any) =>{
-    service.productGuideSave(data)
-  }
+  const guide = (data: any) => {
+    service.productGuideSave(data);
+  };
 
   useEffect(() => {
     const driver = new Driver({
@@ -285,20 +285,20 @@ const Access = () => {
       closeBtnText: '不在提示',
       nextBtnText: '下一步',
       prevBtnText: '上一步',
-      onNext:()=>{
-        ref.current=ref.current+1
+      onNext: () => {
+        ref.current = ref.current + 1;
       },
-      onPrevious:()=>{
-        ref.current=ref.current-1
+      onPrevious: () => {
+        ref.current = ref.current - 1;
       },
-      onReset:()=>{ 
-        if(ref.current!==3){
+      onReset: () => {
+        if (ref.current !== 3) {
           guide({
-            name:'guide',
-            content:'skip'
-          })
+            name: 'guide',
+            content: 'skip',
+          });
         }
-        ref.current=0
+        ref.current = 0;
       },
     });
     const driver1 = new Driver({
@@ -307,20 +307,20 @@ const Access = () => {
       closeBtnText: '不在提示',
       nextBtnText: '下一步',
       prevBtnText: '上一步',
-      onNext:()=>{
-        ref.current=ref.current+1
+      onNext: () => {
+        ref.current = ref.current + 1;
       },
-      onPrevious:()=>{
-        ref.current=ref.current-1
+      onPrevious: () => {
+        ref.current = ref.current - 1;
       },
-      onReset:()=>{
-        if(ref.current!==4){
+      onReset: () => {
+        if (ref.current !== 4) {
           guide({
-            name:'guide',
-            content:'skip'
-          })
+            name: 'guide',
+            content: 'skip',
+          });
         }
-        ref.current=0
+        ref.current = 0;
       },
     });
     setVisible(!!productModel.current?.accessId);
@@ -331,15 +331,15 @@ const Access = () => {
           .then(async (resp: { result: SetStateAction<ConfigMetadata[]> }) => {
             setMetadata(resp.result);
             //判断引导页是否跳过
-            const res = await service.productGuide()
-            if(res.result && res.result?.content==='skip'){
+            const res = await service.productGuide();
+            if (res.result && res.result?.content === 'skip') {
               return;
-            }else{
+            } else {
               if (resp.result && resp.result.length > 0) {
-                driver1.defineSteps(steps1)
+                driver1.defineSteps(steps1);
                 driver1.start();
               } else {
-                driver.defineSteps(steps)
+                driver.defineSteps(steps);
                 driver.start();
               }
             }
@@ -376,7 +376,6 @@ const Access = () => {
     });
   }, [productModel.current]);
 
-
   const form = createForm({
     validateFirst: true,
     initialValues: {
@@ -591,7 +590,7 @@ const Access = () => {
                           更换
                         </Button>
                       </Tooltip>
-                        {/* <Button onClick={async()=>{
+                      {/* <Button onClick={async()=>{
                           await service.productGuideDetail()
                         }}>删除</Button> */}
                     </span>

+ 1 - 1
src/pages/home/device/index.tsx

@@ -94,7 +94,7 @@ const Device = () => {
             {
               name: '产品数量',
               value: productCount,
-              children:require('/public/images/home/top-2.png'),
+              children: require('/public/images/home/top-2.png'),
             },
             {
               name: '设备数量',

+ 5 - 5
src/pages/link/AccessConfig/service.ts

@@ -55,16 +55,16 @@ class Service extends BaseService<AccessItem> {
   public productGuide = () =>
     request(`/${SystemConst.API_BASE}/user/settings/product/guide`, {
       method: 'GET',
-    })
+    });
   public productGuideSave = (data: any) =>
     request(`/${SystemConst.API_BASE}/user/settings/product/guide`, {
       method: 'PATCH',
-      data
-    })
-    public productGuideDetail = () =>
+      data,
+    });
+  public productGuideDetail = () =>
     request(`/${SystemConst.API_BASE}/user/settings/product/guide`, {
       method: 'DELETE',
-    })
+    });
   public getResourcesCurrent = () =>
     request(`${SystemConst.API_BASE}/network/resources/alive/_current`, {
       method: 'GET',

+ 45 - 45
src/pages/rule-engine/Alarm/Log/Detail/index.tsx

@@ -9,7 +9,7 @@ import { AlarmLogModel } from '../model';
 import { observer } from '@formily/reactive-react';
 import { SearchOutlined } from '@ant-design/icons';
 import Info from './Info';
-import { Button, Card } from 'antd';
+import { Button } from 'antd';
 import moment from 'moment';
 import useLocation from '@/hooks/route/useLocation';
 import { useDomFullHeight } from '@/hooks';
@@ -19,8 +19,8 @@ const Detail = observer(() => {
   const location = useLocation();
   const [visible, setVisible] = useState<boolean>(false);
   const [current, setCurrent] = useState<Partial<AlarmLogHistoryItem>>({});
-  const { minHeight } = useDomFullHeight(`.alarm-log`,24);
-  const [detail, setDetail] = useState<any>({})
+  const { minHeight } = useDomFullHeight(`.alarm-log`, 24);
+  const [detail, setDetail] = useState<any>({});
 
   const [param, setParam] = useState<any>({
     terms: [
@@ -97,9 +97,9 @@ const Detail = observer(() => {
 
   useEffect(() => {
     const { state } = location;
-    setCurrent(detail)
+    setCurrent(detail);
     if (state?.param.detail && detail) {
-      setVisible(true)
+      setVisible(true);
     }
   }, [location, detail]);
 
@@ -126,46 +126,46 @@ const Detail = observer(() => {
         }}
       />
       <ProTable<AlarmLogHistoryItem>
-          actionRef={actionRef}
-          params={param}
-          columns={AlarmLogModel.columns}
-          search={false}
-          tableClassName={'alarm-log'}
-          tableStyle={{ minHeight }}
-          scroll={{ x: 1366 }}
-          // headerTitle={'记录列表'}
-          request={async (data) => {
-            const res = await service.queryHistoryList({
-              ...data,
-              sorts: [{ name: 'alarmTime', order: 'desc' }],
-            });
-            if (res.status === 200) {
-              setDetail(res.result.data[0])
-              return {
-                code: res.message,
-                result: {
-                  data: res.result.data,
-                  pageIndex: res.result.pageIndex,
-                  pageSize: res.result.pageSize,
-                  total: res.result.total,
-                },
-                status: res.status,
-              };
-            } else {
-              return {
-                code: 200,
-                result: {
-                  data: [],
-                  pageIndex: 0,
-                  pageSize: 0,
-                  total: 0,
-                },
-                status: 200,
-              };
-            }
-          }}
-          rowKey="id"
-        />
+        actionRef={actionRef}
+        params={param}
+        columns={AlarmLogModel.columns}
+        search={false}
+        tableClassName={'alarm-log'}
+        tableStyle={{ minHeight }}
+        scroll={{ x: 1366 }}
+        // headerTitle={'记录列表'}
+        request={async (data) => {
+          const res = await service.queryHistoryList({
+            ...data,
+            sorts: [{ name: 'alarmTime', order: 'desc' }],
+          });
+          if (res.status === 200) {
+            setDetail(res.result.data[0]);
+            return {
+              code: res.message,
+              result: {
+                data: res.result.data,
+                pageIndex: res.result.pageIndex,
+                pageSize: res.result.pageSize,
+                total: res.result.total,
+              },
+              status: res.status,
+            };
+          } else {
+            return {
+              code: 200,
+              result: {
+                data: [],
+                pageIndex: 0,
+                pageSize: 0,
+                total: 0,
+              },
+              status: 200,
+            };
+          }
+        }}
+        rowKey="id"
+      />
       {visible && (
         <Info
           close={() => {

+ 12 - 10
src/pages/rule-engine/DashBoard/index.tsx

@@ -335,16 +335,18 @@ const Dashboard = observer(() => {
                             {moment(item.alarmTime).format('YYYY-MM-DD HH:mm:ss')}
                           </div>
                           <div className={'new-alarm-item-content ellipsis'}>
-                            <Tooltip title={item.alarmName} placement='topLeft'>
-                              <a onClick={() => {
-                                console.log(item)
-                                const url = getMenuPathByCode('rule-engine/Alarm/Log');
-                                history.push(`${url}/detail/${item.id}`,{
-                                  param: {
-                                    detail: true,
-                                  },
-                                });
-                              }}>
+                            <Tooltip title={item.alarmName} placement="topLeft">
+                              <a
+                                onClick={() => {
+                                  console.log(item);
+                                  const url = getMenuPathByCode('rule-engine/Alarm/Log');
+                                  history.push(`${url}/detail/${item.id}`, {
+                                    param: {
+                                      detail: true,
+                                    },
+                                  });
+                                }}
+                              >
                                 {item.alarmName}
                               </a>
                             </Tooltip>

+ 14 - 14
src/pages/rule-engine/Scene/Save/components/TriggerWay/index.tsx

@@ -40,6 +40,20 @@ export default (props: TriggerWayProps) => {
     <div className={classNames('trigger-way-warp', props.className, { disabled: props.disabled })}>
       <div
         className={classNames('trigger-way-item', {
+          active: type === TriggerWayType.device,
+        })}
+        onClick={() => {
+          onSelect(TriggerWayType.device);
+        }}
+      >
+        <div className={'way-item-title'}>
+          <p>设备触发</p>
+          <span>DEVICE TRIGGER</span>
+        </div>
+        <img className={'way-item-image'} src={'/images/device-trigger.png'} />
+      </div>
+      <div
+        className={classNames('trigger-way-item', {
           active: type === TriggerWayType.manual,
         })}
         onClick={() => {
@@ -66,20 +80,6 @@ export default (props: TriggerWayProps) => {
         </div>
         <img className={'way-item-image'} src={'/images/timing-trigger.png'} />
       </div>
-      <div
-        className={classNames('trigger-way-item', {
-          active: type === TriggerWayType.device,
-        })}
-        onClick={() => {
-          onSelect(TriggerWayType.device);
-        }}
-      >
-        <div className={'way-item-title'}>
-          <p>设备触发</p>
-          <span>DEVICE TRIGGER</span>
-        </div>
-        <img className={'way-item-image'} src={'/images/device-trigger.png'} />
-      </div>
     </div>
   );
 };

+ 57 - 36
src/pages/system/Department/Assets/deivce/bind.tsx

@@ -11,6 +11,7 @@ import PermissionModal from '@/pages/system/Department/Assets/permissionModal';
 import SearchComponent from '@/components/SearchComponent';
 import { ExtraDeviceCard } from '@/components/ProTableCard/CardItems/device';
 import { ProTableCard } from '@/components';
+import { AssetsModel } from '@/pages/system/Department/Assets';
 
 interface Props {
   reload: () => void;
@@ -101,6 +102,7 @@ const Bind = observer((props: Props) => {
   ];
 
   const handleBind = () => {
+    AssetsModel.params = {};
     if (Models.bindKeys.length) {
       saveRef.current?.saveData();
     } else {
@@ -115,16 +117,41 @@ const Bind = observer((props: Props) => {
     }
   }, [props.visible]);
 
+  console.log(AssetsModel);
+
+  const getParams = (params: any) => {
+    console.log(params);
+    const _params: any = [
+      {
+        column: 'id',
+        termType: 'dim-assets$not',
+        value: {
+          assetType: 'device',
+          targets: [
+            {
+              type: 'org',
+              id: props.parentId,
+            },
+          ],
+        },
+      },
+    ];
+    if (Object.keys(params).length) {
+      _params.push({
+        column: 'parentId$product-info',
+        termType: 'eq',
+        value: 'accessId is ' + params.productId[0],
+      });
+    }
+    return _params;
+  };
+
   return (
     <Modal
       visible={props.visible}
       onOk={handleBind}
       onCancel={props.onCancel}
       width={'75vw'}
-      bodyStyle={{
-        height: 'calc(100vh - 240px);',
-        overflowY: 'auto',
-      }}
       title="绑定"
     >
       <PermissionModal
@@ -143,21 +170,7 @@ const Bind = observer((props: Props) => {
         field={columns}
         enableSave={false}
         model={'simple'}
-        defaultParam={[
-          {
-            column: 'id',
-            termType: 'dim-assets$not',
-            value: {
-              assetType: 'device',
-              targets: [
-                {
-                  type: 'org',
-                  id: props.parentId,
-                },
-              ],
-            },
-          },
-        ]}
+        defaultParam={getParams(AssetsModel.bindModal ? AssetsModel.params : {})}
         onSearch={async (data) => {
           actionRef.current?.reset?.();
           setSearchParam(data);
@@ -169,24 +182,32 @@ const Bind = observer((props: Props) => {
         // }}
         target="department-assets-device"
       />
-      <ProTableCard<DeviceItem>
-        actionRef={actionRef}
-        columns={columns}
-        rowKey="id"
-        search={false}
-        gridColumn={2}
-        cardRender={(record) => (
-          <ExtraDeviceCard showBindBtn={false} {...record} cardType={'bind'} />
-        )}
-        rowSelection={{
-          selectedRowKeys: Models.bindKeys,
-          onChange: (selectedRowKeys, selectedRows) => {
-            Models.bindKeys = selectedRows.map((item) => item.id);
-          },
+      <div
+        style={{
+          height: 'calc(100vh - 440px)',
+          overflowY: 'auto',
         }}
-        request={(params) => service.queryDeviceList(params)}
-        params={searchParam}
-      />
+      >
+        <ProTableCard<DeviceItem>
+          actionRef={actionRef}
+          columns={columns}
+          rowKey="id"
+          search={false}
+          gridColumn={2}
+          cardRender={(record) => (
+            <ExtraDeviceCard showBindBtn={false} showTool={false} {...record} cardType={'bind'} />
+          )}
+          rowSelection={{
+            selectedRowKeys: Models.bindKeys,
+            onChange: (selectedRowKeys, selectedRows) => {
+              Models.bindKeys = selectedRows.map((item) => item.id);
+            },
+          }}
+          request={(params) => service.queryDeviceList(params)}
+          params={searchParam}
+          height={'none'}
+        />
+      </div>
     </Modal>
   );
 });

+ 76 - 6
src/pages/system/Department/Assets/deivce/index.tsx

@@ -5,15 +5,16 @@ import { Badge, Button, Popconfirm, Tooltip } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import { observer } from '@formily/react';
 import type { DeviceItem } from '@/pages/system/Department/typings';
-import { DisconnectOutlined, PlusOutlined } from '@ant-design/icons';
+import { DisconnectOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
 import Models from './model';
 import Service from '@/pages/system/Department/Assets/service';
 import Bind from './bind';
 import SearchComponent from '@/components/SearchComponent';
 import { ExtraDeviceCard, handlePermissionsMap } from '@/components/ProTableCard/CardItems/device';
-import { ProTableCard } from '@/components';
+import { PermissionButton, ProTableCard } from '@/components';
 import { onlyMessage } from '@/utils/util';
 import { ASSETS_TABS_ENUM, AssetsModel } from '@/pages/system/Department/Assets';
+import UpdateModal from '@/pages/system/Department/Assets/updateModal';
 
 export const service = new Service<DeviceItem>('assets');
 
@@ -36,6 +37,10 @@ export default observer((props: { parentId: string }) => {
 
   const [searchParam, setSearchParam] = useState({});
 
+  const [updateVisible, setUpdateVisible] = useState(false);
+  const [updateId, setUpdateId] = useState('');
+  const [permissions, setPermissions] = useState<string[]>([]);
+
   useEffect(() => {
     if (AssetsModel.tabsIndex === ASSETS_TABS_ENUM.Device && actionRef.current) {
       actionRef.current.reload();
@@ -170,6 +175,23 @@ export default observer((props: { parentId: string }) => {
       width: 60,
       fixed: 'right',
       render: (text, record) => [
+        <PermissionButton
+          key="update"
+          type={'link'}
+          style={{ padding: 0 }}
+          tooltip={{
+            title: '编辑',
+          }}
+          onClick={(e) => {
+            e?.stopPropagation();
+            setUpdateId(record.id);
+            setPermissions(record.grantedPermissions!);
+            setUpdateVisible(true);
+          }}
+          isPermission={true}
+        >
+          <EditOutlined />
+        </PermissionButton>,
         <Popconfirm
           title={intl.formatMessage({
             id: 'pages.system.role.option.unBindUser',
@@ -199,6 +221,7 @@ export default observer((props: { parentId: string }) => {
     Models.bind = false;
     Models.bindKeys = [];
     if (AssetsModel.bindModal) {
+      AssetsModel.params = {};
       AssetsModel.bindModal = false;
     }
   };
@@ -245,6 +268,21 @@ export default observer((props: { parentId: string }) => {
           parentId={props.parentId}
         />
       )}
+      {updateVisible && (
+        <UpdateModal
+          permissions={permissions}
+          visible={updateVisible}
+          id={updateId}
+          type="device"
+          targetId={props.parentId}
+          onCancel={() => {
+            setUpdateVisible(false);
+          }}
+          onReload={() => {
+            actionRef.current?.reload();
+          }}
+        />
+      )}
       <SearchComponent<DeviceItem>
         field={columns}
         defaultParam={[
@@ -310,10 +348,42 @@ export default observer((props: { parentId: string }) => {
         cardRender={(record) => (
           <ExtraDeviceCard
             {...record}
-            onUnBind={(e) => {
-              e.stopPropagation();
-              singleUnBind(record.id);
-            }}
+            actions={[
+              <PermissionButton
+                key="update"
+                onClick={(e) => {
+                  e?.stopPropagation();
+                  setUpdateId(record.id);
+                  setPermissions(record.grantedPermissions!);
+                  setUpdateVisible(true);
+                }}
+                isPermission={true}
+              >
+                <EditOutlined />
+              </PermissionButton>,
+              <PermissionButton
+                key="unbind"
+                popConfirm={{
+                  title: intl.formatMessage({
+                    id: 'pages.system.role.option.unBindUser',
+                    defaultMessage: '是否解除绑定',
+                  }),
+                  onConfirm: (e) => {
+                    e?.stopPropagation();
+                    singleUnBind(record.id);
+                  },
+                  onCancel: (e) => {
+                    e?.stopPropagation();
+                  },
+                }}
+                onClick={(e) => {
+                  e?.stopPropagation();
+                }}
+                isPermission={true}
+              >
+                <DisconnectOutlined />
+              </PermissionButton>,
+            ]}
           />
         )}
         toolBarRender={() => [

+ 2 - 0
src/pages/system/Department/Assets/index.tsx

@@ -24,10 +24,12 @@ export const AssetsModel = model<{
   tabsIndex: string;
   bindModal: boolean;
   parentId: string;
+  params: any;
 }>({
   tabsIndex: ASSETS_TABS_ENUM.Product,
   bindModal: false,
   parentId: '',
+  params: {},
 });
 
 const Assets = observer((props: AssetsProps) => {

+ 34 - 21
src/pages/system/Department/Assets/product/bind.tsx

@@ -11,6 +11,7 @@ import type { ProductItem } from '@/pages/system/Department/typings';
 import SearchComponent from '@/components/SearchComponent';
 import { ExtraProductCard } from '@/components/ProTableCard/CardItems/product';
 import { ProTableCard } from '@/components';
+import { AssetsModel } from '@/pages/system/Department/Assets';
 
 interface Props {
   reload: () => void;
@@ -73,10 +74,6 @@ const Bind = observer((props: Props) => {
         onOk={handleBind}
         onCancel={props.onCancel}
         width={'75vw'}
-        bodyStyle={{
-          height: 'calc(100vh - 240px);',
-          overflowY: 'auto',
-        }}
         title="绑定"
       >
         <PermissionModal
@@ -121,24 +118,40 @@ const Bind = observer((props: Props) => {
           // }}
           target="department-assets-product"
         />
-        <ProTableCard<ProductItem>
-          actionRef={actionRef}
-          columns={columns}
-          rowKey="id"
-          search={false}
-          gridColumn={2}
-          rowSelection={{
-            selectedRowKeys: Models.bindKeys,
-            onChange: (selectedRowKeys, selectedRows) => {
-              Models.bindKeys = selectedRows.map((item) => item.id);
-            },
+        <div
+          style={{
+            height: 'calc(100vh - 440px)',
+            overflowY: 'auto',
           }}
-          request={(params) => service.queryProductList(params)}
-          params={searchParam}
-          cardRender={(record) => (
-            <ExtraProductCard showBindBtn={false} {...record} cardType={'bind'} />
-          )}
-        />
+        >
+          <ProTableCard<ProductItem>
+            actionRef={actionRef}
+            columns={columns}
+            rowKey="id"
+            search={false}
+            gridColumn={2}
+            rowSelection={{
+              selectedRowKeys: Models.bindKeys,
+              onChange: (selectedRowKeys, selectedRows) => {
+                Models.bindKeys = selectedRows.map((item) => item.id);
+                AssetsModel.params = {
+                  productId: selectedRows.map((item) => item.id),
+                };
+              },
+            }}
+            request={(params) => service.queryProductList(params)}
+            params={searchParam}
+            cardRender={(record) => (
+              <ExtraProductCard
+                showBindBtn={false}
+                showTool={false}
+                {...record}
+                cardType={'bind'}
+              />
+            )}
+            height={'none'}
+          />
+        </div>
       </Modal>
     </>
   );

+ 74 - 5
src/pages/system/Department/Assets/product/index.tsx

@@ -5,7 +5,7 @@ import { Button, Modal, Popconfirm, Tooltip } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import { observer } from '@formily/react';
 import type { ProductItem } from '@/pages/system/Department/typings';
-import { DisconnectOutlined, PlusOutlined } from '@ant-design/icons';
+import { DisconnectOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
 import Service from '@/pages/system/Department/Assets/service';
 import Models from './model';
 import Bind from './bind';
@@ -14,9 +14,10 @@ import {
   ExtraProductCard,
   handlePermissionsMap,
 } from '@/components/ProTableCard/CardItems/product';
-import { ProTableCard } from '@/components';
+import { ProTableCard, PermissionButton } from '@/components';
 import { onlyMessage } from '@/utils/util';
 import { ASSETS_TABS_ENUM, AssetsModel } from '@/pages/system/Department/Assets';
+import UpdateModal from '../updateModal';
 
 export const service = new Service<ProductItem>('assets');
 
@@ -26,6 +27,9 @@ export default observer((props: { parentId: string }) => {
 
   const [searchParam, setSearchParam] = useState({});
   const [deviceVisible, setDeviceVisible] = useState(false);
+  const [updateVisible, setUpdateVisible] = useState(false);
+  const [updateId, setUpdateId] = useState('');
+  const [permissions, setPermissions] = useState<string[]>([]);
 
   useEffect(() => {
     if (AssetsModel.tabsIndex === ASSETS_TABS_ENUM.Product && actionRef.current) {
@@ -114,6 +118,23 @@ export default observer((props: { parentId: string }) => {
       width: 60,
       fixed: 'right',
       render: (text, record) => [
+        <PermissionButton
+          key="update"
+          type={'link'}
+          style={{ padding: 0 }}
+          tooltip={{
+            title: '编辑',
+          }}
+          onClick={(e) => {
+            e?.stopPropagation();
+            setUpdateId(record.id);
+            setPermissions(record.grantedPermissions!);
+            setUpdateVisible(true);
+          }}
+          isPermission={true}
+        >
+          <EditOutlined />
+        </PermissionButton>,
         <Popconfirm
           title={intl.formatMessage({
             id: 'pages.system.role.option.unBindUser',
@@ -206,6 +227,21 @@ export default observer((props: { parentId: string }) => {
           是否继续分配产品下的具体设备
         </Modal>
       )}
+      {updateVisible && (
+        <UpdateModal
+          permissions={permissions}
+          visible={updateVisible}
+          id={updateId}
+          type="product"
+          targetId={props.parentId}
+          onCancel={() => {
+            setUpdateVisible(false);
+          }}
+          onReload={() => {
+            actionRef.current?.reload();
+          }}
+        />
+      )}
       <SearchComponent<ProductItem>
         field={columns}
         defaultParam={[
@@ -272,9 +308,42 @@ export default observer((props: { parentId: string }) => {
         cardRender={(record) => (
           <ExtraProductCard
             {...record}
-            onUnBind={() => {
-              singleUnBind(record.id);
-            }}
+            actions={[
+              <PermissionButton
+                key="update"
+                onClick={(e) => {
+                  e?.stopPropagation();
+                  setUpdateId(record.id);
+                  setPermissions(record.grantedPermissions!);
+                  setUpdateVisible(true);
+                }}
+                isPermission={true}
+              >
+                <EditOutlined />
+              </PermissionButton>,
+              <PermissionButton
+                key="unbind"
+                popConfirm={{
+                  title: intl.formatMessage({
+                    id: 'pages.system.role.option.unBindUser',
+                    defaultMessage: '是否解除绑定',
+                  }),
+                  onConfirm: (e) => {
+                    e?.stopPropagation();
+                    singleUnBind(record.id);
+                  },
+                  onCancel: (e) => {
+                    e?.stopPropagation();
+                  },
+                }}
+                onClick={(e) => {
+                  e?.stopPropagation();
+                }}
+                isPermission={true}
+              >
+                <DisconnectOutlined />
+              </PermissionButton>,
+            ]}
           />
         )}
         toolBarRender={() => [

+ 6 - 0
src/pages/system/Department/Assets/service.ts

@@ -106,6 +106,12 @@ class Service<T> extends BaseService<T> {
         );
       }),
     );
+
+  updatePermission = (type: string, id: string, targetId: string, permission: string[]) =>
+    request(`${SystemConst.API_BASE}/assets/permission/${type}/${id}/org/${targetId}`, {
+      method: 'PUT',
+      data: permission,
+    });
 }
 
 export default Service;

+ 63 - 0
src/pages/system/Department/Assets/updateModal.tsx

@@ -0,0 +1,63 @@
+import { Modal, Form, Checkbox } from 'antd';
+import { useCallback, useEffect } from 'react';
+import Server from './service';
+
+interface UpdateModalProps {
+  id: string;
+  type: string;
+  targetId: string;
+  visible: boolean;
+  permissions: string[];
+  onCancel: () => void;
+  onReload: () => void;
+}
+const server = new Server();
+export default (props: UpdateModalProps) => {
+  const [form] = Form.useForm();
+
+  const saveData = useCallback(async () => {
+    const data = form.getFieldsValue();
+    if (data) {
+      const res = await server.updatePermission(
+        props.type,
+        props.id,
+        props.targetId,
+        data.permissions,
+      );
+      if (res.status === 200 && props.onReload) {
+        props.onCancel();
+        props.onReload();
+      }
+    }
+  }, [props.id]);
+
+  useEffect(() => {
+    if (form) {
+      form.setFieldsValue({
+        permissions: props.permissions,
+      });
+    }
+  }, [props.permissions, form]);
+
+  return (
+    <Modal
+      title={'编辑'}
+      visible={props.visible}
+      width={500}
+      onCancel={props.onCancel}
+      onOk={saveData}
+    >
+      <Form form={form}>
+        <Form.Item name="permissions" label="资产权限" required>
+          <Checkbox.Group
+            options={[
+              { label: '查看', value: 'read', disabled: true },
+              { label: '编辑', value: 'save' },
+              { label: '删除', value: 'delete' },
+            ]}
+          />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};

+ 1 - 1
src/pages/system/Department/Member/bind.tsx

@@ -73,7 +73,7 @@ const Bind = observer((props: Props) => {
       visible={props.visible}
       onOk={handleBind}
       onCancel={props.onCancel}
-      width={'75vw'}
+      width={'800'}
       bodyStyle={{
         height: 'calc(100vh - 240px);',
         overflowY: 'auto',