Browse Source

添加banner,任务添加拖拽、样式调整

一只小菜杨 2 months ago
parent
commit
ff7e15bcd7

+ 1 - 0
components.d.ts

@@ -17,6 +17,7 @@ declare module 'vue' {
     VanCheckbox: typeof import('vant/es')['Checkbox']
     VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
     VanField: typeof import('vant/es')['Field']
+    VanFloatingBubble: typeof import('vant/es')['FloatingBubble']
     VanForm: typeof import('vant/es')['Form']
     VanIcon: typeof import('vant/es')['Icon']
     VanImage: typeof import('vant/es')['Image']

+ 1 - 1
index.html

@@ -8,7 +8,7 @@
       name="viewport"
       content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
     />
-    <title>PLC Editor</title>
+    <title>沃航PLC编程平台</title>
     <!-- <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> -->
     <script>
       const crtAgent = navigator.userAgent

+ 1 - 0
package.json

@@ -20,6 +20,7 @@
     "uuid": "^11.0.5",
     "vant": "^4.9.15",
     "vue": "^3.5.13",
+    "vue-draggable-next": "^2.2.1",
     "vue-router": "^4.5.0"
   },
   "devDependencies": {

+ 17 - 0
pnpm-lock.yaml

@@ -26,6 +26,9 @@ dependencies:
   vue:
     specifier: ^3.5.13
     version: 3.5.13(typescript@5.6.3)
+  vue-draggable-next:
+    specifier: ^2.2.1
+    version: 2.2.1(sortablejs@1.15.6)(vue@3.5.13)
   vue-router:
     specifier: ^4.5.0
     version: 4.5.0(vue@3.5.13)
@@ -3905,6 +3908,10 @@ packages:
     engines: {node: '>=14.16'}
     dev: false
 
+  /sortablejs@1.15.6:
+    resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==}
+    dev: false
+
   /source-map-js@1.2.1:
     resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
     engines: {node: '>=0.10.0'}
@@ -4386,6 +4393,16 @@ packages:
       vue: 3.5.13(typescript@5.6.3)
     dev: false
 
+  /vue-draggable-next@2.2.1(sortablejs@1.15.6)(vue@3.5.13):
+    resolution: {integrity: sha512-EAMS1IRHF0kZO0o5PMOinsQsXIqsrKT1hKmbICxG3UEtn7zLFkLxlAtajcCcUTisNvQ6TtCB5COjD9a1raNADw==}
+    peerDependencies:
+      sortablejs: ^1.14.0
+      vue: ^3.2.2
+    dependencies:
+      sortablejs: 1.15.6
+      vue: 3.5.13(typescript@5.6.3)
+    dev: false
+
   /vue-eslint-parser@10.1.1(eslint@9.22.0):
     resolution: {integrity: sha512-bh2Z/Au5slro9QJ3neFYLanZtb1jH+W2bKqGHXAoYD4vZgNG3KeotL7JpPv5xzY4UXUXJl7TrIsnzECH63kd3Q==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

BIN
src/assets/images/banner1.png


BIN
src/assets/images/banner2.png


BIN
src/assets/images/banner3.png


BIN
src/assets/images/banner4.png


BIN
src/assets/images/banner5.png


BIN
src/assets/images/banner6.png


+ 3 - 0
src/assets/main.css

@@ -24,3 +24,6 @@ body {
 .van-cell__label {
   font-size: 14px !important;
 }
+.van-button {
+  white-space: nowrap;
+}

+ 18 - 2
src/views/Automate.vue

@@ -117,11 +117,22 @@ const onSubmit = (values: Record<string, string>) => {
     showFailToast('工程名称已存在')
     return
   } else {
+    const id = Date.now() + ''
     projectList.value.unshift({
       name: values.newProjectName,
-      id: Date.now() + '',
+      id,
       time: new Date().toLocaleString(),
-      commandList: [],
+      commandList: [
+        {
+          id: Date.now() + '',
+          name: '任务1',
+          parentId: id,
+          stepList: [],
+          time: new Date().toLocaleString(),
+          x: 100,
+          y: 100
+        }
+      ],
       isCompiled: false,
       CompileTime: ''
     })
@@ -163,6 +174,11 @@ const importProject = () => {
       try {
         const newProject = JSON.parse(projectStr)
         if (newProject.qrcodeType !== 'worldflying_plc_editor') return showFailToast('扫描失败')
+        const isExist = projectList.value.some((item) => item.name === newProject.name)
+        if (isExist) {
+          showFailToast('工程名称已存在')
+          return
+        }
         newProject.id = Date.now() + ''
         newProject.time = new Date().toLocaleString()
         newProject.isCompiled = false

+ 243 - 111
src/views/CommandInfo.vue

@@ -1,127 +1,127 @@
 <template>
   <div class="command-info">
     <div class="header">
-      <div class="go-back" @click="router.push(`/project-info/${crtProject!.id}`)">
-        <van-button size="small"><van-icon name="arrow-left" />返回</van-button>
+      <div class="go-back">
+        <van-icon size="16" name="arrow-left" @click="router.push(`/project-info/${crtProject!.id}`)" />
+        <div class="command-name">{{ crtCommand?.name }}</div>
+        <van-icon size="16" name="edit" @click.stop="showEditPopup = true" />
       </div>
       <div class="btn-group">
-        <van-button size="small" type="primary" @click="handleAddCommand('condition')">增加条件</van-button>
-        <van-button size="small" type="primary" @click="handleAddCommand('delay')">增加延时</van-button>
-        <van-button size="small" type="primary" @click="handleAddCommand('exec')">增加执行</van-button>
-        <van-button size="small" type="danger" @click="handleAddCommand('del')">删除任务</van-button>
         <van-icon name="question-o" size="24" @click="showHelpDoc = true" />
       </div>
     </div>
-
     <div class="container">
       <div class="card-group" v-if="crtCommand!.stepList.length">
-        <div class="card" v-for="(item, index) in crtCommand!.stepList">
-          <div class="card-header">
-            <div class="step">
-              第
-              <div class="num">{{ index + 1 }}</div>
-              步
+        <VueDraggableNext :list="crtCommand!.stepList" animation="250" :delay="500">
+          <div class="card" v-for="(item, index) in crtCommand!.stepList">
+            <div class="card-header">
+              <div class="step">
+                第
+                <div class="num">{{ index + 1 }}</div>
+                步
+              </div>
+              <div class="label">{{ textMap[item.type] }}</div>
             </div>
-            <div class="label">{{ textMap[item.type] }}</div>
-          </div>
-          <template v-if="item.type === 'condition' || item.type === 'exec'">
-            <div class="item" v-for="(cItem, cIndex) in item.list">
-              <div class="tip" v-show="cIndex !== 0 && item.type !== 'exec'">或</div>
-              <div class="wrap">
-                <div class="content">
-                  <template v-if="cItem.type !== 'time'">
-                    <van-field
-                      v-model="cItem.label"
-                      is-link
-                      readonly
-                      placeholder="请选择"
-                      style="width: 30%"
-                      @click="
-                        showSelect(
-                          cItem.label.includes('DI') || cItem.label.includes('AI') ? 'inputLabel' : 'outputLabel',
-                          index,
-                          cIndex,
-                          'label'
-                        )
-                      "
-                    />
-                    <template v-if="cItem.label.includes('DI') || cItem.label.includes('DO')">
+            <template v-if="item.type === 'condition' || item.type === 'exec'">
+              <div class="item" v-for="(cItem, cIndex) in item.list">
+                <div class="tip" v-show="cIndex !== 0 && item.type !== 'exec'">或</div>
+                <div class="wrap">
+                  <div class="content">
+                    <template v-if="cItem.type !== 'time'">
                       <van-field
-                        v-model="cItem.value"
+                        v-model="cItem.label"
                         is-link
                         readonly
                         placeholder="请选择"
-                        style="width: 70%"
-                        @click="showSelect('commandValue', index, cIndex, 'value')"
+                        style="width: 30%"
+                        @click="
+                          showSelect(
+                            cItem.label.includes('DI') || cItem.label.includes('AI') ? 'inputLabel' : 'outputLabel',
+                            index,
+                            cIndex,
+                            'label'
+                          )
+                        "
                       />
+                      <template v-if="cItem.label.includes('DI') || cItem.label.includes('DO')">
+                        <van-field
+                          v-model="cItem.value"
+                          is-link
+                          readonly
+                          placeholder="请选择"
+                          style="width: 70%"
+                          @click="showSelect('commandValue', index, cIndex, 'value')"
+                        />
+                      </template>
+                      <template v-if="cItem.label.includes('AI') || cItem.label.includes('AO')">
+                        <van-field
+                          v-if="item.type === 'condition'"
+                          v-model="cItem.operation"
+                          is-link
+                          readonly
+                          placeholder="请选择"
+                          style="width: 35%"
+                          @click="showSelect('operation', index, cIndex, 'operation')"
+                        />
+                        <div class="operation" v-if="item.type === 'exec'">=</div>
+                        <van-field
+                          v-model="cItem.value"
+                          placeholder="请输入"
+                          :number="2.2"
+                          type="number"
+                          :style="{ width: item.type === 'exec' ? '60%' : '35%' }"
+                        />
+                      </template>
                     </template>
-                    <template v-if="cItem.label.includes('AI') || cItem.label.includes('AO')">
+                    <template v-else>
                       <van-field
-                        v-if="item.type === 'condition'"
-                        v-model="cItem.operation"
+                        v-model="cItem.label"
                         is-link
                         readonly
                         placeholder="请选择"
-                        style="width: 35%"
-                        @click="showSelect('operation', index, cIndex, 'operation')"
+                        style="width: 60%"
+                        @click="showSelect('timeLabel', index, cIndex, 'label')"
                       />
-                      <div class="operation" v-if="item.type === 'exec'">=</div>
                       <van-field
                         v-model="cItem.value"
-                        placeholder="请输入"
-                        type="number"
-                        :style="{ width: item.type === 'exec' ? '60%' : '35%' }"
+                        is-link
+                        readonly
+                        placeholder="请选择"
+                        style="width: 40%"
+                        @click="showSelect('timeValue', index, cIndex, 'value')"
                       />
                     </template>
-                  </template>
-                  <template v-else>
-                    <van-field
-                      v-model="cItem.label"
-                      is-link
-                      readonly
-                      placeholder="请选择"
-                      style="width: 60%"
-                      @click="showSelect('timeLabel', index, cIndex, 'label')"
-                    />
-                    <van-field
-                      v-model="cItem.value"
-                      is-link
-                      readonly
-                      placeholder="请选择"
-                      style="width: 40%"
-                      @click="showSelect('timeValue', index, cIndex, 'value')"
-                    />
-                  </template>
-                </div>
-                <div class="del-icon">
-                  <van-icon name="cross" @click="handleEditCommand('delChild', index, cIndex)" />
+                  </div>
+                  <div class="del-icon" v-show="item.list?.length !== 1">
+                    <van-icon name="cross" @click="handleEditCommand('delChild', index, cIndex)" />
+                  </div>
                 </div>
               </div>
-            </div>
-          </template>
+            </template>
 
-          <template v-else-if="item.type === 'delay'">
-            <div class="delay-content">延时<van-stepper v-model="item.value" />秒</div>
-          </template>
-          <div class="btn-group">
-            <template v-if="item.type === 'condition'">
-              <van-button size="small" type="primary" @click="handleEditCommand('condition', index)"
-                >增加条件</van-button
-              >
-              <van-button size="small" type="primary" @click="handleEditCommand('delay', index)">增加定时</van-button>
+            <template v-else-if="item.type === 'delay'">
+              <div class="delay-content">延时<van-stepper v-model="item.value" />秒</div>
             </template>
-            <van-button
-              v-if="item.type === 'exec'"
-              size="small"
-              type="primary"
-              @click="handleEditCommand('exec', index)"
-              >添加执行</van-button
-            >
-            <van-button size="small" type="danger" @click="handleEditCommand('del', index)"
-              >&nbsp;删除&nbsp;</van-button
-            >
+            <div class="btn-group">
+              <template v-if="item.type === 'condition'">
+                <van-button size="small" type="primary" @click="handleEditCommand('condition', index)"
+                  >增加条件</van-button
+                >
+                <van-button size="small" type="primary" @click="handleEditCommand('delay', index)">增加定时</van-button>
+              </template>
+              <van-button
+                v-if="item.type === 'exec'"
+                size="small"
+                type="primary"
+                @click="handleEditCommand('exec', index)"
+                >添加执行</van-button
+              >
+              <van-button size="small" type="danger" @click="handleEditCommand('del', index)"
+                >&nbsp;删除&nbsp;</van-button
+              >
+            </div>
           </div>
-        </div>
+        </VueDraggableNext>
       </div>
       <div class="empty" v-else>当前没有可执行任务</div>
       <div class="footer" v-if="crtCommand!.stepList.length || isExistCnt">
@@ -132,6 +132,15 @@
       </div>
     </div>
 
+    <div class="handle-group" v-show="showBtnGroup">
+      <van-button size="small" type="primary" @click="handleAddCommand('condition')">增加条件</van-button>
+      <van-button size="small" type="primary" @click="handleAddCommand('delay')">增加延时</van-button>
+      <van-button size="small" type="primary" @click="handleAddCommand('exec')">增加执行</van-button>
+      <van-button size="small" type="danger" @click="handleAddCommand('del')">删除任务</van-button>
+      <div class="pack" @click="showBtnGroup = false"><van-icon name="arrow" /></div>
+    </div>
+    <div class="unfold" @click="showBtnGroup = true" v-show="!showBtnGroup"><van-icon name="plus" /></div>
+
     <van-popup v-model:show="showOptionsPicker" destroy-on-close round position="bottom">
       <van-picker
         v-if="pickerType !== 'timeValue'"
@@ -159,6 +168,26 @@
         &nbsp;&nbsp;&nbsp;&nbsp;注意:条件和延时下面需添加执行才能完成自动化控制。整个运行逻辑为从上往下顺序执行。
       </div>
     </van-popup>
+
+    <van-popup v-model:show="showEditPopup" round :style="{ width: '80%' }">
+      <div class="label" style="margin: 16px">编辑任务名称</div>
+      <van-form @submit="onSubmit">
+        <van-cell-group inset>
+          <van-field
+            border
+            v-model="newCommandName"
+            name="newCommandName"
+            placeholder="任务名称"
+            maxlength="20"
+            :rules="[{ required: true, message: '请填写任务名称' }]"
+          />
+        </van-cell-group>
+        <div class="van-button-group">
+          <van-button @click="handleCancel" size="small">取消</van-button>
+          <van-button type="primary" native-type="submit" size="small">确定</van-button>
+        </div>
+      </van-form>
+    </van-popup>
   </div>
 </template>
 
@@ -169,6 +198,7 @@ import { deepClone, deepEqual } from '@/utils/tools'
 import { storeToRefs } from 'pinia'
 import { v4 as uuid4 } from 'uuid'
 import { showConfirmDialog } from 'vant'
+import { VueDraggableNext } from 'vue-draggable-next'
 
 const router = useRouter()
 const route = useRoute()
@@ -188,6 +218,8 @@ watch(
   () => route,
   () => {
     crtProject.value = projectList.value.find((item) => item.id === route.params.pid)
+    console.log(route, projectList.value)
+
     if (crtProject.value) {
       const command = crtProject.value.commandList!.find((item) => item.id === route.params.id)!
       if (command.stepList.length) {
@@ -204,7 +236,7 @@ watch(
   }
 )
 const showOptionsPicker = ref(false)
-const pickerValue = ref([])
+const pickerValue = ref<string[]>([])
 const currentTime = ref(['00', '00'])
 const pickerType = ref('')
 const columnsMap: Record<string, any> = {
@@ -283,15 +315,19 @@ const showSelect = (colType: string, stepIndex: number, index: number, key: keyo
 
 const showCalendar = ref(false)
 const onConfirm = () => {
-  console.log(pickerValue.value)
+  console.log(pickerValue.value, pickerType.value)
   if (crtCommand.value) {
     if (pickerType.value !== 'timeValue') {
       if (pickerValue.value.length && pickerValue.value[0] === '自定义') {
         showCalendar.value = true
       } else {
         crtCommand.value.stepList[changeInfo.stepIndex].list![changeInfo.index][changeInfo.key] = pickerValue.value[0]
-        if (pickerType.value.includes('AI') || pickerType.value.includes('DI')) {
-          crtCommand.value.stepList[changeInfo.stepIndex].list![changeInfo.index].value = ''
+
+        if (pickerValue.value[0].includes('AI')) {
+          crtCommand.value.stepList[changeInfo.stepIndex].list![changeInfo.index].value = '0'
+          crtCommand.value.stepList[changeInfo.stepIndex].list![changeInfo.index].operation = '='
+        } else if (pickerValue.value[0].includes('DI')) {
+          crtCommand.value.stepList[changeInfo.stepIndex].list![changeInfo.index].value = '闭合'
         }
       }
     } else {
@@ -328,8 +364,8 @@ const handleEditCommand = (type: string, index: number, cIndex?: number) => {
       crtCommand.value.stepList[index].list!.push({
         id: '' + Date.now(),
         type: 'input',
-        label: 'DI1',
-        value: '',
+        label: 'DI0',
+        value: '闭合',
         x,
         y
       })
@@ -338,8 +374,8 @@ const handleEditCommand = (type: string, index: number, cIndex?: number) => {
       crtCommand.value.stepList[index].list!.push({
         id: '' + Date.now(),
         type: 'output',
-        label: 'DO1',
-        value: '',
+        label: 'DO0',
+        value: '打开',
         x,
         y
       })
@@ -350,7 +386,7 @@ const handleEditCommand = (type: string, index: number, cIndex?: number) => {
         type: 'time',
         label: '每天',
         operation: '=',
-        value: '',
+        value: '08:00',
         x,
         y
       })
@@ -374,6 +410,7 @@ const handleAddCommand = (type: string) => {
       })
         .then(() => {
           // on confirm
+          if (crtProject.value!.commandList.length === 1) return showFailToast('请至少保留一个任务!')
           console.log(crtProject.value, crtCommand.value)
           crtProject.value!.commandList = crtProject.value!.commandList.filter(
             (item) => crtCommand.value?.id !== item.id
@@ -392,21 +429,31 @@ const handleAddCommand = (type: string) => {
   console.log(endCMD, x)
 
   const y = 100 + crtProject.value!.commandList.length * 2000
+  const id = '' + Date.now()
   switch (type) {
     case 'condition':
       crtCommand.value.stepList.push({
-        id: '' + Date.now(),
+        id,
         type: 'condition',
-        list: [],
+        list: [
+          {
+            id,
+            label: 'DI0',
+            value: '闭合',
+            type: 'input',
+            x,
+            y: y + 100
+          }
+        ],
         x: isExistTime ? (endCMD.x || 0) + 3000 : x + 400,
         y
       })
       break
     case 'delay':
       crtCommand.value.stepList.push({
-        id: '' + Date.now(),
+        id,
         type: 'delay',
-        value: '1000',
+        value: '5',
         unit: 's',
         x: isExistTime ? (endCMD.x || 0) + 3000 : x + 400,
         y
@@ -414,9 +461,18 @@ const handleAddCommand = (type: string) => {
       break
     case 'exec':
       crtCommand.value.stepList.push({
-        id: '' + Date.now(),
+        id,
         type: 'exec',
-        list: [],
+        list: [
+          {
+            id,
+            label: 'DO0',
+            value: '打开',
+            type: 'output',
+            x,
+            y: y + 100
+          }
+        ],
         x: isExistTime ? (endCMD.x || 0) + 3000 : x + 400,
         y
       })
@@ -468,20 +524,51 @@ const handleSaveCommand = () => {
 }
 
 const showHelpDoc = ref(false)
+
+const showEditPopup = ref(false)
+const newCommandName = ref('')
+const onSubmit = (values: Record<string, string>) => {
+  console.log(values)
+  const command = crtProject.value!.commandList!.find((item) => item.id === route.params.id)!
+  console.log(crtProject.value?.commandList)
+  const isExist = crtProject.value?.commandList.some(
+    (item) => item.name === values.newCommandName && item.id !== command.id
+  )
+  if (isExist) {
+    showFailToast('任务名称已存在')
+    return
+  } else {
+    command!.name = values.newCommandName
+    crtCommand.value!.name = values.newCommandName
+    handleCancel()
+  }
+}
+const handleCancel = async () => {
+  showEditPopup.value = false
+  await nextTick()
+  newCommandName.value = ''
+}
+const showBtnGroup = ref(true)
 </script>
 
 <style scoped>
 .command-info {
-  padding: 16px 0;
   height: 100%;
   overflow: hidden;
+  user-select: none;
 }
 .header {
-  padding: 0 16px;
+  padding: 16px;
   margin-bottom: 16px;
   display: flex;
   align-items: center;
   justify-content: space-between;
+  background-color: #fff;
+}
+.go-back {
+  display: flex;
+  align-items: center;
+  gap: 8px;
 }
 .btn-group {
   display: flex;
@@ -504,7 +591,7 @@ const showHelpDoc = ref(false)
 }
 .container {
   padding: 0 16px;
-  height: calc(100% - 34px);
+  height: calc(100% - 86px);
   overflow: auto;
 }
 .step {
@@ -589,4 +676,49 @@ const showHelpDoc = ref(false)
   padding: 0 24px 24px;
   line-height: 2;
 }
+.van-button-group {
+  margin: 16px;
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+}
+.handle-group {
+  position: fixed;
+  bottom: 64px;
+  right: 16px;
+  padding: 16px;
+  background-color: rgba(255, 255, 255, 0.7);
+  border-radius: 4px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 8px;
+  max-width: 90%;
+  transition: all 0.3s;
+}
+.unfold {
+  position: fixed;
+  bottom: 72px;
+  right: 16px;
+  background-color: rgba(255, 255, 255, 0.7);
+  border-radius: 4px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 8px;
+  width: 48px;
+  height: 48px;
+  font-size: 28px;
+  transition: all 0.3s;
+}
+.pack {
+  display: flex;
+  width: 28px;
+  justify-content: center;
+  align-items: center;
+  border-radius: 2px;
+}
+.pack:active {
+  background: #efefef;
+}
 </style>

+ 7 - 6
src/views/Home.vue

@@ -2,7 +2,7 @@
   <div class="home-container">
     <div class="banner">
       <van-swipe :autoplay="3000" indicator-color="white" lazy-render>
-        <van-swipe-item v-for="item in 5">
+        <van-swipe-item v-for="item in 6">
           <a :href="linkMap[item]">
             <van-image :src="requireImgUrl(`banner${item}.png`)" />
           </a>
@@ -35,11 +35,12 @@ const { projectList } = storeToRefs(useGlobalStore())
 const router = useRouter()
 
 const linkMap: { [key: number]: string } = {
-  1: 'https://e.tb.cn/h.6VgnJ1QQUp37BrJ?tk=BDcWewvqUA7',
-  2: 'https://e.tb.cn/h.6VcArZdxgPHriyr?tk=a9mxewvMLrl',
-  3: 'https://e.tb.cn/h.6VX0y8D438gQPYE?tk=PAheewvJPC1',
-  4: 'https://e.tb.cn/h.6VXbDHptv8jInR5?tk=0ic4ewvpj2S',
-  5: 'https://e.tb.cn/h.6VcA06XElNLbZbS?tk=8ayFewvNPrr'
+  1: 'https://item.taobao.com/item.htm?id=846463487388',
+  2: 'https://item.taobao.com/item.htm?id=816872336305',
+  3: 'https://item.taobao.com/item.htm?id=874093615168',
+  4: 'https://item.taobao.com/item.htm?id=893370676910',
+  5: 'https://item.taobao.com/item.htm?id=898547563519',
+  6: 'https://item.taobao.com/item.htm?id=834170367404'
 }
 </script>
 

+ 75 - 39
src/views/ProjectInfo.vue

@@ -15,16 +15,13 @@
     </div>
 
     <div class="command-container">
-      <van-swipe-cell v-for="item in crtProject!.commandList" :key="item.id" :disabled="touchStatus">
+      <van-swipe-cell v-for="item in crtProject!.commandList" :key="item.id">
         <van-cell
           clickable
           :border="false"
           :title="item.name"
           :label="item.time"
-          @click="!touchStatus && router.push(`/command-info/${crtProject!.id}/${item.id}`)"
-          @touchstart="handleTouchStart"
-          @touchmove="handleTouchMove"
-          @touchend="handleTouchEnd"
+          @click="router.push(`/command-info/${crtProject!.id}/${item.id}`)"
         >
         </van-cell>
         <template #right>
@@ -159,26 +156,6 @@ watch(
   }
 )
 
-let touchTimer: any
-const touchStatus = ref(false)
-const handleTouchStart = () => {
-  touchTimer = setTimeout(() => {
-    touchStatus.value = true
-  }, 700)
-}
-const handleTouchMove = () => {
-  if (touchTimer) {
-    clearTimeout(touchTimer)
-    touchTimer = 0
-  }
-}
-const handleTouchEnd = () => {
-  if (touchTimer) {
-    clearTimeout(touchTimer)
-    touchTimer = 0
-  }
-}
-
 const showMorePopup = ref(false)
 const handleEditName = () => {
   showEditPopup.value = true
@@ -190,7 +167,7 @@ const showEditPopup = ref(false)
 const newProjectName = ref('')
 const onSubmit = (values: Record<string, string>) => {
   console.log(values)
-  const isExist = projectList.value.some((item) => item.name === values.newProjectName)
+  const isExist = projectList.value.some((item) => item.name === values.newProjectName && item.id !== crtProject.value?.id)
   if (isExist) {
     showFailToast('工程名称已存在')
     return
@@ -209,6 +186,7 @@ const handleDelete = (val: string) => {
   if (val) {
     const project = projectList.value.find((item) => (item.id = crtProject.value!.id))
     if (project) {
+      if (project.commandList.length === 1) return showFailToast('请至少保留一个任务!')
       project.commandList = project.commandList.filter((item) => item.id !== val)
       if (crtProject.value?.isCompiled) {
         crtProject.value.isCompiled = false
@@ -330,7 +308,7 @@ const getXMLFile = () => {
   let globalVarsXml = ''
   globalVarList.forEach((item) => {
     globalVarsXml += `<variable name="${item}">
-                        <type><${item.includes('A') ? 'INT' : 'BOOL'} /></type>
+                        <type><${item.includes('A') ? 'UINT' : 'BOOL'} /></type>
                       </variable>`
   })
   crtProject.value.commandList.forEach((item, index) => {
@@ -454,17 +432,17 @@ const getXMLFile = () => {
           } else if (cItem.type === 'input' && cItem.label.includes('AI')) {
             // 判断AI变量
             xml += `<variable name="COMPARE_AI${index}_${cIndex}">
-                      <type><INT /></type>
+                      <type><UINT /></type>
                       <initialValue>
-                        <simpleValue value="${cItem.value}" />
+                        <simpleValue value="${+cItem.value * 100}" />
                       </initialValue>
                     </variable>`
           } else if (cItem.type === 'output' && cItem.label.includes('AO')) {
             // 赋值AO变量
             xml += `<variable name="SET_AI${index}_${cIndex}">
-                      <type><INT /></type>
+                      <type><UINT /></type>
                       <initialValue>
-                        <simpleValue value="${cItem.value}" />
+                        <simpleValue value="${+cItem.value * 100}" />
                       </initialValue>
                     </variable>`
           }
@@ -1519,35 +1497,93 @@ const delProject = () => {
 
 const showSharePopup = ref(false)
 const shareImg = ref('')
-// const qrcodeCanvas = ref<HTMLCanvasElement>()
+const qrcodeCanvas = ref<HTMLCanvasElement>()
 const shareProject = async () => {
   if (!window.isInWx) return
   showSharePopup.value = true
   await nextTick()
+  // const canvasEl = document.createElement('canvas')
+  // const ratio = window.devicePixelRatio
+  // const windowWidth = window.innerWidth
+  // const windowHeight = window.innerHeight
+  // const minWindowSize = Math.floor(Math.min(windowWidth, windowHeight) * 0.7)
+  // canvasEl!.style.width = `${minWindowSize}px`
+  // canvasEl!.style.height = `${minWindowSize}px`
+  // canvasEl!.width = minWindowSize * ratio
+  // canvasEl!.height = minWindowSize * ratio
+  // qrcode.toCanvas(canvasEl!, JSON.stringify({ ...crtProject.value, qrcodeType: 'worldflying_plc_editor' }), {
+  //   width: minWindowSize * 2
+  // })
+  // const canvasEl1 = document.createElement('canvas')
+  // canvasEl1.style.width = `${minWindowSize}px`
+  // canvasEl1.style.height = `${minWindowSize + 50}px`
+  // canvasEl1.width = minWindowSize * ratio
+  // canvasEl1.height = (minWindowSize + 50) * ratio
+  // const ctx = canvasEl1?.getContext('2d')
+  // if (ctx) {
+  //   ctx.imageSmoothingEnabled = false
+  //   const fontSize = 16 * ratio
+  //   ctx.font = `${fontSize}px Arial`
+  //   ctx.fillText(`工程名称:${crtProject.value?.name}`, 20 * ratio, 35 * ratio)
+  //   ctx.drawImage(canvasEl, 0, 50 * ratio, minWindowSize * ratio, minWindowSize * ratio)
+  //   // shareImg.value = canvasEl1.toDataURL('image/png', 1)
+  //   canvasEl1.toBlob((blob) => {
+  //     if (!blob) return
+  //     shareImg.value = URL.createObjectURL(blob)
+  //   }, 'image/png', 1)
+  // }
   qrcode.toDataURL(
     JSON.stringify({ ...crtProject.value, qrcodeType: 'worldflying_plc_editor' }),
     function (error, url) {
       if (error) console.error(error)
       // console.log(url)
-      const ratio = window.devicePixelRatio
+      const ratio = devicePixelRatio
       const canvasEl = document.createElement('canvas')
       const windowWidth = window.innerWidth
       const windowHeight = window.innerHeight
       const minWindowSize = Math.floor(Math.min(windowWidth, windowHeight) * 0.7)
       canvasEl.style.width = `${minWindowSize}px`
-      canvasEl.style.height = `${minWindowSize + 50}px`
+      canvasEl.style.height = `${minWindowSize + 120}px`
       canvasEl.width = minWindowSize * ratio
-      canvasEl.height = (minWindowSize + 50) * ratio
+      canvasEl.height = (minWindowSize + 120) * ratio
       const ctx = canvasEl?.getContext('2d')
       if (ctx) {
-        const fontSize = 16 * ratio
+        const fontSize = 12 * ratio
         ctx.font = `${fontSize}px Arial`
-        ctx.fillText(`工程名称:${crtProject.value?.name}`, 20 * ratio, 35 * ratio)
+        ctx.fillText(`工程名称:${crtProject.value?.name}`, 5 * ratio, 35 * ratio)
         const img = new Image()
         img.src = url
         img.onload = function () {
-          ctx.drawImage(img, 0, 50 * ratio, minWindowSize * ratio, minWindowSize * ratio)
-          shareImg.value = canvasEl.toDataURL('image/png')
+          ctx.imageSmoothingEnabled = false
+          ctx.drawImage(img, 0, 40 * ratio, minWindowSize * ratio, minWindowSize * ratio)
+          const tipText = '请打开沃航PLC手机编辑器,通过编辑页面导入该图片'
+          const arr = []
+          for (let i = 1; i <= tipText.length; i++) {
+            const tipMsg = ctx.measureText(tipText.slice(0, i))
+            if (Math.ceil(tipMsg.width / ratio) > minWindowSize) {
+              arr.push(tipText.slice(0, i - 1))
+              arr.push(tipText.slice(i - 1))
+              break
+            }
+            if (i === tipText.length) {
+              arr.push(tipText)
+            }
+          }
+          arr.forEach((item, index) => {
+            ctx.fillText(item, 5 * ratio, (minWindowSize + 55 + index * 15) * ratio)
+          })
+
+          const companyText = '沃航科技有限公司'
+          const companyMsg = ctx.measureText(companyText)
+          ctx.fillText(companyText, (minWindowSize - 10) * ratio - companyMsg.width, (minWindowSize + 100) * ratio)
+          shareImg.value = canvasEl.toDataURL('image/png', 1)
+          // canvasEl.toBlob(
+          //   (blob) => {
+          //     if (!blob) return
+          //     shareImg.value = URL.createObjectURL(blob)
+          //   },
+          //   'image/png',
+          // )
         }
       }
       canvasEl.remove()