干燥机配套车间生产管理系统/云平台前端
bsw215583320
2023-12-04 97dc174b26461df1395394c02401bb51dd6376bc
增加opc设备维护和控制功能
已添加12个文件
已修改3个文件
870 ■■■■■ 文件已修改
src/assets/images/dry/control/cam-close.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/cam.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/cam0.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/cam1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/light-close.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/light.png 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/control/api.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dashboard/control/index.vue 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/DryOpcDeviceList.vue 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/api/DryOpcDevice.api.ts 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/bigScreen/BigWorkShop.vue 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryOpcDeviceForm.vue 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/components/DryOpcDeviceModal.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/dataDefine/DryOpcDevice.data.ts 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/dry/sql/DryOpcDevice_menu_insert.sql 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/images/dry/control/cam-close.png
src/assets/images/dry/control/cam.png
src/assets/images/dry/control/cam0.png
src/assets/images/dry/control/cam1.png
src/assets/images/dry/control/light-close.png
src/assets/images/dry/control/light.png
src/views/dashboard/control/api.ts
@@ -2,6 +2,8 @@
enum Api {
  sendCommand = '/dry/real/sendCommand',
  sendWriteCommand = '/dry/opc/sendWriteCommand',
  listAll = '/dry/dryOpcDevice/listAll'
}
/**
 * å‘送命令
@@ -9,3 +11,15 @@
 */
export const sendCommand = (params) => defHttp.post({ url: Api.sendCommand, params },{ isTransformResponse: false });
/**
 * å‘送OPC写指令
 */
export const sendWriteCommand = (params) => defHttp.post({url: Api.sendWriteCommand, params},{isTransformResponse: false})
/**
 * æŸ¥è¯¢æ‰€æœ‰OPC设备
 */
export const listAll = (params) => defHttp.get({url:Api.listAll, params})
src/views/dashboard/control/index.vue
@@ -7,12 +7,125 @@
  </a-modal>
  <div class="app">
    <a-card title="指令">
    <a-row>
      <a-card title="照明" style="width: 60%">
      <div class="light-box">
        <div v-for="item in lightList" :key="item.identifier"  class="center">
          <!-- <a-button v-if="item.type == 0"  type="primary" class="com-btn"
                  @click="deviceClick(item)">
          {{ item.name }}
        </a-button> -->
          <div v-if="item.type==0" @click="deviceClick(item)">
            <div   :class="[ item.value?'lightOn':'lightOff' ,'device']">
            </div>
            <div class="text-center">{{ item.name }}</div>
            </div>
        </div>
      </div>
    </a-card>
    <a-card title="监控" style="width: 35%; margin-left: 10px;">
      <div class="monitor-box">
        <div v-for="item in lightList" :key="item.identifier" class="center" >
          <div v-if="item.type==1" @click="deviceClick(item)">
            <div   :class="[ item.value?'camOn':'camOff' ,'device']">
            </div>
            <div class="text-center">{{ item.name }}</div>
            </div>
      </div>
      </div>
    </a-card>
    </a-row>
    <a-card title="指令" style="margin-top: 10px">
      <div class="com-box">
        <a-button v-for="item in comList" :key="item.id" type="primary" class="com-btn"
        <!-- <a-button v-for="item in comList" :key="item.id" type="primary" class="com-btn"
                  @click="comClick(item)">
          {{ item.title }}
        </a-button> -->
        <div style="width: 500px;">
        <a-row class="button-row">
          <a-button type="primary" @click="clickButton(1010, '干燥启动')" class="com-btn">
            <div> <PlayCircleOutlined style="font-size: 20px;" /> </div>
          <div>  å¹²ç‡¥å¯åЍ </div>
        </a-button>
        <!-- <a-button type="normal" @click="clickButton(1012)" class="com-btn">
          <div> <SplitCellsOutlined style="font-size: 20px;" /> </div>
          <div>  åŽé—¨å¼€å…³ </div>
        </a-button> -->
        <a-button type="default" @click="clickButton(1003,'滚筒升')" class="com-btn w300">
          <div> <UpCircleOutlined style="font-size: 20px;" /> </div>
          <div>  æ»šç­’升 </div>
        </a-button>
        <a-button type="info" @click="clickButton(1014, '开门观察')" class="com-btn">
          <div> <SettingOutlined style="font-size: 20px;" /> </div>
          <div>  å¼€é—¨è§‚察 </div>
        </a-button>
        </a-row>
        <a-row class="button-row">
          <a-button type="normal" @click="clickButton(1005,'滚筒正转')" class="com-btn h100">
            <div>  <RedoOutlined style="font-size: 20px;" /> </div>
            <div> æ»šç­’正转 </div>
        </a-button>
          <a-button type="danger" @click="clickButton(1007,'停止')" class="com-btn h100 w300 " >
            <div> <PauseCircleOutlined style="font-size: 20px;" />  </div>
            <div>  åœæ­¢ </div>
        </a-button>
        <a-button type="normal" @click="clickButton(1006,'滚筒反转')" class="com-btn h100">
          <div> <UndoOutlined style="font-size: 20px;" /> </div>
          <div>  æ»šç­’反转 </div>
        </a-button>
        </a-row>
        <a-row class="button-row">
          <a-button type="success" @click="clickButton(1015,'出料')" class="com-btn">
            <div> <DownloadOutlined style="font-size: 20px;" /> </div>
            <div>  å‡ºæ–™ </div>
        </a-button>
        <!-- <a-button type="normal" @click="clickButton(1010)" class="com-btn">
          <div> <SplitCellsOutlined style="font-size: 20px;" /> </div>
          <div>  å‰é—¨å¼€å…³ </div>
        </a-button> -->
        <a-button type="normal" @click="clickButton(1004,'滚筒降')" class="com-btn w300">
          <div> <DownCircleOutlined style="font-size: 20px;" /> </div>
          <div>  æ»šç­’降 </div>
        </a-button>
        <a-button type="normal" @click="clickButton(1013,'热风启动')" class="com-btn">
          <div> <FireOutlined style="font-size: 20px;" /> </div>
            <div>  çƒ­é£Žå¯åЍ </div>
        </a-button>
      </a-row>
        </div>
        <div style="margin-left: 100px;">
          <div>
          <a-button type="warning" @click="clickButton(1017,'手动/自动')" class="com-btn">
            <div> <SyncOutlined style="font-size: 20px;" /> </div>
          <div>  æ‰‹åЍ/自动 </div>
        </a-button>
      </div>
      <div>
        <a-button type="normal" @click="clickButton(1016,'清除')" class="com-btn">
          <div> <ClearOutlined style="font-size: 20px;" /> </div>
          <div>  æ¸…除 </div>
        </a-button>
      </div>
        </div>
      </div>
    </a-card>
    <a-card title="控制台" style="margin-top: 10px">
@@ -26,16 +139,28 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { sendCommand } from "./api";
import { sendCommand, sendWriteCommand, listAll } from "./api";
import { useGlobSetting } from '/@/hooks/setting';
import { connectWebSocket, onWebSocket } from '/@/hooks/web/useWebSocket';
import { useUserStore } from '/@/store/modules/user';
import { RedoOutlined ,SettingOutlined, UndoOutlined,FireOutlined, SplitCellsOutlined, DownloadOutlined, PauseCircleOutlined,ClearOutlined, SyncOutlined,PlayCircleOutlined, SwapOutlined , UpCircleOutlined, DownCircleOutlined, LeftCircleOutlined, RightCircleOutlined} from '@ant-design/icons-vue';
const visible = ref<boolean>(false);
  const glob = useGlobSetting();
interface Commant {
  id: number;
  title: string;
  icon: string;
}
interface Device {
  name: string;
  identifier: string;
  value: boolean;
  sort_order: number;
}
const userStore = useUserStore();
const logList = ref<string[]>([]);
const comList = ref<Commant[]>([
  {
@@ -88,6 +213,9 @@
    icon: "caret-right-outlined"
  }]);
const lightList = ref<Device[]>([]);
const monitorList = ref<Device[]>([]);
function comClick(item) {
  addLog("发送", item.title);
  //发送指令
@@ -133,9 +261,81 @@
  visible.value = false;
};
/**
 * æŸ¥è¯¢ç¯å’Œæ‘„像头列表
 */
function queryDevice() {
  listAll({}).then(res => {
    console.log("ress000:",res);
    lightList.value = res
  })
}
function clickButton(cmd, msg) {
  console.log("target::::",cmd);
  var params = { msg: msg, code: cmd, tenantId: 1003, machineId: "GM001" };
  sendCommand(params).then(res => {
    if (res.success) {
      addLog("响应", res.message);
      console.info(res);
    } else {
      console.info(res);
      addLog("响应", res.message);
    }
  });
}
function deviceClick(item) {
  //console.log("item===",item.value);
  let params = {code: item.identifier, msg: !item.value}
  sendWriteCommand(params).then(res => {
    console.log("写指令结果::", res)
    if(res.success) {
      lightList.value.forEach(device => {
          if(device.identifier == item.identifier) {
            device.value = !item.value
          }
        })
    }
  })
}
    // åˆå§‹åŒ– WebSocket
    function initWebSocket() {
        let user = userStore.getUserInfo;
        let tenantId = user.loginTenantId;
        console.log(user);
        // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
         let url = glob.domainUrl?.replace('https://', 'wss://').replace('http://', 'ws://') + '/drySocket/' + tenantId;
         connectWebSocket(url);
         onWebSocket(onWebSocketMessage);
      }
      function onWebSocketMessage(data) {
        console.log("drysocket:data::",data)
        lightList.value.forEach(item => {
          if(item.identifier == data.identifier) {
            item.value = data.value
          }
        })
      }
onMounted(() => {
  showModal();
  initWebSocket();
});
queryDevice()
</script>
@@ -146,23 +346,101 @@
}
.com-box {
  display: flex;
  flex-wrap: wrap;
  // margin-left: 200px;
  display: flex;
  .com-btn {
    border-radius: 5px;
    width: 100px;
    margin: 10px;
    height: 70px;
  }
}
.button-row {
  display: flex; justify-content: space-between;
}
.log-box {
  min-height: 200px;
  max-height: 300px;
  overflow-y: scroll;
  min-height: 150px;
  max-height: 150px;
  overflow-y: auto;
}
.indented-text {
  text-indent: 20px;
  font-size: 16px;
}
.light-box {
  display: flex;
  justify-items: center;
justify-content: center;
  flex-wrap: wrap;
}
.center {
  display: flex;
justify-items: center;
justify-content: center;
}
.text-center {
  text-align: center;
}
.lightOn {
    background-image: url(/src/assets/images/dry/control/light.png);
    background-repeat: no-repeat;
        background-size: 50px;
        /* border-radius: 10px; */
        background-position: 48px 0px;
  }
  .lightOff {
    background-image: url(/src/assets/images/dry/control/light-close.png);
    background-repeat: no-repeat;
        background-size: 50px;
        /* border-radius: 10px; */
        background-position: 48px 0px;
  }
  .camOn {
    background-image: url(/src/assets/images/dry/control/cam1.png);
    background-repeat: no-repeat;
        background-size: 50px;
        /* border-radius: 10px; */
        background-position: 48px 0px;
  }
  .camOff {
    background-image: url(/src/assets/images/dry/control/cam0.png);
    background-repeat: no-repeat;
        background-size: 50px;
        /* border-radius: 10px; */
        background-position: 48px 0px;
  }
.device {
  width: 150px; height: 60px;
}
.h100 {
  height: 70px !important;
}
.w300 {
  width: 100px !important;
}
.monitor-box {
  display: flex;
  justify-items: center;
justify-content: center;
flex-wrap: wrap;
}
</style>
src/views/dry/DryOpcDeviceList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,174 @@
<template>
  <div>
    <!--引用表格-->
   <BasicTable @register="registerTable" :rowSelection="rowSelection">
     <!--插槽:table标题-->
      <template #tableTitle>
          <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> æ–°å¢ž</a-button>
          <a-button  type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> å¯¼å‡º</a-button>
          <j-upload-button  type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
          <a-dropdown v-if="selectedRowKeys.length > 0">
              <template #overlay>
                <a-menu>
                  <a-menu-item key="1" @click="batchHandleDelete">
                    <Icon icon="ant-design:delete-outlined"></Icon>
                    åˆ é™¤
                  </a-menu-item>
                </a-menu>
              </template>
              <a-button>批量操作
                <Icon icon="mdi:chevron-down"></Icon>
              </a-button>
        </a-dropdown>
      </template>
       <!--操作栏-->
      <template #action="{ record }">
        <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
      </template>
      <!--字段回显插槽-->
      <template #htmlSlot="{text}">
         <div v-html="text"></div>
      </template>
      <!--省市区字段回显插槽-->
      <template #pcaSlot="{text}">
         {{ getAreaTextByCode(text) }}
      </template>
      <template #fileSlot="{text}">
         <span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
         <a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
      </template>
    </BasicTable>
    <!-- è¡¨å•区域 -->
    <DryOpcDeviceModal @register="registerModal" @success="handleSuccess"></DryOpcDeviceModal>
  </div>
</template>
<script lang="ts" name="dry-dryOpcDevice" setup>
  import {ref, computed, unref} from 'vue';
  import {BasicTable, useTable, TableAction} from '/@/components/Table';
  import {useModal} from '/@/components/Modal';
  import { useListPage } from '/@/hooks/system/useListPage'
  import DryOpcDeviceModal from './components/DryOpcDeviceModal.vue'
  import {columns, searchFormSchema} from './dataDefine/DryOpcDevice.data';
  import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './api/DryOpcDevice.api';
  import { downloadFile } from '/@/utils/common/renderUtils';
  const checkedKeys = ref<Array<string | number>>([]);
  //注册model
  const [registerModal, {openModal}] = useModal();
  //注册table数据
  const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
      tableProps:{
           title: 'dry_opc_device',
           api: list,
           columns,
           canResize:false,
           formConfig: {
              //labelWidth: 120,
              schemas: searchFormSchema,
              autoSubmitOnEnter:true,
              showAdvancedButton:true,
              fieldMapToNumber: [
              ],
              fieldMapToTime: [
              ],
            },
           actionColumn: {
               width: 120,
               fixed:'right'
            },
      },
       exportConfig: {
            name:"dry_opc_device",
            url: getExportUrl,
          },
          importConfig: {
            url: getImportUrl,
            success: handleSuccess
          },
  })
  const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
   /**
    * æ–°å¢žäº‹ä»¶
    */
  function handleAdd() {
     openModal(true, {
       isUpdate: false,
       showFooter: true,
     });
  }
   /**
    * ç¼–辑事件
    */
  function handleEdit(record: Recordable) {
     openModal(true, {
       record,
       isUpdate: true,
       showFooter: true,
     });
   }
   /**
    * è¯¦æƒ…
   */
  function handleDetail(record: Recordable) {
     openModal(true, {
       record,
       isUpdate: true,
       showFooter: false,
     });
   }
   /**
    * åˆ é™¤äº‹ä»¶
    */
  async function handleDelete(record) {
    console.log(record);
     await deleteOne({id: record.id}, handleSuccess);
   }
   /**
    * æ‰¹é‡åˆ é™¤äº‹ä»¶
    */
  async function batchHandleDelete() {
     await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
   }
   /**
    * æˆåŠŸå›žè°ƒ
    */
  function handleSuccess() {
      (selectedRowKeys.value = []) && reload();
   }
   /**
      * æ“ä½œæ 
      */
  function getTableAction(record){
       return [
         {
           label: '编辑',
           onClick: handleEdit.bind(null, record),
         }
       ]
   }
     /**
        * ä¸‹æ‹‰æ“ä½œæ 
        */
  function getDropDownAction(record){
       return [
         {
           label: '详情',
           onClick: handleDetail.bind(null, record),
         }, {
           label: '删除',
           popConfirm: {
             title: '是否确认删除',
             confirm: handleDelete.bind(null, record),
           }
         }
       ]
   }
</script>
<style scoped>
</style>
src/views/dry/api/DryOpcDevice.api.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
import {defHttp} from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
  list = '/dry/dryOpcDevice/list',
  save='/dry/dryOpcDevice/add',
  edit='/dry/dryOpcDevice/edit',
  deleteOne = '/dry/dryOpcDevice/delete',
  deleteBatch = '/dry/dryOpcDevice/deleteBatch',
  importExcel = '/dry/dryOpcDevice/importExcel',
  exportXls = '/dry/dryOpcDevice/exportXls',
}
/**
 * å¯¼å‡ºapi
 * @param params
 */
export const getExportUrl = Api.exportXls;
/**
 * å¯¼å…¥api
 */
export const getImportUrl = Api.importExcel;
/**
 * åˆ—表接口
 * @param params
 */
export const list = (params) =>
  defHttp.get({url: Api.list, params});
/**
 * åˆ é™¤å•个
 */
export const deleteOne = (params,handleSuccess) => {
  return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
    handleSuccess();
  });
}
/**
 * æ‰¹é‡åˆ é™¤
 * @param params
 */
export const batchDelete = (params, handleSuccess) => {
  createConfirm({
    iconType: 'warning',
    title: '确认删除',
    content: '是否删除选中数据',
    okText: '确认',
    cancelText: '取消',
    onOk: () => {
      return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
        handleSuccess();
      });
    }
  });
}
/**
 * ä¿å­˜æˆ–者更新
 * @param params
 */
export const saveOrUpdate = (params, isUpdate) => {
  let url = isUpdate ? Api.edit : Api.save;
  return defHttp.post({url: url, params});
}
src/views/dry/bigScreen/BigWorkShop.vue
@@ -19,12 +19,12 @@
                        <div style="display: flex; width: 120px">
                            <Icon style="color: #ba9853" icon="mdi:home-temperature-outline" :size="28" />
                            &nbsp;
                            <div style="font-size: 20px; line-height: 26px"> 26 â„ƒ </div>
                            <div style="font-size: 20px; line-height: 26px"> {{envTemp}} â„ƒ </div>
                        </div>
                        <div style="display: flex">
                            <Icon style="color: #ba9853" icon="wi:humidity" :size="28" />
                            &nbsp;
                            <div style="font-size: 20px; line-height: 26px"> 53 %rh </div>
                            <div style="font-size: 20px; line-height: 26px"> {{envHum}} %rh </div>
                        </div>
                    </div>
                    <div class="feed">
@@ -58,7 +58,7 @@
                                            ç”µé‡æ¶ˆè€—
                                        </div>
                                        <div class="stat_value">
                                            462
                                            {{dianneng.toFixed(2)}}
                                            <span class="font16">
                                                Kwh
                                        </span>
@@ -79,7 +79,7 @@
                                            è’¸æ±½æ¶ˆè€—
                                        </div>
                                        <div class="stat_value">
                                            683
                                            {{zhengqi.toFixed(2)}}
                                            <span class="font16">
                                                m³
                                        </span>
@@ -285,6 +285,7 @@
                            </div>
                        </div>
                    </div>
                    <div class="footer">
                        
                        <div >{{ nowDate }}</div> 
@@ -311,6 +312,13 @@
    const eqps = ref([] as dryEquipment[])
    const title = ref("兰浦智能干燥车间")
    const userStore = useUserStore()
    const envHum = ref();
    const envTemp = ref();
    const zhengqi = ref();
    const dianneng = ref()
    const steam = ref();
    const watt = ref()
    console.log(userStore.dictItems.title)
    userStore.dictItems.title?.forEach(element => {
        if (element.value === "bigscreentitle") {
@@ -343,18 +351,34 @@
    function updateRealTime() {
        //console.log(`output->定时刷新数据`)
        eqps.value.forEach((item) => {
            queryRealTime(item)
        zhengqi.value = 0;
        dianneng.value = 0
     new Promise(async function(resolve){
        await    eqps.value.forEach(async (item) => {
             await queryRealTime(item)
            })
            resolve()
            console.log("执行完成");
        }).then(()=>{
            console.log("then:::::" + dianneng.value,zhengqi.value );
            steam.value = zhengqi.value
            watt.value = dianneng.value
        })
    }
    function queryRealTime(eqp: dryEquipment) {
    async function queryRealTime(eqp: dryEquipment) {
        console.log("sdkfjlasjdflkajsldfjlsd")
        let tenantId = userStore.getTenant
        let eqpCode = eqp.code
        let queryRealTimeUrl = '/dry/real/getRealTimeData'
        defHttp.get({ url: queryRealTimeUrl, params: { tenantid: tenantId, machineid: eqpCode } }).then((res) => {
            //console.log(`output->res`, res)
     await    defHttp.get({ url: queryRealTimeUrl, params: { tenantid: tenantId, machineid: eqpCode } }).then( async (res) => {
            console.log(`output->res`, res)
            if (res && res.trendVo) {
                res.tempValue = [res.windTemp, 100]
                res.totalRemain = res.remain
@@ -367,6 +391,11 @@
                }
                
                res.percent = ((res.dryTime / (res.dryTime + res.totalRemain)) * 100).toFixed(2)
                envHum.value = res.envHum
                envTemp.value = res.envTemp
                zhengqi.value += res.steam
                dianneng.value += res.watt
                console.log("zhengqi:::" , res.steam);
            } else {
                res = {
                    tempValue: [0, 100],
@@ -627,7 +656,7 @@
    }
 
    .stat_value {
        font-size: 32px
        font-size: 28px
    }
    .herb_weight {
src/views/dry/components/DryOpcDeviceForm.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
<template>
    <div style="min-height: 400px">
        <BasicForm @register="registerForm"></BasicForm>
        <div style="width: 100%;text-align: center" v-if="!formDisabled">
            <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 äº¤</a-button>
        </div>
    </div>
</template>
<script lang="ts">
    import {BasicForm, useForm} from '/@/components/Form/index';
    import {computed, defineComponent} from 'vue';
    import {defHttp} from '/@/utils/http/axios';
    import { propTypes } from '/@/utils/propTypes';
    import {getBpmFormSchema} from '../dataDefine/DryOpcDevice.data';
    import {saveOrUpdate} from '../api/DryOpcDevice.api';
    export default defineComponent({
        name: "DryOpcDeviceForm",
        components:{
            BasicForm
        },
        props:{
            formData: propTypes.object.def({}),
            formBpm: propTypes.bool.def(true),
        },
        setup(props){
            const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
                labelWidth: 150,
                schemas: getBpmFormSchema(props.formData),
                showActionButtonGroup: false,
                baseColProps: {span: 24}
            });
            const formDisabled = computed(()=>{
                if(props.formData.disabled === false){
                    return false;
                }
                return true;
            });
            let formData = {};
            const queryByIdUrl = '/dry/dryOpcDevice/queryById';
            async function initFormData(){
                let params = {id: props.formData.dataId};
                const data = await defHttp.get({url: queryByIdUrl, params});
                formData = {...data}
                //设置表单的值
                await setFieldsValue(formData);
                //默认是禁用
                await setProps({disabled: formDisabled.value})
            }
            async function submitForm() {
                let data = getFieldsValue();
                let params = Object.assign({}, formData, data);
                console.log('表单数据', params)
                await saveOrUpdate(params, true)
            }
            initFormData();
            return {
                registerForm,
                formDisabled,
                submitForm,
            }
        }
    });
</script>
src/views/dry/components/DryOpcDeviceModal.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
<template>
  <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
      <BasicForm @register="registerForm"/>
  </BasicModal>
</template>
<script lang="ts" setup>
    import {ref, computed, unref} from 'vue';
    import {BasicModal, useModalInner} from '/@/components/Modal';
    import {BasicForm, useForm} from '/@/components/Form/index';
    import {formSchema} from '../dataDefine/DryOpcDevice.data';
    import {saveOrUpdate} from '../api/DryOpcDevice.api';
    // Emits声明
    const emit = defineEmits(['register','success']);
    const isUpdate = ref(true);
    //表单配置
    const [registerForm, {setProps,resetFields, setFieldsValue, validate}] = useForm({
        //labelWidth: 150,
        schemas: formSchema,
        showActionButtonGroup: false,
        baseColProps: {span: 24}
    });
    //表单赋值
    const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
        //重置表单
        await resetFields();
        setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
        isUpdate.value = !!data?.isUpdate;
        if (unref(isUpdate)) {
            //表单赋值
            await setFieldsValue({
                ...data.record,
            });
        }
        // éšè—åº•部时禁用整个表单
       setProps({ disabled: !data?.showFooter })
    });
    //设置标题
    const title = computed(() => (!unref(isUpdate) ? '新增' : '编辑'));
    //表单提交事件
    async function handleSubmit(v) {
        try {
            let values = await validate();
            setModalProps({confirmLoading: true});
            //提交表单
            await saveOrUpdate(values, isUpdate.value);
            //关闭弹窗
            closeModal();
            //刷新列表
            emit('success');
        } finally {
            setModalProps({confirmLoading: false});
        }
    }
</script>
<style lang="less" scoped>
    /** æ—¶é—´å’Œæ•°å­—输入框样式 */
  :deep(.ant-input-number){
        width: 100%
    }
    :deep(.ant-calendar-picker){
        width: 100%
    }
</style>
src/views/dry/dataDefine/DryOpcDevice.data.ts
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [
   {
    title: '名称',
    align:"center",
    dataIndex: 'name'
   },
   {
    title: '点位标识',
    align:"center",
    dataIndex: 'identifier'
   },
   {
    title: '排序',
    align:"center",
    dataIndex: 'sortOrder'
   },
   {
    title: '类型',
    align:"center",
    dataIndex: 'type_dictText'
   },
];
//查询数据
export const searchFormSchema: FormSchema[] = [
    {
      label: "名称",
      field: 'name',
      component: 'Input',
      colProps: {span: 6},
     },
    {
      label: "类型",
      field: 'type',
      component: 'JDictSelectTag',
      componentProps:{
          dictCode:"device_type"
      },
      colProps: {span: 6},
     },
];
//表单数据
export const formSchema: FormSchema[] = [
  {
    label: '名称',
    field: 'name',
    component: 'Input',
    dynamicRules: ({model,schema}) => {
          return [
                 { required: true, message: '请输入名称!'},
          ];
     },
  },
  {
    label: '点位标识',
    field: 'identifier',
    component: 'Input',
    dynamicRules: ({model,schema}) => {
          return [
                 { required: true, message: '请输入点位标识!'},
          ];
     },
  },
  {
    label: '排序',
    field: 'sortOrder',
    component: 'InputNumber',
    dynamicRules: ({model,schema}) => {
          return [
                 { required: true, message: '请输入排序!'},
          ];
     },
  },
  {
    label: '类型',
    field: 'type',
    component: 'JDictSelectTag',
    componentProps:{
        dictCode:"device_type"
     },
    dynamicRules: ({model,schema}) => {
          return [
                 { required: true, message: '请输入类型!'},
          ];
     },
  },
    // TODO ä¸»é”®éšè—å­—段,目前写死为ID
    {
      label: '',
      field: 'id',
      component: 'Input',
      show: false
    },
];
/**
* æµç¨‹è¡¨å•调用这个方法获取formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[]{
  // é»˜è®¤å’ŒåŽŸå§‹è¡¨å•ä¿æŒä¸€è‡´ å¦‚果流程中配置了权限数据,这里需要单独处理formSchema
  return formSchema;
}
src/views/dry/sql/DryOpcDevice_menu_insert.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
-- æ³¨æ„ï¼šè¯¥é¡µé¢å¯¹åº”的前台目录为views/dry文件夹下
-- å¦‚果你想更改到其他目录,请修改sql中component字段对应的值
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
VALUES ('2023112802425240080', NULL, 'dry_opc_device', '/dry/dryOpcDeviceList', 'dry/DryOpcDeviceList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0);
-- æƒé™æŽ§åˆ¶sql
-- æ–°å¢ž
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240081', '2023112802425240080', '添加dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);
-- ç¼–辑
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240082', '2023112802425240080', '编辑dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);
-- åˆ é™¤
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240083', '2023112802425240080', '删除dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);
-- æ‰¹é‡åˆ é™¤
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240084', '2023112802425240080', '批量删除dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);
-- å¯¼å‡ºexcel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240085', '2023112802425240080', '导出excel_dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);
-- å¯¼å…¥excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2023112802425240086', '2023112802425240080', '导入excel_dry_opc_device', NULL, NULL, 0, NULL, NULL, 2, 'dry:dry_opc_device:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2023-11-28 14:42:08', NULL, NULL, 0, 0, '1', 0);