Jelajahi Sumber

任务指令

Hwf 11 bulan lalu
induk
melakukan
e9f059b1c4

+ 0 - 8
.vscode/extensions.json

@@ -1,8 +0,0 @@
-{
-  "recommendations": [
-    "Vue.volar",
-    "bradlc.vscode-tailwindcss",
-    "mikestead.dotenv",
-    "antfu.iconify"
-  ]
-}

+ 0 - 14
.vscode/settings.json

@@ -1,14 +0,0 @@
-{
-  "editor.formatOnSave": true,
-  "eslint.format.enable": true,
-  "[vue]": {
-    "editor.defaultFormatter": "esbenp.prettier-vscode"
-  },
-  "[css]": {
-    "editor.defaultFormatter": "esbenp.prettier-vscode"
-  },
-  "editor.codeActionsOnSave": {
-    "source.fixAll.eslint": "explicit"
-  },
-  "typescript.tsdk": "node_modules/typescript/lib"
-}

+ 0 - 18
.vscode/vue3.2.code-snippets

@@ -1,18 +0,0 @@
-{
-  "Vue3.2+快速生成模板": {
-    "prefix": ["<", "Vue3.2+"],
-    "body": [
-      "<script setup lang='ts'>",
-      "$1",
-      "</script>\n",
-      "<template>",
-      "\t<div>",
-      "\t\t$2",
-      "\t</div>",
-      "</template>\n",
-      "<style scoped>\n",
-      "</style>",
-    ],
-    "description": "Vue3.2+"
-  }
-}

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "await-to-js": "^3.0.0",
     "axios": "^1.6.8",
     "crypto-js": "^4.2.0",
+    "element-plus": "^2.8.5",
     "jsencrypt": "^3.3.2",
     "nanoid": "^5.0.7",
     "normalize.css": "^8.0.1",

+ 157 - 0
pnpm-lock.yaml

@@ -23,6 +23,9 @@ importers:
       crypto-js:
         specifier: ^4.2.0
         version: 4.2.0
+      element-plus:
+        specifier: ^2.8.5
+        version: 2.8.5(vue@3.5.11(typescript@5.4.5))
       jsencrypt:
         specifier: ^3.3.2
         version: 3.3.2
@@ -390,6 +393,15 @@ packages:
     resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==}
     engines: {node: '>=v18'}
 
+  '@ctrl/tinycolor@3.6.1':
+    resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
+    engines: {node: '>=10'}
+
+  '@element-plus/icons-vue@2.3.1':
+    resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
+    peerDependencies:
+      vue: ^3.2.0
+
   '@esbuild/aix-ppc64@0.21.5':
     resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
     engines: {node: '>=12'}
@@ -562,6 +574,15 @@ packages:
     resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
+  '@floating-ui/core@1.6.8':
+    resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
+
+  '@floating-ui/dom@1.6.11':
+    resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==}
+
+  '@floating-ui/utils@0.2.8':
+    resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
+
   '@hapi/bourne@3.0.0':
     resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
 
@@ -752,6 +773,9 @@ packages:
   '@rushstack/eslint-patch@1.10.4':
     resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==}
 
+  '@sxzz/popperjs-es@2.11.7':
+    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
+
   '@trysound/sax@0.2.0':
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
@@ -765,6 +789,12 @@ packages:
   '@types/json-schema@7.0.15':
     resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
 
+  '@types/lodash-es@4.17.12':
+    resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
+
+  '@types/lodash@4.17.10':
+    resolution: {integrity: sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==}
+
   '@types/minimist@1.2.5':
     resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
 
@@ -783,6 +813,9 @@ packages:
   '@types/svgo@2.6.4':
     resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==}
 
+  '@types/web-bluetooth@0.0.16':
+    resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
+
   '@types/web-bluetooth@0.0.20':
     resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
 
@@ -951,12 +984,21 @@ packages:
   '@vueuse/core@11.1.0':
     resolution: {integrity: sha512-P6dk79QYA6sKQnghrUz/1tHi0n9mrb/iO1WTMk/ElLmTyNqgDeSZ3wcDf6fRBGzRJbeG1dxzEOvLENMjr+E3fg==}
 
+  '@vueuse/core@9.13.0':
+    resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
+
   '@vueuse/metadata@11.1.0':
     resolution: {integrity: sha512-l9Q502TBTaPYGanl1G+hPgd3QX5s4CGnpXriVBR5fEZ/goI6fvDaVmIl3Td8oKFurOxTmbXvBPSsgrd6eu6HYg==}
 
+  '@vueuse/metadata@9.13.0':
+    resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
+
   '@vueuse/shared@11.1.0':
     resolution: {integrity: sha512-YUtIpY122q7osj+zsNMFAfMTubGz0sn5QzE5gPzAIiCmtt2ha3uQUY1+JPyL4gRCTsLPX82Y9brNbo/aqlA91w==}
 
+  '@vueuse/shared@9.13.0':
+    resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
+
   '@xn-sakina/rml-darwin-arm64@2.5.0':
     resolution: {integrity: sha512-JC1ODK8KdsrtadS/efvIXVAknllyLjt7Aov67TymUwD9sqZMQUivziPmsKrdh8FO2a7vxSZoSzuokWVEN0xgdw==}
     engines: {node: '>=14'}
@@ -1120,6 +1162,9 @@ packages:
     resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
     engines: {node: '>=0.10.0'}
 
+  async-validator@4.2.5:
+    resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+
   async@3.2.6:
     resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
 
@@ -1542,6 +1587,9 @@ packages:
   dateformat@3.0.3:
     resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
 
+  dayjs@1.11.13:
+    resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
+
   de-indent@1.0.2:
     resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
 
@@ -1684,6 +1732,11 @@ packages:
   electron-to-chromium@1.5.35:
     resolution: {integrity: sha512-hOSRInrIDm0Brzp4IHW2F/VM+638qOL2CzE0DgpnGzKW27C95IqqeqgKz/hxHGnvPxvQGpHUGD5qRVC9EZY2+A==}
 
+  element-plus@2.8.5:
+    resolution: {integrity: sha512-Px+kPbRTVvn5oa5+9saa7QEOnUweKXm0JVI7yJHzKF/doQGixwcFMsQEF2+3Fy62EA/7dRRKVuhsNGGZYNk3cw==}
+    peerDependencies:
+      vue: ^3.2.0
+
   emoji-regex@8.0.0:
     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
 
@@ -1748,6 +1801,9 @@ packages:
     resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
     engines: {node: '>=6'}
 
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
   escape-string-regexp@1.0.5:
     resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
     engines: {node: '>=0.8.0'}
@@ -2537,6 +2593,16 @@ packages:
     resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
+  lodash-es@4.17.21:
+    resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
+  lodash-unified@1.0.3:
+    resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
+    peerDependencies:
+      '@types/lodash-es': '*'
+      lodash: '*'
+      lodash-es: '*'
+
   lodash.camelcase@4.3.0:
     resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
 
@@ -2616,6 +2682,9 @@ packages:
     resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
     engines: {node: '>= 0.6'}
 
+  memoize-one@6.0.0:
+    resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
+
   memorystream@0.3.1:
     resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==}
     engines: {node: '>= 0.10.0'}
@@ -2765,6 +2834,9 @@ packages:
     resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
     engines: {node: '>=0.10.0'}
 
+  normalize-wheel-es@1.2.0:
+    resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
+
   normalize.css@8.0.1:
     resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==}
 
@@ -4264,6 +4336,12 @@ snapshots:
       '@types/conventional-commits-parser': 5.0.0
       chalk: 5.3.0
 
+  '@ctrl/tinycolor@3.6.1': {}
+
+  '@element-plus/icons-vue@2.3.1(vue@3.5.11(typescript@5.4.5))':
+    dependencies:
+      vue: 3.5.11(typescript@5.4.5)
+
   '@esbuild/aix-ppc64@0.21.5':
     optional: true
 
@@ -4372,6 +4450,17 @@ snapshots:
     dependencies:
       levn: 0.4.1
 
+  '@floating-ui/core@1.6.8':
+    dependencies:
+      '@floating-ui/utils': 0.2.8
+
+  '@floating-ui/dom@1.6.11':
+    dependencies:
+      '@floating-ui/core': 1.6.8
+      '@floating-ui/utils': 0.2.8
+
+  '@floating-ui/utils@0.2.8': {}
+
   '@hapi/bourne@3.0.0': {}
 
   '@humanfs/core@0.19.0': {}
@@ -4513,6 +4602,8 @@ snapshots:
 
   '@rushstack/eslint-patch@1.10.4': {}
 
+  '@sxzz/popperjs-es@2.11.7': {}
+
   '@trysound/sax@0.2.0': {}
 
   '@types/conventional-commits-parser@5.0.0':
@@ -4523,6 +4614,12 @@ snapshots:
 
   '@types/json-schema@7.0.15': {}
 
+  '@types/lodash-es@4.17.12':
+    dependencies:
+      '@types/lodash': 4.17.10
+
+  '@types/lodash@4.17.10': {}
+
   '@types/minimist@1.2.5': {}
 
   '@types/node@20.16.11':
@@ -4539,6 +4636,8 @@ snapshots:
     dependencies:
       '@types/node': 20.16.11
 
+  '@types/web-bluetooth@0.0.16': {}
+
   '@types/web-bluetooth@0.0.20': {}
 
   '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5))(eslint@9.12.0(jiti@1.21.6))(typescript@5.4.5)':
@@ -4783,8 +4882,20 @@ snapshots:
       - '@vue/composition-api'
       - vue
 
+  '@vueuse/core@9.13.0(vue@3.5.11(typescript@5.4.5))':
+    dependencies:
+      '@types/web-bluetooth': 0.0.16
+      '@vueuse/metadata': 9.13.0
+      '@vueuse/shared': 9.13.0(vue@3.5.11(typescript@5.4.5))
+      vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
   '@vueuse/metadata@11.1.0': {}
 
+  '@vueuse/metadata@9.13.0': {}
+
   '@vueuse/shared@11.1.0(vue@3.5.11(typescript@5.4.5))':
     dependencies:
       vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5))
@@ -4792,6 +4903,13 @@ snapshots:
       - '@vue/composition-api'
       - vue
 
+  '@vueuse/shared@9.13.0(vue@3.5.11(typescript@5.4.5))':
+    dependencies:
+      vue-demi: 0.14.10(vue@3.5.11(typescript@5.4.5))
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+
   '@xn-sakina/rml-darwin-arm64@2.5.0':
     optional: true
 
@@ -4911,6 +5029,8 @@ snapshots:
 
   assign-symbols@1.0.0: {}
 
+  async-validator@4.2.5: {}
+
   async@3.2.6: {}
 
   asynckit@0.4.0: {}
@@ -5418,6 +5538,8 @@ snapshots:
 
   dateformat@3.0.3: {}
 
+  dayjs@1.11.13: {}
+
   de-indent@1.0.2: {}
 
   debug@2.6.9:
@@ -5547,6 +5669,27 @@ snapshots:
 
   electron-to-chromium@1.5.35: {}
 
+  element-plus@2.8.5(vue@3.5.11(typescript@5.4.5)):
+    dependencies:
+      '@ctrl/tinycolor': 3.6.1
+      '@element-plus/icons-vue': 2.3.1(vue@3.5.11(typescript@5.4.5))
+      '@floating-ui/dom': 1.6.11
+      '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
+      '@types/lodash': 4.17.10
+      '@types/lodash-es': 4.17.12
+      '@vueuse/core': 9.13.0(vue@3.5.11(typescript@5.4.5))
+      async-validator: 4.2.5
+      dayjs: 1.11.13
+      escape-html: 1.0.3
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+      lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
+      memoize-one: 6.0.0
+      normalize-wheel-es: 1.2.0
+      vue: 3.5.11(typescript@5.4.5)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+
   emoji-regex@8.0.0: {}
 
   emoji-regex@9.2.2: {}
@@ -5669,6 +5812,8 @@ snapshots:
 
   escalade@3.2.0: {}
 
+  escape-html@1.0.3: {}
+
   escape-string-regexp@1.0.5: {}
 
   escape-string-regexp@4.0.0: {}
@@ -6487,6 +6632,14 @@ snapshots:
     dependencies:
       p-locate: 6.0.0
 
+  lodash-es@4.17.21: {}
+
+  lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
+    dependencies:
+      '@types/lodash-es': 4.17.12
+      lodash: 4.17.21
+      lodash-es: 4.17.21
+
   lodash.camelcase@4.3.0: {}
 
   lodash.ismatch@4.4.0: {}
@@ -6551,6 +6704,8 @@ snapshots:
 
   media-typer@0.3.0: {}
 
+  memoize-one@6.0.0: {}
+
   memorystream@0.3.1: {}
 
   meow@12.1.1: {}
@@ -6725,6 +6880,8 @@ snapshots:
 
   normalize-range@0.1.2: {}
 
+  normalize-wheel-es@1.2.0: {}
+
   normalize.css@8.0.1: {}
 
   npm-run-all@4.1.5:

+ 104 - 0
src/api/command/JointDuty.ts

@@ -0,0 +1,104 @@
+import request from '@/utils/request';
+import CryptoJS from 'crypto-js';
+import { nanoid } from 'nanoid';
+
+// 二维码
+export const getMapProduct = (data) => {
+  const nonce = nanoid();
+  const timestampHeader = (Date.now() / 1000).toFixed();
+  const token = 'dsfsdfsdfsf';
+  const signatureHeader = CryptoJS.SHA256(timestampHeader + token + nonce + timestampHeader).toString(CryptoJS.enc.Hex);
+  return request({
+    url: 'https://yzh.gdgov.cn/yzm-generator/generateCode',
+    method: 'POST',
+    headers: {
+      'x-yzm-nonce': nonce,
+      'x-yzm-signature': signatureHeader,
+      'x-yzm-paasid': token,
+      'x-yzm-timestamp': timestampHeader,
+      'Content-Type': 'application/json;charset=utf-8'
+    },
+    data: {
+      id: data.id,
+      name: data.name,
+      areaCode: data.areaCode,
+      ext: data.ext
+    }
+  });
+};
+
+// 获取签名列表
+export function getCheckinList(eventId: string) {
+  return request({
+    url: '/api/event_management/checkin/list',
+    method: 'get',
+    params: {
+      event_id: eventId
+    }
+  });
+}
+
+// 更新事发地点 (用于将表单数据发送到服务器以更新某个事件的位置信息)
+export function updateEventLocation(eventId: string, address: string, longitude: string, latitude: string) {
+  return request({
+    url: '/api/event_management/event/save_address',
+    method: 'post',
+    params: {
+      eventId: eventId,
+      address: address,
+      longitude: longitude,
+      latitude: latitude
+    }
+  });
+}
+
+// 新建事件
+export function addEvent(data) {
+  return request({
+    url: '/api/event_management/event/create',
+    method: 'post',
+    data: data
+  });
+}
+// 任务新增
+export function addTask(data) {
+  return request({
+    url: '/api/taskRegistration/create',
+    method: 'post',
+    data: data
+  });
+}
+// 任务查询
+export function selectTask(params) {
+  return request({
+    url: '/api/taskRegistration/select',
+    method: 'get',
+    params: params
+  });
+}
+// 任务更新
+export function updateTaskRegistration(data) {
+  return request({
+    url: '/api/taskRegistration/update',
+    method: 'put',
+    data: data
+  });
+}
+
+// 查看事业单位
+export function getUnits(params) {
+  return request({
+    url: '/api/taskRegistration/selectUnit',
+    method: 'get',
+    params: params
+  });
+}
+
+// 任务删除
+export function deleteTask(data) {
+  return request({
+    url: '/api/taskRegistration/delete',
+    method: 'post',
+    data: data
+  });
+}

TEMPAT SAMPAH
src/assets/images/command/shadow.png


TEMPAT SAMPAH
src/assets/images/selectIcon.png


+ 77 - 0
src/components/CommonSelect/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <div ref="selectRef" class="select-container">
+    <div class="common-select" @click="handleShow">{{ modelValue ? modelValue : placeholder }}</div>
+    <div v-show="showOptions" class="options-group">
+      <div v-for="item in options" :key="item[valueName]" class="option" @click="handleClick(item[valueName])">{{ item[labelName] }}</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {ref} from "vue";
+import {onClickOutside} from "@vueuse/core";
+
+defineProps({
+  modelValue: String,
+  placeholder: String,
+  options: Object,
+  labelName: {
+    type: String,
+    default: 'label'
+  },
+  valueName: {
+    type: String,
+    default: 'value'
+  }
+});
+
+const emits = defineEmits(['update:modelValue']);
+const showOptions = ref(false);
+const selectRef = ref(null)
+
+onClickOutside(selectRef, (event) => {
+  showOptions.value = false;
+})
+const handleShow = () => {
+  showOptions.value = true;
+}
+const handleClick = (value: string) => {
+  showOptions.value = false;
+  emits('update:modelValue', value);
+}
+</script>
+
+<style lang="scss" scoped>
+.select-container {
+  position: relative;
+  .common-select {
+    padding: 0 32px;
+    width: 161px;
+    height: 30px;
+    line-height: 30px;
+    background: #FFFFFF;
+    border: 1px solid #DCE0EE;
+    border-radius: 2px;
+    position: relative;
+    text-align: center;
+    cursor: pointer;
+  }
+  .options-group {
+    position: absolute;
+    top: 35px;
+    left: 0;
+    z-index: 999;
+    width: 100%;
+    border: 1px solid #DCE0EE;
+    border-radius: 2px;
+    background-color: #ffffff;
+    padding: 0 32px;
+    .option {
+      width: 100%;
+      min-height: 34px;
+      line-height: 34px;
+    }
+  }
+}
+
+</style>

+ 1 - 0
src/main.ts

@@ -1,5 +1,6 @@
 import { createApp } from "vue";
 import { store } from "./store";
+import 'element-plus/dist/index.css'
 // normalize.css
 import "normalize.css/normalize.css";
 // 全局样式

+ 9 - 9
src/router/routes.ts

@@ -39,15 +39,15 @@ const routes: Array<RouteRecordRaw> = [
           noCache: true
         }
       },
-      {
-        path: "mobile_control2",
-        name: "MobileControl2",
-        component: () => import("@/views/mobileControl2/index.vue"),
-        meta: {
-          title: "移动指挥(应急态)",
-          noCache: true
-        }
-      },
+      // {
+      //   path: "mobile_control2",
+      //   name: "MobileControl2",
+      //   component: () => import("@/views/mobileControl2/index.vue"),
+      //   meta: {
+      //     title: "移动指挥(应急态)",
+      //     noCache: true
+      //   }
+      // },
       {
         path: "address_book",
         name: "AddressBook",

+ 17 - 0
src/styles/element-ui.less

@@ -0,0 +1,17 @@
+.el-select__wrapper {
+  border-radius: 2px;
+  box-shadow: 0 0 0 1px #DCE0EE inset;
+  .el-select__caret {
+    width: 13px;
+    height: 16px;
+    background: url("@/assets/images/selectIcon.png") no-repeat;
+    background-size: 100% 100%;
+    svg {
+      display: none;
+    }
+  }
+  .el-select__caret.is-reverse {
+    -webkit-transform: none;
+    transform: none;
+  }
+}

+ 6 - 0
src/styles/index.less

@@ -1,5 +1,6 @@
 @import "./variables.less";
 @import "./vant-ui.less";
+@import "./element-ui.less";
 
 body {
   height: 100%;
@@ -16,6 +17,7 @@ body {
     sans-serif;
   color: var(--van-text-color);
   background-color: var(--color-background-2);
+  word-break: break-all;
   /* 针对 WebKit 浏览器(如 Chrome、Safari) */
   ::-webkit-scrollbar {
     display: none;
@@ -67,3 +69,7 @@ a:hover {
 .dark::view-transition-new(root) {
   z-index: 1;
 }
+
+.amap-logo, .amap-copyright {
+  display: none !important;
+}

+ 45 - 0
src/styles/vant-ui.less

@@ -38,3 +38,48 @@
 .van-button {
   border-radius: 2px;
 }
+/* vant4的 Toast 和 Popup 样式冲突,会导致 Toast 变成白底 */
+.van-popup.van-toast{
+  background: var(--van-toast-background) !important;
+  box-sizing: content-box !important;
+  /* 下面3条css,影响不大 */
+  transition: all var(--van-duration-fast) !important;
+  width: var(--van-toast-default-width) !important;
+  max-width: var(--van-toast-max-width) !important;
+}
+.van-popup.van-toast .van-toast__icon{
+  font-size: var(--van-toast-icon-size) !important;
+}
+.van-dialog {
+   border-radius: 8px !important;
+  .van-dialog__header {
+    padding: 12px 16px;
+    font-size: 14px;
+    color: #445267;
+    font-weight: bold;
+    border-bottom: 1px solid #EEEEEE;
+  }
+  .van-dialog__footer {
+    justify-content: center;
+    margin: 16px 0 24px;
+    &:after {
+      display: none;
+    }
+    .van-dialog__confirm, .van-dialog__cancel {
+      flex: unset;
+      width: 90px;
+      height: 40px;
+      border-radius: 2px;
+      font-size: 16px;
+    }
+    .van-dialog__confirm {
+      color: #FFFFFF;
+      background: #2C81FF;
+    }
+    .van-dialog__cancel {
+      border: 1px solid #D1D1D1;
+      color: #B3B3B3;
+      margin-right: 24px;
+    }
+  }
+}

+ 1 - 0
src/styles/variables.less

@@ -6,6 +6,7 @@
   --van-button-primary-border-color: #2C81FF !important;
   --van-button-danger-background: #EC2734 !important;
   --van-button-danger-border-color: #EC2734 !important;
+  //--van-popup-background: rgba(0, 0, 0, 0.7) !important;
 }
 
 html.dark {

+ 38 - 0
src/utils/index.ts

@@ -0,0 +1,38 @@
+/**
+ * This is just a simple version of deep copy
+ * Has a lot of edge cases bug
+ * If you want to use a perfect deep copy, use lodash's _.cloneDeep
+ * @param {Object} source
+ * @returns {Object}
+ */
+export const deepClone = (source: any) => {
+    if (!source && typeof source !== 'object') {
+        throw new Error('error arguments', 'deepClone' as any);
+    }
+    const targetObj: any = source.constructor === Array ? [] : {};
+    Object.keys(source).forEach((keys) => {
+        if (source[keys] && typeof source[keys] === 'object') {
+            targetObj[keys] = deepClone(source[keys]);
+        } else {
+            targetObj[keys] = source[keys];
+        }
+    });
+    return targetObj;
+};
+
+export const getRgba = (rgbaString) => {
+    // 使用正则表达式匹配rgba的各个部分
+    const match = rgbaString.match(/^rgba\(\s*(\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\s*\)$/);
+    if (!match) {
+        return rgbaString;
+    }
+
+    // 提取出红、绿、蓝和原始透明度
+    const [r, g, b, opacity] = match.slice(1).map(Number);
+
+    // 返回新的RGBA字符串,其中透明度被设置为1
+    return {
+        color: 'rgb(' + r + ',' + g + ',' + b + ')',
+        opacity: opacity
+    }
+}

+ 11 - 6
src/views/event/detail.vue

@@ -23,7 +23,9 @@
                 <i class="icon1" />
                 <div class="event-data-item-title">事件类型:</div>
               </div>
-              <div class="event-data-item-value"><dict-tag :options="mm_event_type" :value="eventInfo.event_type"></dict-tag></div>
+              <div class="event-data-item-value">
+                <dict-tag :options="mm_event_type" :value="eventInfo.event_type"></dict-tag>
+              </div>
             </div>
             <div class="event-data-item">
               <div class="item-left">
@@ -127,6 +129,7 @@
 
     <div class="footer">
       <div style="display: flex; flex-direction: row; justify-content: space-between;">
+        <van-button v-if="eventInfo.event_status == '0'" type="primary" @click="handleEnterEvent">开始指挥</van-button>
         <van-button v-if="eventInfo.event_status == '1'" type="primary" @click="handleEnterEvent">进入指挥</van-button>
         <van-button v-if="eventInfo.event_status == '2'" type="danger"  @click="handleCloseEvent">关闭事件</van-button>
       </div>
@@ -188,7 +191,12 @@ const data = reactive({
 });
 const { eventInfo, eventTrackState } = toRefs(data);
 const handleEnterEvent = () => {
-    router.push("/mobile_control2/index?event_id="+eventId.value);
+  router.push({
+    path: '/leader/mobile_control',
+    query: {
+      event_id: eventId.value
+    }
+  });
 };
 
 // 关闭事件
@@ -244,7 +252,7 @@ onMounted(()=>{
   refreshData();
 })
 let hideMap = ref(false);
-let map
+let map;
 const refreshData = () => {
     eventId.value = route.query.event_id as string;
     getEventDetail({event_id: eventId.value}).then((res) => {
@@ -262,8 +270,6 @@ const refreshData = () => {
           center: lnglat,
           dragEnable: true,
           scrollWheel: true,
-          showScale: true,
-          enableMouseTool: true,
           // 加载完成事件
           onLoadCompleted: (AMap) => {
             map = getMap();
@@ -289,7 +295,6 @@ const refreshData = () => {
 .event_map {
     width: 100%;
     height: 170px;
-    background: #ccffcc;
 }
 .box {
   margin: 0 16px 16px;

+ 21 - 11
src/views/event/index.vue

@@ -32,7 +32,7 @@
             <van-button v-if="item.event_status == '0'" type="primary" @click="handleStartEvent(index)">
               开始指挥
             </van-button>
-            <van-button v-if="item.event_status == '1'" type="danger" size="small" @click="handleCloseEvent(index)">
+            <van-button v-if="item.event_status == '1'" type="danger" size="small" @click="handleCloseEvent(item)">
               结束指挥
             </van-button>
           </div>
@@ -92,11 +92,11 @@
 <script lang="ts" setup>
 import {getCurrentInstance, reactive, ref, toRefs, onMounted} from 'vue';
 import {useRouter} from 'vue-router'
-import {showConfirmDialog} from 'vant';
+import {showConfirmDialog, showSuccessToast} from 'vant';
 import StartEventDialog from './StartEventDialog.vue';
 import CloseEventDialog from './CloseEventDialog.vue';
 
-import {getEventList} from "@/api/event";
+import {closeEvent, getEventList} from "@/api/event";
 import searchImg from "@/assets/images/search.png";
 import closeImg from "@/assets/images/close.png";
 
@@ -271,14 +271,24 @@ const handleStartEvent = (index) => {
   return false;
 }
 
-const handleCloseEvent = (index) => {
-  current_item.value = event_list.value[index];
-  console.log('handleStartEvent', current_item.value.event_id);
-  closeEventState.form.event_id = current_item.value.event_id;
-  closeEventState.form.event_title = current_item.value.event_title;
-  closeEventState.form.event_level = current_item.value.event_level;
-  closeEventState.show = true;
-  return false;
+const handleCloseEvent = (item) => {
+  showConfirmDialog({
+    title: '结束指挥',
+    message:
+        '是否确定结束指挥?',
+  }).then(() => {
+    const params = {
+      eventId: item.value.event_id,
+      address: item.value.address,
+      latitude: item.value.latitude,
+      longitude: item.value.longitude
+    };
+    // 如果 flag 为 true,则直接调用 closeEvent 接口关闭事件
+    closeEvent(params).then(() => {
+      showSuccessToast('结束指挥成功');
+      router.push('/leader/index')
+    })
+  })
 }
 
 

+ 1 - 1
src/views/index.vue

@@ -46,7 +46,7 @@ const handleJump = (path: string) => {
   }
   .icon1 {
     width: 100px;
-    height: 75px;
+    height: 100px;
     background: url(@/assets/images/staffIcon.png) no-repeat;
     background-size: 100% 100%;
   }

+ 74 - 0
src/views/mobileControl/EventBox.vue

@@ -0,0 +1,74 @@
+<template>
+  <div class="bottom-content">
+    <div class="tabs">
+      <div class="tabs-content">
+        <div :class="tabActive === '0' ? 'tab tab-active' : 'tab'" @click="handleClickTab('0')">事件信息</div>
+        <div :class="tabActive === '1' ? 'tab tab-active' : 'tab'" @click="handleClickTab('1')">任务指令</div>
+      </div>
+      <EventInfo v-show="tabActive === '0'" :eventId="eventId" />
+      <TaskCommand v-show="tabActive === '1'" :eventId="eventId" />
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import EventInfo from './EventInfo.vue'
+import {ref} from "vue";
+import TaskCommand from "@/views/mobileControl/TaskCommand.vue";
+
+defineProps({
+  eventId: String
+});
+// 底部信息栏
+let tabActive = ref('0');
+
+const handleClickTab = (index) => {
+  tabActive.value = index;
+};
+
+</script>
+
+<style lang="scss" scoped>
+.bottom-content {
+  width: 100%;
+  height: 304px;
+  background-color: #fff;
+  border: 0.5px solid #EAEDF7;
+  box-shadow: 0 0 4px 0 #4554661a;
+  border-top-left-radius: 4px;
+  border-top-right-radius: 4px;
+  .tabs {
+    width: 100%;
+    height: 41px;
+    background-image: linear-gradient(180deg, #F3F7FD 0%, #FFFFFF 100%);
+    border-radius: 2px 2px 0 0;
+    //padding: 0 13px;
+    .tabs-content {
+      width: 100%;
+      border-bottom: 1px solid #F6F6F6;
+      display: flex;
+    }
+    .tab {
+      flex: 1;
+      height: 41px;
+      line-height: 41px;
+      text-align: center;
+      color: #414f64;
+    }
+    .tab-active {
+      position: relative;
+      color: #2c81ff;
+      &::before {
+        content: '';
+        position: absolute;
+        bottom: -1px;
+        left: 50%;
+        transform: translateX(-50%);
+        width: 70px;
+        height: 2px;
+        background: #2C81FF;
+      }
+    }
+  }
+}
+</style>

+ 182 - 0
src/views/mobileControl/EventInfo.vue

@@ -0,0 +1,182 @@
+<template>
+    <div class="event-info">
+        <div class="info-title">
+            <div class="info-title-text">
+                <div class="info-title-text-label">事件标题:</div>
+                <div class="info-title-text-value">{{eventDetails.event_title}}</div>
+            </div>
+            <div class="info-title-control">
+                <van-button v-if="eventDetails.event_status == '0'" type="primary" class="btn1" @click="handleStartEvent">开始指挥</van-button>
+                <van-button v-if="eventDetails.event_status == '1'" type="danger" class="btn2" @click="handleCloseEvent">结束指挥</van-button>
+            </div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">持续时长:</div>
+            <div class="info-data-value">{{eventDetails.keep_time}}</div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">事件类型:</div>
+            <div class="info-data-value">
+              <dict-tag :options="mm_event_type" :value="eventDetails.event_type"></dict-tag>
+            </div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">事件等级:</div>
+            <div class="info-data-value">
+              <dict-tag :options="mm_event_level" :value="eventDetails.event_level"></dict-tag>
+            </div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">事发地点:</div>
+            <div class="info-data-value">{{eventDetails.address}}</div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">事件状态:</div>
+            <div class="info-data-value">
+              <dict-tag :options="mm_event_state" :value="eventDetails.event_status"></dict-tag>
+            </div>
+        </div>
+        <div class="info-data">
+            <div class="info-data-label">事发时间:</div>
+            <div class="info-data-value">{{eventDetails.event_time}}</div>
+        </div>
+       <div class="info-data">
+         <div class="info-data-label">事发来源:</div>
+         <div class="info-data-value">{{eventDetails.event_source}}</div>
+       </div>
+    </div>
+</template>
+
+<script lang="ts" setup name="EventInfo">
+import {closeEvent, getEventDetail} from "@/api/event";
+import {reactive, ref, toRefs, watch} from "vue";
+import {useDict} from "@/utils/dict";
+import {useRouter} from "vue-router";
+import {showConfirmDialog, showSuccessToast} from "vant";
+
+const props = defineProps({
+  eventId: String
+});
+const router = useRouter();
+const { mm_event_type, mm_event_level, mm_event_state } = toRefs<any>(useDict('mm_event_type', 'mm_event_level', 'mm_event_state'));
+let eventDetails = ref({
+  event_id: '',
+  event_title: '',
+  event_status: '',
+  keep_time: '',
+  event_type: '',
+  event_level: '',
+  address: '',
+  event_time: '',
+  event_source: '',
+  latitude: '',
+  longitude: ''
+});
+
+const getDataList = () => {
+  if (!!props.eventId) {
+    getEventDetail({event_id: props.eventId}).then((res) => {
+      eventDetails.value = res.data
+    })
+  }
+};
+
+watch(() => props.eventId, () => {
+  getDataList();
+}, {
+  immediate: true
+})
+
+const handleStartEvent = () => {
+
+}
+
+const handleCloseEvent = () => {
+  showConfirmDialog({
+    title: '结束指挥',
+    message:
+        '是否确定结束指挥?',
+  }).then(() => {
+    const params = {
+      eventId: eventDetails.value.event_id,
+      address: eventDetails.value.address,
+      latitude: eventDetails.value.latitude,
+      longitude: eventDetails.value.longitude
+    };
+    // 如果 flag 为 true,则直接调用 closeEvent 接口关闭事件
+    closeEvent(params).then(() => {
+      showSuccessToast('结束指挥成功');
+      router.push('/leader/index')
+    })
+  })
+}
+</script>
+
+
+<style lang="scss" scoped>
+.event-info {
+    position: relative;
+    margin: 0px;
+    padding: 16px;
+    overflow-y: auto;
+    height: 260px;
+    .info-title {
+        line-height: 28px;
+        display: flex;
+        flex-direction: row;
+        justify-content: space-between;
+        font-size: 14px;
+        color: #414F64;
+        .info-title-text-label {
+          font-weight: bold;
+          flex-shrink: 0;
+        }
+        .info-title-text {
+
+            display: flex;
+            flex-direction: row;
+            justify-content: start;
+        }
+
+        .info-title-control {
+            display: inline-flex;
+            justify-content: center;
+            align-items: center;
+        }
+    }
+
+    .info-data {
+        line-height: 28px;
+        display: flex;
+        flex-direction: row;
+        justify-content: start;
+        font-size: 14px;
+        color: #414F64;
+        .info-data-label {
+          flex-shrink: 0;
+          font-weight: bold;
+        }
+
+    }
+}
+.btn1 {
+  min-width: 73px;
+  height: 24px;
+  background: #2c81ff;
+  border-radius: 2px;
+  padding: 0 11px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.btn2 {
+  min-width: 73px;
+  height: 24px;
+  background: #EC2734;
+  border-radius: 2px;
+  padding: 0 11px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 32 - 0
src/views/mobileControl/SearchBtn.vue

@@ -0,0 +1,32 @@
+<template>
+  <div class="search-container">
+    <div class="search-button" @click="handleShowSearch" />
+    <van-popup v-model:show="showSearch" position="top" style="height: 70%">
+
+    </van-popup>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {ref} from "vue";
+
+const showSearch = ref(false);
+
+const handleShowSearch = () => {
+  showSearch.value = true;
+}
+</script>
+
+<style lang="scss" scoped>
+.search-button {
+  background: url('@/assets/images/command/search.png') no-repeat;
+  width: 24px;
+  height: 24px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-size: 100% 100%;
+  cursor: pointer;
+  margin-bottom: 10px;
+}
+</style>

+ 262 - 0
src/views/mobileControl/TaskCommand.vue

@@ -0,0 +1,262 @@
+<template>
+  <div class="list-container">
+    <van-list
+        v-model:loading="loading"
+        v-model:error="error"
+        error-text="请求失败,点击重新加载"
+        :finished="finished"
+        finished-text="没有更多了"
+        @load="getList">
+
+      <div class="task-list">
+        <div class="task-item" v-for="item in taskList" :key="item.id">
+          <div class="task-title-box">
+            <div class="task-title">{{ item.unit_name }}</div>
+          </div>
+          <div class="task-header">{{ item.processing_status }}</div>
+          <div class="task-item-content">
+            <van-text-ellipsis :content="item.task_description" rows="3" expand-text="展开" collapse-text="收起"/>
+          </div>
+          <div class="task-item-bottom">
+            <div class="task-item-time">{{ item.creation_time }}</div>
+            <van-button v-if="item.processing_status !== '已完成'" type="primary" class="update-btn" @click="showUpdate(item)">更新</van-button>
+          </div>
+        </div>
+      </div>
+    </van-list>
+    <van-dialog v-model:show="show" title="任务进度更新" show-cancel-button @confirm="handleUpdate">
+      <div class="box-item">
+        <div class="item-header">
+          <i class="icon" />
+          <div class="label">任务描述:</div>
+        </div>
+        <div class="item-data">{{ updateData.task_description }}</div>
+      </div>
+      <div class="box-item" style="padding-bottom: 0">
+        <div class="item-header" style="padding-bottom: 0">
+          <i class="icon" />
+          <div class="label">完成进度:</div>
+          <el-select v-model="updateData.processing_status">
+            <el-option
+                v-for="item in options"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+            />
+          </el-select>
+        </div>
+      </div>
+    </van-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ElSelect, ElOption } from 'element-plus';
+import {ref, reactive, watch} from 'vue';
+import {selectTask, updateTaskRegistration} from "@/api/command/JointDuty";
+import {parseTime} from "@/utils/ruoyi";
+import {showSuccessToast} from "vant";
+
+const props = defineProps({
+  eventId: String
+});
+
+const queryParams = reactive({
+  page: 1,
+  page_size: 10,
+  event_code: ''
+});
+const taskList = ref([]);
+const total = ref(0);
+const loading = ref(false);
+const error = ref(false);
+const finished = ref(false);
+const show = ref(false)
+const options = reactive([
+  { label: '处理中', value: '处理中' },
+  { label: '已完成', value: '已完成' },
+]);
+let updateData = ref({
+  task_id: '',
+  task_description: '',
+  processing_status: ''
+});
+
+const getList = () => {
+  queryParams.page++;
+  selectTask(queryParams).then((res) => {
+    const items = res.data || [];
+    items.forEach(item => {
+      item.creation_time = parseTime(item.creation_time);
+    })
+    total.value = res.total;
+    if (queryParams.page == 1) {
+      taskList.value = [];
+    }
+    items.forEach(val => {
+      taskList.value.push(val)
+    });
+    if (queryParams.page_size * queryParams.page >= total.value) {
+      finished.value = true;
+    }
+  }).catch(() => {
+    error.value = true;
+    finished.value = false;
+  }).finally(() => {
+    loading.value = false;
+  });
+}
+watch(() => props.eventId, () => {
+  queryParams.event_code = props.eventId;
+  getList()
+}, {
+  immediate: true
+})
+const showUpdate = (item) => {
+  updateData.value = item;
+  show.value = true
+}
+const handleUpdate = () => {
+  updateTaskRegistration({
+    task_id: updateData.value.task_id,
+    processing_status: updateData.value.processing_status,
+    event_code: props.eventId
+  }).then(() => {
+    showSuccessToast('更新成功');
+    queryParams.page = 0;
+    getList();
+  })
+}
+</script>
+
+<style lang="scss" scoped>
+.list-container {
+  overflow-y: auto;
+  height: 260px;
+  background-color: #f4f8fa;
+}
+.task-list {
+  position: relative;
+  margin: 0;
+  .task-item {
+    margin-top: 21px;
+    border-bottom: 1px solid #EEEEEE;
+    position: relative;
+    background-color: #ffffff;
+    box-shadow: 0 0 4px 0 rgba(69, 84, 102, 0.1);
+    border-radius: 4px;
+    padding-bottom: 16px;
+    .task-title-box {
+      position: absolute;
+      top: -5px;
+      left: 19px;
+      height: 30px;
+      background-color: #ffffff; /* 背景颜色 */
+      transform: skewX(-20deg); /* 斜切变形 */
+      box-shadow:0 2px 4px 0 rgba(44, 129, 255, 0.18);
+      padding: 0 20px;
+      display: flex;
+      align-items: center;
+      border-radius: 2px;
+      &::after {
+        content: '';
+        position: absolute;
+        top: 0;
+        right: -6px;
+        transform: skewX(20deg);
+        width: 11px;
+        height: 27px;
+        background: url('@/assets/images/command/shadow.png') no-repeat;
+        background-size: 100% 100%;
+      }
+      .task-title {
+        font-size: 12px;
+        color: #2C81FF;
+        transform: skewX(20deg);
+      }
+    }
+    .task-header {
+      width: 100%;
+      height: 30px;
+      background-image: linear-gradient(180deg, #F3F7FD 0%, #FFFFFF 100%);
+      border-radius: 2px 2px 0 0;
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      font-size: 12px;
+      color: #A2A2A2;
+      padding: 0 19px;
+    }
+    .task-item-content {
+      color: #414F64;
+      font-size: 14px;
+      margin: 12px 19px;
+    }
+
+    .task-item-bottom {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin: 0 19px;
+      .task-item-time {
+        font-size: 14px;
+        color: rgba(0, 0, 0, 0.45);
+      }
+      .update-btn {
+        width: 86px;
+        height: 32px;
+        line-height: 32px;
+        background: #2C81FF;
+        border-radius: 2px;
+      }
+    }
+  }
+}
+.success-bg,
+.processing-bg,
+.btn {
+  width: 64px;
+  height: 24px;
+  line-height: 24px;
+  border-radius: 2px;
+  font-size: 12px;
+  color: #ffffff;
+  text-align: center;
+  margin-left: 10px;
+}
+.success-bg {
+  background-color: #38c95a;
+}
+.processing-bg {
+  background-color: #ffaf00;
+}
+.box-item {
+  padding: 8px 16px;
+  .item-header {
+    display: flex;
+    align-items: center;
+    padding: 8px 0;
+    .icon {
+      flex-shrink: 0;
+      display: inline-block;
+      width: 17px;
+      height: 16px;
+      background: url('@/assets/images/event/icon1.png') no-repeat;
+      background-size: 100% 100%;
+      margin-right: 8px;
+    }
+    .label {
+      flex-shrink: 0;
+      font-size: 14px;
+      color: #445267;
+      font-weight: bold;
+    }
+  }
+  .item-data {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.65);
+    max-height: 200px;
+    overflow-y: auto;
+  }
+}
+</style>

+ 63 - 24
src/views/mobileControl/index.vue

@@ -2,19 +2,25 @@
   <div class="container">
     <div class="top-content">
       <!--    <YztMap v-if="['satellite2', 'satellite3'].includes(activeMap)" ref="map2Ref" :active-map="activeMap" :point-type="pointType" />-->
-      <Map ref="mapRef" :active-map="activeMap" :point-type="pointType" :class="showMenu ? 'containerHeight1' : 'containerHeight2'"/>
-      <div class="top-left-panel">
-        <div class="search-button button1" />
-        <div class="add-button button1 " />
-        <div class="reduce-button button1" />
-        <div class="fullScreen-button button1" />
-        <div class="layer-button button1" />
+      <Map ref="mapRef" :active-map="activeMap" :point-type="pointType" :class="showMenu && !!eventId && !fullscreen ? 'containerHeight1' : 'containerHeight2'"/>
+      <div v-show="!fullscreen" class="top-left-panel">
       </div>
-      <div class="bottom-left-panel">
+      <div class="top-right-panel">
+        <SearchBtn v-show="!fullscreen" />
+        <div v-show="!fullscreen" class="add-button button1" @click="handleAdd" />
+        <div v-show="!fullscreen" class="reduce-button button1" @click="handleReduce" />
+        <div class="fullScreen-button button1" @click="handleFullscreen" />
+        <div v-show="!fullscreen" class="layer-button button1" />
+      </div>
+      <div v-show="!fullscreen" class="bottom-left-panel">
         <div class="button2" @click="toIndex">
           <i class="back-btn" />
           <div>首页</div>
         </div>
+        <div v-show="!eventId" class="button2">
+          <i class="material-btn" />
+          <div>指挥作战</div>
+        </div>
         <div class="button2">
           <i class="material-btn" />
           <div>物资</div>
@@ -28,19 +34,26 @@
           <div>协同标绘</div>
         </div>
       </div>
-      <i :class="showMenu ? 'arrow-icon' : 'arrow-icon turn'" @click="showMenu = !showMenu" />
+      <i v-if="!!eventId" v-show="!fullscreen" :class="showMenu ? 'arrow-icon' : 'arrow-icon turn'" @click="showMenu = !showMenu" />
     </div>
-    <div v-show="showMenu" class="bottom-content">
+
+    <div v-if="!!eventId" v-show="showMenu && !fullscreen">
+      <EventBox :eventId="eventId" />
     </div>
   </div>
 
 </template>
 
 <script lang="ts" setup>
-import {ref} from "vue";
-import {useRouter} from "vue-router";
+import {onMounted, ref} from "vue";
+import {useRoute, useRouter} from "vue-router";
+import EventBox from "./EventBox.vue";
+import SearchBtn from "./SearchBtn.vue";
 
 const router = useRouter();
+const route = useRoute();
+const eventId = ref('');
+let fullscreen = ref(false);
 let showMenu = ref(true);
 let mapRef = ref(null);
 let map2Ref = ref(null);
@@ -48,9 +61,41 @@ let map2Ref = ref(null);
 let activeMap = ref('vectorgraph');
 let pointType = ref([]);
 
+
 const toIndex = () => {
   router.push('/leader/index');
+};
+
+// 地图增加一级
+const handleAdd = () => {
+  let map = getMap();
+  if (!!map) {
+    map.setZoom(map.getZoom() + 1);
+  }
+}
+// 地图减小一级
+const handleReduce = () => {
+  let map = getMap();
+  if (!!map) {
+    map.setZoom(map.getZoom() - 1);
+  }
+}
+// 全屏
+const handleFullscreen = () => {
+  fullscreen.value = !fullscreen.value;
 }
+const getMap = () => {
+  let map;
+  if (['satellite2', 'satellite3'].includes(activeMap.value)) {
+
+  } else {
+    map = mapRef.value.getMap();
+  }
+  return map;
+}
+onMounted(() => {
+  eventId.value = route.query.event_id as string;
+})
 </script>
 
 <style lang="scss" scoped>
@@ -73,25 +118,19 @@ const toIndex = () => {
       transform: translateX(-50%) rotateX(180deg);
     }
   }
-  .bottom-content {
-    height: 304px;
-    background-color: #fff;
-    border: 0.5px solid #EAEDF7;
-    box-shadow: 0 0 4px 0 #4554661a;
-    border-top-left-radius: 4px;
-    border-top-right-radius: 4px;
-  }
 }
 
 .top-left-panel {
+  position: absolute;
+  top: 16px;
+  left: 16px;
+}
+.top-right-panel {
   display: flex;
   flex-direction: column;
   position: absolute;
   right: 16px;
   top: 16px;
-  .search-button {
-    background: url('@/assets/images/command/search.png') no-repeat;
-  }
   .add-button {
     background: url('@/assets/images/command/add.png') no-repeat;
   }
@@ -134,7 +173,7 @@ const toIndex = () => {
     background: #2C81FF;
     border-radius: 2px;
     display: flex;
-    justify-content: center;
+    padding: 0 11px;
     align-items: center;
     font-size: 12px;
     color: #ffffff;

+ 0 - 107
src/views/mobileControl2/EventInfo.vue

@@ -1,107 +0,0 @@
-<template>
-    <div class="event-info">
-        <div class="info-title">
-            <div class="info-title-text">
-                <div class="info-title-text-label">事件标题:</div>
-                <div class="info-title-text-value">{{info.event_title}}</div>
-            </div>
-            <div class="info-title-control">                    
-                <van-button v-if="info.event_status == '0'" type="primary" size="small" @click="handleStartEvent">开始指挥</van-button>
-                <van-button v-if="info.event_status == '1'" type="danger" size="small" @click="handleCloseEvent">结束指挥</van-button>
-            </div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">持续时长:</div>
-            <div class="info-data-value">{{info.keep_time}}</div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">事件类型:</div>
-            <div class="info-data-value">{{info.event_type}}</div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">事件等级:</div>
-            <div class="info-data-value">{{info.event_level}}</div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">事发地点:</div>
-            <div class="info-data-value">{{info.address}}</div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">事件状态:</div>
-            <div class="info-data-value">{{info.event_status}}</div>
-        </div>
-        <div class="info-data">
-            <div class="info-data-label">事发时间:</div>
-            <div class="info-data-value">{{info.event_time}}</div>
-        </div>
-       <div class="info-data">
-            <div class="info-data-label">事件来源:</div>
-            <div class="info-data-value">{{info.event_source}}</div>
-        </div>
-    </div>
-</template>
-
-<script lang="ts" setup>
-const info = {
-    event_title: 'xxxx应急指挥事件',
-    keep_time: '1天12小时14分20秒',
-    event_status: '0',
-    event_type: '0',
-    event_level: '0',
-    event_time: '1090-09-09 10:10',
-    address: 'zzz',
-    event_source: '110'
-};
-
-const handleStartEvent = () => {
-    
-}
-
-const handleCloseEvent = () => {
-
-}
-
-</script>
-
-
-<style lang="scss" scoped>
-.event-info {
-    position: relative;
-    margin: 0px;
-    padding: 16px;
-    .info-title {
-        line-height: 28px;
-        display: flex;
-        flex-direction: row;
-        justify-content: space-between;
-
-        .info-title-text {
-            color: #888;
-            display: flex;
-            flex-direction: row;
-            justify-content: start;
-        }
-
-        .info-title-control {
-            display: inline-flex;
-            justify-content: center;
-            align-items: center;
-        }
-    }
-
-    .info-data {
-        line-height: 28px;
-        display: flex;
-        flex-direction: row;
-        justify-content: start;
-
-        .info-data-label {
-            color: #888;
-        }
-
-        .info-data-value {
-            color: #888;
-        }
-    }
-}
-</style>

+ 0 - 53
src/views/mobileControl2/InstructionDialog.vue

@@ -1,53 +0,0 @@
-<template>
-    <div class="instruction_dialog" >
-        <div class="header">领导批示</div>
-        <van-form @submit="onSubmit">
-            <van-field
-            v-model="form.content"
-            name="用户名"
-            type="textarea"
-            rows="5"
-            maxlength="50"
-            placeholder="请填写内容"
-            :rules="[{ required: true, message: '请填写内容' }]"
-            />
-            <div class="action-bar">
-                <van-button type="primary" native-type="submit" size="small">
-                确定
-                </van-button>
-                <van-button size="small">
-                取消
-                </van-button>
-            </div>
-        </van-form>
-    </div>
-</template>
-
-
-<script lang="ts" setup>
-import { ref, reactive } from 'vue';
-const form = {
-    content: ''
-}
-const onSubmit = () => {
-
-}
-
-</script>
-
-<style lang="scss" scoped>
-.instruction_dialog {
-    position: relative;
-
-    .header {
-        position: relative;
-    }
-}
-
-.action-bar {
-    padding: 16px;
-    display: flex;
-            flex-direction: row;
-            justify-content: space-between;
-}
-</style>

+ 0 - 166
src/views/mobileControl2/TaskCommand.vue

@@ -1,166 +0,0 @@
-<template>
-    <van-list
-        v-model:loading="loading"
-        :finished="finished"
-        finished-text="没有更多了"
-        @load="onLoad">
-
-        <div class="task-list">
-            <div class="task-item" v-for="item in task_list" :key="item.id">
-                <div class="task-item-header">
-                    <div class="task-item-header-text">
-                        <div class="task-item-header-username">{{item.username}}</div>
-                        <div class="task-item-header-time">{{item.time}}</div>
-                    </div>
-                    <div class="task-item-header-process">
-                        {{item.process}}
-                    </div>
-                </div>
-                <div class="task-item-content">
-                    <van-text-ellipsis :content="item.content" rows="3" expand-text="展开" collapse-text="收起"/>
-                </div>
-                <div class="task-item-bottom">
-                    <van-button type="primary" size="small" @click="showInstruction">领导批示</van-button>
-                </div>
-                <van-steps direction="vertical" :active="-1">
-                    <van-step v-for="reply in item.replys" :key="reply.id">
-                        <h3>{{reply.name}}{{reply.time}}</h3>
-                        <p>{{reply.content}}</p>
-                        <p>附件:{{reply.attachment}}</p>
-                    </van-step>
-                </van-steps>
-            </div>
-        </div>
-    </van-list>
-    <van-popup v-model:show="instruction_show" :style="{ padding: '64px' }">
-        <InstructionDialog @closeDialog="instructionDialogClose" ></InstructionDialog>
-
-    </van-popup>
-</template>
-
-<script lang="ts" setup>
-import { ref, reactive } from 'vue';
-import InstructionDialog from './InstructionDialog.vue';
-
-const task_list = ref([]);
-const loading = ref(false);
-const finished = ref(false);
-const refreshing = ref(false);
-const instruction_show = ref(false)
-
-const onLoad = () => {
-    getList();
-}
-
-const getList = () => {
-    if (refreshing.value) {
-        task_list.value = [];
-        refreshing.value = false;
-    }
-
-    // ....
-    loading.value = false;
-
-    task_list.value.push({
-        id: 1,
-        username: '执行人名字',
-        time: '2024-7-10 08:09:00',
-        content: '这是一段很长的任务描述这是一段很长的任务描述这是一段很长的任务描述这是一段很长的任务描述这是一段很长的任的任务描述这是一段很长的任务描述',
-        process: "20",
-        replys: [
-            {
-                id: 1,
-                name: '任务反馈',
-                time: '2024-7-10  10:15:09',
-                content: '这是一段很长的领导批示这是一段很长的领导批示这是一段很长的领导批示这是一段很长的领导批示',
-                attachment: 'xxx.png'
-            },
-            {
-                id: 1,
-                name: '任务反馈',
-                time: '2024-7-10  10:15:09',
-                content: '这是一段很长的领导批示这是一段很长的领导批示这是一段很长的领导批示这是一段很长的领导批示',
-                attachment: 'xxx.png'
-            }
-        ]
-    })
-
-    task_list.value.push({
-        id: 2,
-        username: '执行人名字',
-        time: '2024-7-10 08:09:00',
-        content: '1',
-        process: "20",
-    })
-
-    task_list.value.push({
-        id: 3,
-        username: '执行人名字',
-        time: '2024-7-10 08:09:00',
-        content: '1',
-        process: "20",
-    })
-
-    if (task_list.value.length >= 1) {
-        finished.value = true;
-    }
-
-}
-
-const showInstruction = () => {
-    instruction_show.value = true
-}
-
-const instructionDialogClose = () => {
-    instruction_show.value = false
-}
-</script>
-
-<style lang="scss" scoped>
-.task-list {
-    position: relative;
-    margin: 0px;
-    padding: 16px;
-
-    .task-item {
-        .task-item-header {
-            display: flex;
-            flex-direction: row;
-            justify-content: space-between;
-            
-            
-            .task-item-header-text {
-                display: flex;
-                flex-direction: row;
-                justify-content: start;
-
-                
-                
-                .task-item-header-username {
-                    color: #888;
-                    background: #aaa;
-                    padding-left: 4px;
-                    padding-right: 4px;
-                }
-                .task-item-header-time {
-                    color: #888;
-                }
-            }
-            .task-item-header-process {
-
-            }
-        }
-
-        .task-item-content {
-            color: #888;
-            background-color: #ececec;
-        }
-
-        .task-item-bottom {
-            display: flex;
-            flex-direction: row;
-            justify-content: end;
-        }
-    }
-}
-</style>

+ 0 - 119
src/views/mobileControl2/index.vue

@@ -1,119 +0,0 @@
-<template>
-    <div class="top-left-panel">
-        <div>搜索</div>
-        <div>放大</div>
-        <div>缩小</div>
-        <div>全屏</div>
-    </div>
-
-    <div class="bottom-left-panel">
-        <router-link to="/">
-            <div>首页</div>
-        </router-link>
-        <div>物资</div>
-        <router-link to="/event/index">
-            <div>事件</div>
-        </router-link>
-        <div>协同<br/>标绘</div>
-    </div>
-
-    <div class="popup">
-        <!-- 弹出层 -->
-        <transition name="slide">
-            <div class="popup-content" :class="['popup-header', popup_visible ? 'popup-header-show_1': 'popup-content-hide']" >
-                <div class="popup-header">
-                    <van-icon v-if="popup_visible" name="play" @click="toggerPopup"/>
-                    <van-icon v-if="!popup_visible" name="pause" @click="toggerPopup"/>
-                </div>
-                <div class="popup-data">
-                    <van-tabs v-model:active="active_tab">
-                        <van-tab title="事件信息">
-                            <EventInfo></EventInfo>
-                        </van-tab>
-                        <van-tab title="任务指令">
-                            <TaskCommand></TaskCommand>
-                        </van-tab>
-                    </van-tabs>
-                </div>
-            </div>
-        </transition>
-    </div>
-</template>
-
-<script lang="ts" setup>
-import { ref, reactive } from 'vue';
-import EventInfo from './EventInfo.vue';
-import TaskCommand from './TaskCommand.vue';
-
-const popup_visible = ref(false);
-const active_tab = ref(0);
-
-const toggerPopup = () => {
-    console.log("toggerPopup");
-    popup_visible.value = !popup_visible.value;
-    if(popup_visible.value) {
-
-    }
-    else {
-
-    }
-}
-</script>
-
-<style lang="scss" scoped>
-.top-left-panel {
-    display: flex;
-    flex-direction: column;
-    position: fixed;
-    right: 10px;
-    top: 10px;
-
-    div {
-        margin-bottom:10px;
-    }
-}
-
-.bottom-left-panel {
-    display: flex;
-    flex-direction: column;
-    position: fixed;
-    left: 10px;
-    bottom: 60px;
-
-    div {
-        margin-bottom:10px;
-    }
-}
-
-.popup {
-  .popup-content {
-    width: 100%;
-    position: fixed;
-    bottom: 0;
-    left: 0;
-    transition: transform .3s;
-
-    .popup-header {
-        width: 100vw;
-        display: inline-flex;
-        justify-content: center;
-        align-items: center;
-    }
-    .popup-data {
-        height:540px;
-        background: #fff;
-    }
-  }
-
-  .popup-content-hide {
-    height:70px;
-  }
-
-  .popup-content-show_1 {
-    height:590px;
-  }
-  .popup-content-show_2 {
-    height:590px;
-  }
-}
-</style>

+ 5 - 0
vite.config.ts

@@ -68,6 +68,11 @@ export default defineConfig(({ mode }) => {
       //   }
       // }
     },
+    css: {
+      preprocessorOptions: {
+        scss: { api: 'modern-compiler' },
+      }
+    },
     build: {
       rollupOptions: {
         output: {